Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions examples/config_groups_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pymmcore_plus import CMMCorePlus
from qtpy.QtCore import QModelIndex
from qtpy.QtWidgets import QApplication

from pymmcore_widgets import ConfigGroupsTree

app = QApplication([])

core = CMMCorePlus()
core.loadSystemConfiguration()

tree = ConfigGroupsTree.create_from_core(core)
tree.show()
tree.expandRecursively(QModelIndex())
tree.resize(600, 600)

app.exec()
2 changes: 2 additions & 0 deletions src/pymmcore_widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"ChannelGroupWidget",
"ChannelTable",
"ChannelWidget",
"ConfigGroupsTree",
"ConfigWizard",
"ConfigurationWidget",
"CoreLogWidget",
Expand Down Expand Up @@ -48,6 +49,7 @@
from ._install_widget import InstallWidget
from ._log import CoreLogWidget
from .config_presets import (
ConfigGroupsTree,
GroupPresetTableWidget,
ObjectivesPixelConfigurationWidget,
PixelConfigurationWidget,
Expand Down
2 changes: 2 additions & 0 deletions src/pymmcore_widgets/config_presets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from ._objectives_pixel_configuration_widget import ObjectivesPixelConfigurationWidget
from ._pixel_configuration_widget import PixelConfigurationWidget
from ._qmodel._config_model import QConfigGroupsModel
from ._views._config_groups_tree import ConfigGroupsTree

__all__ = [
"ConfigGroupsTree",
"GroupPresetTableWidget",
"ObjectivesPixelConfigurationWidget",
"PixelConfigurationWidget",
Expand Down
Empty file.
39 changes: 39 additions & 0 deletions src/pymmcore_widgets/config_presets/_views/_config_groups_tree.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from qtpy.QtWidgets import QTreeView, QWidget

from pymmcore_widgets.config_presets._qmodel._config_model import QConfigGroupsModel
from pymmcore_widgets.config_presets._views._property_setting_delegate import (
PropertySettingDelegate,
)

if TYPE_CHECKING:
from pymmcore_plus import CMMCorePlus
from qtpy.QtCore import QAbstractItemModel


class ConfigGroupsTree(QTreeView):
"""A tree widget that displays configuration groups."""

@classmethod
def create_from_core(
cls, core: CMMCorePlus, parent: QWidget | None = None
) -> ConfigGroupsTree:
"""Create a ConfigGroupsTree from a CMMCorePlus instance."""
obj = cls(parent)
model = QConfigGroupsModel.create_from_core(core)
obj.setModel(model)
return obj

def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent)
self.setItemDelegateForColumn(2, PropertySettingDelegate(self))

def setModel(self, model: QAbstractItemModel | None) -> None:
"""Set the model for the tree view."""
super().setModel(model)
if hh := self.header():
for col in range(hh.count()):
hh.setSectionResizeMode(col, hh.ResizeMode.Stretch)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import annotations

from pymmcore_plus.model import Setting
from qtpy.QtCore import QAbstractItemModel, QModelIndex, Qt
from qtpy.QtWidgets import QStyledItemDelegate, QStyleOptionViewItem, QWidget

from pymmcore_widgets.device_properties import PropertyWidget


class PropertySettingDelegate(QStyledItemDelegate):
"""Item delegate that uses a PropertyWidget for editing PropertySetting values."""

def createEditor(
self, parent: QWidget | None, option: QStyleOptionViewItem, index: QModelIndex
) -> QWidget | None:
if not isinstance((setting := index.data(Qt.ItemDataRole.UserRole)), Setting):
return super().createEditor(parent, option, index) # pragma: no cover
dev, prop, *_ = setting
widget = PropertyWidget(dev, prop, parent=parent, connect_core=False)
widget.setValue(setting.property_value) # avoids commitData warnings
widget.valueChanged.connect(lambda: self.commitData.emit(widget))
widget.setAutoFillBackground(True)
return widget

def setEditorData(self, editor: QWidget | None, index: QModelIndex) -> None:
setting = index.data(Qt.ItemDataRole.UserRole)
if isinstance(setting, Setting) and isinstance(editor, PropertyWidget):
editor.setValue(setting.property_value)
else: # pragma: no cover
super().setEditorData(editor, index)

def setModelData(
self,
editor: QWidget | None,
model: QAbstractItemModel | None,
index: QModelIndex,
) -> None:
if model and isinstance(editor, PropertyWidget):
model.setData(index, editor.value(), Qt.ItemDataRole.EditRole)
else: # pragma: no cover
super().setModelData(editor, model, index)
46 changes: 46 additions & 0 deletions tests/test_config_groups_widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from __future__ import annotations

from typing import TYPE_CHECKING

from pymmcore_plus import CMMCorePlus

from pymmcore_widgets import ConfigGroupsTree
from pymmcore_widgets.config_presets._qmodel._config_model import QConfigGroupsModel
from pymmcore_widgets.config_presets._views._property_setting_delegate import (
PropertySettingDelegate,
)
from pymmcore_widgets.device_properties._property_widget import PropertyWidget

if TYPE_CHECKING:
from pytestqt.qtbot import QtBot


def test_config_groups_tree(qtbot: QtBot) -> None:
core = CMMCorePlus()
core.loadSystemConfiguration()
tree = ConfigGroupsTree.create_from_core(core)
qtbot.addWidget(tree)
tree.show()
model = tree.model()
assert isinstance(model, QConfigGroupsModel)

# test the editor delegate -----------------------------

delegate = tree.itemDelegateForColumn(2)
assert isinstance(delegate, PropertySettingDelegate)

setting_value = model.index(0, 2, model.index(0, 0, model.index(0, 0)))
assert model.data(setting_value) == "1"

# open an editor
tree.edit(setting_value)
editor = tree.focusWidget()
assert isinstance(editor, PropertyWidget)
with qtbot.waitSignal(delegate.commitData):
editor.setValue("2")

# make sure the model is updated
assert model.data(setting_value) == "2"
group0 = model.get_groups()[0]
preset0 = next(iter(group0.presets.values()))
assert preset0.settings[0].property_value == "2"