zaq*_*est 4 python gtk cairo pygobject python-3.x
我正在尝试使用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()
Run Code Online (Sandbox Code Playgroud)
对不起我的英语,这不是我的母语.
您需要使用双缓冲技术:
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()
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
依赖关系:
Python 2:
sudo apt-get install python-cairo
Run Code Online (Sandbox Code Playgroud)
Python 3:
sudo apt-get install python3-gi-cairo
Run Code Online (Sandbox Code Playgroud)
现在执行:
python test.py
Run Code Online (Sandbox Code Playgroud)
要么
python3 test.py
Run Code Online (Sandbox Code Playgroud)
它看起来像什么:

cairo的所有文档都可以在http://cairographics.org/documentation/pycairo/3/reference/index.html中找到.
以上是我很久以前为Gtk 2.16制作的一个例子的端口,你也可以检查它,但是用西班牙语:
http://carlos.jenkins.co.cr/gtkcairo
亲切的问候