pyqtgraph/pyqtgraph/widgets/DataFilterWidget.py

129 lines
4.4 KiB
Python
Raw Normal View History

2013-02-10 19:10:30 +00:00
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph.parametertree as ptree
import numpy as np
from pyqtgraph.pgcollections import OrderedDict
__all__ = ['DataFilterWidget']
class DataFilterWidget(ptree.ParameterTree):
"""
This class allows the user to filter multi-column data sets by specifying
multiple criteria
"""
sigFilterChanged = QtCore.Signal(object)
def __init__(self):
ptree.ParameterTree.__init__(self, showHeader=False)
self.params = DataFilterParameter()
self.setParameters(self.params)
self.params.sigTreeStateChanged.connect(self.filterChanged)
self.setFields = self.params.setFields
self.filterData = self.params.filterData
def filterChanged(self):
self.sigFilterChanged.emit(self)
def parameters(self):
return self.params
class DataFilterParameter(ptree.types.GroupParameter):
sigFilterChanged = QtCore.Signal(object)
def __init__(self):
self.fields = {}
ptree.types.GroupParameter.__init__(self, name='Data Filter', addText='Add filter..', addList=[])
self.sigTreeStateChanged.connect(self.filterChanged)
def filterChanged(self):
self.sigFilterChanged.emit(self)
def addNew(self, name):
mode = self.fields[name].get('mode', 'range')
if mode == 'range':
self.addChild(RangeFilterItem(name, self.fields[name]))
elif mode == 'enum':
self.addChild(EnumFilterItem(name, self.fields[name]))
def fieldNames(self):
return self.fields.keys()
def setFields(self, fields):
self.fields = OrderedDict(fields)
names = self.fieldNames()
self.setAddList(names)
def filterData(self, data):
if len(data) == 0:
return data
return data[self.generateMask(data)]
def generateMask(self, data):
mask = np.ones(len(data), dtype=bool)
if len(data) == 0:
return mask
for fp in self:
if fp.value() is False:
continue
mask &= fp.generateMask(data)
#key, mn, mx = fp.fieldName, fp['Min'], fp['Max']
#vals = data[key]
#mask &= (vals >= mn)
#mask &= (vals < mx) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
return mask
class RangeFilterItem(ptree.types.SimpleParameter):
def __init__(self, name, opts):
self.fieldName = name
units = opts.get('units', '')
ptree.types.SimpleParameter.__init__(self,
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
children=[
#dict(name="Field", type='list', value=name, values=fields),
dict(name='Min', type='float', value=0.0, suffix=units, siPrefix=True),
dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),
])
def generateMask(self, data):
vals = data[self.fieldName]
2013-03-07 20:29:56 +00:00
return (vals >= self['Min']) & (vals < self['Max']) ## Use inclusive minimum and non-inclusive maximum. This makes it easier to create non-overlapping selections
2013-02-10 19:10:30 +00:00
class EnumFilterItem(ptree.types.SimpleParameter):
def __init__(self, name, opts):
self.fieldName = name
vals = opts.get('values', [])
2013-03-07 20:29:56 +00:00
childs = []
for v in vals:
ch = ptree.Parameter.create(name=str(v), type='bool', value=True)
ch.maskValue = v
childs.append(ch)
2013-03-19 20:04:46 +00:00
ch = ptree.Parameter.create(name='(other)', type='bool', value=True)
ch.maskValue = '__other__'
childs.append(ch)
2013-02-10 19:10:30 +00:00
ptree.types.SimpleParameter.__init__(self,
name=name, autoIncrementName=True, type='bool', value=True, removable=True, renamable=True,
children=childs)
def generateMask(self, data):
vals = data[self.fieldName]
mask = np.ones(len(data), dtype=bool)
2013-03-19 20:04:46 +00:00
otherMask = np.ones(len(data), dtype=bool)
2013-02-10 19:10:30 +00:00
for c in self:
2013-03-07 20:29:56 +00:00
key = c.maskValue
2013-03-19 20:04:46 +00:00
if key == '__other__':
m = ~otherMask
else:
m = vals != key
otherMask &= m
if c.value() is False:
mask &= m
2013-02-10 19:10:30 +00:00
return mask