python - 如何快速绘制 10000+ 二维图形对象?

标签 python c++ qt gtk 2d

我需要绘制数万个,将来可能会绘制数十万个简单的二维对象(圆形、矩形、一些填充的、标记的...)到一个小部件。

在同一个程序中,我需要 GUI 小部件(按钮、文本输入、复选框)。

我用 C++ 和 Python 尝试了 Gtk、Qt 和 SDL。第一个结果是意料之中的:C++ 和 Python 表现出相同的性能,因为它们在后端调用相同的 C 或 C++ 例程。

第二个结果是没有一个库有很大的不同。在数量上:22500 个矩形 (150*150) 需要大约一秒钟的时间来更新。由于 (a) 新数据,即更多矩形,以及 (b) 用户交互,即缩放、平移等,将不断更新,因此一秒钟太长了!

什么是更快的方法。非常感谢小例子。 Python 和 C++ 很好。其他库应该可以在 Linux 上轻松访问和安装。

也许我只是做错了。

附言。我没有发布我的测试代码,因为我不想让答案产生偏见。而且我不希望我的代码被更正,我想要最快的方法...

编辑:

好的,我将添加我的 gtk 测试:

#!/bin/env python2

import gtk
import gobject
import gtk.gdk

class GraphWidget(gtk.DrawingArea):
    __gsignals__ = {
        'expose-event': 'override',
        'clicked' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, (gobject.TYPE_STRING, gtk.gdk.Event))
        }

    def __init__(self,window):
        gtk.DrawingArea.__init__(self)
        #self.win = window
        self.zoom_ratio = 1.0
        self.dx = 40
        self.dy = 40

    def do_expose_event(self, event):
        cr = self.window.cairo_create()
        cr.set_source_rgba(1.0, 0.9, 0.8, 1.0)
        cr.paint()

        cr.translate(self.dx, self.dy)
        cr.scale(self.zoom_ratio,self.zoom_ratio)

        self.draw(cr)

    def draw(self, cr):
        n = 150
        cr.set_source_rgba(0.,1.,1.,1.0)
        for i in range(n):
            for j in range(n):
                cr.arc(i*30, j*30, 10, 0, 6.2832)

                cr.close_path()
                cr.fill()

        cr.set_source_rgba(0.,0.,1.,1.0)
        for i in range(n):
            for j in range(n):
                cr.arc(i*30, j*30, 10, 0, 6.2832)
                cr.move_to(i*30-10, j*30)
                cr.show_text("hu")
                cr.stroke()

    def on_zoom(self, zoom_factor):
        self.zoom_ratio *= zoom_factor
        self.queue_draw()

    def on_translate(self,dx,dy):
        self.dx += dx
        self.dy += dy
        self.queue_draw()

class TestWindow(gtk.Window):
    def __init__(self):
        gtk.Window.__init__(self)

        self.widget = GraphWidget(self)

        self.add(self.widget)

        self.show_all()

        # connect key press events

        self.connect('key-press-event', self.on_key_press_event)
        self.connect('destroy', gtk.main_quit)

        self.widget.queue_draw()

    def on_key_press_event(self, widget, event):

        if event.keyval == gtk.keysyms.space and not (event.state & gtk.gdk.CONTROL_MASK):
            self.on_run(widget)
            return True
        elif event.keyval == gtk.keysyms.r:
            self.on_refresh(widget)
            return True
        elif event.keyval == gtk.keysyms.Left:
            self.widget.on_translate(-100, 0)
        elif event.keyval == gtk.keysyms.Right:
            self.widget.on_translate(100, 0)
        elif event.keyval == gtk.keysyms.Up:
            self.widget.on_translate(0, -100)
        elif event.keyval == gtk.keysyms.Down:
            self.widget.on_translate(0, 100)
        elif event.keyval == gtk.keysyms.Page_Down:
            self.widget.on_zoom(0.7)
        elif event.keyval == gtk.keysyms.Page_Up:
            self.widget.on_zoom(1.3)

if __name__ == '__main__':
    win = TestWindow()
    gtk.main()

以及 SDL 实验:

#!/usr/bin/env python

import sdl2
import sdl2.ext as sdl2ext

dx = 0
dy = 0
zoom_factor = 1.
n_objects = 150

sdl2ext.init()

window = sdl2ext.Window('hallo', 
                        size=(800, 600), 
                        flags= sdl2.SDL_WINDOW_RESIZABLE)
window.show()

renderer = sdl2ext.RenderContext(window)
renderer.color = sdl2ext.Color(255,155,25)

