如何并行绘制到 PaintDC 和 MemoryDC 中?

how to draw into a PaintDC and MemoryDC in parallel?

我正在寻找一段代码,如何并行使用 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

imgbufself.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: 为什么我要在线程中构建位图:
我有一个 class 为给定的 window 大小、缩放级别和 lat/lon-coordinate 提供位图(显示 OpenStreetMap-tiles)。 class 还将 GPS 轨迹和点列表绘制到 map/bitmap 上。 因为位图的尺寸高于 window 尺寸,所以我可以将位图移动到 window 下,而无需构建新的位图。 为了移动位图,调用 dc.DrawBitmap(imgbuf, sx, sy) 并稍微改变 (sx, sy) 的值。每个新剪辑需要 0.1 毫秒。构建新位图最多需要 150 毫秒。
从一个位置滚动到另一个位置时,滚动非常平滑,直到需要新的位图。
如果可以准备新的位图,在 old 位图上滚动时,应该可以在长距离上连续平滑滚动。

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