Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • Home
  • SEARCH
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 7932587
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: June 3, 20262026-06-03T21:03:50+00:00 2026-06-03T21:03:50+00:00

I’m trying to write simple graphic editor using PyGObject and python 3. I need

  • 0

I’m trying to write simple graphic editor using PyGObject and python 3.
I need to draw lines with different color and width using mouse. I found many examples like this but nothing more complex.

How do I save drawn image between ‘draw’ events? Is there incremental way of drawing or do I have to redraw pane on each ‘draw’ event? I found out that I can save path but how can I save width and colors of drawn lines? Is there way create image outside ‘draw’ callback and only apply (draw) it inside callback?

Here is what I have for now.

#!/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()

Sorry for my English, it is not my native language.

  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-06-03T21:03:52+00:00Added an answer on June 3, 2026 at 9:03 pm

    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:

    enter image description here

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

    Kind regards

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I have thousands of HTML files to process using Groovy/Java and I need to
I'm making a simple page using Google Maps API 3. My first. One marker
I am trying to understand how to use SyndicationItem to display feed which is
Basically, what I'm trying to create is a page of div tags, each has
I'm new to using the Perl treebuilder module for HTML parsing and can't figure
link Im having trouble converting the html entites into html characters, (&# 8217;) i
That's pretty much it. I'm using Nokogiri to scrape a web page what has
I have just tried to save a simple *.rtf file with some websites and
I have a string like this: La Torre Eiffel paragonata all&#8217;Everest What PHP function
I am reading a book about Javascript and jQuery and using one of the

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.