Better parameter tree testing (#1953)
* Allows values to be numpy arrays * Bugfix: Slider now works when limits didn't change during `optsChanged` * Improved testing + layout of param tree example * Also fix numpy-like values in list `setValue` * use proper hex formatting for value * Fix code warnings * Avoids use of configfile in parametertree * Avoid shadowing variable names * Add explanatory comment to `makeAllParamTypes` * Allow string options to be 'unset' in file, etc. parameters example * Bugfix: unintunitive option unsetting for file window title * don't use lambda in signal connect * Remove unused import
This commit is contained in:
parent
e9d3b6ddd2
commit
8d3e6cbd22
93
examples/_buildParamTypes.py
Normal file
93
examples/_buildParamTypes.py
Normal file
@ -0,0 +1,93 @@
|
||||
from pyqtgraph.parametertree import Parameter
|
||||
from pyqtgraph.parametertree.Parameter import PARAM_TYPES
|
||||
from pyqtgraph.parametertree.parameterTypes import GroupParameter
|
||||
from ._paramtreecfg import cfg
|
||||
|
||||
_encounteredTypes = {'group'}
|
||||
|
||||
def makeChild(chType, cfgDict):
|
||||
_encounteredTypes.add(chType)
|
||||
param = Parameter.create(name='widget', type=chType)
|
||||
param.setDefault(param.value())
|
||||
|
||||
def setOpt(_param, _val):
|
||||
# Treat blank strings as "None" to allow 'unsetting' that option
|
||||
if isinstance(_val, str) and _val == '':
|
||||
_val = None
|
||||
param.setOpts(**{_param.name(): _val})
|
||||
|
||||
optsChildren = []
|
||||
metaChildren = []
|
||||
for optName, optVals in cfgDict.items():
|
||||
child = Parameter.create(name=optName, **optVals)
|
||||
if ' ' in optName:
|
||||
metaChildren.append(child)
|
||||
else:
|
||||
optsChildren.append(child)
|
||||
child.sigValueChanged.connect(setOpt)
|
||||
# Poplate initial options
|
||||
for p in optsChildren:
|
||||
setOpt(p, p.value())
|
||||
|
||||
grp = Parameter.create(name=f'Sample {chType.title()}', type='group', children=metaChildren + [param] + optsChildren)
|
||||
grp.setOpts(expanded=False)
|
||||
return grp
|
||||
|
||||
def makeMetaChild(name, cfgDict):
|
||||
children = []
|
||||
for chName, chOpts in cfgDict.items():
|
||||
if not isinstance(chOpts, dict):
|
||||
ch = Parameter.create(name=chName, type=chName, value=chOpts)
|
||||
else:
|
||||
ch = Parameter.create(name=chName, **chOpts)
|
||||
_encounteredTypes.add(ch.type())
|
||||
children.append(ch)
|
||||
param = Parameter.create(name=name, type='group', children=children)
|
||||
param.setOpts(expanded=False)
|
||||
return param
|
||||
|
||||
def makeAllParamTypes():
|
||||
children = []
|
||||
for name, paramCfg in cfg.items():
|
||||
if ' ' in name:
|
||||
children.append(makeMetaChild(name, paramCfg))
|
||||
else:
|
||||
children.append(makeChild(name, paramCfg))
|
||||
|
||||
params = Parameter.create(name='Example Parameters', type='group', children=children)
|
||||
|
||||
# Slider needs minor tweak
|
||||
sliderGrp = params.child('Sample Slider')
|
||||
slider = sliderGrp.child('widget')
|
||||
slider.setOpts(limits=[0, 100])
|
||||
|
||||
# Also minor tweak to meta opts
|
||||
def setOpt(_param, _val):
|
||||
infoChild.setOpts(**{_param.name(): _val})
|
||||
meta = params.child('Applies to All Types')
|
||||
infoChild = meta.child('Extra Information')
|
||||
for child in meta.children()[1:]:
|
||||
child.sigValueChanged.connect(setOpt)
|
||||
|
||||
def onChange(_param, _val):
|
||||
if _val == 'Use span':
|
||||
span = slider.opts.pop('span', None)
|
||||
slider.setOpts(span=span)
|
||||
else:
|
||||
limits = slider.opts.pop('limits', None)
|
||||
slider.setOpts(limits=limits)
|
||||
sliderGrp.child('How to Set').sigValueChanged.connect(onChange)
|
||||
|
||||
def activate(action):
|
||||
for ch in params:
|
||||
if isinstance(ch, GroupParameter):
|
||||
ch.setOpts(expanded=action.name() == 'Expand All')
|
||||
|
||||
for name in 'Collapse', 'Expand':
|
||||
btn = Parameter.create(name=f'{name} All', type='action')
|
||||
btn.sigActivated.connect(activate)
|
||||
params.insertChild(0, btn)
|
||||
missing = set(PARAM_TYPES).difference(_encounteredTypes)
|
||||
if missing:
|
||||
raise RuntimeError(f'{missing} parameters are not represented')
|
||||
return params
|
187
examples/_paramtreecfg.py
Normal file
187
examples/_paramtreecfg.py
Normal file
@ -0,0 +1,187 @@
|
||||
import numpy as np
|
||||
|
||||
from pyqtgraph.Qt import QtWidgets
|
||||
from pyqtgraph.parametertree.parameterTypes import QtEnumParameter as enum
|
||||
|
||||
dlg = QtWidgets.QFileDialog
|
||||
|
||||
cfg = {
|
||||
'list': {
|
||||
'limits': {
|
||||
'type': 'checklist',
|
||||
'limits': ['a', 'b', 'c']
|
||||
}
|
||||
},
|
||||
'file': {
|
||||
'acceptMode': {
|
||||
'type': 'list',
|
||||
'limits': list(enum(dlg.AcceptMode, dlg).enumMap)
|
||||
},
|
||||
'fileMode': {
|
||||
'type': 'list',
|
||||
'limits': list(enum(dlg.FileMode, dlg).enumMap)
|
||||
},
|
||||
'viewMode': {
|
||||
'type': 'list',
|
||||
'limits': list(enum(dlg.ViewMode, dlg).enumMap)
|
||||
},
|
||||
'dialogLabel': {
|
||||
'type': 'list',
|
||||
'limits': list(enum(dlg.DialogLabel, dlg).enumMap)
|
||||
},
|
||||
'relativeTo': {
|
||||
'type': 'str',
|
||||
'value': None
|
||||
},
|
||||
'directory': {
|
||||
'type': 'str',
|
||||
'value': None
|
||||
},
|
||||
'windowTitle': {
|
||||
'type': 'str',
|
||||
'value': None
|
||||
},
|
||||
'nameFilter': {
|
||||
'type': 'str',
|
||||
'value': None
|
||||
}
|
||||
},
|
||||
'float': {
|
||||
'Float Information': {
|
||||
'type': 'str',
|
||||
'readonly': True,
|
||||
'value': 'Note that all options except "finite" also apply to "int" parameters',
|
||||
},
|
||||
'step': {
|
||||
'type': 'float',
|
||||
'limits': [0, None],
|
||||
'value': 1,
|
||||
},
|
||||
'limits': {
|
||||
'type': 'list',
|
||||
'limits': {'[0, None]': [0, None], '[1, 5]': [1, 5]},
|
||||
},
|
||||
'suffix': {
|
||||
'type': 'list',
|
||||
'limits': ['Hz', 's', 'm'],
|
||||
},
|
||||
'siPrefix': {
|
||||
'type': 'bool',
|
||||
'value': True
|
||||
},
|
||||
'finite': {
|
||||
'type': 'bool',
|
||||
'value': True,
|
||||
},
|
||||
'dec': {
|
||||
'type': 'bool',
|
||||
'value': False,
|
||||
},
|
||||
'minStep': {
|
||||
'type': 'float',
|
||||
'value': 1.0e-12,
|
||||
},
|
||||
},
|
||||
|
||||
'checklist': {
|
||||
'limits': {
|
||||
'type': 'checklist',
|
||||
'limits': ['one', 'two', 'three', 'four'],
|
||||
},
|
||||
'exclusive': {
|
||||
'type': 'bool',
|
||||
'value': False,
|
||||
}
|
||||
},
|
||||
|
||||
'pen': {
|
||||
'Pen Information': {
|
||||
'type': 'str',
|
||||
'value': 'Click the button to see options',
|
||||
'readonly': True,
|
||||
},
|
||||
},
|
||||
|
||||
'slider': {
|
||||
'step': {
|
||||
'type': 'float',
|
||||
'limits': [0, None],
|
||||
'value': 1, },
|
||||
'format': {
|
||||
'type': 'str',
|
||||
'value': '{0:>3}',
|
||||
},
|
||||
'precision': {
|
||||
'type': 'int',
|
||||
'value': 2,
|
||||
'limits': [1, None],
|
||||
},
|
||||
'span': {
|
||||
'type': 'list',
|
||||
'limits': {'linspace(-pi, pi)': np.linspace(-np.pi, np.pi), 'arange(10)**2': np.arange(10) ** 2},
|
||||
},
|
||||
|
||||
'How to Set': {
|
||||
'type': 'list',
|
||||
'limits': ['Use span', 'Use step + limits'],
|
||||
}
|
||||
},
|
||||
|
||||
'calendar': {
|
||||
'format': {
|
||||
'type': 'str',
|
||||
'value': 'MM DD',
|
||||
}
|
||||
},
|
||||
|
||||
'Applies to All Types': {
|
||||
'Extra Information': {
|
||||
'type': 'text',
|
||||
'value': 'These apply to all parameters. Watch how this text box is altered by any setting you change.',
|
||||
'default': 'These apply to all parameters. Watch how this text box is altered by any setting you change.',
|
||||
'readonly': True,
|
||||
},
|
||||
'readonly': {
|
||||
'type': 'bool',
|
||||
'value': True,
|
||||
},
|
||||
'removable': {
|
||||
'type': 'bool',
|
||||
'tip': 'Adds a context menu option to remove this parameter',
|
||||
'value': False,
|
||||
},
|
||||
'visible': {
|
||||
'type': 'bool',
|
||||
'value': True,
|
||||
},
|
||||
'disabled': {
|
||||
'type': 'bool',
|
||||
'value': False,
|
||||
},
|
||||
'title': {
|
||||
'type': 'str',
|
||||
'value': 'Meta Options',
|
||||
},
|
||||
'default': {
|
||||
'tip': 'The default value that gets set when clicking the arrow in the right column',
|
||||
'type': 'str',
|
||||
},
|
||||
'expanded': {
|
||||
'type': 'bool',
|
||||
'value': True,
|
||||
},
|
||||
},
|
||||
|
||||
'No Extra Options': {
|
||||
'text': 'Unlike the other parameters shown, these don\'t have extra settable options.\n' \
|
||||
+ 'Note: "int" *does* have the same options as float, mentioned above',
|
||||
'int': 10,
|
||||
'str': 'Hi, world!',
|
||||
'color': '#fff',
|
||||
'bool': False,
|
||||
'colormap': None,
|
||||
'progress': 50,
|
||||
'action': None,
|
||||
'font': 'Inter',
|
||||
}
|
||||
}
|
@ -1,4 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
This example demonstrates the use of pyqtgraph's parametertree system. This provides
|
||||
a simple way to generate user interfaces that control sets of parameters. The example
|
||||
@ -6,17 +5,20 @@ demonstrates a variety of different parameter types (int, float, list, etc.)
|
||||
as well as some customized parameter types
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
import initExample ## Add path to library (just for examples; you do not need this)
|
||||
|
||||
import pyqtgraph as pg
|
||||
from pyqtgraph.Qt import QtCore, QtGui
|
||||
# `makeAllParamTypes` creates several parameters from a dictionary of config specs.
|
||||
# This contains information about the options for each parameter so they can be directly
|
||||
# inserted into the example parameter tree. To create your own parameters, simply follow
|
||||
# the guidelines demonstrated by other parameters created here.
|
||||
from examples._buildParamTypes import makeAllParamTypes
|
||||
from pyqtgraph.Qt import QtGui
|
||||
|
||||
|
||||
app = pg.mkQApp("Parameter Tree Example")
|
||||
import pyqtgraph.parametertree.parameterTypes as pTypes
|
||||
from pyqtgraph.parametertree import Parameter, ParameterTree, ParameterItem, registerParameterType
|
||||
from pyqtgraph.parametertree import Parameter, ParameterTree
|
||||
|
||||
|
||||
## test subclassing parameters
|
||||
@ -62,39 +64,7 @@ class ScalableGroup(pTypes.GroupParameter):
|
||||
|
||||
|
||||
params = [
|
||||
{'name': 'Basic parameter data types', 'type': 'group', 'children': [
|
||||
{'name': 'Integer', 'type': 'int', 'value': 10},
|
||||
{'name': 'Float', 'type': 'float', 'value': 10.5, 'step': 0.1, 'finite': False},
|
||||
{'name': 'String', 'type': 'str', 'value': "hi", 'tip': 'Well hello'},
|
||||
{'name': 'Checklist', 'type': 'checklist', 'limits': [1,2,3], 'value': 2},
|
||||
{'name': 'List', 'type': 'list', 'limits': [1,2,3], 'value': 2},
|
||||
{'name': 'Named List', 'type': 'list', 'limits': {"one": 1, "two": "twosies", "three": [3,3,3]}, 'value': 2},
|
||||
{'name': 'Boolean', 'type': 'bool', 'value': True, 'tip': "This is a checkbox"},
|
||||
{'name': 'Color', 'type': 'color', 'value': "#FF0", 'tip': "This is a color button"},
|
||||
{'name': 'Gradient', 'type': 'colormap'},
|
||||
{'name': 'Subgroup', 'type': 'group', 'children': [
|
||||
{'name': 'Sub-param 1', 'type': 'int', 'value': 10},
|
||||
{'name': 'Sub-param 2', 'type': 'float', 'value': 1.2e6},
|
||||
]},
|
||||
{'name': 'Text Parameter', 'type': 'text', 'value': 'Some text...'},
|
||||
{'name': 'Action Parameter', 'type': 'action', 'tip': 'Click me'},
|
||||
]},
|
||||
{'name': 'Custom Parameter Options', 'type': 'group', 'children': [
|
||||
{'name': 'Pen', 'type': 'pen', 'value': pg.mkPen(color=(255,0,0), width=2)},
|
||||
{'name': 'Progress bar', 'type': 'progress', 'value':50, 'limits':(0,100)},
|
||||
{'name': 'Slider', 'type': 'slider', 'value':50, 'limits':(0,100)},
|
||||
{'name': 'Font', 'type': 'font', 'value':QtGui.QFont("Inter")},
|
||||
{'name': 'Calendar', 'type': 'calendar', 'value':QtCore.QDate.currentDate().addMonths(1)},
|
||||
{'name': 'Open python file', 'type': 'file', 'fileMode': 'ExistingFile', 'nameFilter': 'Python file (*.py);;',
|
||||
'value': 'parametertree.py', 'relativeTo': os.getcwd(), 'options': ['DontResolveSymlinks']}
|
||||
]},
|
||||
{'name': 'Numerical Parameter Options', 'type': 'group', 'children': [
|
||||
{'name': 'Units + SI prefix', 'type': 'float', 'value': 1.2e-6, 'step': 1e-6, 'siPrefix': True, 'suffix': 'V'},
|
||||
{'name': 'Limits (min=7;max=15)', 'type': 'int', 'value': 11, 'limits': (7, 15), 'default': -6},
|
||||
{'name': 'Int suffix', 'type': 'int', 'value': 9, 'suffix': 'V'},
|
||||
{'name': 'DEC stepping', 'type': 'float', 'value': 1.2e6, 'dec': True, 'step': 1, 'minStep': 1.0e-12, 'siPrefix': True, 'suffix': 'Hz'},
|
||||
|
||||
]},
|
||||
makeAllParamTypes(),
|
||||
{'name': 'Save/Restore functionality', 'type': 'group', 'children': [
|
||||
{'name': 'Save State', 'type': 'action'},
|
||||
{'name': 'Restore State', 'type': 'action', 'children': [
|
||||
@ -102,12 +72,6 @@ params = [
|
||||
{'name': 'Remove extra items', 'type': 'bool', 'value': True},
|
||||
]},
|
||||
]},
|
||||
{'name': 'Extra Parameter Options', 'type': 'group', 'children': [
|
||||
{'name': 'Read-only', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'readonly': True},
|
||||
{'name': 'Disabled', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'enabled': False},
|
||||
{'name': 'Renamable', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'renamable': True},
|
||||
{'name': 'Removable', 'type': 'float', 'value': 1.2e6, 'siPrefix': True, 'suffix': 'Hz', 'removable': True},
|
||||
]},
|
||||
{'name': 'Custom context menu', 'type': 'group', 'children': [
|
||||
{'name': 'List contextMenu', 'type': 'float', 'value': 0, 'context': [
|
||||
'menu1',
|
||||
|
@ -43,7 +43,7 @@ def writeConfigFile(data, fname):
|
||||
fd.write(s)
|
||||
|
||||
|
||||
def readConfigFile(fname):
|
||||
def readConfigFile(fname, **scope):
|
||||
#cwd = os.getcwd()
|
||||
global GLOBAL_PATH
|
||||
if GLOBAL_PATH is not None:
|
||||
@ -52,6 +52,21 @@ def readConfigFile(fname):
|
||||
fname = fname2
|
||||
|
||||
GLOBAL_PATH = os.path.dirname(os.path.abspath(fname))
|
||||
|
||||
local = {**scope, **units.allUnits}
|
||||
local['OrderedDict'] = OrderedDict
|
||||
local['readConfigFile'] = readConfigFile
|
||||
local['Point'] = Point
|
||||
local['QtCore'] = QtCore
|
||||
local['ColorMap'] = ColorMap
|
||||
local['datetime'] = datetime
|
||||
# Needed for reconstructing numpy arrays
|
||||
local['array'] = numpy.array
|
||||
for dtype in ['int8', 'uint8',
|
||||
'int16', 'uint16', 'float16',
|
||||
'int32', 'uint32', 'float32',
|
||||
'int64', 'uint64', 'float64']:
|
||||
local[dtype] = getattr(numpy, dtype)
|
||||
|
||||
try:
|
||||
#os.chdir(newDir) ## bad.
|
||||
@ -59,7 +74,7 @@ def readConfigFile(fname):
|
||||
s = fd.read()
|
||||
s = s.replace("\r\n", "\n")
|
||||
s = s.replace("\r", "\n")
|
||||
data = parseString(s)[1]
|
||||
data = parseString(s, **local)[1]
|
||||
except ParseError:
|
||||
sys.exc_info()[1].fileName = fname
|
||||
raise
|
||||
@ -93,7 +108,7 @@ def genString(data, indent=''):
|
||||
s += indent + sk + ': ' + repr(data[k]).replace("\n", "\\\n") + '\n'
|
||||
return s
|
||||
|
||||
def parseString(lines, start=0):
|
||||
def parseString(lines, start=0, **scope):
|
||||
|
||||
data = OrderedDict()
|
||||
if isinstance(lines, str):
|
||||
@ -135,33 +150,19 @@ def parseString(lines, start=0):
|
||||
v = v.strip()
|
||||
|
||||
## set up local variables to use for eval
|
||||
local = units.allUnits.copy()
|
||||
local['OrderedDict'] = OrderedDict
|
||||
local['readConfigFile'] = readConfigFile
|
||||
local['Point'] = Point
|
||||
local['QtCore'] = QtCore
|
||||
local['ColorMap'] = ColorMap
|
||||
local['datetime'] = datetime
|
||||
# Needed for reconstructing numpy arrays
|
||||
local['array'] = numpy.array
|
||||
for dtype in ['int8', 'uint8',
|
||||
'int16', 'uint16', 'float16',
|
||||
'int32', 'uint32', 'float32',
|
||||
'int64', 'uint64', 'float64']:
|
||||
local[dtype] = getattr(numpy, dtype)
|
||||
|
||||
if len(k) < 1:
|
||||
raise ParseError('Missing name preceding colon', ln+1, l)
|
||||
if k[0] == '(' and k[-1] == ')': ## If the key looks like a tuple, try evaluating it.
|
||||
try:
|
||||
k1 = eval(k, local)
|
||||
k1 = eval(k, scope)
|
||||
if type(k1) is tuple:
|
||||
k = k1
|
||||
except:
|
||||
# If tuple conversion fails, keep the string
|
||||
pass
|
||||
if re.search(r'\S', v) and v[0] != '#': ## eval the value
|
||||
try:
|
||||
val = eval(v, local)
|
||||
val = eval(v, scope)
|
||||
except:
|
||||
ex = sys.exc_info()[1]
|
||||
raise ParseError("Error evaluating expression '%s': [%s: %s]" % (v, ex.__class__.__name__, str(ex)), (ln+1), l)
|
||||
@ -171,7 +172,7 @@ def parseString(lines, start=0):
|
||||
val = {}
|
||||
else:
|
||||
#print "Going deeper..", ln+1
|
||||
(ln, val) = parseString(lines, start=ln+1)
|
||||
(ln, val) = parseString(lines, start=ln+1, **scope)
|
||||
data[k] = val
|
||||
#print k, repr(val)
|
||||
except ParseError:
|
||||
|
@ -516,7 +516,7 @@ class Parameter(QtCore.QObject):
|
||||
self.setLimits(opts[k])
|
||||
elif k == 'default':
|
||||
self.setDefault(opts[k])
|
||||
elif k not in self.opts or self.opts[k] != opts[k]:
|
||||
elif k not in self.opts or not fn.eq(self.opts[k], opts[k]):
|
||||
self.opts[k] = opts[k]
|
||||
changed[k] = opts[k]
|
||||
|
||||
|
@ -145,7 +145,8 @@ class FileParameterItem(StrParameterItem):
|
||||
startDir = os.path.dirname(startDir)
|
||||
if os.path.exists(startDir):
|
||||
opts['directory'] = startDir
|
||||
opts.setdefault('windowTitle', self.param.title())
|
||||
if opts.get('windowTitle') is None:
|
||||
opts['windowTitle'] = self.param.title()
|
||||
|
||||
fname = popupFilePicker(None, **opts)
|
||||
if not fname:
|
||||
|
@ -4,6 +4,7 @@ from collections import OrderedDict
|
||||
from .basetypes import WidgetParameterItem
|
||||
from .. import Parameter
|
||||
from ...Qt import QtWidgets
|
||||
from ... import functions as fn
|
||||
|
||||
|
||||
class ListParameterItem(WidgetParameterItem):
|
||||
@ -34,10 +35,12 @@ class ListParameterItem(WidgetParameterItem):
|
||||
|
||||
def setValue(self, val):
|
||||
self.targetValue = val
|
||||
if val not in self.reverse[0]:
|
||||
match = [fn.eq(val, limVal) for limVal in self.reverse[0]]
|
||||
if not any(match):
|
||||
self.widget.setCurrentIndex(0)
|
||||
else:
|
||||
key = self.reverse[1][self.reverse[0].index(val)]
|
||||
idx = match.index(True)
|
||||
key = self.reverse[1][idx]
|
||||
ind = self.widget.findText(key)
|
||||
self.widget.setCurrentIndex(ind)
|
||||
|
||||
@ -104,7 +107,9 @@ class ListParameter(Parameter):
|
||||
self.forward, self.reverse = self.mapping(limits)
|
||||
|
||||
Parameter.setLimits(self, limits)
|
||||
if len(self.reverse[0]) > 0 and self.value() not in self.reverse[0]:
|
||||
# 'value in limits' expression will break when reverse contains numpy array
|
||||
curVal = self.value()
|
||||
if len(self.reverse[0]) > 0 and not any(fn.eq(curVal, limVal) for limVal in self.reverse[0]):
|
||||
self.setValue(self.reverse[0][0])
|
||||
|
||||
@staticmethod
|
||||
@ -112,16 +117,11 @@ class ListParameter(Parameter):
|
||||
# Return forward and reverse mapping objects given a limit specification
|
||||
forward = OrderedDict() ## {name: value, ...}
|
||||
reverse = ([], []) ## ([value, ...], [name, ...])
|
||||
if isinstance(limits, dict):
|
||||
for k, v in limits.items():
|
||||
forward[k] = v
|
||||
reverse[0].append(v)
|
||||
reverse[1].append(k)
|
||||
else:
|
||||
for v in limits:
|
||||
n = str(v)
|
||||
forward[n] = v
|
||||
reverse[0].append(v)
|
||||
reverse[1].append(n)
|
||||
if not isinstance(limits, dict):
|
||||
limits = {str(l): l for l in limits}
|
||||
for k, v in limits.items():
|
||||
forward[k] = v
|
||||
reverse[0].append(v)
|
||||
reverse[1].append(k)
|
||||
return forward, reverse
|
||||
|
||||
|
@ -33,6 +33,7 @@ class SliderParameterItem(WidgetParameterItem):
|
||||
def makeWidget(self):
|
||||
param = self.param
|
||||
opts = param.opts
|
||||
opts.setdefault('limits', [0, 0])
|
||||
self._suffix = opts.get('suffix')
|
||||
|
||||
self.slider = QtWidgets.QSlider()
|
||||
@ -94,7 +95,7 @@ class SliderParameterItem(WidgetParameterItem):
|
||||
span = opts.get('span', None)
|
||||
if span is None:
|
||||
step = opts.get('step', 1)
|
||||
start, stop = opts['limits']
|
||||
start, stop = opts.get('limits', param.opts['limits'])
|
||||
# Add a bit to 'stop' since python slicing excludes the last value
|
||||
span = np.arange(start, stop + step, step)
|
||||
precision = opts.get('precision', 2)
|
||||
|
Loading…
x
Reference in New Issue
Block a user