Cairo и Gdk.Window с Gtk.DrawingArea с использованием python gobject-introspection и gtk3

Я пытаюсь следовать руководству по Gtk+ v3, которое можно найти в справочной документации. В частности, первый пример рисования с использованием cairo для обработки рисунка на Gtk.DrawingArea.

https://developer.gnome.org/gtk3/stable/ch01s03.html

Для справки, я использую эти ресурсы:

https://python-gtk-3-tutorial.readthedocs.org/en/latest/

http://lazka.github.io/pgi-docs/

Пожалуйста, взгляните на мой (частичный) перевод программы ch01s03 на python. Основная проблема, с которой я столкнулся, связана с configure_event_cb(), где программа должна создать объект cairo.Surface, связанный с Gdk.Window. Я не знаю, как добраться до этого Gdk.Window или даже где посмотреть справочную документацию.

from gi.repository import Gtk, Gdk, cairo

surface = None

def clear_surface():
    global surface
    surface = cairo.Surface()
    surface.set_source_rgb(1,1,1)
    surface.paint()

def configure_event_cb(wid,evt):
    global surface

    if surface is not None:
        surface.destroy()
        surface = None

    '''
    Here, I am trying to implement the following C code:

    surface = gdk_window_create_similar_surface(
                gtk_widget_get_window(widget),
                CAIRO_CONTENT_COLOR,
                gtk_widget_get_allocated_width(widget),
                gtk_widget_get_allocated_height(widget) );
    '''

    clear_surface()
    return True

def close_window(wid):
    global surface
    if surface is not None:
        surface.destroy()
    Gtk.main_quit()

if __name__ == '__main__':
    win = Gtk.Window(Gtk.WindowType.TOPLEVEL)
    win.set_title('Drawing Area')
    win.connect('destroy',close_window)
    win.set_border_width(8)

    frame = Gtk.Frame()
    frame.set_shadow_type(Gtk.ShadowType.IN)
    win.add(frame)

    da = Gtk.DrawingArea()
    da.set_size_request(100,100)
    frame.add(da)

    da.connect('configure-event',configure_event_cb)

    win.show_all()
    Gtk.main()

person Johann    schedule 14.02.2015    source источник
comment
почему вы добавили cb к именам функций? к чему это относится?   -  person oz123    schedule 12.06.2016
comment
Я использовал cb для обозначения обратного вызова.   -  person Johann    schedule 12.06.2016
comment
Почему «поверхность» должна быть глобальной переменной? Я пытался сделать его атрибутом окна, но это мешает рисованию.   -  person oz123    schedule 13.06.2016


Ответы (2)


Я понял, что DrawingArea теперь использует событие draw вместо configure-event, вот простой код, который работает:

from gi.repository import Gtk

def draw_cb(widget, cr):
  cr.set_source_rgba(0,0,0,0.5)
  cr.rectangle(50,75,100,100)
  cr.fill()
  return False

win = Gtk.Window()

win.set_title("test")
win.set_default_size(800,600)
win.connect('delete-event', Gtk.main_quit)
da=Gtk.DrawingArea()
da.connect('draw', draw_cb)
win.add(da)
win.show_all()
Gtk.main()

Но вместо этого, если вы действительно хотите создать контекст cairo в обратном вызове события expose, просто используйте это, чтобы получить gtk.gdk.window:

myGdkWindow = mywin.get_window()
cr = myGdkWindow.cairo_create()
person cedlemo    schedule 14.02.2015
comment
событие draw уже используется в примере, и мне удалось найти подобные примеры. Я предполагаю, что мой вопрос больше о том, как получить объект cairo Context (cr в вашем случае) без использования события draw. - person Johann; 14.02.2015

С помощью (принятого) ответа @cedlemo я смог воссоздать пример ch01s03 Gtk+ в python. Однако мне пришлось импортировать cairo actual, поскольку конструктора gi.repository.cairo.Context() не существовало. Сначала это было не очевидно, но, возможно, в этом есть смысл. Вот полностью рабочая версия. См. исходную C версию с комментариями.

from gi.repository import Gtk, Gdk
import cairo


surface = None


def clear_surface():
    global surface

    cr = cairo.Context(surface)
    cr.set_source_rgb(1,1,1)
    cr.paint()

    del cr


def configure_event_cb(wid,evt):
    global surface

    if surface is not None:
        del surface
        surface = None

    win = wid.get_window()
    width = wid.get_allocated_width()
    height = wid.get_allocated_height()

    surface = win.create_similar_surface(
        cairo.CONTENT_COLOR,
        width,
        height)

    clear_surface()
    return True


def draw_cb(wid,cr):
    global surface

    cr.set_source_surface(surface,0,0)
    cr.paint()
    return False


def draw_brush(wid,x,y):
    global surface

    cr = cairo.Context(surface)
    cr.set_source_rgb(0,0,0)
    cr.rectangle(x-3,y-3,6,6)
    cr.fill()
    del cr

    wid.queue_draw_area(x-3,y-3,6,6)


def button_press_event_cb(wid,evt):
    global surface

    if surface is None:
        return False

    if evt.button == Gdk.BUTTON_PRIMARY:
        draw_brush(wid,evt.x,evt.y)
    elif evt.button == Gdk.BUTTON_SECONDARY:
        clear_surface()
        wid.queue_draw()

    return True


def motion_notify_event_cb(wid,evt):
    global surface

    if surface is None:
        return False

    if evt.state & Gdk.EventMask.BUTTON_PRESS_MASK:
        draw_brush(wid,evt.x,evt.y)

    return True


def close_window(wid):
    global surface

    if surface is not None:
        del surface
        surface = None

    Gtk.main_quit()


if __name__ == '__main__':
    win = Gtk.Window()
    win.set_title('Drawing Area')
    win.connect('destroy',close_window)
    win.set_border_width(8)

    frame = Gtk.Frame()
    frame.set_shadow_type(Gtk.ShadowType.IN)
    win.add(frame)

    da = Gtk.DrawingArea()
    da.set_size_request(100,100)
    frame.add(da)

    da.connect('draw',draw_cb)
    da.connect('configure-event',configure_event_cb)

    da.connect('motion-notify-event',motion_notify_event_cb)
    da.connect('button-press-event',button_press_event_cb)
    da.set_events(da.get_events() | Gdk.EventMask.BUTTON_PRESS_MASK | Gdk.EventMask.POINTER_MOTION_MASK)

    win.show_all()
    Gtk.main()
person Johann    schedule 14.02.2015