Change Image to DrawingArea, allow to place a box around the text of interest
authorNick Downing <nick@ndcode.org>
Sun, 7 Jan 2024 06:01:21 +0000 (17:01 +1100)
committerNick Downing <nick@ndcode.org>
Sun, 7 Jan 2024 06:37:39 +0000 (17:37 +1100)
ocr_fixed.glade
ocr_fixed.py

index 6d33cf0..4c8a737 100644 (file)
                 <property name="can-focus">True</property>
                 <property name="shadow-type">in</property>
                 <child>
-                  <object class="GtkViewport">
+                  <object class="GtkViewport" id="viewport">
                     <property name="visible">True</property>
                     <property name="can-focus">False</property>
-                    <child>
-                      <object class="GtkImage" id="image">
-                        <property name="visible">True</property>
-                        <property name="can-focus">False</property>
-                        <property name="pixbuf">a-008.png</property>
-                      </object>
-                    </child>
                   </object>
                 </child>
               </object>
index ead4d80..64bfb5a 100755 (executable)
@@ -1,12 +1,15 @@
 #!/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):
@@ -15,34 +18,136 @@ class App:
     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()