Source code for schrodinger.ui.qt.datamapper

from schrodinger.Qt import QtCore
from schrodinger.Qt import QtWidgets
from schrodinger.Qt.QtCore import Qt

MSuper = QtWidgets.QDataWidgetMapper


[docs]class TrackingDataWidgetMapper(MSuper): """ This class augments QtWidgets.QDataWidgetMapper with three additional features: 1) Emits a widgetChanged signal whenever any mapped widget is modified by the user 2) Provides an isDirty() method to determine whether any of the current widget values differ from the model state. 3) Allows QButtonGroup objects to be mapped """ widgetChanged = QtCore.pyqtSignal()
[docs] def __init__(self, *args, **kwargs): MSuper.__init__(self, *args, **kwargs) self.all_widgets = []
[docs] def isDirty(self): """ Determines whether the values in the widgets match the values in the model. This will happen if the user has edited widget values but not submitted the changes. This method is primarily intended to be used with the submitPolicy set to ManualSubmit. If the user edits a value back to its original state, isDirty() will correctly return False. """ if self.currentIndex() == -1: return False model = self.model() if self.orientation() == Qt.Horizontal: count = model.columnCount() else: count = model.rowCount() for i in range(count): widget = self.mappedWidgetAt(i) if not widget: continue prop_name = self.mappedPropertyName(widget) value = widget.property(prop_name) if self.orientation() == Qt.Horizontal: row = self.currentIndex() index = model.index(row, i) else: col = self.currentIndex() index = model.index(i, col) orig_value = model.data(index, Qt.EditRole) if value != orig_value: return True return False
[docs] def addMapping(self, widget, section): """ See parent class for full documentation for this method. In addition, this method connects the appropriate signal on the widget so that all widget changes result in the widgetChanged signal being emitted. This method also allows QButtonGroup to be mapped, which the parent class does not. Only exclusive button groups will work correctly - non- exclusive groups will only register the first checked button in the group. This override of addMapping also eliminates the optional propertyName argument. """ if isinstance(widget, QtWidgets.QButtonGroup): widget = MappableButtonGroup(widget) MSuper.addMapping(self, widget, section, b'currentSelection') elif isinstance(widget, QtWidgets.QComboBox): # The default, currentText, does not work correctly, because there # is no corresponding setter in QComboBox MSuper.addMapping(self, widget, section, b'currentIndex') else: MSuper.addMapping(self, widget, section) self.all_widgets.append(widget) signal = self._findWidgetSignal(widget) signal.connect(self.widgetChanged.emit)
[docs] def removeMapping(self, widget): # See parent class for documentation signal = self._findWidgetSignal(widget) signal.disconnect(self.widgetChanged.emit) MSuper.removeMapping(self, widget)
def _findWidgetSignal(self, widget): """ Returns the appropriate signal for the widget that indicates that the contents of the widget has changed, using the SIGNAL_MAP. """ for widget_class, signal_name in SIGNAL_MAP.items(): if isinstance(widget, widget_class): return getattr(widget, signal_name) else: raise TypeError('Unhandled widget type: %s' % type(widget))
[docs]class MappableButtonGroup(QtWidgets.QWidget): """ This widget is a container for a QButtonGroup used to enable button groups to be mapped by the widget mapper. """ selectionChanged = QtCore.pyqtSignal(int)
[docs] def __init__(self, button_group, parent=None): QtWidgets.QWidget.__init__(self, parent) self._button_group = button_group for i, button in enumerate(button_group.buttons()): self._button_group.setId(button, i) button.toggled.connect(self.selectionChanged.emit)
[docs] def buttons(self): return self._button_group.buttons()
[docs] def getCurrentSelection(self): return self._button_group.checkedId()
[docs] def setCurrentSelection(self, sel): self._button_group.button(sel).setChecked(True)
currentSelection = QtCore.pyqtProperty(int, fget=getCurrentSelection, fset=setCurrentSelection)
# Maps widget types to the name of the signal indicating widget value changed # Add widget types as needed SIGNAL_MAP = { QtWidgets.QLineEdit: 'textChanged', QtWidgets.QComboBox: 'currentIndexChanged', QtWidgets.QSpinBox: 'valueChanged', QtWidgets.QDoubleSpinBox: 'valueChanged', QtWidgets.QCheckBox: 'toggled', MappableButtonGroup: 'selectionChanged', }