Ведение чертежа Cairo в Gtk.DrawingArea() после события прокрутки

#scroll #gtk #pygtk #cairo #pygobject

#прокрутка #gtk #pygtk #cairo #pygobject

Вопрос:

Я работаю над инструментом аннотирования для некоторых изображений и решил использовать GTK для этой задачи. У меня есть Gtk.DrawingArea() вложена в Gtk.Viewport(), который вложен в Gtk.ScrolledWindow(), чтобы включить прокрутку области рисования. Область рисования содержит изображение, и фигуры рисуются поверх изображения с использованием Cairo при каждом событии щелчка мыши.

Если я правильно понимаю, прокрутка по умолчанию вызывает перерисовку Gtk.DrawingArea(), из-за которого все фигуры исчезают. Есть ли какой-либо способ (кроме сохранения списка координат и перерисовки каждой фигуры при каждом событии прокрутки) сохранить эти фигуры?

 import gi
import math
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf

class MainWindow(Gtk.Window):

  def __init__(self):
    Gtk.Window.__init__(self, title = "Test")
    self.drag = False
    self.drag_x = 0
    self.drag_y = 0
    self.pos = []
    viewport = Gtk.Viewport()
    self.darea = Gtk.DrawingArea()
    self.darea.connect("draw", self.expose)

    self.pixbuf = GdkPixbuf.Pixbuf.new_from_file("anntool/test.jpg")
    self.darea.set_size_request(self.pixbuf.get_width(), self.pixbuf.get_height());

    self.maximize() # maximize window on load
    grid = Gtk.Grid()
    self.add(grid)

    scrolled = Gtk.ScrolledWindow()
    scrolled.set_hexpand(True)
    scrolled.set_vexpand(True)
    scrolled.set_kinetic_scrolling(True)
    self.v_scroll = scrolled.get_vadjustment()
    self.h_scroll = scrolled.get_hadjustment()
    scrolled.add_events(Gdk.EventMask.POINTER_MOTION_MASK | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.BUTTON_RELEASE_MASK)
    scrolled.connect("button-release-event", self.release)
    scrolled.connect("button-press-event", self.click)
    scrolled.connect("motion-notify-event", self.mousemove)
    # scrolled.connect("scroll_event", self.scroll)

    viewport.add(self.darea)
    scrolled.add(viewport)

    grid.add(scrolled)

  def click(self, widget, event):
    if (event.button == 1):
      cr = self.darea.get_parent_window().cairo_create()
      x = self.h_scroll.get_value()   event.x
      y = self.v_scroll.get_value()   event.y
      cr.arc(x, y, 10, 0, 2 * math.pi)
      cr.set_source_rgba(0.0, 0.6, 0.0, 1)
      cr.fill()
    if (event.button == 2):
      self.drag =  True
      self.drag_x = event.x
      self.drag_y = event.y
      self.pos = [self.h_scroll.get_value(), self.v_scroll.get_value()]

  def release(self, widget, event):
    self.drag =  False
    default = Gdk.Cursor(Gdk.CursorType.ARROW)
    widget.get_window().set_cursor(default)

  def mousemove(self, widget, event):
    if self.drag:
      self.h_scroll.set_value(self.pos[0]   self.drag_x - event.x)
      self.v_scroll.set_value(self.pos[1]   self.drag_y - event.y)
      hand = Gdk.Cursor(Gdk.CursorType.HAND1)
      widget.get_window().set_cursor(hand)

  def scroll(self, widget, event):
    print("scrolled")

  def expose(self, widget, event):
    Gdk.cairo_set_source_pixbuf(event, self.pixbuf, 0, 0)
    event.paint()

win = MainWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()
  

Комментарии:

1. Разве событие expose не означает, что вам нужно все перерисовать? Что означает также материал, который вы нарисовали вне событий expose? Кроме того, я смутно помню, что рисование вне события expose не рекомендуется. Вместо этого следует использовать gtk_widget_queue_draw , чтобы запланировать перерисовку.

2. @UliSchlachter, это из-за моего недостатка опыта, я не понимал, что событие expose (или событие «draw» GTK 3) вызывается при каждом событии прокрутки. В конце концов, я решил отслеживать список координат для добавленных точек и перерисовывать их все при каждом событии «рисования».

Ответ №1:

Как указал Ули Шлахтер, перерисовка выполняется при каждом событии прокрутки, поэтому необходимо отслеживать добавленные точки (например, с помощью списка) и перерисовывать каждую точку в expose() функции в приведенном выше коде.