gpt4 book ai didi

performance - 开罗+橡皮筋选择: gui very very slow

转载 作者:行者123 更新时间:2023-12-04 02:31:24 26 4
gpt4 key购买 nike

我正在开发一个用 Cairo + Gtk 编写的应用程序。请注意,由于逆向兼容性问题,我不得不使用 Python 作为编程语言,使用 PyGTK 作为包装器,并使用 GTK 库 v.2.24。没有机会使用 C/C++ 和/或 GTK3!

我的应用程序需要在每次调用 expose 方法时(很明显)在屏幕上(重新)绘制大量数据。

我只是给用户一个机会,让他们手动选择一些以前用 Cairo 绘制的对象。由于我是在 gtk.DrawingAreas 上绘图,看来我必须手动实现橡皮筋选择功能。

问题是:

有没有办法在每次鼠标移动时重绘橡皮筋矩形,避免重绘屏幕上的所有其他对象?

出于性能原因,我将重绘选择矩形。

由于大量的图形对象,我的 GUI 非常慢。不幸的是,尽管进行了多次尝试,我别无选择:全部重绘或重绘任何内容!

我首先想到的是:有什么方法可以在具有大部分数据的 DrawingArea 和鼠标光标之间覆盖一个中间层?通过调用 queue_draw_area() 函数没有性能提升。

下面是一个简单的、自包含的代码示例:显然,在这种情况下,我只使用 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()

谢谢!

最佳答案

使用 Gtk+/Cairo 绘图时的一些一般提示:

  • 只画必要的部分。这个想法是跟踪已更改的区域,并仅重绘那些区域。 Gdk 会自动为您部分完成此操作。当它调用 expose(在 Gtk3 draw 中)时,它会应用剪贴蒙版,因此您的绘图只会更改“无效”像素。
  • 您可以使用 GdkWindow.invalidate_rect 告诉 Gdk 哪些区域应该重绘。现在您对 widget.queue_draw 的调用会使整个窗口无效,因此您绘制了太多像素。
  • 如果您在非无效区域中有复杂的元素,您仍然会在 expose 中绘制/计算它们 - 它们只是不会出现在屏幕上。为此,您可以检查 event.area(一个 GdkRectangle)。如果您的元素不与该区域相交,则不必费心绘制它们,因为这些像素无论如何都会被剪掉。
    • 这是一个权衡。如果您计算元素是否可见,有时会节省很多。如果节省大量几何计算,有时只绘制几个额外的像素会更快。你必须根据具体情况做出决定。
  • 可能会为每个 invalidate_rect 调用一次 Expose,但当您使几个小的/重叠的 rect 无效时,Gdk 也有可能识别出来,并且只用一个较大的 rect 调用它一次。
  • 您不应混合使用“原始”Gdk 和 cairo 调用。 Gdk 调用(在 gc 上运行)将在 Gtk3 中消失。现在你说你只在 Gtk2 中写这个,但是如果你想有一天在另一个项目中重用代码,如果你现在用 Cairo 写它会有所帮助。也可能存在一些性能差异。
  • 你应该知道抗锯齿。默认情况下,开罗的一切都是抗锯齿的(我不知道你是否可以关闭它)。大多数时候这是一件好事,但有时清晰的像素看起来更好 - 而且它们也快得多。如果您想要非抗锯齿矩形,请在整数坐标上绘制填充矩形,并在半整数坐标上绘制描边矩形(1 像素线)。
  • 在您的具体示例中,您的 Pixmap 似乎是不必要的。不过,您可以尝试的是在它发生变化时将您的东西渲染到像素图或 cairo 表面,然后在 expose 中从像素图中复制无效区域,并在顶部绘制橡皮筋。然而,我发现直接画图通常更容易、更快捷。如果您正在执行此手动缓冲,您可能需要考虑禁用内置双缓冲 (GtkWidget.set_double_buffered) 和背景的自动绘制 (GtkWidget.set_app_paintable).

这是我制作的一个小程序:rubberband.py .我从我的一个项目中获取了代码,并添加了几个可以选择的圆圈。希望您可以以此为起点。

关于performance - 开罗+橡皮筋选择: gui very very slow,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16079617/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com