2012-04-22 64 views
3

我想用PyGObject和python 3编写简单的图形编辑器。 我需要使用鼠标绘制不同颜色和宽度的线条。我发现很多例子,如this,但没有更复杂的。在PyGobject(python3)中绘图

如何在'绘制'事件之间保存绘制图像?是否有渐进的绘图方式,或者我必须在每个“绘制”事件上重新绘制窗格?我发现我可以保存路径,但我怎样才能保存画出的线条的宽度和颜色? “draw”回调之外有没有创建图像的方法,只能在回调中应用(绘制)它?

Here是我现在所拥有的。

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

你可以在Qt的检查潦草例如获得的逻辑。我也会这样做。在内部拥有的cairo_surface中绘制,然后在每个绘制信号中,将缓冲区复制到窗口小部件上下文中,然后完成, – erick2red 2012-04-25 12:27:16

回答

6

您需要使用双缓冲技术:

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

那就是你有一个形象,你画在该图像:该图像是“幕后”缓冲区。你可以有很多方法来绘制图像。 然后,在回应'draw'信号的回调函数上,也就是实际绘制图形内存的方法,您只需抛出“幕后”图像。

理论代码(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 

是什么样子:

enter image description here

所有开罗文档可以在http://cairographics.org/documentation/pycairo/3/reference/index.html

上述发现是我用C在很久以前为GTK 2.16示例的端口,你可以检查它,但在西班牙:

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

亲切的问候