在PyGobject中绘图(python3)

发布于 2021-01-29 14:10:03

我正在尝试使用PyGObject和python
3编写简单的图形编辑器。我需要使用鼠标绘制具有不同颜色和宽度的线条。我发现类似的例子很多,但没有更复杂。

如何在“绘制”事件之间保存绘制的图像?是否有增量绘制方式,或者在每个“绘制”事件上都必须重新绘制窗格?我发现可以保存路径,但是如何保存绘制线条的宽度和颜色?有没有办法在’draw’回调之外创建图像并仅在回调内部应用(draw)它?

是我现在所拥有的。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from gi.repository import Gtk, Gdk
import os

class App(object):

    main_ui = os.path.join(os.path.dirname(__file__), 'gui.glade')

    def __init__(self):
        self.builder = Gtk.Builder()
        self.builder.add_from_file(self.main_ui)

        self.main_window.connect('destroy', self.quit)
        self.mw_quit_button.connect('clicked', self.quit)

        self.mw_graph_editor_button.connect('clicked', self.show_window, self.graph_editor_window)
        self.graph_editor_window.connect('delete-event', self.hide_window_delete)

        self.ge_menubar_file_quit.connect('activate', self.hide_window, self.graph_editor_window)
        self.ge_toolbar_quit.connect('clicked', self.hide_window, self.graph_editor_window)

        self.ge_drawingarea.connect('motion-notify-event', self.pointer_motion)
        self.ge_drawingarea.connect('motion-notify-event', self.show_coordinates)
        self.ge_drawingarea.connect('draw', self.draw_callback)

        self.path = None
        self.coord = (0, 0)
        self.rgb = (0, 0, 0)

    def __getattr__(self, name):
        obj = self.builder.get_object(name)
        if not obj:
            raise AttributeError("Object {0} has no attribute {1}".format(self, name))
        setattr(self, name, obj)
        return obj

    def draw_callback(self, drawingarea, cr):
        if self.path:
            cr.append_path(self.path)
        cr.line_to(self.coord[0], self.coord[1])
        cr.set_source_rgba(*self.rgb)
        self.path = cr.copy_path_flat()
        cr.stroke()

    def show_coordinates(self, window, event):
        self.ge_mouse_coordinates.set_label('X: {0:.0f} Y: {1:.0f}'.format(event.x, event.y))

    def pointer_motion(self, widget, event):
        if event.state & Gdk.ModifierType.BUTTON1_MASK:
            self.draw(widget, event.x, event.y)
        elif event.state & Gdk.ModifierType.BUTTON3_MASK:
            self.draw(widget, event.x, event.y, True)

    def draw(self, widget, x, y, erase=False):
        self.coord = (x,y)
        if erase:
            self.rgb = (256, 256, 256)
        else:
            self.rgb = (0, 0, 0)
        widget.queue_draw()

    def show_window(self, widget, data):
        data.show_all()

    def hide_window_delete(self, widget, event):
        widget.hide()
        return True

    def hide_window(self, widget, window):
        window.hide()

    def run(self):
        self.main_window.show_all()
        Gtk.main()

    def quit(self, widget=None, data=None):
        self.main_window.destroy()
        Gtk.main_quit()


if __name__ == "__main__":
    app = App()
    app.run()

对不起,我的英语不是我的母语。

关注者
0
被浏览
167
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    您需要使用双缓冲区技术:

    http://en.wikipedia.org/wiki/Multiple_buffering#Double_buffering_in_computer_graphics

    那就是您有一个图像,然后绘制该图像:该图像是“后台”缓冲区。您可以使用很多方法来绘制图像。然后,在响应“绘制”信号的回调上,也就是说,实际上在图形存储器中绘制内容的方法只是将您的“幕后”图像丢了。

    代码理论( test.py ):

    import cairo
    from gi.repository import Gtk
    from os.path import abspath, dirname, join
    
    WHERE_AM_I = abspath(dirname(__file__))
    
    class MyApp(object):
        """Double buffer in PyGObject with cairo"""
    
        def __init__(self):
            # Build GUI
            self.builder = Gtk.Builder()
            self.glade_file = join(WHERE_AM_I, 'test.glade')
            self.builder.add_from_file(self.glade_file)
    
            # Get objects
            go = self.builder.get_object
            self.window = go('window')
    
            # Create buffer
            self.double_buffer = None
    
            # Connect signals
            self.builder.connect_signals(self)
    
            # Everything is ready
            self.window.show()
    
        def draw_something(self):
            """Draw something into the buffer"""
            db = self.double_buffer
            if db is not None:
                # Create cairo context with double buffer as is DESTINATION
                cc = cairo.Context(db)
    
                # Scale to device coordenates
                cc.scale(db.get_width(), db.get_height())
    
                # Draw a white background
                cc.set_source_rgb(1, 1, 1)
    
                # Draw something, in this case a matrix
                rows = 10
                columns = 10
                cell_size = 1.0 / rows
                line_width = 1.0
                line_width, notused = cc.device_to_user(line_width, 0.0)
    
                for i in range(rows):
                    for j in range(columns):
                        cc.rectangle(j * cell_size, i * cell_size, cell_size, cell_size)
                        cc.set_line_width(line_width)
                        cc.set_source_rgb(0, 0, 0)
                        cc.stroke()
    
                # Flush drawing actions
                db.flush()
    
            else:
                print('Invalid double buffer')
    
        def main_quit(self, widget):
            """Quit Gtk"""
            Gtk.main_quit()
    
        def on_draw(self, widget, cr):
            """Throw double buffer into widget drawable"""
    
            if self.double_buffer is not None:
                cr.set_source_surface(self.double_buffer, 0.0, 0.0)
                cr.paint()
            else:
                print('Invalid double buffer')
    
            return False
    
        def on_configure(self, widget, event, data=None):
            """Configure the double buffer based on size of the widget"""
    
            # Destroy previous buffer
            if self.double_buffer is not None:
                self.double_buffer.finish()
                self.double_buffer = None
    
            # Create a new buffer
            self.double_buffer = cairo.ImageSurface(\
                    cairo.FORMAT_ARGB32,
                    widget.get_allocated_width(),
                    widget.get_allocated_height()
                )
    
            # Initialize the buffer
            self.draw_something()
    
            return False
    
    if __name__ == '__main__':
        gui = MyApp()
        Gtk.main()
    

    Glade文件( test.glade ):

    <?xml version="1.0" encoding="UTF-8"?>
    <interface>
      <!-- interface-requires gtk+ 3.0 -->
      <object class="GtkWindow" id="window">
        <property name="can_focus">False</property>
        <property name="window_position">center-always</property>
        <property name="default_width">800</property>
        <property name="default_height">600</property>
        <signal name="destroy" handler="main_quit" swapped="no"/>
        <child>
          <object class="GtkDrawingArea" id="drawingarea1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <signal name="draw" handler="on_draw" swapped="no"/>
            <signal name="configure-event" handler="on_configure" swapped="no"/>
          </object>
        </child>
      </object>
    </interface>
    

    依存关系:

    Python 2:

    sudo apt-get install python-cairo
    

    Python 3:

    sudo apt-get install python3-gi-cairo
    

    现在执行:

    python test.py
    

    要么

    python3 test.py
    

    看起来像什么:

    在此处输入图片说明

    可以在http://cairographics.org/documentation/pycairo/3/reference/index.html中找到有关cairo的所有文档。

    亲切的问候



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看