Drawing in PyGobject (python3)

Deadly 提交于 2019-11-29 11:06:21

You need to use a Double Buffer technique:

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

That is you have an image, and you draw over that image: that image is the "behind the scenes" buffer. You can have a lot of methods that draw something to that image. Then, on the callback that responds to 'draw' signal, that is, the method that actually draws something to the graphics memory you just throw your "behind the scenes" image.

Theory in code (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 file (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>

Dependencies:

Python 2:

sudo apt-get install python-cairo

Python 3:

sudo apt-get install python3-gi-cairo

Now execute with:

python test.py

or

python3 test.py

What it looks like:

All the documentation for cairo can be found in http://cairographics.org/documentation/pycairo/3/reference/index.html

The above is a port of an example I've made in C long ago for Gtk 2.16, you can check it too, but is in Spanish:

http://carlos.jenkins.co.cr/gtkcairo

Kind regards

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!