#!/usr/bin/env python3
-from gi import require_version
-require_version("Gtk", "3.0")
+import cairo
+import math
+from gi import require_version
+require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf, GObject
-import sys
-import os
-import math
+
+LINE_WIDTH = 2
+NODE_RADIUS = 5
+DRAG_RADIUS = 7
class App:
def __init__(self):
self.application_window = self.builder.get_object('application_window')
self.button1 = self.builder.get_object('button1')
self.button2 = self.builder.get_object('button2')
- self.image = self.builder.get_object('image')
+ self.viewport = self.builder.get_object('viewport')
self.application_window.connect('destroy', Gtk.main_quit)
+ # see https://stackoverflow.com/questions/24844489/how-to-use-gdk-device-get-position
+ # this requires GTK+ 3.20+
+ self.pointer = Gdk.Display.get_default().get_default_seat().get_pointer()
+
self.button1.connect('button_press_event', self.on_button1_press)
self.button1.connect('button_release_event', self.on_button1_release)
self.button2.connect('button_press_event', self.on_button2_press)
self.button2.connect('button_release_event', self.on_button2_release)
+ # see https://gitlab.gnome.org/GNOME/pygobject/-/blob/master/examples/demo/demos/drawingarea.py
+ # has to be done before the DrawingArea is realized, don't use Glade
+ self.drawing_area = Gtk.DrawingArea()
+ self.drawing_area.set_property('visible', True)
+ self.drawing_area.set_property('can-focus', True)
+ self.drawing_area.set_events(
+ self.drawing_area.get_events() |
+ Gdk.EventMask.LEAVE_NOTIFY_MASK |
+ Gdk.EventMask.BUTTON_PRESS_MASK |
+ Gdk.EventMask.POINTER_MOTION_MASK |
+ Gdk.EventMask.POINTER_MOTION_HINT_MASK
+ )
+ self.viewport.add(self.drawing_area)
+
+ self.set_image('a-008.png')
+ self.drawing_area.connect('draw', self.on_draw)
+ self.drawing_area.connect('button_press_event', self.on_button_press)
+ self.drawing_area.connect('button_release_event', self.on_button_release)
+ self.drawing_area.connect('motion_notify_event', self.on_motion_notify)
+
+ self.nodes = []
+ self.node = -1
+
def on_button1_press(self, button, event):
- self.button1.set_active(True)
- self.button2.set_active(False)
- self.image.set_from_file('a-008.png')
+ if event.button == 1:
+ self.button1.set_active(True)
+ self.button2.set_active(False)
+ self.set_image('a-008.png')
return True
def on_button1_release(self, button, event):
return True
-
+
def on_button2_press(self, button, event):
- self.button1.set_active(False)
- self.button2.set_active(True)
- self.image.set_from_file('a-009.png')
+ if event.button == 1:
+ self.button1.set_active(False)
+ self.button2.set_active(True)
+ self.set_image('a-009.png')
return True
def on_button2_release(self, button, event):
return True
+ def set_image(self, file_png):
+ self.surface = cairo.ImageSurface.create_from_png(file_png)
+ self.drawing_area.set_size_request(
+ self.surface.get_width(),
+ self.surface.get_height()
+ )
+ self.drawing_area.queue_draw_area(
+ 0,
+ 0,
+ self.surface.get_width(),
+ self.surface.get_height()
+ )
+
+ def on_draw(self, area, context):
+ context.set_source_surface(self.surface, 0., 0.)
+ context.paint()
+
+ if len(self.nodes):
+ context.set_source_rgb(1., 0., 0.)
+ for nx, ny in self.nodes:
+ context.arc(nx, ny, NODE_RADIUS, -math.pi, math.pi)
+ context.fill()
+
+ context.set_line_width(LINE_WIDTH)
+ nx, ny = self.nodes[-1]
+ context.move_to(nx, ny)
+ for nx, ny in self.nodes:
+ context.line_to(nx, ny)
+ context.stroke()
+
+ return True
+
+ def on_button_press(self, button, event):
+ if event.button == 1:
+ (window, x, y, state) = event.window.get_device_position(self.pointer)
+ for i in range(len(self.nodes)):
+ nx, ny = self.nodes[i]
+ if (x - nx) ** 2 + (y - ny) ** 2 < DRAG_RADIUS ** 2:
+ self.node = i
+ self.nodes[i] = (x, y)
+ break
+ else:
+ self.node = len(self.nodes)
+ self.nodes.append((x, y))
+ self.drawing_area.queue_draw_area(
+ 0,
+ 0,
+ self.surface.get_width(),
+ self.surface.get_height()
+ )
+ return True
+
+ def on_button_release(self, button, event):
+ if event.button == 1:
+ self.node = -1
+ return True
+
+ def on_motion_notify(self, drawing_area, event):
+ # see https://gitlab.gnome.org/GNOME/pygobject/-/blob/master/examples/demo/demos/drawingarea.py
+ # Gdk.Window.get_device_pointer() is deprecated, use get_device_position()
+ (window, x, y, state) = event.window.get_device_position(self.pointer)
+
+ if state & Gdk.ModifierType.BUTTON1_MASK:
+ if self.node >= 0:
+ self.nodes[self.node] = (x, y)
+ self.drawing_area.queue_draw_area(
+ 0,
+ 0,
+ self.surface.get_width(),
+ self.surface.get_height()
+ )
+ return True
+
app = App()
app.application_window.set_position(Gtk.WindowPosition.CENTER)
-app.application_window.present()
+app.application_window.present()
Gtk.main()