Merge pull request #1310 from campagnola/acq4-merge

Acq4 merge
This commit is contained in:
Luke Campagnola 2020-07-13 16:13:38 -07:00 committed by GitHub
commit dce9a5bf0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 73 deletions

View File

@ -3,6 +3,7 @@ from .Qt import QtCore
from .ptime import time
from . import ThreadsafeTimer
import weakref
from .functions import SignalBlock
__all__ = ['SignalProxy']
@ -34,7 +35,7 @@ class SignalProxy(QtCore.QObject):
self.args = None
self.timer = ThreadsafeTimer.ThreadsafeTimer()
self.timer.timeout.connect(self.flush)
self.block = False
self.blockSignal = False
self.slot = weakref.ref(slot)
self.lastFlushTime = None
if slot is not None:
@ -45,7 +46,7 @@ class SignalProxy(QtCore.QObject):
def signalReceived(self, *args):
"""Received signal. Cancel previous timer and store args to be forwarded later."""
if self.block:
if self.blockSignal:
return
self.args = args
if self.rateLimit == 0:
@ -61,11 +62,10 @@ class SignalProxy(QtCore.QObject):
self.timer.stop()
self.timer.start((min(leakTime, self.delay)*1000)+1)
def flush(self):
"""If there is a signal queued up, send it now."""
if self.args is None or self.block:
if self.args is None or self.blockSignal:
return False
args, self.args = self.args, None
self.timer.stop()
@ -75,7 +75,7 @@ class SignalProxy(QtCore.QObject):
return True
def disconnect(self):
self.block = True
self.blockSignal = True
try:
self.signal.disconnect(self.signalReceived)
except:
@ -85,18 +85,10 @@ class SignalProxy(QtCore.QObject):
except:
pass
#def proxyConnect(source, signal, slot, delay=0.3):
#"""Connect a signal to a slot with delay. Returns the SignalProxy
#object that was created. Be sure to store this object so it is not
#garbage-collected immediately."""
#sp = SignalProxy(source, signal, delay)
#if source is None:
#sp.connect(sp, QtCore.SIGNAL('signal'), slot)
#else:
#sp.connect(sp, signal, slot)
#return sp
def block(self):
"""Return a SignalBlocker that temporarily blocks input signals to this proxy.
"""
return SignalBlock(self.signal, self.signalReceived)
if __name__ == '__main__':

View File

@ -14,22 +14,33 @@ class Vector(QtGui.QVector3D):
def __init__(self, *args):
if len(args) == 1:
if isinstance(args[0], QtCore.QSizeF):
QtGui.QVector3D.__init__(self, float(args[0].width()), float(args[0].height()), 0)
return
x = float(args[0].width())
y = float(args[0].height())
z = 0
elif isinstance(args[0], QtCore.QPoint) or isinstance(args[0], QtCore.QPointF):
QtGui.QVector3D.__init__(self, float(args[0].x()), float(args[0].y()), 0)
x = float(args[0].x())
y = float(args[0].y())
z = 0
elif isinstance(args[0], QtGui.QVector3D):
x = args[0].x()
y = args[0].y()
z = args[0].z()
elif hasattr(args[0], '__getitem__'):
vals = list(args[0])
if len(vals) == 2:
vals.append(0)
if len(vals) != 3:
raise Exception('Cannot init Vector with sequence of length %d' % len(args[0]))
QtGui.QVector3D.__init__(self, *vals)
return
x, y = vals
z = 0
elif len(vals) == 3:
x, y, z = vals
else:
raise ValueError('Cannot init Vector with sequence of length %d' % len(args[0]))
elif len(args) == 2:
QtGui.QVector3D.__init__(self, args[0], args[1], 0)
return
QtGui.QVector3D.__init__(self, *args)
x, y = args
z = 0
else:
x, y, z = args # Could raise ValueError
QtGui.QVector3D.__init__(self, x, y, z)
def __len__(self):
return 3

View File

@ -447,7 +447,7 @@ class ConsoleWidget(QtGui.QWidget):
filterStr = str(self.ui.filterText.text())
if filterStr != '':
if isinstance(exc, Exception):
msg = exc.message
msg = traceback.format_exception_only(type(exc), exc)
elif isinstance(exc, basestring):
msg = exc
else:

View File

@ -1151,7 +1151,22 @@ class ThreadTrace(object):
for id, frame in sys._current_frames().items():
if id == threading.current_thread().ident:
continue
print("<< thread %d >>" % id)
# try to determine a thread name
try:
name = threading._active.get(id, None)
except:
name = None
if name is None:
try:
# QThread._names must be manually set by thread creators.
name = QtCore.QThread._names.get(id)
except:
name = None
if name is None:
name = "???"
print("<< thread %d \"%s\" >>" % (id, name))
traceback.print_stack(frame)
print("===============================================\n")

