Source code for dawiq.multitype

"""
Data widget container
=====================

:mod:`dawiq.multitype` provides widgets that contain multiple :class:`DataWidget`
to support multiple dataclass types.
"""

from .qt_compat import QtWidgets, QtCore
from .datawidget import DataWidget
from typing import Type, Optional
from .typing import DataclassProtocol


__all__ = [
    "DataclassStackedWidget",
    "DataclassTabWidget",
]


[docs]class DataclassStackedWidget(QtWidgets.QStackedWidget): """ Stacked widget containing multiple :class:`DataWidget` and their dataclasses. To add :class:`DataWidget`, pass the widget and its dataclass to :meth:`addDataWidget` or to :meth:`insertDataWidget`. When the data value of current data widget changes, this widget emits :attr:`currentDataValueChanged` signal. When the current data widget is edited by user, :attr:`currentDataEdited` signal is emitted. """ currentDataValueChanged = QtCore.Signal(dict) currentDataEdited = QtCore.Signal() def __init__(self, parent=None): super().__init__(parent) self._dataWidgets = {} self._previousIndex = -1 self.currentChanged.connect(self._onCurrentChange) def _onCurrentChange(self, index: int): """Handle the signals of old widget and current widget.""" old = self.widget(self._previousIndex) if isinstance(old, DataWidget): old.dataValueChanged.disconnect(self.currentDataValueChanged) old.dataEdited.disconnect(self.currentDataEdited) new = self.widget(index) if isinstance(new, DataWidget): new.dataValueChanged.connect(self.currentDataValueChanged) new.dataEdited.connect(self.currentDataEdited) self._previousIndex = index
[docs] def addDataWidget( self, widget: DataWidget, dataclass: Type[DataclassProtocol] ) -> int: """Add *widget* with binding it to *dataclass*.""" index = self.addWidget(widget) self._dataWidgets[widget] = dataclass return index
def insertDataWidget( self, index: int, widget: DataWidget, dataclass: Type[DataclassProtocol] ) -> int: index = self.insertWidget(index, widget) self._dataWidgets[widget] = dataclass return index def removeWidget(self, widget: QtWidgets.QWidget): if isinstance(widget, DataWidget): self._dataWidgets.pop(widget, None) if widget == self.currentWidget(): widget.dataValueChanged.disconnect(self.currentDataValueChanged) widget.dataEdited.disconnect(self.currentDataEdited) super().removeWidget(widget) def currentDataclass(self) -> Optional[Type[DataclassProtocol]]: return self._dataWidgets.get(self.currentWidget())
[docs] def indexOfDataclass(self, dataclass: Type[DataclassProtocol]) -> int: """Return the index of the widget bound to *dataclass*.""" for widget, dcls in self._dataWidgets.items(): if dcls == dataclass: index = self.indexOf(widget) break else: index = -1 return index
[docs]class DataclassTabWidget(QtWidgets.QTabWidget): """ Tab widget containing multiple :class:`DataWidget` and dataclasses. To add :class:`DataWidget`, pass the widget and its dataclass to :meth:`addDataWidget` or to :meth:`insertDataWidget`. When current index is changed by user, :attr:`activated` signal is emitted. When the data value of current data widget changes, this widget emits :attr:`currentDataValueChanged` signal. When the current data widget is edited by user, :attr:`currentDataEdited` signal is emitted. """ activated = QtCore.Signal(int) currentDataValueChanged = QtCore.Signal(dict) currentDataEdited = QtCore.Signal() def __init__(self, parent=None): super().__init__(parent) self._dataWidgets = {} self._previousIndex = -1 self._blockActivated = False self.currentChanged.connect(self._onCurrentChange) def setCurrentIndex(self, index): self._blockActivated = True super().setCurrentIndex(index) self._blockActivated = False def _onCurrentChange(self, index: int): """Handle the signals of old widget and current widget.""" old = self.widget(self._previousIndex) if isinstance(old, DataWidget): old.dataValueChanged.disconnect(self.currentDataValueChanged) old.dataEdited.disconnect(self.currentDataEdited) new = self.widget(index) if isinstance(new, DataWidget): new.dataValueChanged.connect(self.currentDataValueChanged) new.dataEdited.connect(self.currentDataEdited) self._previousIndex = index if not self._blockActivated: self.activated.emit(index)
[docs] def addDataWidget(self, widget, dataclass, icon=None, label=None) -> int: """Add *widget* with binding it to *dataclass*.""" args = [arg for arg in [icon, label] if arg is not None] index = self.addTab(widget, *args) self._dataWidgets[widget] = dataclass return index
[docs] def insertDataWidget(self, index, widget, dataclass, icon=None, label=None) -> int: """Insert *widget* with binding it to *dataclass*.""" args = [arg for arg in [icon, label] if arg is not None] index = self.insertTab(index, widget, *args) self._dataWidgets[widget] = dataclass return index
def removeTab(self, index: int): widget = self.widget(index) if isinstance(widget, DataWidget): self._dataWidgets.pop(widget, None) if widget == self.currentWidget(): widget.dataValueChanged.disconnect(self.currentDataValueChanged) widget.dataEdited.disconnect(self.currentDataEdited) super().removeTab(index) def currentDataclass(self) -> Optional[Type[DataclassProtocol]]: return self._dataWidgets.get(self.currentWidget())
[docs] def indexOfDataclass(self, dataclass: Type[DataclassProtocol]) -> int: """Return the index of the widget bound to *dataclass*.""" for widget, dcls in self._dataWidgets.items(): if dcls == dataclass: index = self.indexOf(widget) break else: index = -1 return index