我正在开发一个用Cairo + Gtk编写的应用程序。 请注意,由于反向兼容性问题,我不得不使用Python作为编程语言,PyGTK作为包装器,使用GTK库,v.2.24。 没机会使用C / C ++和/或GTK3!
我的应用需要(重新)在屏幕上为每次调用曝光方法绘制 大 数据量(显然)。
我只是让用户有机会手动选择以前用Cairo绘制的一些对象。 由于我正在使用gtk.DrawingAreas,似乎我必须手动实现橡皮筋选择功能。
这就是问题:
有没有办法在每次鼠标移动时重绘橡皮筋矩形,避免重绘屏幕上的所有其他对象?
出于性能原因,我会重新选择 选择矩形。
由于图形对象数量很大,我的GUI非常慢。不幸的是,尽管经过多次尝试,我别无选择:重绘全部或重绘任何内容!
我想到的第一件事是:有没有办法在DrawingArea与大多数数据和鼠标光标之间叠加中间级? 通过调用queue_draw_area()函数没有性能提升。
下面是一个简单的,自包含的代码示例:很明显,在这种情况下,Ionly使用cairo绘制极其简单的图形对象。
import gtk
from gtk import gdk
class Canvas(gtk.DrawingArea):
# the list of points is passed as argument
def __init__(self, points):
super(Canvas, self).__init__()
self.points = points
self.set_size_request(400, 400)
# Coordinates of the left-top angle of the selection rect
self.startPoint = None
self.endPoint = None
# Pixmap to drawing rubber band selection
self.pixmap = None
self.connect("expose_event", self.expose_handler)
self.connect("motion_notify_event", self.mouseMove_handler)
self.set_events(gtk.gdk.EXPOSURE_MASK
| gtk.gdk.LEAVE_NOTIFY_MASK
| gtk.gdk.BUTTON_PRESS_MASK
| gtk.gdk.POINTER_MOTION_MASK
| gtk.gdk.POINTER_MOTION_HINT_MASK)
# Method to paint lines and/or rubberband on screen
def expose_handler(self, widget, event):
rect = widget.get_allocation()
w = rect.width
h = rect.height
ctx = widget.window.cairo_create()
ctx.set_line_width(7)
ctx.set_source_rgb(255, 0, 0)
ctx.save()
for i in range(0, len(self.points)):
currPoint = self.points[i]
currX = float(currPoint[0])
currY = float(currPoint[1])
nextIndex = i + 1
if (nextIndex == len(self.points)):
continue
nextPoint = self.points[nextIndex]
nextX = float(nextPoint[0])
nextY = float(nextPoint[1])
ctx.move_to(currX, currY)
ctx.line_to(nextX, nextY)
ctx.restore()
ctx.close_path()
ctx.stroke()
# rubber band
if self.pixmap != None:
width = self.endPoint[0] - self.startPoint[0]
height = self.endPoint[1] - self.startPoint[1]
if width < 0 or height < 0:
tempEndPoint = self.endPoint
self.endPoint = self.startPoint
self.startPoint = tempEndPoint
height = self.endPoint[1] - self.startPoint[1]
width = self.endPoint[0] - self.startPoint[0]
widget.window.draw_drawable(widget.get_style().fg_gc[gtk.STATE_NORMAL], self.pixmap, self.startPoint[0], self.startPoint[1], self.startPoint[0], self.startPoint[1], abs(width), abs(height))
def mouseMove_handler(self, widget, event):
x, y, state = event.window.get_pointer()
if (state & gtk.gdk.BUTTON1_MASK):
if (state & gtk.gdk.CONTROL_MASK):
if self.startPoint == None:
self.startPoint = (x,y)
self.endPoint = (x,y)
else:
self.endPoint = (x,y)
tempPixmap = gtk.gdk.Pixmap(widget.window, 400, 400)
height = self.endPoint[1] - self.startPoint[1]
width = self.endPoint[0] - self.startPoint[0]
gc = self.window.new_gc()
gc.set_foreground(self.get_colormap().alloc_color("#FF8000"))
gc.fill = gtk.gdk.STIPPLED
tempPixmap.draw_rectangle(gc, True, self.startPoint[0], self.startPoint[1], abs(width), abs(height))
self.pixmap = tempPixmap
# widget.queue_draw_area()
widget.queue_draw()
else:
if (self.pixmap != None):
self.pixmap = None
# ...do something...
# widget.queue_draw_area(self.startPoint[0], self.startPoint[1], )
widget.queue_draw()
li1 = [(20,20), (380,20), (380,380), (20,380)]
window = gtk.Window()
canvas = Canvas(li1)
window.add(canvas)
window.connect("destroy", lambda w: gtk.main_quit())
window.show_all()
gtk.main()
谢谢!
IT
答案 0 :(得分:2)
使用Gtk + / Cairo绘图时的一些一般提示:
expose
时(在Gtk3 draw
中),它会应用剪贴蒙版,因此图形只会更改“无效”像素。GdkWindow.invalidate_rect
重新绘制。现在,您对widget.queue_draw
的调用会使整个窗口无效,因此您绘制了太多像素。event.area
(GdkRectangle)。如果你的元素不与这个区域相交,你就不必费心去绘制它们了,因为这些像素都被剪裁了。
expose
中将像素图中的无效区域复制过来,并在顶部绘制橡皮筋。但是,我发现直接进行绘图通常更容易,更快捷。如果您正在进行此手动缓冲,您可能需要考虑禁用内置双缓冲(GtkWidget.set_double_buffered
)和自动绘制背景(GtkWidget.set_app_paintable
)。这是我制作的一个小程序:rubberband.py。我从我的一个项目中获取了代码并添加了几个可以选择的圈子。希望你能以此为出发点。
答案 1 :(得分:1)
我知道旧线程,但是一个模型可能会将渲染表面复制到另一个缓冲区,然后重绘无效区域。可以找到这种Julia语言技术的示例here。在实践中,似乎取得了非常好的表现。