|
@@ -0,0 +1,171 @@
|
|
|
+#!/usr/bin/env python3
|
|
|
+
|
|
|
+import argparse
|
|
|
+import random
|
|
|
+
|
|
|
+import gi
|
|
|
+
|
|
|
+gi.require_version("Gtk", "3.0")
|
|
|
+gi.require_version("PangoCairo", "1.0")
|
|
|
+gi.require_version("Gdk", "3.0")
|
|
|
+gi.require_version("GLib", "2.0")
|
|
|
+from gi.repository import Gtk, Gio, cairo, Pango, Gdk, PangoCairo, GLib
|
|
|
+
|
|
|
+
|
|
|
+def get_parser():
|
|
|
+ p = argparse.ArgumentParser()
|
|
|
+ p.add_argument(
|
|
|
+ "--read-only",
|
|
|
+ action="store_true",
|
|
|
+ dest="readonly",
|
|
|
+ help="Display but dont change the number",
|
|
|
+ )
|
|
|
+ p.add_argument("--fullscreen", action="store_true", default=True, dest="fullscreen")
|
|
|
+ p.add_argument(
|
|
|
+ "--no-fullscreen", action="store_false", default=True, dest="fullscreen"
|
|
|
+ )
|
|
|
+ p.add_argument("--background", default="#111")
|
|
|
+ p.add_argument("--foreground", default="#eee")
|
|
|
+ p.add_argument(
|
|
|
+ "--invert-every",
|
|
|
+ type=float,
|
|
|
+ metavar="SECONDS",
|
|
|
+ default=60,
|
|
|
+ help=(
|
|
|
+ "Swap foreground and background periodically; "
|
|
|
+ "this is useful to avoid CRT-burning; use <= 0 to disable"
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ return p
|
|
|
+
|
|
|
+
|
|
|
+class App(Gtk.Application):
|
|
|
+ def __init__(self, cli_args, *args, **kwargs):
|
|
|
+ super().__init__(
|
|
|
+ *args,
|
|
|
+ application_id="org.hackmeeting.numeretti",
|
|
|
+ flags=Gio.ApplicationFlags.FLAGS_NONE,
|
|
|
+ **kwargs
|
|
|
+ )
|
|
|
+ self.window = None
|
|
|
+ self.cli_args = cli_args
|
|
|
+
|
|
|
+ def do_startup(self):
|
|
|
+ Gtk.Application.do_startup(self)
|
|
|
+
|
|
|
+ action = Gio.SimpleAction.new("quit", None)
|
|
|
+ action.connect("activate", self.on_quit)
|
|
|
+ self.add_action(action)
|
|
|
+
|
|
|
+ # self.set_app_menu(…)
|
|
|
+
|
|
|
+ def do_activate(self):
|
|
|
+ # We only allow a single window and raise any existing ones
|
|
|
+ if not self.window:
|
|
|
+ # Windows are associated with the application
|
|
|
+ # when the last one is closed the application shuts down
|
|
|
+ self.window = AppWindow(application=self, title="Main Window")
|
|
|
+
|
|
|
+ self.window.present()
|
|
|
+
|
|
|
+ def on_quit(self, action, param):
|
|
|
+ self.quit()
|
|
|
+
|
|
|
+
|
|
|
+def get_color(description: str) -> Gdk.RGBA:
|
|
|
+ rgba = Gdk.RGBA()
|
|
|
+ ret = rgba.parse(description)
|
|
|
+ if ret is False:
|
|
|
+ raise ValueError("Error parsing color! %s" % description)
|
|
|
+ return rgba
|
|
|
+
|
|
|
+
|
|
|
+class AppWindow(Gtk.ApplicationWindow):
|
|
|
+ def __init__(self, *args, **kwargs):
|
|
|
+ self.app = kwargs["application"]
|
|
|
+ super().__init__(*args, **kwargs)
|
|
|
+ self.text = "666"
|
|
|
+ self.rotation = 0
|
|
|
+ self.invert_colors = False
|
|
|
+ self.drawing_area = Gtk.DrawingArea()
|
|
|
+ self.drawing_area.set_can_focus(True)
|
|
|
+ self.drawing_area.connect("draw", self.redraw)
|
|
|
+ vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
|
+ vbox.pack_start(self.drawing_area, True, True, 0)
|
|
|
+ self.add(vbox)
|
|
|
+ if self.app.cli_args.fullscreen:
|
|
|
+ self.fullscreen()
|
|
|
+ self.show_all()
|
|
|
+ GLib.timeout_add(1000, self.refresh_number)
|
|
|
+ if self.app.cli_args.invert_every > 0:
|
|
|
+ GLib.timeout_add(self.app.cli_args.invert_every * 1000, self.swap_colors)
|
|
|
+
|
|
|
+ def force_redraw(self):
|
|
|
+ self.drawing_area.queue_draw()
|
|
|
+ def refresh_number(self):
|
|
|
+ self.text = str(random.randint(1, 150))
|
|
|
+ self.force_redraw()
|
|
|
+ return True
|
|
|
+
|
|
|
+ def swap_colors(self):
|
|
|
+ self.invert_colors = not self.invert_colors
|
|
|
+ self.force_redraw()
|
|
|
+ return True
|
|
|
+
|
|
|
+ @property
|
|
|
+ def font(self):
|
|
|
+ font = Pango.FontDescription()
|
|
|
+ font.set_family("sans-serif")
|
|
|
+ font.set_size(200 * Pango.SCALE)
|
|
|
+
|
|
|
+ @property
|
|
|
+ def colors(self):
|
|
|
+ fg = self.app.cli_args.foreground
|
|
|
+ bg = self.app.cli_args.background
|
|
|
+ if self.invert_colors:
|
|
|
+ fg, bg = bg, fg
|
|
|
+ return (fg, bg)
|
|
|
+
|
|
|
+ @property
|
|
|
+ def bg(self):
|
|
|
+ return get_color(self.colors[1])
|
|
|
+
|
|
|
+ @property
|
|
|
+ def fg(self):
|
|
|
+ return get_color(self.colors[0])
|
|
|
+
|
|
|
+ def redraw(self, widget, cr):
|
|
|
+ # clearly stolen from screen-message. thanks!
|
|
|
+ draw = self.drawing_area
|
|
|
+ Gdk.cairo_set_source_rgba(cr, self.bg)
|
|
|
+ cr.paint()
|
|
|
+ layout = draw.create_pango_layout(self.text)
|
|
|
+ layout.set_font_description(self.font)
|
|
|
+ layout.set_alignment(Pango.Alignment.CENTER)
|
|
|
+ w1, h1 = layout.get_pixel_size()
|
|
|
+ if w1 and h1:
|
|
|
+ w2 = draw.get_allocated_width()
|
|
|
+ h2 = draw.get_allocated_height()
|
|
|
+ if self.rotation in [0, 2]:
|
|
|
+ rw1 = w1
|
|
|
+ rh1 = h1
|
|
|
+ else:
|
|
|
+ rw1 = h1
|
|
|
+ rh1 = w1
|
|
|
+ s = min(w2 / rw1, h2 / rh1)
|
|
|
+ cr.translate(w2 // 2, h2 // 2)
|
|
|
+ cr.rotate(0)
|
|
|
+ cr.scale(s, s)
|
|
|
+ cr.translate(-w1 / 2, -h1 / 2)
|
|
|
+ Gdk.cairo_set_source_rgba(cr, self.fg)
|
|
|
+ PangoCairo.show_layout(cr, layout)
|
|
|
+
|
|
|
+
|
|
|
+def main():
|
|
|
+ args = get_parser().parse_args()
|
|
|
+ app = App(args)
|
|
|
+ app.run()
|
|
|
+
|
|
|
+
|
|
|
+if __name__ == "__main__":
|
|
|
+ main()
|