Basic functionality in RemoteGraphicsView is working.
This commit is contained in:
parent
5b6f77be58
commit
96202aed3e
@ -7,10 +7,9 @@ app = pg.mkQApp()
|
||||
v = pg.RemoteGraphicsView()
|
||||
v.show()
|
||||
|
||||
QtGui = v.pg.QtGui
|
||||
rect = QtGui.QGraphicsRectItem(0,0,10,10)
|
||||
rect.setPen(QtGui.QPen(QtGui.QColor(255,255,0)))
|
||||
v.scene().addItem(rect)
|
||||
plt = v.pg.PlotItem()
|
||||
v.setCentralItem(plt)
|
||||
plt.plot([1,4,2,3,6,2,3,4,2,3], pen='g')
|
||||
|
||||
|
||||
|
||||
|
@ -511,24 +511,31 @@ class LocalObjectProxy(object):
|
||||
del cls.proxiedObjects[pid]
|
||||
#print "release:", cls.proxiedObjects
|
||||
|
||||
def __init__(self, obj):
|
||||
def __init__(self, obj, **opts):
|
||||
"""
|
||||
Create a 'local' proxy object that, when sent to a remote host,
|
||||
will appear as a normal ObjectProxy to *obj*.
|
||||
Any extra keyword arguments are passed to proxy._setProxyOptions()
|
||||
on the remote side.
|
||||
"""
|
||||
self.processId = os.getpid()
|
||||
#self.objectId = id(obj)
|
||||
self.typeStr = repr(obj)
|
||||
#self.handler = handler
|
||||
self.obj = obj
|
||||
self.opts = opts
|
||||
|
||||
def __reduce__(self):
|
||||
## a proxy is being pickled and sent to a remote process.
|
||||
## every time this happens, a new proxy will be generated in the remote process,
|
||||
## so we keep a new ID so we can track when each is released.
|
||||
pid = LocalObjectProxy.registerObject(self.obj)
|
||||
return (unpickleObjectProxy, (self.processId, pid, self.typeStr))
|
||||
return (unpickleObjectProxy, (self.processId, pid, self.typeStr, None, self.opts))
|
||||
|
||||
## alias
|
||||
proxy = LocalObjectProxy
|
||||
|
||||
def unpickleObjectProxy(processId, proxyId, typeStr, attributes=None):
|
||||
def unpickleObjectProxy(processId, proxyId, typeStr, attributes=None, opts=None):
|
||||
if processId == os.getpid():
|
||||
obj = LocalObjectProxy.lookupProxyId(proxyId)
|
||||
if attributes is not None:
|
||||
@ -536,7 +543,10 @@ def unpickleObjectProxy(processId, proxyId, typeStr, attributes=None):
|
||||
obj = getattr(obj, attr)
|
||||
return obj
|
||||
else:
|
||||
return ObjectProxy(processId, proxyId=proxyId, typeStr=typeStr)
|
||||
proxy = ObjectProxy(processId, proxyId=proxyId, typeStr=typeStr)
|
||||
if opts is not None:
|
||||
proxy._setProxyOptions(**opts)
|
||||
return proxy
|
||||
|
||||
class ObjectProxy(object):
|
||||
"""
|
||||
|
@ -2,11 +2,18 @@ from pyqtgraph.Qt import QtGui, QtCore
|
||||
import pyqtgraph.multiprocess as mp
|
||||
import pyqtgraph as pg
|
||||
import numpy as np
|
||||
import ctypes, os
|
||||
import mmap, tempfile, ctypes, atexit
|
||||
|
||||
__all__ = ['RemoteGraphicsView']
|
||||
|
||||
class RemoteGraphicsView(QtGui.QWidget):
|
||||
"""
|
||||
Replacement for GraphicsView that does all scene management and rendering on a remote process,
|
||||
while displaying on the local widget.
|
||||
|
||||
GraphicsItems must be created by proxy to the remote process.
|
||||
|
||||
"""
|
||||
def __init__(self, parent=None, *args, **kwds):
|
||||
self._img = None
|
||||
self._imgReq = None
|
||||
@ -16,10 +23,16 @@ class RemoteGraphicsView(QtGui.QWidget):
|
||||
rpgRemote = self._proc._import('pyqtgraph.widgets.RemoteGraphicsView')
|
||||
self._view = rpgRemote.Renderer(*args, **kwds)
|
||||
self._view._setProxyOptions(deferGetattr=True)
|
||||
self.setFocusPolicy(self._view.focusPolicy())
|
||||
|
||||
shmFileName = self._view.shmFileName()
|
||||
self.shmFile = open(shmFileName, 'r')
|
||||
self.shm = mmap.mmap(self.shmFile.fileno(), mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_READ)
|
||||
|
||||
self._view.sceneRendered.connect(mp.proxy(self.remoteSceneChanged))
|
||||
|
||||
def scene(self):
|
||||
return self._view.scene()
|
||||
for method in ['scene', 'setCentralItem']:
|
||||
setattr(self, method, getattr(self._view, method))
|
||||
|
||||
def resizeEvent(self, ev):
|
||||
ret = QtGui.QWidget.resizeEvent(self, ev)
|
||||
@ -27,21 +40,63 @@ class RemoteGraphicsView(QtGui.QWidget):
|
||||
return ret
|
||||
|
||||
def remoteSceneChanged(self, data):
|
||||
self._img = pg.makeQImage(data, alpha=True)
|
||||
w, h, size = data
|
||||
if self.shm.size != size:
|
||||
self.shm.close()
|
||||
self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ)
|
||||
self.shm.seek(0)
|
||||
self._img = QtGui.QImage(self.shm.read(w*h*4), w, h, QtGui.QImage.Format_ARGB32)
|
||||
self.update()
|
||||
|
||||
def paintEvent(self, ev):
|
||||
if self._img is None:
|
||||
return
|
||||
p = QtGui.QPainter(self)
|
||||
p.drawImage(self.rect(), self._img, self.rect())
|
||||
p.drawImage(self.rect(), self._img, QtCore.QRect(0, 0, self._img.width(), self._img.height()))
|
||||
p.end()
|
||||
|
||||
def mousePressEvent(self, ev):
|
||||
self._view.mousePressEvent(ev.type(), ev.pos(), ev.globalPos(), ev.button(), int(ev.buttons()), int(ev.modifiers()), _callSync='off')
|
||||
ev.accept()
|
||||
return QtGui.QWidget.mousePressEvent(self, ev)
|
||||
|
||||
def mouseReleaseEvent(self, ev):
|
||||
self._view.mouseReleaseEvent(ev.type(), ev.pos(), ev.globalPos(), ev.button(), int(ev.buttons()), int(ev.modifiers()), _callSync='off')
|
||||
ev.accept()
|
||||
return QtGui.QWidget.mouseReleaseEvent(self, ev)
|
||||
|
||||
def mouseMoveEvent(self, ev):
|
||||
self._view.mouseMoveEvent(ev.type(), ev.pos(), ev.globalPos(), ev.button(), int(ev.buttons()), int(ev.modifiers()), _callSync='off')
|
||||
ev.accept()
|
||||
return QtGui.QWidget.mouseMoveEvent(self, ev)
|
||||
|
||||
def wheelEvent(self, ev):
|
||||
self._view.wheelEvent(ev.pos(), ev.globalPos(), ev.delta(), int(ev.buttons()), int(ev.modifiers()), ev.orientation(), _callSync='off')
|
||||
ev.accept()
|
||||
return QtGui.QWidget.wheelEvent(self, ev)
|
||||
|
||||
def keyEvent(self, ev):
|
||||
if self._view.keyEvent(ev.type(), int(ev.modifiers()), text, autorep, count):
|
||||
ev.accept()
|
||||
return QtGui.QWidget.keyEvent(self, ev)
|
||||
|
||||
|
||||
|
||||
class Renderer(pg.GraphicsView):
|
||||
|
||||
sceneRendered = QtCore.Signal(object)
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
## Create shared memory for rendered image
|
||||
#fd = os.open('/tmp/mmaptest', os.O_CREAT | os.O_TRUNC | os.O_RDWR)
|
||||
#os.write(fd, '\x00' * mmap.PAGESIZE)
|
||||
self.shmFile = tempfile.NamedTemporaryFile(prefix='pyqtgraph_shmem_')
|
||||
self.shmFile.write('\x00' * mmap.PAGESIZE)
|
||||
#fh.flush()
|
||||
fd = self.shmFile.fileno()
|
||||
self.shm = mmap.mmap(fd, mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_WRITE)
|
||||
atexit.register(self.close)
|
||||
|
||||
pg.GraphicsView.__init__(self, *args, **kwds)
|
||||
self.scene().changed.connect(self.update)
|
||||
self.img = None
|
||||
@ -49,22 +104,67 @@ class Renderer(pg.GraphicsView):
|
||||
self.renderTimer.timeout.connect(self.renderView)
|
||||
self.renderTimer.start(16)
|
||||
|
||||
def close(self):
|
||||
self.shm.close()
|
||||
self.shmFile.close()
|
||||
|
||||
def shmFileName(self):
|
||||
return self.shmFile.name
|
||||
|
||||
def update(self):
|
||||
self.img = None
|
||||
return pg.GraphicsView.update(self)
|
||||
|
||||
def resize(self, size):
|
||||
oldSize = self.size()
|
||||
pg.GraphicsView.resize(self, size)
|
||||
self.resizeEvent(QtGui.QResizeEvent(size, oldSize))
|
||||
self.update()
|
||||
|
||||
def renderView(self):
|
||||
if self.img is None:
|
||||
self.img = QtGui.QImage(self.width(), self.height(), QtGui.QImage.Format_ARGB32)
|
||||
## make sure shm is large enough and get its address
|
||||
size = self.width() * self.height() * 4
|
||||
if size > self.shm.size():
|
||||
self.shm.resize(size)
|
||||
address = ctypes.addressof(ctypes.c_char.from_buffer(self.shm, 0))
|
||||
|
||||
## render the scene directly to shared memory
|
||||
self.img = QtGui.QImage(address, self.width(), self.height(), QtGui.QImage.Format_ARGB32)
|
||||
self.img.fill(0xffffffff)
|
||||
p = QtGui.QPainter(self.img)
|
||||
self.render(p, self.viewRect(), self.rect())
|
||||
p.end()
|
||||
self.data = np.fromstring(ctypes.string_at(int(self.img.bits()), self.img.byteCount()), dtype=np.ubyte).reshape(self.height(), self.width(),4).transpose(1,0,2)
|
||||
#self.data = ctypes.string_at(int(self.img.bits()), self.img.byteCount())
|
||||
self.sceneRendered.emit(self.data)
|
||||
self.sceneRendered.emit((self.width(), self.height(), self.shm.size()))
|
||||
|
||||
def mousePressEvent(self, typ, pos, gpos, btn, btns, mods):
|
||||
typ = QtCore.QEvent.Type(typ)
|
||||
btns = QtCore.Qt.MouseButtons(btns)
|
||||
mods = QtCore.Qt.KeyboardModifiers(mods)
|
||||
return pg.GraphicsView.mousePressEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
|
||||
|
||||
def mouseMoveEvent(self, typ, pos, gpos, btn, btns, mods):
|
||||
typ = QtCore.QEvent.Type(typ)
|
||||
btns = QtCore.Qt.MouseButtons(btns)
|
||||
mods = QtCore.Qt.KeyboardModifiers(mods)
|
||||
return pg.GraphicsView.mouseMoveEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
|
||||
|
||||
def mouseReleaseEvent(self, typ, pos, gpos, btn, btns, mods):
|
||||
typ = QtCore.QEvent.Type(typ)
|
||||
btns = QtCore.Qt.MouseButtons(btns)
|
||||
mods = QtCore.Qt.KeyboardModifiers(mods)
|
||||
return pg.GraphicsView.mouseReleaseEvent(self, QtGui.QMouseEvent(typ, pos, gpos, btn, btns, mods))
|
||||
|
||||
def wheelEvent(self, pos, gpos, d, btns, mods, ori):
|
||||
btns = QtCore.Qt.MouseButtons(btns)
|
||||
mods = QtCore.Qt.KeyboardModifiers(mods)
|
||||
return pg.GraphicsView.wheelEvent(self, QtGui.QWheelEvent(pos, gpos, d, btns, mods, ori))
|
||||
|
||||
def keyEvent(self, typ, mods, text, autorep, count):
|
||||
typ = QtCore.QEvent.Type(typ)
|
||||
mods = QtCore.Qt.KeyboardModifiers(mods)
|
||||
pg.GraphicsView.keyEvent(self, QtGui.QKeyEvent(typ, mods, text, autorep, count))
|
||||
return ev.accepted()
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user