View File

@ -222,8 +222,8 @@ class InfiniteLine(GraphicsObject):
def setPos(self, pos):
if type(pos) in [list, tuple]:
newPos = pos
if type(pos) in [list, tuple, np.ndarray]:
newPos = list(pos)
elif isinstance(pos, QtCore.QPointF):
newPos = [pos.x(), pos.y()]
else:

View File

@ -5,12 +5,12 @@ from .ParameterItem import ParameterItem
from ..widgets.SpinBox import SpinBox
from ..widgets.ColorButton import ColorButton
from ..colormap import ColorMap
#from ..widgets.GradientWidget import GradientWidget ## creates import loop
from .. import pixmaps as pixmaps
from .. import functions as fn
import os, sys
from ..pgcollections import OrderedDict
class WidgetParameterItem(ParameterItem):
"""
ParameterTree item with:
@ -38,7 +38,6 @@ class WidgetParameterItem(ParameterItem):
self.hideWidget = True ## hide edit widget, replace with label when not selected
## set this to False to keep the editor widget always visible
## build widget into column 1 with a display label and default button.
w = self.makeWidget()
self.widget = w
@ -162,8 +161,6 @@ class WidgetParameterItem(ParameterItem):
self.focusNext(forward=False)
return True ## don't let anyone else see this event
#elif ev.type() == ev.FocusOut:
#self.hideEditor()
return False
def setFocus(self):
@ -272,7 +269,6 @@ class WidgetParameterItem(ParameterItem):
def optsChanged(self, param, opts):
"""Called when any options are changed that are not
name, value, default, or limits"""
#print "opts changed:", opts
ParameterItem.optsChanged(self, param, opts)
if 'readonly' in opts:
@ -317,6 +313,11 @@ class SimpleParameter(Parameter):
self.value = self.colorValue
self.saveState = self.saveColorState
def setValue(self, value, blockSignal=None):
if self.opts['type'] == 'int':
value = int(value)
Parameter.setValue(self, value, blockSignal)
def colorValue(self):
return fn.mkColor(Parameter.value(self))
@ -343,9 +344,8 @@ class SimpleParameter(Parameter):
if not isinstance(v, ColorMap):
raise TypeError("Cannot set colormap parameter from object %r" % v)
return v
registerParameterType('int', SimpleParameter, override=True)
registerParameterType('float', SimpleParameter, override=True)
registerParameterType('bool', SimpleParameter, override=True)
@ -354,8 +354,6 @@ registerParameterType('color', SimpleParameter, override=True)
registerParameterType('colormap', SimpleParameter, override=True)
class GroupParameterItem(ParameterItem):
"""
Group parameters are used mainly as a generic parent item that holds (and groups!) a set
@ -383,7 +381,6 @@ class GroupParameterItem(ParameterItem):
w.setLayout(l)
l.addWidget(self.addWidget)
l.addStretch()
#l.addItem(QtGui.QSpacerItem(200, 10, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum))
self.addWidgetBox = w
self.addItem = QtGui.QTreeWidgetItem([])
self.addItem.setFlags(QtCore.Qt.ItemIsEnabled)
@ -458,7 +455,8 @@ class GroupParameterItem(ParameterItem):
self.addWidget.addItem(t)
finally:
self.addWidget.blockSignals(False)
class GroupParameter(Parameter):
"""
Group parameters are used mainly as a generic parent item that holds (and groups!) a set
@ -486,14 +484,10 @@ class GroupParameter(Parameter):
"""Change the list of options available for the user to add to the group."""
self.setOpts(addList=vals)
registerParameterType('group', GroupParameter, override=True)
class ListParameterItem(WidgetParameterItem):
"""
WidgetParameterItem subclass providing comboBox that lets the user select from a list of options.
@ -503,7 +497,6 @@ class ListParameterItem(WidgetParameterItem):
self.targetValue = None
WidgetParameterItem.__init__(self, param, depth)
def makeWidget(self):
opts = self.param.opts
t = opts['type']
@ -551,7 +544,6 @@ class ListParameterItem(WidgetParameterItem):
self.updateDisplayLabel()
finally:
self.widget.blockSignals(False)
class ListParameter(Parameter):
@ -561,7 +553,7 @@ class ListParameter(Parameter):
self.forward = OrderedDict() ## {name: value, ...}
self.reverse = ([], []) ## ([value, ...], [name, ...])
## Parameter uses 'limits' option to define the set of allowed values
# Parameter uses 'limits' option to define the set of allowed values
if 'values' in opts:
opts['limits'] = opts['values']
if opts.get('limits', None) is None:
@ -576,24 +568,9 @@ class ListParameter(Parameter):
if len(self.reverse[0]) > 0 and self.value() not in self.reverse[0]:
self.setValue(self.reverse[0][0])
#def addItem(self, name, value=None):
#if name in self.forward:
#raise Exception("Name '%s' is already in use for this parameter" % name)
#limits = self.opts['limits']
#if isinstance(limits, dict):
#limits = limits.copy()
#limits[name] = value
#self.setLimits(limits)
#else:
#if value is not None:
#raise Exception ## raise exception or convert to dict?
#limits = limits[:]
#limits.append(name)
## what if limits == None?
@staticmethod
def mapping(limits):
## Return forward and reverse mapping objects given a limit specification
# Return forward and reverse mapping objects given a limit specification
forward = OrderedDict() ## {name: value, ...}
reverse = ([], []) ## ([value, ...], [name, ...])
if isinstance(limits, dict):
@ -658,7 +635,6 @@ class ActionParameter(Parameter):
registerParameterType('action', ActionParameter, override=True)
class TextParameterItem(WidgetParameterItem):
def __init__(self, param, depth):
WidgetParameterItem.__init__(self, param, depth)
@ -693,7 +669,6 @@ class TextParameterItem(WidgetParameterItem):
class TextParameter(Parameter):
"""Editable string; displayed as large text box in the tree."""
itemClass = TextParameterItem
registerParameterType('text', TextParameter, override=True)

