Multiprocess / RemoteGraphicsView now works in Windows
Bugfix in GraphicsItem.pixelVectors Fixes for PySide and Python3
This commit is contained in:
commit
ba7e4af422
@ -8,32 +8,32 @@ import time
|
||||
|
||||
|
||||
|
||||
print "\n=================\nStart Process"
|
||||
print("\n=================\nStart Process")
|
||||
proc = mp.Process()
|
||||
import os
|
||||
print "parent:", os.getpid(), "child:", proc.proc.pid
|
||||
print "started"
|
||||
print("parent:", os.getpid(), "child:", proc.proc.pid)
|
||||
print("started")
|
||||
rnp = proc._import('numpy')
|
||||
arr = rnp.array([1,2,3,4])
|
||||
print repr(arr)
|
||||
print str(arr)
|
||||
print "return value:", repr(arr.mean(_returnType='value'))
|
||||
print "return proxy:", repr(arr.mean(_returnType='proxy'))
|
||||
print "return auto: ", repr(arr.mean(_returnType='auto'))
|
||||
print(repr(arr))
|
||||
print(str(arr))
|
||||
print("return value:", repr(arr.mean(_returnType='value')))
|
||||
print( "return proxy:", repr(arr.mean(_returnType='proxy')))
|
||||
print( "return auto: ", repr(arr.mean(_returnType='auto')))
|
||||
proc.join()
|
||||
print "process finished"
|
||||
print( "process finished")
|
||||
|
||||
|
||||
|
||||
print "\n=================\nStart ForkedProcess"
|
||||
print( "\n=================\nStart ForkedProcess")
|
||||
proc = mp.ForkedProcess()
|
||||
rnp = proc._import('numpy')
|
||||
arr = rnp.array([1,2,3,4])
|
||||
print repr(arr)
|
||||
print str(arr)
|
||||
print repr(arr.mean())
|
||||
print( repr(arr))
|
||||
print( str(arr))
|
||||
print( repr(arr.mean()))
|
||||
proc.join()
|
||||
print "process finished"
|
||||
print( "process finished")
|
||||
|
||||
|
||||
|
||||
@ -42,10 +42,10 @@ import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
app = pg.QtGui.QApplication([])
|
||||
|
||||
print "\n=================\nStart QtProcess"
|
||||
print( "\n=================\nStart QtProcess")
|
||||
import sys
|
||||
if (sys.flags.interactive != 1):
|
||||
print " (not interactive; remote process will exit immediately.)"
|
||||
print( " (not interactive; remote process will exit immediately.)")
|
||||
proc = mp.QtProcess()
|
||||
d1 = proc.transfer(np.random.normal(size=1000))
|
||||
d2 = proc.transfer(np.random.normal(size=1000))
|
||||
|
@ -5,7 +5,7 @@ import pyqtgraph.multiprocess as mp
|
||||
import pyqtgraph as pg
|
||||
import time
|
||||
|
||||
print "\n=================\nParallelize"
|
||||
print( "\n=================\nParallelize")
|
||||
|
||||
## Do a simple task:
|
||||
## for x in range(N):
|
||||
@ -36,7 +36,7 @@ with pg.ProgressDialog('processing serially..', maximum=len(tasks)) as dlg:
|
||||
dlg += 1
|
||||
if dlg.wasCanceled():
|
||||
raise Exception('processing canceled')
|
||||
print "Serial time: %0.2f" % (time.time() - start)
|
||||
print( "Serial time: %0.2f" % (time.time() - start))
|
||||
|
||||
### Use parallelize, but force a single worker
|
||||
### (this simulates the behavior seen on windows, which lacks os.fork)
|
||||
@ -47,8 +47,8 @@ with mp.Parallelize(enumerate(tasks), results=results2, workers=1, progressDialo
|
||||
for j in xrange(size):
|
||||
tot += j * x
|
||||
tasker.results[i] = tot
|
||||
print "\nParallel time, 1 worker: %0.2f" % (time.time() - start)
|
||||
print "Results match serial: ", results2 == results
|
||||
print( "\nParallel time, 1 worker: %0.2f" % (time.time() - start))
|
||||
print( "Results match serial: %s" % str(results2 == results))
|
||||
|
||||
### Use parallelize with multiple workers
|
||||
start = time.time()
|
||||
@ -58,6 +58,6 @@ with mp.Parallelize(enumerate(tasks), results=results3, progressDialog='processi
|
||||
for j in xrange(size):
|
||||
tot += j * x
|
||||
tasker.results[i] = tot
|
||||
print "\nParallel time, %d workers: %0.2f" % (mp.Parallelize.suggestedWorkerCount(), time.time() - start)
|
||||
print "Results match serial: ", results3 == results
|
||||
print( "\nParallel time, %d workers: %0.2f" % (mp.Parallelize.suggestedWorkerCount(), time.time() - start))
|
||||
print( "Results match serial: %s" % str(results3 == results))
|
||||
|
||||
|
@ -4,7 +4,7 @@ PyQtGraph - Scientific Graphics and GUI Library for Python
|
||||
www.pyqtgraph.org
|
||||
"""
|
||||
|
||||
__version__ = None
|
||||
__version__ = '0.9.5'
|
||||
|
||||
### import all the goodies and add some helper functions for easy CLI use
|
||||
|
||||
@ -16,6 +16,9 @@ from .Qt import QtGui
|
||||
#if QtGui.QApplication.instance() is None:
|
||||
#app = QtGui.QApplication([])
|
||||
|
||||
import numpy ## pyqtgraph requires numpy
|
||||
## (import here to avoid massive error dump later on if numpy is not available)
|
||||
|
||||
import os, sys
|
||||
|
||||
## check python version
|
||||
@ -49,6 +52,8 @@ CONFIG_OPTIONS = {
|
||||
'background': (0, 0, 0), ## default background for GraphicsWidget
|
||||
'antialias': False,
|
||||
'editorCommand': None, ## command used to invoke code editor from ConsoleWidgets
|
||||
'useWeave': True, ## Use weave to speed up some operations, if it is available
|
||||
'weaveDebug': False, ## Print full error message if weave compile fails
|
||||
}
|
||||
|
||||
|
||||
|
@ -393,7 +393,7 @@ class Profiler:
|
||||
if self.delayed:
|
||||
self.msgs.append(msg2)
|
||||
else:
|
||||
print msg2
|
||||
print(msg2)
|
||||
self.t0 = ptime.time()
|
||||
self.t1 = self.t0
|
||||
|
||||
@ -410,7 +410,7 @@ class Profiler:
|
||||
if self.delayed:
|
||||
self.msgs.append(msg2)
|
||||
else:
|
||||
print msg2
|
||||
print(msg2)
|
||||
self.t1 = ptime.time() ## don't measure time it took to print
|
||||
|
||||
def finish(self, msg=None):
|
||||
@ -425,10 +425,10 @@ class Profiler:
|
||||
self.msgs.append(msg)
|
||||
if self.depth == 0:
|
||||
for line in self.msgs:
|
||||
print line
|
||||
print(line)
|
||||
Profiler.msgs = []
|
||||
else:
|
||||
print msg
|
||||
print(msg)
|
||||
Profiler.depth = self.depth
|
||||
self.finished = True
|
||||
|
||||
|
@ -220,7 +220,7 @@ def _generateItemSvg(item, nodes=None, root=None):
|
||||
## get list of sub-groups
|
||||
g2 = [n for n in g1.childNodes if isinstance(n, xml.Element) and n.tagName == 'g']
|
||||
except:
|
||||
print doc.toxml()
|
||||
print(doc.toxml())
|
||||
raise
|
||||
|
||||
|
||||
|
@ -236,7 +236,7 @@ class EvalNode(Node):
|
||||
text = fn + "\n".join([" "+l for l in str(self.text.toPlainText()).split('\n')]) + run
|
||||
exec(text)
|
||||
except:
|
||||
print "Error processing node:", self.name()
|
||||
print("Error processing node: %s" % self.name())
|
||||
raise
|
||||
return output
|
||||
|
||||
|
@ -23,6 +23,7 @@ SI_PREFIXES_ASCII = 'yzafpnum kMGTPEZY'
|
||||
|
||||
|
||||
from .Qt import QtGui, QtCore, USE_PYSIDE
|
||||
from pyqtgraph import getConfigOption
|
||||
import numpy as np
|
||||
import decimal, re
|
||||
import ctypes
|
||||
@ -30,9 +31,10 @@ import ctypes
|
||||
try:
|
||||
import scipy.ndimage
|
||||
HAVE_SCIPY = True
|
||||
WEAVE_DEBUG = getConfigOption('weaveDebug')
|
||||
try:
|
||||
import scipy.weave
|
||||
USE_WEAVE = True
|
||||
USE_WEAVE = getConfigOption('useWeave')
|
||||
except:
|
||||
USE_WEAVE = False
|
||||
except ImportError:
|
||||
@ -631,7 +633,8 @@ def rescaleData(data, scale, offset, dtype=None):
|
||||
data = newData.reshape(data.shape)
|
||||
except:
|
||||
if USE_WEAVE:
|
||||
debug.printExc("Error; disabling weave.")
|
||||
if WEAVE_DEBUG:
|
||||
debug.printExc("Error; disabling weave.")
|
||||
USE_WEAVE = False
|
||||
|
||||
#p = np.poly1d([scale, -offset*scale])
|
||||
@ -795,7 +798,7 @@ def makeARGB(data, lut=None, levels=None, scale=None, useRGBA=False):
|
||||
if levels.shape != (data.shape[-1], 2):
|
||||
raise Exception('levels must have shape (data.shape[-1], 2)')
|
||||
else:
|
||||
print levels
|
||||
print(levels)
|
||||
raise Exception("levels argument must be 1D or 2D.")
|
||||
#levels = np.array(levels)
|
||||
#if levels.shape == (2,):
|
||||
@ -1257,7 +1260,7 @@ def isocurve(data, level, connected=False, extendToEdge=False, path=False):
|
||||
points[b[1]].append([b,a])
|
||||
|
||||
## rearrange into chains
|
||||
for k in points.keys():
|
||||
for k in list(points.keys()):
|
||||
try:
|
||||
chains = points[k]
|
||||
except KeyError: ## already used this point elsewhere
|
||||
|
@ -4,7 +4,7 @@ from pyqtgraph.Point import Point
|
||||
import pyqtgraph.functions as fn
|
||||
import weakref
|
||||
from pyqtgraph.pgcollections import OrderedDict
|
||||
import operator
|
||||
import operator, sys
|
||||
|
||||
class FiniteCache(OrderedDict):
|
||||
"""Caches a finite number of objects, removing
|
||||
@ -17,7 +17,7 @@ class FiniteCache(OrderedDict):
|
||||
self.pop(item, None) # make sure item is added to end
|
||||
OrderedDict.__setitem__(self, item, val)
|
||||
while len(self) > self._length:
|
||||
del self[self.keys()[0]]
|
||||
del self[list(self.keys())[0]]
|
||||
|
||||
def __getitem__(self, item):
|
||||
val = dict.__getitem__(self, item)
|
||||
@ -197,14 +197,14 @@ class GraphicsItem(object):
|
||||
|
||||
## check local cache
|
||||
if direction is None and dt == self._pixelVectorCache[0]:
|
||||
return self._pixelVectorCache[1]
|
||||
return tuple(map(Point, self._pixelVectorCache[1])) ## return a *copy*
|
||||
|
||||
## check global cache
|
||||
key = (dt.m11(), dt.m21(), dt.m31(), dt.m12(), dt.m22(), dt.m32(), dt.m31(), dt.m32())
|
||||
pv = self._pixelVectorGlobalCache.get(key, None)
|
||||
if pv is not None:
|
||||
self._pixelVectorCache = [dt, pv]
|
||||
return pv
|
||||
return tuple(map(Point,pv)) ## return a *copy*
|
||||
|
||||
|
||||
if direction is None:
|
||||
|
@ -1783,8 +1783,7 @@ class LineSegmentROI(ROI):
|
||||
dh = h2-h1
|
||||
if dh.length() == 0:
|
||||
return p
|
||||
pxv = self.pixelVectors(h2-h1)[1]
|
||||
|
||||
pxv = self.pixelVectors(dh)[1]
|
||||
if pxv is None:
|
||||
return p
|
||||
|
||||
@ -1809,7 +1808,7 @@ class LineSegmentROI(ROI):
|
||||
for i in range(len(imgPts)-1):
|
||||
d = Point(imgPts[i+1] - imgPts[i])
|
||||
o = Point(imgPts[i])
|
||||
r = fn.affineSlice(data, shape=(int(d.length()),), vectors=[d.norm()], origin=o, axes=axes, order=1)
|
||||
r = fn.affineSlice(data, shape=(int(d.length()),), vectors=[Point(d.norm())], origin=o, axes=axes, order=1)
|
||||
rgns.append(r)
|
||||
|
||||
return np.concatenate(rgns, axis=axes[0])
|
||||
|
@ -41,7 +41,7 @@ def drawSymbol(painter, symbol, size, pen, brush):
|
||||
if isinstance(symbol, basestring):
|
||||
symbol = Symbols[symbol]
|
||||
if np.isscalar(symbol):
|
||||
symbol = Symbols.values()[symbol % len(Symbols)]
|
||||
symbol = list(Symbols.values())[symbol % len(Symbols)]
|
||||
painter.drawPath(symbol)
|
||||
|
||||
|
||||
|
@ -1198,7 +1198,7 @@ class ViewBox(GraphicsWidget):
|
||||
if ViewBox is None: ## can happen as python is shutting down
|
||||
return
|
||||
## Called with ID and name of view (the view itself is no longer available)
|
||||
for v in ViewBox.AllViews.keys():
|
||||
for v in list(ViewBox.AllViews.keys()):
|
||||
if id(v) == vid:
|
||||
ViewBox.AllViews.pop(v)
|
||||
break
|
||||
|
@ -2,15 +2,23 @@
|
||||
import sys, pickle, os
|
||||
|
||||
if __name__ == '__main__':
|
||||
os.setpgrp() ## prevents signals (notably keyboard interrupt) being forwarded from parent to this process
|
||||
name, port, authkey, targetStr, path = pickle.load(sys.stdin)
|
||||
if hasattr(os, 'setpgrp'):
|
||||
os.setpgrp() ## prevents signals (notably keyboard interrupt) being forwarded from parent to this process
|
||||
if sys.version[0] == '3':
|
||||
name, port, authkey, ppid, targetStr, path, pyside = pickle.load(sys.stdin.buffer)
|
||||
else:
|
||||
name, port, authkey, ppid, targetStr, path, pyside = pickle.load(sys.stdin)
|
||||
#print "key:", ' '.join([str(ord(x)) for x in authkey])
|
||||
if path is not None:
|
||||
## rewrite sys.path without assigning a new object--no idea who already has a reference to the existing list.
|
||||
while len(sys.path) > 0:
|
||||
sys.path.pop()
|
||||
sys.path.extend(path)
|
||||
|
||||
if pyside:
|
||||
import PySide
|
||||
#import pyqtgraph
|
||||
#import pyqtgraph.multiprocess.processes
|
||||
target = pickle.loads(targetStr) ## unpickling the target should import everything we need
|
||||
target(name, port, authkey)
|
||||
target(name, port, authkey, ppid)
|
||||
sys.exit(0)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import os, sys, time, multiprocessing, re
|
||||
from processes import ForkedProcess
|
||||
from remoteproxy import ClosedError
|
||||
from .processes import ForkedProcess
|
||||
from .remoteproxy import ClosedError
|
||||
|
||||
class CanceledError(Exception):
|
||||
"""Raised when the progress dialog is canceled during a processing operation."""
|
||||
@ -19,7 +19,7 @@ class Parallelize(object):
|
||||
for task in tasks:
|
||||
result = processTask(task)
|
||||
results.append(result)
|
||||
print results
|
||||
print(results)
|
||||
|
||||
|
||||
## Here is the parallelized version:
|
||||
@ -30,7 +30,7 @@ class Parallelize(object):
|
||||
for task in tasker:
|
||||
result = processTask(task)
|
||||
tasker.results.append(result)
|
||||
print results
|
||||
print(results)
|
||||
|
||||
|
||||
The only major caveat is that *result* in the example above must be picklable,
|
||||
|
@ -1,7 +1,11 @@
|
||||
from remoteproxy import RemoteEventHandler, ClosedError, NoResultError, LocalObjectProxy, ObjectProxy
|
||||
from .remoteproxy import RemoteEventHandler, ClosedError, NoResultError, LocalObjectProxy, ObjectProxy
|
||||
import subprocess, atexit, os, sys, time, random, socket, signal
|
||||
import cPickle as pickle
|
||||
import multiprocessing.connection
|
||||
from pyqtgraph.Qt import USE_PYSIDE
|
||||
try:
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import pickle
|
||||
|
||||
__all__ = ['Process', 'QtProcess', 'ForkedProcess', 'ClosedError', 'NoResultError']
|
||||
|
||||
@ -54,12 +58,13 @@ class Process(RemoteEventHandler):
|
||||
executable = sys.executable
|
||||
|
||||
## random authentication key
|
||||
authkey = ''.join([chr(random.getrandbits(7)) for i in range(20)])
|
||||
|
||||
authkey = os.urandom(20)
|
||||
#print "key:", ' '.join([str(ord(x)) for x in authkey])
|
||||
## Listen for connection from remote process (and find free port number)
|
||||
port = 10000
|
||||
while True:
|
||||
try:
|
||||
## hmac authentication appears to be broken on windows (says AuthenticationError: digest received was wrong)
|
||||
l = multiprocessing.connection.Listener(('localhost', int(port)), authkey=authkey)
|
||||
break
|
||||
except socket.error as ex:
|
||||
@ -73,7 +78,11 @@ class Process(RemoteEventHandler):
|
||||
self.proc = subprocess.Popen((executable, bootstrap), stdin=subprocess.PIPE)
|
||||
targetStr = pickle.dumps(target) ## double-pickle target so that child has a chance to
|
||||
## set its sys.path properly before unpickling the target
|
||||
pickle.dump((name+'_child', port, authkey, targetStr, sysPath), self.proc.stdin)
|
||||
pid = os.getpid() # we must sent pid to child because windows does not have getppid
|
||||
pyside = USE_PYSIDE
|
||||
|
||||
## Send everything the remote process needs to start correctly
|
||||
pickle.dump((name+'_child', port, authkey, pid, targetStr, sysPath, pyside), self.proc.stdin)
|
||||
self.proc.stdin.close()
|
||||
|
||||
## open connection for remote process
|
||||
@ -92,10 +101,11 @@ class Process(RemoteEventHandler):
|
||||
time.sleep(0.05)
|
||||
|
||||
|
||||
def startEventLoop(name, port, authkey):
|
||||
def startEventLoop(name, port, authkey, ppid):
|
||||
conn = multiprocessing.connection.Client(('localhost', int(port)), authkey=authkey)
|
||||
global HANDLER
|
||||
HANDLER = RemoteEventHandler(conn, name, os.getppid())
|
||||
#ppid = 0 if not hasattr(os, 'getppid') else os.getppid()
|
||||
HANDLER = RemoteEventHandler(conn, name, ppid)
|
||||
while True:
|
||||
try:
|
||||
HANDLER.processRequests() # exception raised when the loop should exit
|
||||
@ -161,6 +171,7 @@ class ForkedProcess(RemoteEventHandler):
|
||||
proxyId = LocalObjectProxy.registerObject(v)
|
||||
proxyIDs[k] = proxyId
|
||||
|
||||
ppid = os.getpid() # write this down now; windows doesn't have getppid
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
self.isParent = False
|
||||
@ -200,9 +211,9 @@ class ForkedProcess(RemoteEventHandler):
|
||||
if 'random' in sys.modules:
|
||||
sys.modules['random'].seed(os.getpid() ^ int(time.time()*10000%10000))
|
||||
|
||||
RemoteEventHandler.__init__(self, remoteConn, name+'_child', pid=os.getppid())
|
||||
#ppid = 0 if not hasattr(os, 'getppid') else os.getppid()
|
||||
RemoteEventHandler.__init__(self, remoteConn, name+'_child', pid=ppid)
|
||||
|
||||
ppid = os.getppid()
|
||||
self.forkedProxies = {}
|
||||
for name, proxyId in proxyIDs.iteritems():
|
||||
self.forkedProxies[name] = ObjectProxy(ppid, proxyId=proxyId, typeStr=repr(preProxy[name]))
|
||||
@ -228,7 +239,7 @@ class ForkedProcess(RemoteEventHandler):
|
||||
except ClosedError:
|
||||
break
|
||||
except:
|
||||
print "Error occurred in forked event loop:"
|
||||
print("Error occurred in forked event loop:")
|
||||
sys.excepthook(*sys.exc_info())
|
||||
sys.exit(0)
|
||||
|
||||
@ -293,7 +304,7 @@ class QtProcess(Process):
|
||||
btn.show()
|
||||
|
||||
def slot():
|
||||
print 'slot invoked on parent process'
|
||||
print('slot invoked on parent process')
|
||||
btn.clicked.connect(proxy(slot)) # be sure to send a proxy of the slot
|
||||
"""
|
||||
|
||||
@ -318,7 +329,7 @@ class QtProcess(Process):
|
||||
except ClosedError:
|
||||
self.timer.stop()
|
||||
|
||||
def startQtEventLoop(name, port, authkey):
|
||||
def startQtEventLoop(name, port, authkey, ppid):
|
||||
conn = multiprocessing.connection.Client(('localhost', int(port)), authkey=authkey)
|
||||
from pyqtgraph.Qt import QtGui, QtCore
|
||||
#from PyQt4 import QtGui, QtCore
|
||||
@ -330,7 +341,8 @@ def startQtEventLoop(name, port, authkey):
|
||||
## until it is explicitly closed by the parent process.
|
||||
|
||||
global HANDLER
|
||||
HANDLER = RemoteQtEventHandler(conn, name, os.getppid())
|
||||
#ppid = 0 if not hasattr(os, 'getppid') else os.getppid()
|
||||
HANDLER = RemoteQtEventHandler(conn, name, ppid)
|
||||
HANDLER.startEventTimer()
|
||||
app.exec_()
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
import os, __builtin__, time, sys, traceback, weakref
|
||||
import cPickle as pickle
|
||||
import os, time, sys, traceback, weakref
|
||||
import numpy as np
|
||||
try:
|
||||
import __builtin__ as builtins
|
||||
import cPickle as pickle
|
||||
except ImportError:
|
||||
import builtins
|
||||
import pickle
|
||||
|
||||
class ClosedError(Exception):
|
||||
"""Raised when an event handler receives a request to close the connection
|
||||
@ -68,7 +73,7 @@ class RemoteEventHandler(object):
|
||||
try:
|
||||
return cls.handlers[pid]
|
||||
except:
|
||||
print pid, cls.handlers
|
||||
print(pid, cls.handlers)
|
||||
raise
|
||||
|
||||
def getProxyOption(self, opt):
|
||||
@ -103,7 +108,7 @@ class RemoteEventHandler(object):
|
||||
else:
|
||||
raise
|
||||
except:
|
||||
print "Error in process %s" % self.name
|
||||
print("Error in process %s" % self.name)
|
||||
sys.excepthook(*sys.exc_info())
|
||||
|
||||
return numProcessed
|
||||
@ -181,7 +186,7 @@ class RemoteEventHandler(object):
|
||||
elif cmd == 'import':
|
||||
name = opts['module']
|
||||
fromlist = opts.get('fromlist', [])
|
||||
mod = __builtin__.__import__(name, fromlist=fromlist)
|
||||
mod = builtins.__import__(name, fromlist=fromlist)
|
||||
|
||||
if len(fromlist) == 0:
|
||||
parts = name.lstrip('.').split('.')
|
||||
@ -239,7 +244,7 @@ class RemoteEventHandler(object):
|
||||
self.send(request='result', reqId=reqId, callSync='off', opts=dict(result=result))
|
||||
|
||||
def replyError(self, reqId, *exc):
|
||||
print "error:", self.name, reqId, exc[1]
|
||||
print("error: %s %s %s" % (self.name, str(reqId), str(exc[1])))
|
||||
excStr = traceback.format_exception(*exc)
|
||||
try:
|
||||
self.send(request='error', reqId=reqId, callSync='off', opts=dict(exception=exc[1], excString=excStr))
|
||||
@ -352,9 +357,9 @@ class RemoteEventHandler(object):
|
||||
try:
|
||||
optStr = pickle.dumps(opts)
|
||||
except:
|
||||
print "==== Error pickling this object: ===="
|
||||
print opts
|
||||
print "======================================="
|
||||
print("==== Error pickling this object: ====")
|
||||
print(opts)
|
||||
print("=======================================")
|
||||
raise
|
||||
|
||||
nByteMsgs = 0
|
||||
@ -404,12 +409,12 @@ class RemoteEventHandler(object):
|
||||
#print ''.join(result)
|
||||
exc, excStr = result
|
||||
if exc is not None:
|
||||
print "===== Remote process raised exception on request: ====="
|
||||
print ''.join(excStr)
|
||||
print "===== Local Traceback to request follows: ====="
|
||||
print("===== Remote process raised exception on request: =====")
|
||||
print(''.join(excStr))
|
||||
print("===== Local Traceback to request follows: =====")
|
||||
raise exc
|
||||
else:
|
||||
print ''.join(excStr)
|
||||
print(''.join(excStr))
|
||||
raise Exception("Error getting result. See above for exception from remote process.")
|
||||
|
||||
else:
|
||||
@ -535,7 +540,7 @@ class Request(object):
|
||||
raise ClosedError()
|
||||
time.sleep(0.005)
|
||||
if timeout >= 0 and time.time() - start > timeout:
|
||||
print "Request timed out:", self.description
|
||||
print("Request timed out: %s" % self.description)
|
||||
import traceback
|
||||
traceback.print_stack()
|
||||
raise NoResultError()
|
||||
|
@ -6,10 +6,10 @@ class GLTest(QtOpenGL.QGLWidget):
|
||||
def __init__(self):
|
||||
QtOpenGL.QGLWidget.__init__(self)
|
||||
self.makeCurrent()
|
||||
print "GL version:", glGetString(GL_VERSION)
|
||||
print "MAX_TEXTURE_SIZE:", glGetIntegerv(GL_MAX_TEXTURE_SIZE)
|
||||
print "MAX_3D_TEXTURE_SIZE:", glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE)
|
||||
print "Extensions:", glGetString(GL_EXTENSIONS)
|
||||
print("GL version:" + glGetString(GL_VERSION))
|
||||
print("MAX_TEXTURE_SIZE: %d" % glGetIntegerv(GL_MAX_TEXTURE_SIZE))
|
||||
print("MAX_3D_TEXTURE_SIZE: %d" % glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE))
|
||||
print("Extensions: " + glGetString(GL_EXTENSIONS))
|
||||
|
||||
GLTest()
|
||||
|
||||
|
@ -267,14 +267,14 @@ class A(object):
|
||||
object.__init__(self)
|
||||
self.msg = msg
|
||||
def fn(self, pfx = ""):
|
||||
print pfx+"A class:", self.__class__, id(self.__class__)
|
||||
print pfx+" %%s: %d" %% self.msg
|
||||
print(pfx+"A class: %%s %%s" %% (str(self.__class__), str(id(self.__class__))))
|
||||
print(pfx+" %%s: %d" %% self.msg)
|
||||
|
||||
class B(A):
|
||||
def fn(self, pfx=""):
|
||||
print pfx+"B class:", self.__class__, id(self.__class__)
|
||||
print pfx+" %%s: %d" %% self.msg
|
||||
print pfx+" calling superclass.. (%%s)" %% id(A)
|
||||
print(pfx+"B class:", self.__class__, id(self.__class__))
|
||||
print(pfx+" %%s: %d" %% self.msg)
|
||||
print(pfx+" calling superclass.. (%%s)" %% id(A) )
|
||||
A.fn(self, " ")
|
||||
"""
|
||||
|
||||
@ -294,7 +294,7 @@ class C(A):
|
||||
A.__init__(self, msg + "(init from C)")
|
||||
|
||||
def fn():
|
||||
print "fn: %s"
|
||||
print("fn: %s")
|
||||
"""
|
||||
|
||||
open(modFile1, 'w').write(modCode1%(1,1))
|
||||
|
@ -3,7 +3,7 @@ import pyqtgraph.multiprocess as mp
|
||||
import pyqtgraph as pg
|
||||
from .GraphicsView import GraphicsView
|
||||
import numpy as np
|
||||
import mmap, tempfile, ctypes, atexit
|
||||
import mmap, tempfile, ctypes, atexit, sys, random
|
||||
|
||||
__all__ = ['RemoteGraphicsView']
|
||||
|
||||
@ -27,13 +27,15 @@ 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())
|
||||
self.setFocusPolicy(QtCore.Qt.FocusPolicy(self._view.focusPolicy()))
|
||||
self.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding)
|
||||
self.setMouseTracking(True)
|
||||
|
||||
self.shm = None
|
||||
shmFileName = self._view.shmFileName()
|
||||
self.shmFile = open(shmFileName, 'r')
|
||||
self.shm = mmap.mmap(self.shmFile.fileno(), mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_READ)
|
||||
if 'win' in sys.platform:
|
||||
self.shmtag = shmFileName
|
||||
else:
|
||||
self.shmFile = open(shmFileName, 'r')
|
||||
|
||||
self._view.sceneRendered.connect(mp.proxy(self.remoteSceneChanged)) #, callSync='off'))
|
||||
## Note: we need synchronous signals
|
||||
@ -53,11 +55,16 @@ class RemoteGraphicsView(QtGui.QWidget):
|
||||
return QtCore.QSize(*self._sizeHint)
|
||||
|
||||
def remoteSceneChanged(self, data):
|
||||
w, h, size = data
|
||||
w, h, size, newfile = data
|
||||
#self._sizeHint = (whint, hhint)
|
||||
if self.shm.size != size:
|
||||
self.shm.close()
|
||||
self.shm = mmap.mmap(self.shmFile.fileno(), size, mmap.MAP_SHARED, mmap.PROT_READ)
|
||||
if self.shm is None or self.shm.size != size:
|
||||
if self.shm is not None:
|
||||
self.shm.close()
|
||||
if 'win' in sys.platform:
|
||||
self.shmtag = newfile ## on windows, we create a new tag for every resize
|
||||
self.shm = mmap.mmap(-1, size, self.shmtag) ## can't use tmpfile on windows because the file can only be opened once.
|
||||
else:
|
||||
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()
|
||||
@ -112,13 +119,14 @@ class Renderer(GraphicsView):
|
||||
|
||||
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)
|
||||
if 'win' in sys.platform:
|
||||
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
|
||||
else:
|
||||
self.shmFile = tempfile.NamedTemporaryFile(prefix='pyqtgraph_shmem_')
|
||||
self.shmFile.write('\x00' * mmap.PAGESIZE)
|
||||
fd = self.shmFile.fileno()
|
||||
self.shm = mmap.mmap(fd, mmap.PAGESIZE, mmap.MAP_SHARED, mmap.PROT_WRITE)
|
||||
atexit.register(self.close)
|
||||
|
||||
GraphicsView.__init__(self, *args, **kwds)
|
||||
@ -130,10 +138,14 @@ class Renderer(GraphicsView):
|
||||
|
||||
def close(self):
|
||||
self.shm.close()
|
||||
self.shmFile.close()
|
||||
if 'win' not in sys.platform:
|
||||
self.shmFile.close()
|
||||
|
||||
def shmFileName(self):
|
||||
return self.shmFile.name
|
||||
if 'win' in sys.platform:
|
||||
return self.shmtag
|
||||
else:
|
||||
return self.shmFile.name
|
||||
|
||||
def update(self):
|
||||
self.img = None
|
||||
@ -152,7 +164,14 @@ class Renderer(GraphicsView):
|
||||
return
|
||||
size = self.width() * self.height() * 4
|
||||
if size > self.shm.size():
|
||||
self.shm.resize(size)
|
||||
if 'win' in sys.platform:
|
||||
## windows says "WindowsError: [Error 87] the parameter is incorrect" if we try to resize the mmap
|
||||
self.shm.close()
|
||||
## it also says (sometimes) 'access is denied' if we try to reuse the tag.
|
||||
self.shmtag = "pyqtgraph_shmem_" + ''.join([chr((random.getrandbits(20)%25) + 97) for i in range(20)])
|
||||
self.shm = mmap.mmap(-1, size, self.shmtag)
|
||||
else:
|
||||
self.shm.resize(size)
|
||||
address = ctypes.addressof(ctypes.c_char.from_buffer(self.shm, 0))
|
||||
|
||||
## render the scene directly to shared memory
|
||||
@ -161,7 +180,7 @@ class Renderer(GraphicsView):
|
||||
p = QtGui.QPainter(self.img)
|
||||
self.render(p, self.viewRect(), self.rect())
|
||||
p.end()
|
||||
self.sceneRendered.emit((self.width(), self.height(), self.shm.size()))
|
||||
self.sceneRendered.emit((self.width(), self.height(), self.shm.size(), self.shmFileName()))
|
||||
|
||||
def mousePressEvent(self, typ, pos, gpos, btn, btns, mods):
|
||||
typ = QtCore.QEvent.Type(typ)
|
||||
|
Loading…
Reference in New Issue
Block a user