diff --git a/examples/__main__.py b/examples/__main__.py index 87673208..d8456781 100644 --- a/examples/__main__.py +++ b/examples/__main__.py @@ -68,6 +68,7 @@ examples = OrderedDict([ ('GraphicsScene', 'GraphicsScene.py'), ('Flowcharts', 'Flowchart.py'), + ('Custom Flowchart Nodes', 'FlowchartCustomNode.py'), #('Canvas', '../canvas'), #('MultiPlotWidget', 'MultiPlotWidget.py'), ]) diff --git a/examples/multiprocess.py b/examples/multiprocess.py index f6756345..ba550f7f 100644 --- a/examples/multiprocess.py +++ b/examples/multiprocess.py @@ -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)) diff --git a/examples/parallelize.py b/examples/parallelize.py index d2ba0ce0..768d6f00 100644 --- a/examples/parallelize.py +++ b/examples/parallelize.py @@ -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)) diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py index 93d9f7b8..da197aa6 100644 --- a/pyqtgraph/__init__.py +++ b/pyqtgraph/__init__.py @@ -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 } diff --git a/pyqtgraph/debug.py b/pyqtgraph/debug.py index d5f86139..7fa169a4 100644 --- a/pyqtgraph/debug.py +++ b/pyqtgraph/debug.py @@ -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 diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py index 70f1f632..587282e0 100644 --- a/pyqtgraph/exporters/SVGExporter.py +++ b/pyqtgraph/exporters/SVGExporter.py @@ -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 diff --git a/pyqtgraph/flowchart/library/Data.py b/pyqtgraph/flowchart/library/Data.py index 85ab6232..1c612e08 100644 --- a/pyqtgraph/flowchart/library/Data.py +++ b/pyqtgraph/flowchart/library/Data.py @@ -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 diff --git a/pyqtgraph/functions.py b/pyqtgraph/functions.py index 23b2580c..d5899c8c 100644 --- a/pyqtgraph/functions.py +++ b/pyqtgraph/functions.py @@ -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 @@ -1871,4 +1874,4 @@ def pseudoScatter(data, spacing=None, shuffle=True): yvals[i] = y - return yvals[np.argsort(inds)] ## un-shuffle values before returning \ No newline at end of file + return yvals[np.argsort(inds)] ## un-shuffle values before returning diff --git a/pyqtgraph/graphicsItems/GraphicsItem.py b/pyqtgraph/graphicsItems/GraphicsItem.py index 2314709d..94615fe3 100644 --- a/pyqtgraph/graphicsItems/GraphicsItem.py +++ b/pyqtgraph/graphicsItems/GraphicsItem.py @@ -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: @@ -547,4 +547,4 @@ class GraphicsItem(object): #def update(self): #self._qtBaseClass.update(self) - #print "Update:", self \ No newline at end of file + #print "Update:", self diff --git a/pyqtgraph/graphicsItems/ROI.py b/pyqtgraph/graphicsItems/ROI.py index e3f094ff..4da8fa4a 100644 --- a/pyqtgraph/graphicsItems/ROI.py +++ b/pyqtgraph/graphicsItems/ROI.py @@ -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]) diff --git a/pyqtgraph/graphicsItems/ScatterPlotItem.py b/pyqtgraph/graphicsItems/ScatterPlotItem.py index 2e41cb7c..0b422596 100644 --- a/pyqtgraph/graphicsItems/ScatterPlotItem.py +++ b/pyqtgraph/graphicsItems/ScatterPlotItem.py @@ -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) diff --git a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py index ae7298ba..f5aa03b8 100644 --- a/pyqtgraph/graphicsItems/ViewBox/ViewBox.py +++ b/pyqtgraph/graphicsItems/ViewBox/ViewBox.py @@ -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 diff --git a/pyqtgraph/multiprocess/bootstrap.py b/pyqtgraph/multiprocess/bootstrap.py index e0d1c02c..28818135 100644 --- a/pyqtgraph/multiprocess/bootstrap.py +++ b/pyqtgraph/multiprocess/bootstrap.py @@ -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) diff --git a/pyqtgraph/multiprocess/parallelizer.py b/pyqtgraph/multiprocess/parallelizer.py index 2d03c000..9925a573 100644 --- a/pyqtgraph/multiprocess/parallelizer.py +++ b/pyqtgraph/multiprocess/parallelizer.py @@ -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, diff --git a/pyqtgraph/multiprocess/processes.py b/pyqtgraph/multiprocess/processes.py index f95a3ec4..4c3be4e9 100644 --- a/pyqtgraph/multiprocess/processes.py +++ b/pyqtgraph/multiprocess/processes.py @@ -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_() diff --git a/pyqtgraph/multiprocess/remoteproxy.py b/pyqtgraph/multiprocess/remoteproxy.py index 94cc6048..096f2006 100644 --- a/pyqtgraph/multiprocess/remoteproxy.py +++ b/pyqtgraph/multiprocess/remoteproxy.py @@ -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() diff --git a/pyqtgraph/opengl/glInfo.py b/pyqtgraph/opengl/glInfo.py index 95f59630..28da1f69 100644 --- a/pyqtgraph/opengl/glInfo.py +++ b/pyqtgraph/opengl/glInfo.py @@ -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() diff --git a/pyqtgraph/reload.py b/pyqtgraph/reload.py index b9459073..ccf83913 100644 --- a/pyqtgraph/reload.py +++ b/pyqtgraph/reload.py @@ -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)) diff --git a/pyqtgraph/widgets/RemoteGraphicsView.py b/pyqtgraph/widgets/RemoteGraphicsView.py index d8e720b5..3722e87e 100644 --- a/pyqtgraph/widgets/RemoteGraphicsView.py +++ b/pyqtgraph/widgets/RemoteGraphicsView.py @@ -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) @@ -202,4 +221,4 @@ class Renderer(GraphicsView): - \ No newline at end of file +