CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
WidgetGroup.py282 linesDownload Raw Back to pyqtgraph
1"""2WidgetGroup.py -  WidgetGroup class for easily managing lots of Qt widgets3Copyright 2010  Luke Campagnola4Distributed under MIT/X11 license. See license.txt for more information.5 6This class addresses the problem of having to save and restore the state7of a large group of widgets. 8"""9 10import inspect11import weakref12 13from .Qt import QtCore, QtWidgets14 15__all__ = ['WidgetGroup']16 17def splitterState(w):18    s = w.saveState().toPercentEncoding().data().decode()19    return s20    21def restoreSplitter(w, s):22    if type(s) is list:23        w.setSizes(s)24    elif type(s) is str:25        w.restoreState(QtCore.QByteArray.fromPercentEncoding(s.encode()))26    else:27        print("Can't configure QSplitter using object of type", type(s))28    if w.count() > 0:   # make sure at least one item is not collapsed29        for i in w.sizes():30            if i > 0:31                return32        w.setSizes([50] * w.count())33        34def comboState(w):35    ind = w.currentIndex()36    data = w.itemData(ind)37    if data is not None:38        try:39            if not data.isValid():40                data = None41            else:42                data = data.toInt()[0]43        except AttributeError:44            pass45    if data is None:46        return str(w.itemText(ind))47    else:48        return data49    50def setComboState(w, v):51    if type(v) is int:52        ind = w.findData(v)53        if ind > -1:54            w.setCurrentIndex(ind)55            return56    w.setCurrentIndex(w.findText(str(v)))57        58 59class WidgetGroup(QtCore.QObject):60    """State manager for groups of widgets.61 62    WidgetGroup handles common problems that arise when dealing with groups of widgets like a control63    panel:64    - Provide a single place for saving / restoring the state of all widgets in the group65    - Provide a single signal for detecting when any of the widgets have changed66    """67    68    # List of widget types that can be handled by WidgetGroup.69    # The value for each type is a tuple (change signal function, get function, set function, [auto-add children])70    # The change signal function that takes an object and returns a signal that is emitted any time the state of the widget changes, not just71    #   when it is changed by user interaction. (for example, 'clicked' is not a valid signal here)72    # If the change signal is None, the value of the widget is not cached.73    # Custom widgets not in this list can be made to work with WidgetGroup by giving them a 'widgetGroupInterface' method74    #   which returns the tuple.75    classes = {76        QtWidgets.QSpinBox: (77            lambda w: w.valueChanged,78            QtWidgets.QSpinBox.value, 79            QtWidgets.QSpinBox.setValue80        ),81        QtWidgets.QDoubleSpinBox: (82            lambda w: w.valueChanged,83            QtWidgets.QDoubleSpinBox.value, 84            QtWidgets.QDoubleSpinBox.setValue85        ),86        QtWidgets.QSplitter: (87            None,88            splitterState,89            restoreSplitter,90            True91        ),92        QtWidgets.QCheckBox: (93            lambda w: w.stateChanged,94            QtWidgets.QCheckBox.isChecked,95            QtWidgets.QCheckBox.setChecked96        ),97        QtWidgets.QComboBox: (98            lambda w: w.currentIndexChanged,99            comboState,100            setComboState101        ),102        QtWidgets.QGroupBox: (103            lambda w: w.toggled,104            QtWidgets.QGroupBox.isChecked,105            QtWidgets.QGroupBox.setChecked,106            True107        ),108        QtWidgets.QLineEdit: (109            lambda w: w.editingFinished,110            lambda w: str(w.text()),111            QtWidgets.QLineEdit.setText112        ),113        QtWidgets.QRadioButton: (114            lambda w: w.toggled,115            QtWidgets.QRadioButton.isChecked,116            QtWidgets.QRadioButton.setChecked117        ),118        QtWidgets.QSlider: (119            lambda w: w.valueChanged,120            QtWidgets.QSlider.value,121            QtWidgets.QSlider.setValue122        ),123    }124    125    sigChanged = QtCore.Signal(str, object)126    127    128    def __init__(self, widgetList=None):129        """Initialize WidgetGroup, adding specified widgets into this group.130        widgetList can be: 131         - a list of widget specifications (widget, [name], [scale])132         - a dict of name: widget pairs133         - any QObject, and all compatible child widgets will be added recursively.134        135        The 'scale' parameter for each widget allows QSpinBox to display a different value than the value recorded136        in the group state (for example, the program may set a spin box value to 100e-6 and have it displayed as 100 to the user)137        """138        QtCore.QObject.__init__(self)139        self.widgetList = weakref.WeakKeyDictionary()  # Make sure widgets don't stick around just because they are listed here140        self.scales = weakref.WeakKeyDictionary()141        self.cache = {}  # name:value pairs142        self.uncachedWidgets = weakref.WeakKeyDictionary()143        if isinstance(widgetList, QtCore.QObject):144            self.autoAdd(widgetList)145        elif isinstance(widgetList, list):146            for w in widgetList:147                self.addWidget(*w)148        elif isinstance(widgetList, dict):149            for name, w in widgetList.items():150                self.addWidget(w, name)151        elif widgetList is None:152            return153        else:154            raise Exception("Wrong argument type %s" % type(widgetList))155        156    def addWidget(self, w, name=None, scale=None):157        if not self.acceptsType(w):158            raise Exception("Widget type %s not supported by WidgetGroup" % type(w))159        if name is None:160            name = str(w.objectName())161        if name == '':162            raise Exception("Cannot add widget '%s' without a name." % str(w))163        self.widgetList[w] = name164        self.scales[w] = scale165        self.readWidget(w)166            167        if type(w) in WidgetGroup.classes:168            signal = WidgetGroup.classes[type(w)][0]169        else:170            signal = w.widgetGroupInterface()[0]171            172        if signal is not None:173            if inspect.isfunction(signal) or inspect.ismethod(signal):174                signal = signal(w)175            signal.connect(self.widgetChanged)176        else:177            self.uncachedWidgets[w] = None178       179    def findWidget(self, name):180        for w in self.widgetList:181            if self.widgetList[w] == name:182                return w183        return None184       185    def interface(self, obj):186        t = type(obj)187        if t in WidgetGroup.classes:188            return WidgetGroup.classes[t]189        else:190            return obj.widgetGroupInterface()191 192    def checkForChildren(self, obj):193        """Return true if we should automatically search the children of this object for more."""194        iface = self.interface(obj)195        return (len(iface) > 3 and iface[3])196       197    def autoAdd(self, obj):198        # Find all children of this object and add them if possible.199        accepted = self.acceptsType(obj)200        if accepted:201            self.addWidget(obj)202            203        if not accepted or self.checkForChildren(obj):204            for c in obj.children():205                self.autoAdd(c)206 207    def acceptsType(self, obj):208        for c in WidgetGroup.classes:209            if isinstance(obj, c):210                return True211        if hasattr(obj, 'widgetGroupInterface'):212            return True213        return False214 215    def setScale(self, widget, scale):216        val = self.readWidget(widget)217        self.scales[widget] = scale218        self.setWidget(widget, val)219 220    @QtCore.Slot()221    @QtCore.Slot(bool)222    @QtCore.Slot(int)223    def widgetChanged(self, *args):224        w = self.sender()225        n = self.widgetList[w]226        v1 = self.cache[n]227        v2 = self.readWidget(w)228        if v1 != v2:229            self.sigChanged.emit(self.widgetList[w], v2)230        231    def state(self):232        for w in self.uncachedWidgets:233            self.readWidget(w)234        return self.cache.copy()235 236    def setState(self, s):237        for w in self.widgetList:238            n = self.widgetList[w]239            if n not in s:240                continue241            self.setWidget(w, s[n])242 243    def readWidget(self, w):244        if type(w) in WidgetGroup.classes:245            getFunc = WidgetGroup.classes[type(w)][1]246        else:247            getFunc = w.widgetGroupInterface()[1]248        249        if getFunc is None:250            return None251            252        # if the getter function provided in the interface is a bound method,253        # then just call the method directly. Otherwise, pass in the widget as the first arg254        # to the function.255        if inspect.ismethod(getFunc) and getFunc.__self__ is not None:  256            val = getFunc()257        else:258            val = getFunc(w)259            260        if self.scales[w] is not None:261            val /= self.scales[w]262        n = self.widgetList[w]263        self.cache[n] = val264        return val265 266    def setWidget(self, w, v):267        if self.scales[w] is not None:268            v *= self.scales[w]269        270        if type(w) in WidgetGroup.classes:271            setFunc = WidgetGroup.classes[type(w)][2]272        else:273            setFunc = w.widgetGroupInterface()[2]274            275        # if the setter function provided in the interface is a bound method,276        # then just call the method directly. Otherwise, pass in the widget as the first arg277        # to the function.278        if inspect.ismethod(setFunc) and setFunc.__self__ is not None:279            setFunc(v)280        else:281            setFunc(w, v)282 
Aluode/PerceptionLabPortable · CoolFace