Publish showcase_scenes/ from marimo
Browse files- showcase_scenes/gtk_daily.py +135 -0
showcase_scenes/gtk_daily.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A small but real GTK desktop: the fixture for the daily-usage evaluation.
|
| 2 |
+
|
| 3 |
+
It is not a toy widget gallery - it is the set of controls a person actually meets:
|
| 4 |
+
a menu bar with a submenu, a text entry, buttons, a scrolling list, a status bar,
|
| 5 |
+
and a second window. Every interaction is appended to a JSON marker file, so the
|
| 6 |
+
evaluation can verify a task by the *application's own* record of what happened
|
| 7 |
+
rather than by asking the agent whether it thinks it succeeded.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import json
|
| 11 |
+
import pathlib
|
| 12 |
+
import sys
|
| 13 |
+
|
| 14 |
+
import gi
|
| 15 |
+
|
| 16 |
+
gi.require_version("Gtk", "3.0")
|
| 17 |
+
from gi.repository import Gtk # noqa: E402
|
| 18 |
+
|
| 19 |
+
MARKER = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else "/tmp/gtk_daily.json")
|
| 20 |
+
READY = pathlib.Path(str(MARKER) + ".ready")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def record(**event):
|
| 24 |
+
history = json.loads(MARKER.read_text()) if MARKER.exists() else []
|
| 25 |
+
history.append(event)
|
| 26 |
+
MARKER.write_text(json.dumps(history))
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
class Everyday(Gtk.Application):
|
| 30 |
+
def __init__(self):
|
| 31 |
+
# one instance per marker, or GTK forwards a second launch to the first
|
| 32 |
+
# and the new window never appears - which silently mis-runs an evaluation
|
| 33 |
+
super().__init__(application_id=f"dev.tinycomputeragent.daily.{MARKER.stem}")
|
| 34 |
+
self.connect("activate", self.on_activate)
|
| 35 |
+
self.status = None
|
| 36 |
+
self.entry = None
|
| 37 |
+
|
| 38 |
+
def on_activate(self, app):
|
| 39 |
+
window = Gtk.ApplicationWindow(application=app, title="Everyday")
|
| 40 |
+
window.move(160, 160)
|
| 41 |
+
window.set_default_size(620, 430)
|
| 42 |
+
column = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
| 43 |
+
window.add(column)
|
| 44 |
+
|
| 45 |
+
self.status = Gtk.Label(label="ready")
|
| 46 |
+
self.status.set_xalign(0)
|
| 47 |
+
|
| 48 |
+
menubar = Gtk.MenuBar()
|
| 49 |
+
file_item = Gtk.MenuItem(label="File")
|
| 50 |
+
menubar.append(file_item)
|
| 51 |
+
submenu = Gtk.Menu()
|
| 52 |
+
file_item.set_submenu(submenu)
|
| 53 |
+
for label in ("New", "Save As...", "Quit"):
|
| 54 |
+
item = Gtk.MenuItem(label=label)
|
| 55 |
+
item.connect("activate", self.on_menu, label)
|
| 56 |
+
submenu.append(item)
|
| 57 |
+
column.pack_start(menubar, False, False, 0)
|
| 58 |
+
|
| 59 |
+
self.entry = Gtk.Entry()
|
| 60 |
+
self.entry.set_text("report.txt")
|
| 61 |
+
self.entry.get_accessible().set_name("File name") # so the index can click it
|
| 62 |
+
padding = Gtk.Box(spacing=6)
|
| 63 |
+
padding.pack_start(Gtk.Label(label="File name"), False, False, 0)
|
| 64 |
+
padding.pack_start(self.entry, True, True, 0)
|
| 65 |
+
column.pack_start(padding, False, False, 6)
|
| 66 |
+
|
| 67 |
+
buttons = Gtk.Box(spacing=8)
|
| 68 |
+
for label in ("Save", "Cancel", "Settings", "Terminal"):
|
| 69 |
+
button = Gtk.Button(label=label)
|
| 70 |
+
button.set_size_request(104, 38)
|
| 71 |
+
button.connect("clicked", self.on_button, label)
|
| 72 |
+
buttons.pack_start(button, False, False, 0)
|
| 73 |
+
column.pack_start(buttons, False, False, 6)
|
| 74 |
+
|
| 75 |
+
scroller = Gtk.ScrolledWindow()
|
| 76 |
+
scroller.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
|
| 77 |
+
scroller.set_size_request(560, 150)
|
| 78 |
+
listing = Gtk.ListBox()
|
| 79 |
+
for index in range(1, 25):
|
| 80 |
+
row = Gtk.ListBoxRow()
|
| 81 |
+
row.add(Gtk.Label(label=f"Item {index:02d}", xalign=0))
|
| 82 |
+
listing.add(row)
|
| 83 |
+
listing.connect("row-activated", self.on_row)
|
| 84 |
+
scroller.add(listing)
|
| 85 |
+
column.pack_start(scroller, True, True, 0)
|
| 86 |
+
column.pack_start(self.status, False, False, 4)
|
| 87 |
+
|
| 88 |
+
window.show_all()
|
| 89 |
+
window.connect("key-press-event", self.on_key)
|
| 90 |
+
READY.write_text("mapped")
|
| 91 |
+
|
| 92 |
+
# -- the app's own record of what happened --------------------------------
|
| 93 |
+
|
| 94 |
+
def note(self, text):
|
| 95 |
+
self.status.set_text(text)
|
| 96 |
+
|
| 97 |
+
def on_button(self, button, label):
|
| 98 |
+
entry_text = self.entry.get_text() if self.entry is not None else ""
|
| 99 |
+
record(action="button", label=label, entry=entry_text)
|
| 100 |
+
self.note("pressed " + label)
|
| 101 |
+
if label == "Settings":
|
| 102 |
+
self.open_settings()
|
| 103 |
+
|
| 104 |
+
def on_menu(self, item, label):
|
| 105 |
+
record(action="menu", label=label)
|
| 106 |
+
self.note("menu " + label)
|
| 107 |
+
|
| 108 |
+
def on_row(self, listing, row):
|
| 109 |
+
label = row.get_child().get_text() if row.get_child() else ""
|
| 110 |
+
record(action="row", label=label)
|
| 111 |
+
self.note("row " + label)
|
| 112 |
+
|
| 113 |
+
def on_key(self, widget, event):
|
| 114 |
+
from gi.repository import Gdk
|
| 115 |
+
if event.state & Gdk.ModifierType.CONTROL_MASK and event.keyval == Gdk.KEY_s:
|
| 116 |
+
record(action="shortcut", label="ctrl+s", entry=self.entry.get_text())
|
| 117 |
+
self.note("saved by shortcut")
|
| 118 |
+
return True
|
| 119 |
+
return False
|
| 120 |
+
|
| 121 |
+
def open_settings(self):
|
| 122 |
+
window = Gtk.Window(title="Settings")
|
| 123 |
+
window.set_default_size(320, 180)
|
| 124 |
+
window.move(840, 200)
|
| 125 |
+
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8)
|
| 126 |
+
window.add(box)
|
| 127 |
+
for label in ("Apply", "Close"):
|
| 128 |
+
button = Gtk.Button(label=label)
|
| 129 |
+
button.connect("clicked", self.on_button, label)
|
| 130 |
+
box.pack_start(button, False, False, 0)
|
| 131 |
+
window.show_all()
|
| 132 |
+
|
| 133 |
+
|
| 134 |
+
app = Everyday()
|
| 135 |
+
sys.exit(app.run([]))
|