240 行
6.8 KiB
Python
可执行文件
240 行
6.8 KiB
Python
可执行文件
#!/usr/bin/env python3
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import random
|
|
from subprocess import Popen
|
|
|
|
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, GObject
|
|
|
|
|
|
def get_parser():
|
|
p = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
p.add_argument(
|
|
"--read-only",
|
|
action="store_true",
|
|
dest="readonly",
|
|
help="Display but dont change the number",
|
|
)
|
|
p.add_argument(
|
|
"--stdin",
|
|
action="store_true",
|
|
default=True,
|
|
dest="stdin",
|
|
help="stdin is read and shown",
|
|
)
|
|
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"
|
|
),
|
|
)
|
|
|
|
p.add_argument("--on-key-press",
|
|
type=str,
|
|
default=None,
|
|
help="A program to call on up/down keypress. "
|
|
"It will be called with appropriate arguments "
|
|
"representing the action to take.")
|
|
|
|
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
|
|
|
|
self._text = ""
|
|
|
|
self._stdin_buffer = ""
|
|
|
|
if self.cli_args.stdin:
|
|
os.set_blocking(sys.stdin.fileno(), False)
|
|
GLib.io_add_watch(sys.stdin.fileno(), GLib.IO_IN, self._on_stdin_data)
|
|
|
|
def _on_stdin_data(self, *args, **kwargs):
|
|
max_line_length = 16
|
|
incoming_data = sys.stdin.read(max_line_length)
|
|
if not incoming_data:
|
|
return True
|
|
self._stdin_buffer += incoming_data
|
|
if len(self._stdin_buffer) == max_line_length:
|
|
self._stdin_buffer += '\n'
|
|
if "\n" in self._stdin_buffer:
|
|
parts = self._stdin_buffer.split("\n")
|
|
self._stdin_buffer = parts[-1]
|
|
self.text = parts[-2]
|
|
return True
|
|
|
|
|
|
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):
|
|
sys.stdin.close()
|
|
self.quit()
|
|
|
|
@GObject.Property(type=str,
|
|
default="",
|
|
flags=GObject.ParamFlags.READWRITE)
|
|
def text(self) -> str:
|
|
return self._text
|
|
|
|
@text.setter
|
|
def text(self, new_value: str):
|
|
self._text = new_value
|
|
|
|
def on_binding_pressed(self, action):
|
|
print('calling:', action)
|
|
Popen(self.cli_args.on_key_press.split() + [action])
|
|
|
|
|
|
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.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()
|
|
|
|
if self.app.cli_args.invert_every > 0:
|
|
GLib.timeout_add(self.app.cli_args.invert_every * 1000, self.swap_colors)
|
|
|
|
self.app.connect("notify::text", self.on_text_changed)
|
|
|
|
if self.app.cli_args.on_key_press:
|
|
self.connect("key-press-event", self.on_key_press)
|
|
|
|
def on_key_press(self, widget, key: Gdk.EventKey):
|
|
if key.state != 0 or key.type != Gdk.EventType.KEY_PRESS:
|
|
return
|
|
|
|
if key.keyval == Gdk.KEY_Up:
|
|
self.app.on_binding_pressed('+1')
|
|
elif key.keyval == Gdk.KEY_Down:
|
|
self.app.on_binding_pressed('-1')
|
|
|
|
|
|
def force_redraw(self):
|
|
self.drawing_area.queue_draw()
|
|
|
|
def on_text_changed(self, app, text: str):
|
|
self.force_redraw()
|
|
|
|
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.app.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()
|