blob: 7caf299d4d76f89cea59d294019891426e271676 [file] [log] [blame]
Parker Schuh19b93b12018-03-02 23:26:58 -08001import gi
2gi.require_version('Gtk', '3.0')
3from gi.repository import Gtk
4from gi.repository import GLib
5from gi.repository import Gdk
6from gi.repository import GdkX11
7import cairo
8
9identity = cairo.Matrix()
10
Parker Schuhdc682952018-03-03 18:24:01 -080011
Parker Schuh19b93b12018-03-02 23:26:58 -080012# Override the matrix of a cairo context.
13class OverrideMatrix(object):
Parker Schuhdc682952018-03-03 18:24:01 -080014 def __init__(self, cr, matrix):
15 self.cr = cr
16 self.matrix = matrix
Parker Schuh19b93b12018-03-02 23:26:58 -080017
Parker Schuhdc682952018-03-03 18:24:01 -080018 def __enter__(self):
19 self.cr.save()
20 self.cr.set_matrix(self.matrix)
21
22 def __exit__(self, type, value, traceback):
23 self.cr.restore()
24
Parker Schuh19b93b12018-03-02 23:26:58 -080025
26mainloop = GLib.MainLoop()
27
Parker Schuhdc682952018-03-03 18:24:01 -080028
Parker Schuh19b93b12018-03-02 23:26:58 -080029def quit_main_loop(*args):
Parker Schuhdc682952018-03-03 18:24:01 -080030 mainloop.quit()
31
Parker Schuh19b93b12018-03-02 23:26:58 -080032
33def RunApp():
Parker Schuhdc682952018-03-03 18:24:01 -080034 try:
35 mainloop.run()
36 except KeyboardInterrupt:
37 print('\nCtrl+C hit, quitting')
38 mainloop.quit()
39
Parker Schuh19b93b12018-03-02 23:26:58 -080040
41# Create a GTK+ widget on which we will draw using Cairo
42class BaseWindow(Gtk.DrawingArea):
Parker Schuhdc682952018-03-03 18:24:01 -080043 def method_connect(self, event, cb):
44 def handler(obj, *args):
45 cb(*args)
Parker Schuh19b93b12018-03-02 23:26:58 -080046
Parker Schuhdc682952018-03-03 18:24:01 -080047 self.window.connect(event, handler)
Parker Schuh19b93b12018-03-02 23:26:58 -080048
Parker Schuhdc682952018-03-03 18:24:01 -080049 # Draw in response to an expose-event
50 def __init__(self):
51 super(BaseWindow, self).__init__()
52 self.window = Gtk.Window()
53 self.window.set_title("DrawingArea")
54 self.window.connect("destroy", quit_main_loop)
55 self.window.set_events(Gdk.EventMask.BUTTON_PRESS_MASK
56 | Gdk.EventMask.BUTTON_RELEASE_MASK
57 | Gdk.EventMask.POINTER_MOTION_MASK
58 | Gdk.EventMask.SCROLL_MASK
59 | Gdk.EventMask.KEY_PRESS_MASK)
60 self.method_connect("key-press-event", self.do_key_press)
61 self.method_connect("button-press-event",
62 self._do_button_press_internal)
63 self.method_connect("configure-event", self._do_configure)
Parker Schuh19b93b12018-03-02 23:26:58 -080064
Parker Schuhdc682952018-03-03 18:24:01 -080065 self.set_size_request(640, 400)
66 self.window.add(self)
67 self.window.show_all()
68 self.center = (0, 0)
69 self.shape = (640, 400)
70 self.needs_redraw = False
Parker Schuh19b93b12018-03-02 23:26:58 -080071
Parker Schuhdc682952018-03-03 18:24:01 -080072 def get_current_scale(self):
73 w_w, w_h = self.window_shape
74 w, h = self.shape
75 return min((w_w / w), (w_h / h))
Parker Schuh19b93b12018-03-02 23:26:58 -080076
Parker Schuhdc682952018-03-03 18:24:01 -080077 def init_extents(self, center, shape):
78 self.center = center
79 self.shape = shape
Parker Schuh19b93b12018-03-02 23:26:58 -080080
Parker Schuhdc682952018-03-03 18:24:01 -080081 # The gtk system creates cr which is a cairo_context_t (in the c docs), and then it
82 # passes it as a function argument to the "draw" event. do_draw is the default name.
83 def do_draw(self, cr):
84 cr.save()
85 cr.set_font_size(20)
86 cr.translate(self.window_shape[0] / 2, self.window_shape[1] / 2)
87 scale = self.get_current_scale()
88 cr.scale(scale, -scale)
89 cr.translate(-self.center[0], -self.center[1])
90 self.needs_redraw = False
91 self.handle_draw(cr)
92 cr.restore()
Parker Schuh19b93b12018-03-02 23:26:58 -080093
Parker Schuhdc682952018-03-03 18:24:01 -080094 # Handle the expose-event by drawing
95 def handle_draw(self, cr):
96 pass
Parker Schuh19b93b12018-03-02 23:26:58 -080097
Parker Schuhdc682952018-03-03 18:24:01 -080098 def do_key_press(self, event):
99 pass
Parker Schuh19b93b12018-03-02 23:26:58 -0800100
Parker Schuhdc682952018-03-03 18:24:01 -0800101 def _do_button_press_internal(self, event):
102 o_x = event.x
103 o_y = event.y
104 x = event.x - self.window_shape[0] / 2
105 y = self.window_shape[1] / 2 - event.y
106 scale = self.get_current_scale()
107 event.x = x / scale + self.center[0]
108 event.y = y / scale + self.center[1]
109 self.do_button_press(event)
110 event.x = o_x
111 event.y = o_y
Parker Schuh19b93b12018-03-02 23:26:58 -0800112
Parker Schuhdc682952018-03-03 18:24:01 -0800113 def do_button_press(self, event):
114 pass
Parker Schuh19b93b12018-03-02 23:26:58 -0800115
Parker Schuhdc682952018-03-03 18:24:01 -0800116 def _do_configure(self, event):
117 self.window_shape = (event.width, event.height)
118
119 def redraw(self):
120 if not self.needs_redraw:
121 self.needs_redraw = True
122 self.window.queue_draw()