如何并行绘制到PaintDC和MemoryDC?

时间:2017-04-20 15:37:19

标签: multithreading wxpython device-context

我正在寻找一些代码,如何并行使用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.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:为什么我要在线程中构建位图:
我有一个类为给定的窗口大小,缩放级别和纬度/经度坐标提供位图(显示OpenStreetMap-tiles)。 该课程还将GPS轨迹和点列表绘制到地图/位图上。 因为位图的尺寸高于窗口尺寸,所以我可以在窗口下移动位图而不构建新的位图。 要移动位图,将调用dc.DrawBitmap(imgbuf,sx​​,sy),并为(sx,sy)略微更改值。每个新剪辑需要0.1ms。构建新的位图需要150毫秒 当从一个位置滚动时,它会滚动得非常平滑,直到需要新的位图。
如果可以准备新的位图,在滚动位图时,应该可以在长距离上连续平滑滚动。

1 个答案:

答案 0 :(得分:1)

您不应该(并且在大多数情况下,不能)从主UI线程以外的任何操作UI对象。这包括DC和位图。你到底想要完成什么?可能还有其他一些方法可以做到。