Multithreading 如何并行绘制PaintDC和MemoryDC?
我正在寻找一段代码,如何并行使用wx.PaintDC()和wx.MemoryDC。 我的wxPython版本是2.8.12,我无法将wx.PaintDC()绘制到wx.Window中,而 还有一个正在运行的线程,它将wx.MemoryDC绘制成位图 像这样:Multithreading 如何并行绘制PaintDC和MemoryDC?,multithreading,wxpython,gdi,Multithreading,Wxpython,Gdi,我正在寻找一段代码,如何并行使用wx.PaintDC()和wx.MemoryDC。 我的wxPython版本是2.8.12,我无法将wx.PaintDC()绘制到wx.Window中,而 还有一个正在运行的线程,它将wx.MemoryDC绘制成位图 像这样: def onPaint(self, evt): self.dc=wx.PaintDC(self) imgbuf, (sx, sy), self.refresh_needed=self.osm.getBitmap() self.d
def onPaint(self, evt):
self.dc=wx.PaintDC(self)
imgbuf, (sx, sy), self.refresh_needed=self.osm.getBitmap()
self.dc.DrawBitmap(imgbuf, sx, sy)
as_thread()
w, h=self.__getBitmapSize()
self.bmpbuf=wx.EmptyBitmapRGBA(w, h, 204, 204, 204, 1)
self.mdc=wx.MemoryDC()
self.mdc.SelectObject(self.bmpbuf)
[.....]
y=0
for yi in imgs:
x=0
for tn, (status, xi) in yi:
if status!=self.status["GOOD"]:
xi=wx.EmptyBitmapRGBA(256, 256, red=255, alpha=1)
if status!=self.status["INVALID"]:
needs_refresh=True
self.mdc.DrawBitmap(xi, x, y)
x+=self.ts
y+=self.ts
imgbuf和self.bmpbuf不是同一个对象
self.bmpbuf与以下内容一起复制:
w, h=self.__getBitmapSize()
buf=numpy.empty((w, h, 3), dtype=numpy.uint8)
self.bmpbuf.CopyToBuffer(buf)
self.v[handle].bmpbuf=wx.BitmapFromBuffer(w, h, buf)
但总是会出现如下错误:
[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
python: xcb_io.c:165: dequeue_pending_request: Zusicherung »!xcb_xlib_unknown_req_in_deq« nicht erfüllt.
编辑:下面是一个完整的演示脚本,它显示了问题:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import wx
import random
import time
import threading
class TestWin(wx.Window):
def __init__(self, parent, title, size):
wx.Window.__init__(self, parent)
self.Bind(wx.EVT_PAINT, self.onPaint)
self.Bind(wx.EVT_TIMER, self.onTimer)
t=threading.Thread(target=self.asThread, name="draw")
t.setDaemon(True)
t.start()
self.timer=wx.Timer(self)
self.timer.Start(100)
def onPaint(self, evt):
dc=wx.PaintDC(self)
dc.SetPen(wx.Pen("BLACK"))
dc.SetBrush(wx.Brush("BLUE"))
w, h=self.GetSize()
dc.DrawCirclePoint((random.randint(0, w), random.randint(0, h)), 5)
def onTimer(self, evt):
self.Refresh()
def asThread(self):
w, h=self.GetSize()
bmpbuf=wx.EmptyBitmapRGBA(w, h, 204, 204, 204, 1)
mdc=wx.MemoryDC()
mdc.SelectObject(bmpbuf)
time.sleep(1)
mdc.SetPen(wx.Pen("BLACK"))
mdc.SetBrush(wx.Brush("RED"))
print "now writing to MemoryDC"
while True:
#time.sleep(0.0001)
mdc.DrawCirclePoint((random.randint(0, w), random.randint(0, h)), 5)
wx.Yield()
class TestFrame(wx.Frame):
def __init__(self, parent, title, size):
wx.Frame.__init__(self, None, wx.ID_ANY, title)
win=TestWin(self, title, size)
if __name__=="__main__":
app=wx.App(False)
frame=TestFrame(None, "Test", size=(200, 200))
frame.Show()
app.MainLoop()
EDIT2:为什么要在线程中构建位图:我有一个类为给定的窗口大小、缩放级别和lat/lon坐标提供位图(显示OpenStreetMap分幅)。 该类还将在地图/位图上绘制GPS轨迹和点列表。 因为位图的尺寸高于窗口尺寸,所以我可以在窗口下移动位图,而无需构建新位图。 要移动位图,将调用dc.DrawBitmap(imgbuf,sx,sy),并稍微更改(sx,sy)的值。每个新剪辑需要0.1ms。创建新位图最多需要150毫秒。
从一个位置滚动到另一个位置时,它会非常平滑地滚动,直到需要新的位图为止。
如果可以在滚动旧位图的同时准备新位图,则可以在长距离上连续平滑滚动。您不应该(在大多数情况下,不能)从主UI线程以外的任何位置操作UI对象。这包括DC和位图。你到底想完成什么?可能还有其他方法可以做到这一点。似乎正在使用wxpython 4.0.0a参见我的编辑2……非常欢迎使用“其他方法”来平滑滚动动态创建的位图序列。如果您使用numpy数组获取和构建图像数据,则可以在工作线程上完成这一部分。转换为位图显示需要在GUI线程上进行。我尝试的一种方法是使用一些较小的位图,而不是一个较大的位图,并使用缓存在内存中保留一些N个最近使用的位图。然后,当滚动到一个边缘时,你可以使用线程获取数据,然后在EVT_空闲事件或类似事件中转换为位图,然后将它们添加到缓存中,以便它们在需要时等待EVT_绘制。好主意……我也有这个想法……使用numpy和枕头而不是wx运行我的类。也可以通过这种方式绘制地图和点列表。但是对于轨迹,我需要在位图上画线。在Python中每像素的绘制速度太慢了(我猜)。我现在已经重写了我的类,用枕头来绘制所有的图形。只需要调用一个wx函数:wx.BitmapFromBuffer(w,h,self.v[handle].bmpbuf.tostring())。