num_display.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. #!/usr/bin/env python3
  2. import argparse
  3. import random
  4. import gi
  5. gi.require_version("Gtk", "3.0")
  6. gi.require_version("PangoCairo", "1.0")
  7. gi.require_version("Gdk", "3.0")
  8. gi.require_version("GLib", "2.0")
  9. from gi.repository import Gtk, Gio, cairo, Pango, Gdk, PangoCairo, GLib
  10. def get_parser():
  11. p = argparse.ArgumentParser()
  12. p.add_argument(
  13. "--read-only",
  14. action="store_true",
  15. dest="readonly",
  16. help="Display but dont change the number",
  17. )
  18. p.add_argument("--fullscreen", action="store_true", default=True, dest="fullscreen")
  19. p.add_argument(
  20. "--no-fullscreen", action="store_false", default=True, dest="fullscreen"
  21. )
  22. p.add_argument("--background", default="#111")
  23. p.add_argument("--foreground", default="#eee")
  24. p.add_argument(
  25. "--invert-every",
  26. type=float,
  27. metavar="SECONDS",
  28. default=60,
  29. help=(
  30. "Swap foreground and background periodically; "
  31. "this is useful to avoid CRT-burning; use <= 0 to disable"
  32. ),
  33. )
  34. return p
  35. class App(Gtk.Application):
  36. def __init__(self, cli_args, *args, **kwargs):
  37. super().__init__(
  38. *args,
  39. application_id="org.hackmeeting.numeretti",
  40. flags=Gio.ApplicationFlags.FLAGS_NONE,
  41. **kwargs
  42. )
  43. self.window = None
  44. self.cli_args = cli_args
  45. def do_startup(self):
  46. Gtk.Application.do_startup(self)
  47. action = Gio.SimpleAction.new("quit", None)
  48. action.connect("activate", self.on_quit)
  49. self.add_action(action)
  50. # self.set_app_menu(…)
  51. def do_activate(self):
  52. # We only allow a single window and raise any existing ones
  53. if not self.window:
  54. # Windows are associated with the application
  55. # when the last one is closed the application shuts down
  56. self.window = AppWindow(application=self, title="Main Window")
  57. self.window.present()
  58. def on_quit(self, action, param):
  59. self.quit()
  60. def get_color(description: str) -> Gdk.RGBA:
  61. rgba = Gdk.RGBA()
  62. ret = rgba.parse(description)
  63. if ret is False:
  64. raise ValueError("Error parsing color! %s" % description)
  65. return rgba
  66. class AppWindow(Gtk.ApplicationWindow):
  67. def __init__(self, *args, **kwargs):
  68. self.app = kwargs["application"]
  69. super().__init__(*args, **kwargs)
  70. self.text = "666"
  71. self.rotation = 0
  72. self.invert_colors = False
  73. self.drawing_area = Gtk.DrawingArea()
  74. self.drawing_area.set_can_focus(True)
  75. self.drawing_area.connect("draw", self.redraw)
  76. vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
  77. vbox.pack_start(self.drawing_area, True, True, 0)
  78. self.add(vbox)
  79. if self.app.cli_args.fullscreen:
  80. self.fullscreen()
  81. self.show_all()
  82. GLib.timeout_add(1000, self.refresh_number)
  83. if self.app.cli_args.invert_every > 0:
  84. GLib.timeout_add(self.app.cli_args.invert_every * 1000, self.swap_colors)
  85. def force_redraw(self):
  86. self.drawing_area.queue_draw()
  87. def refresh_number(self):
  88. self.text = str(random.randint(1, 150))
  89. self.force_redraw()
  90. return True
  91. def swap_colors(self):
  92. self.invert_colors = not self.invert_colors
  93. self.force_redraw()
  94. return True
  95. @property
  96. def font(self):
  97. font = Pango.FontDescription()
  98. font.set_family("sans-serif")
  99. font.set_size(200 * Pango.SCALE)
  100. @property
  101. def colors(self):
  102. fg = self.app.cli_args.foreground
  103. bg = self.app.cli_args.background
  104. if self.invert_colors:
  105. fg, bg = bg, fg
  106. return (fg, bg)
  107. @property
  108. def bg(self):
  109. return get_color(self.colors[1])
  110. @property
  111. def fg(self):
  112. return get_color(self.colors[0])
  113. def redraw(self, widget, cr):
  114. # clearly stolen from screen-message. thanks!
  115. draw = self.drawing_area
  116. Gdk.cairo_set_source_rgba(cr, self.bg)
  117. cr.paint()
  118. layout = draw.create_pango_layout(self.text)
  119. layout.set_font_description(self.font)
  120. layout.set_alignment(Pango.Alignment.CENTER)
  121. w1, h1 = layout.get_pixel_size()
  122. if w1 and h1:
  123. w2 = draw.get_allocated_width()
  124. h2 = draw.get_allocated_height()
  125. if self.rotation in [0, 2]:
  126. rw1 = w1
  127. rh1 = h1
  128. else:
  129. rw1 = h1
  130. rh1 = w1
  131. s = min(w2 / rw1, h2 / rh1)
  132. cr.translate(w2 // 2, h2 // 2)
  133. cr.rotate(0)
  134. cr.scale(s, s)
  135. cr.translate(-w1 / 2, -h1 / 2)
  136. Gdk.cairo_set_source_rgba(cr, self.fg)
  137. PangoCairo.show_layout(cr, layout)
  138. def main():
  139. args = get_parser().parse_args()
  140. app = App(args)
  141. app.run()
  142. if __name__ == "__main__":
  143. main()