def draw():
    renderer.clear()
    for i in xrange(n_objects):
        for j in xrange(n_objects):
            renderer.fill([int((i*30+dx)*zoom_factor), 
                           int((j*30+dy)*zoom_factor), 
                           int(20*zoom_factor), 
                           int(20*zoom_factor)], 
                          sdl2ext.Color(255,25,55))
            renderer.draw_rect([int((i*30+dx)*zoom_factor), 
                                int((j*30+dy)*zoom_factor), 
                                int(20*zoom_factor), 
                                int(20*zoom_factor)], 
                               sdl2ext.Color(255,255,255))

    renderer.present()

draw()

running = True
while running:
    for e in sdl2ext.get_events():
        if e.type == sdl2.SDL_QUIT:
            running = False
            break
        if e.type == sdl2.SDL_KEYDOWN:
            if e.key.keysym.sym == sdl2.SDLK_ESCAPE:
                running = False
                break
            elif e.key.keysym.sym == sdl2.SDLK_RIGHT:
                dx += 50
                draw()
            elif e.key.keysym.sym == sdl2.SDLK_LEFT:
                dx -= 50
                draw()
            elif e.key.keysym.sym == sdl2.SDLK_UP:
                dy += 50
                draw()
            elif e.key.keysym.sym == sdl2.SDLK_DOWN:
                dy -= 50
                draw()
            elif e.key.keysym.sym == sdl2.SDLK_PAGEUP:
                zoom_factor *= 1.2
                draw()
            elif e.key.keysym.sym == sdl2.SDLK_PAGEDOWN:
                zoom_factor /= 1.2
                draw()

Qt 测试是我同事做的,所以我现在没有代码...

最佳答案

看起来您正在绘制每个对象,无论它是否在可视区域中。您是否考虑过存储每个对象的位置数组,然后在绘制之前测试每个对象以确定它是否在屏幕的可视区域中?

我做了一个快速而肮脏的测试来尝试一下(我敢肯定,代码可能会更干净),并且它只绘制可见的内容响应更快:

首先,我创建了一些变量来确定可见边界(每次移动和缩放、调整窗口大小等时,您都会更新边界):

boundX = [-60, 160]
boundY = [-60, 160]

在绘制事件中,我在绘制之前检查位置是否在边界内。我还整合了你的循环以提高效率,你可以在同一次迭代中绘制和添加文本。即使使用 n = 50000 进行测试,也比之前的响应更快。

def draw(self, cr):
    n = 150

    for i in range(n):
        if min(boundX) < i * 30 < max(boundX):
            for j in range(n):
                if min(boundY) < j * 30 < max(boundY):
                    cr.set_source_rgba(0.,1.,1.,1.0)
                    cr.arc(i*30, j*30, 10, 0, 6.2832)

                    cr.close_path()
                    cr.fill()

                    cr.set_source_rgba(0.,0.,1.,1.0)
                    cr.arc(i*30, j*30, 10, 0, 6.2832)
                    cr.move_to(i*30-10, j*30)
                    cr.show_text("hu")
                    cr.stroke()

在按键事件中,只需根据小部件的翻译量来增加和减少边界:

def on_key_press_event(self, widget, event):

    if event.keyval == gtk.keysyms.space and not (event.state & gtk.gdk.CONTROL_MASK):
        self.on_run(widget)
        return True
    elif event.keyval == gtk.keysyms.r:
        self.on_refresh(widget)
        return True
    elif event.keyval == gtk.keysyms.Left:
        boundX[0] += 100
        boundX[1] += 100
        self.widget.on_translate(-100, 0)
    elif event.keyval == gtk.keysyms.Right:
        boundX[0] -= 100
        boundX[1] -= 100
        self.widget.on_translate(100, 0)
    elif event.keyval == gtk.keysyms.Up:
        boundY[0] += 100
        boundY[1] += 100
        self.widget.on_translate(0, -100)
    elif event.keyval == gtk.keysyms.Down:
        boundY[0] -= 100
        boundY[1] -= 100
        self.widget.on_translate(0, 100)
    elif event.keyval == gtk.keysyms.Page_Down:
        self.widget.on_zoom(0.7)
    elif event.keyval == gtk.keysyms.Page_Up:
        self.widget.on_zoom(1.3)

关于python - 如何快速绘制 10000+ 二维图形对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21437045/

相关文章:

python - 等待应用程序窗口 : pywinauto. timings.WaitUntilPasses in Python

python - Pyqtgraph 强制轴标签具有小数点

c++ - 将元组成员包装在其他模板化类型中

c++ - 使用多个框架给出 clang : error: linker command failed with exit code 1

qt - 为 QWebEngineView 模拟鼠标点击

python - 数组_a = 数组_b[ :] but changing a changes b aswell (numpy)

python - pygame key.set_repeat 不工作

c++ - 使用空标记结构,编译器声称我缺少初始化器,但我不是

c++ - Qml没有收到基类成员变量的更新值

c++ - 部署 Qt 应用程序时缺少功能