View File

@ -98,6 +98,19 @@ def check_interpolateArray(order):
assert_array_almost_equal(r1, r2)
def test_subArray():
a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0])
b = pg.subArray(a, offset=2, shape=(2,2,3), stride=(10,4,1))
c = np.array([[[111,112,113], [121,122,123]], [[211,212,213], [221,222,223]]])
assert np.all(b == c)
# operate over first axis; broadcast over the rest
aa = np.vstack([a, a/100.]).T
cc = np.empty(c.shape + (2,))
cc[..., 0] = c
cc[..., 1] = c / 100.
bb = pg.subArray(aa, offset=2, shape=(2,2,3), stride=(10,4,1))
assert np.all(bb == cc)
def test_subArray():
a = np.array([0, 0, 111, 112, 113, 0, 121, 122, 123, 0, 0, 0, 211, 212, 213, 0, 221, 222, 223, 0, 0, 0, 0])

View File

@ -10,7 +10,7 @@
## No unicode variable names (μ,Ω) allowed until python 3
SI_PREFIXES = 'yzafpnum kMGTPEZY'
UNITS = 'm,s,g,W,J,V,A,F,T,Hz,Ohm,S,N,C,px,b,B'.split(',')
UNITS = 'm,s,g,W,J,V,A,F,T,Hz,Ohm,S,N,C,px,b,B,Pa'.split(',')
allUnits = {}
def addUnit(p, n):

View File

@ -31,4 +31,5 @@ class BusyCursor(object):
def __exit__(self, *args):
if self._active:
BusyCursor.active.pop(-1)
QtGui.QApplication.restoreOverrideCursor()
if len(BusyCursor.active) == 0:
QtGui.QApplication.restoreOverrideCursor()

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from ..Qt import QtGui, QtCore
from .. import ptime
__all__ = ['ProgressDialog']
@ -40,7 +41,7 @@ class ProgressDialog(QtGui.QProgressDialog):
nested (bool) If True, then this progress bar will be displayed inside
any pre-existing progress dialogs that also allow nesting.
============== ================================================================
"""
"""
# attributes used for nesting dialogs
self.nestedLayout = None
self._nestableWidgets = None
@ -48,6 +49,9 @@ class ProgressDialog(QtGui.QProgressDialog):
self._topDialog = None
self._subBars = []
self.nested = nested
# for rate-limiting Qt event processing during progress bar update
self._lastProcessEvents = None
isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread()
self.disabled = disable or (not isGuiThread)
@ -203,7 +207,10 @@ class ProgressDialog(QtGui.QProgressDialog):
# Qt docs say this should happen automatically, but that doesn't seem
# to be the case.
if self.windowModality() == QtCore.Qt.WindowModal:
QtGui.QApplication.processEvents()
now = ptime.time()
if self._lastProcessEvents is None or (now - self._lastProcessEvents) > 0.2:
QtGui.QApplication.processEvents()
self._lastProcessEvents = now
def setLabelText(self, val):
if self.disabled: