Bugfixes:

- GraphicsView.render now correctly invokes GraphicsScene.prepareForPaint
 - Fixed RemoteGraphicsView renderer to use new PyQt QImage API.
 - multiprocess.Process now pipes stdout/err directly to console when in debugging mode
This commit is contained in:
Luke Campagnola 2013-11-06 23:14:27 -05:00
parent ea8079334f
commit 31928e70a5
6 changed files with 33 additions and 12 deletions

View File

@ -13,7 +13,8 @@ from pyqtgraph.widgets.RemoteGraphicsView import RemoteGraphicsView
app = pg.mkQApp() app = pg.mkQApp()
## Create the widget ## Create the widget
v = RemoteGraphicsView(debug=False) v = RemoteGraphicsView(debug=False) # setting debug=True causes both processes to print information
# about interprocess communication
v.show() v.show()
v.setWindowTitle('pyqtgraph example: RemoteGraphicsView') v.setWindowTitle('pyqtgraph example: RemoteGraphicsView')

View File

@ -311,13 +311,15 @@ def image(*args, **kargs):
return w return w
show = image ## for backward compatibility show = image ## for backward compatibility
def dbg(): def dbg(*args, **kwds):
""" """
Create a console window and begin watching for exceptions. Create a console window and begin watching for exceptions.
All arguments are passed to :func:`ConsoleWidget.__init__() <pyqtgraph.console.ConsoleWidget.__init__>`.
""" """
mkQApp() mkQApp()
from . import console from . import console
c = console.ConsoleWidget() c = console.ConsoleWidget(*args, **kwds)
c.catchAllExceptions() c.catchAllExceptions()
c.show() c.show()
global consoles global consoles

View File

@ -20,10 +20,8 @@ if __name__ == '__main__':
if opts.pop('pyside', False): if opts.pop('pyside', False):
import PySide import PySide
#import pyqtgraph
#import pyqtgraph.multiprocess.processes
targetStr = opts.pop('targetStr') targetStr = opts.pop('targetStr')
target = pickle.loads(targetStr) ## unpickling the target should import everything we need target = pickle.loads(targetStr) ## unpickling the target should import everything we need
#target(name, port, authkey, ppid)
target(**opts) ## Send all other options to the target function target(**opts) ## Send all other options to the target function
sys.exit(0) sys.exit(0)

View File

@ -48,9 +48,10 @@ class Process(RemoteEventHandler):
it must be picklable (bound methods are not). it must be picklable (bound methods are not).
copySysPath If True, copy the contents of sys.path to the remote process copySysPath If True, copy the contents of sys.path to the remote process
debug If True, print detailed information about communication debug If True, print detailed information about communication
with the child process. with the child process. Note that this option may cause
strange behavior on some systems due to a python bug:
http://bugs.python.org/issue3905
============ ============================================================= ============ =============================================================
""" """
if target is None: if target is None:
target = startEventLoop target = startEventLoop
@ -82,7 +83,13 @@ class Process(RemoteEventHandler):
## note: we need all three streams to have their own PIPE due to this bug: ## note: we need all three streams to have their own PIPE due to this bug:
## http://bugs.python.org/issue3905 ## http://bugs.python.org/issue3905
self.proc = subprocess.Popen((executable, bootstrap), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if debug is True: # when debugging, we need to keep the usual stdout
stdout = sys.stdout
stderr = sys.stderr
else:
stdout = subprocess.PIPE
stderr = subprocess.PIPE
self.proc = subprocess.Popen((executable, bootstrap), stdin=subprocess.PIPE, stdout=stdout, stderr=stderr)
targetStr = pickle.dumps(target) ## double-pickle target so that child has a chance to targetStr = pickle.dumps(target) ## double-pickle target so that child has a chance to
## set its sys.path properly before unpickling the target ## set its sys.path properly before unpickling the target

View File

@ -147,6 +147,11 @@ class GraphicsView(QtGui.QGraphicsView):
#print "GV: paint", ev.rect() #print "GV: paint", ev.rect()
return QtGui.QGraphicsView.paintEvent(self, ev) return QtGui.QGraphicsView.paintEvent(self, ev)
def render(self, *args, **kwds):
self.scene().prepareForPaint()
return QtGui.QGraphicsView.render(self, *args, **kwds)
def close(self): def close(self):
self.centralWidget = None self.centralWidget = None
self.scene().clear() self.scene().clear()

View File

@ -18,12 +18,15 @@ class RemoteGraphicsView(QtGui.QWidget):
""" """
def __init__(self, parent=None, *args, **kwds): def __init__(self, parent=None, *args, **kwds):
"""
The keyword arguments 'debug' and 'name', if specified, are passed to QtProcess.__init__().
"""
self._img = None self._img = None
self._imgReq = None self._imgReq = None
self._sizeHint = (640,480) ## no clue why this is needed, but it seems to be the default sizeHint for GraphicsView. self._sizeHint = (640,480) ## no clue why this is needed, but it seems to be the default sizeHint for GraphicsView.
## without it, the widget will not compete for space against another GraphicsView. ## without it, the widget will not compete for space against another GraphicsView.
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self._proc = mp.QtProcess(debug=kwds.pop('debug', False)) self._proc = mp.QtProcess(debug=kwds.pop('debug', False), name=kwds.pop('name', None))
self.pg = self._proc._import('pyqtgraph') self.pg = self._proc._import('pyqtgraph')
self.pg.setConfigOptions(**self.pg.CONFIG_OPTIONS) self.pg.setConfigOptions(**self.pg.CONFIG_OPTIONS)
rpgRemote = self._proc._import('pyqtgraph.widgets.RemoteGraphicsView') rpgRemote = self._proc._import('pyqtgraph.widgets.RemoteGraphicsView')
@ -123,6 +126,7 @@ class Renderer(GraphicsView):
def __init__(self, *args, **kwds): def __init__(self, *args, **kwds):
## Create shared memory for rendered image ## Create shared memory for rendered image
#pg.dbg(namespace={'r': self})
if sys.platform.startswith('win'): if sys.platform.startswith('win'):
self.shmtag = "pyqtgraph_shmem_" + ''.join([chr((random.getrandbits(20)%25) + 97) for i in range(20)]) self.shmtag = "pyqtgraph_shmem_" + ''.join([chr((random.getrandbits(20)%25) + 97) for i in range(20)])
self.shm = mmap.mmap(-1, mmap.PAGESIZE, self.shmtag) # use anonymous mmap on windows self.shm = mmap.mmap(-1, mmap.PAGESIZE, self.shmtag) # use anonymous mmap on windows
@ -184,7 +188,11 @@ class Renderer(GraphicsView):
self.img = QtGui.QImage(ch, self.width(), self.height(), QtGui.QImage.Format_ARGB32) self.img = QtGui.QImage(ch, self.width(), self.height(), QtGui.QImage.Format_ARGB32)
else: else:
address = ctypes.addressof(ctypes.c_char.from_buffer(self.shm, 0)) address = ctypes.addressof(ctypes.c_char.from_buffer(self.shm, 0))
try:
self.img = QtGui.QImage(sip.voidptr(address), self.width(), self.height(), QtGui.QImage.Format_ARGB32) self.img = QtGui.QImage(sip.voidptr(address), self.width(), self.height(), QtGui.QImage.Format_ARGB32)
except TypeError:
# different versions of pyqt have different requirements here..
self.img = QtGui.QImage(memoryview(buffer(self.shm)), self.width(), self.height(), QtGui.QImage.Format_ARGB32)
self.img.fill(0xffffffff) self.img.fill(0xffffffff)
p = QtGui.QPainter(self.img) p = QtGui.QPainter(self.img)
self.render(p, self.viewRect(), self.rect()) self.render(p, self.viewRect(), self.rect())