Source code for qudi.tools.config_editor.tree_widgets
# -*- coding: utf-8 -*-
"""
"""
__all__ = [
'AvailableModulesTreeWidget',
'SelectedModulesTreeWidget',
'ConfigModulesTreeWidget',
]
from re import compile
from PySide2 import QtCore, QtWidgets, QtGui
from typing import Optional, Iterable, Mapping, Tuple, Dict, List, Sequence
[docs]
class AvailableModulesTreeWidget(QtWidgets.QTreeWidget):
""" """
[docs]
def __init__(
self,
modules: Optional[Iterable[str]] = None,
parent: Optional[QtWidgets.QWidget] = None,
) -> None:
super().__init__(parent=parent)
self.setColumnCount(2)
self.setHeaderLabels(('Base', 'module.Class'))
self.setSelectionMode(self.ExtendedSelection)
self.setEditTriggers(self.EditTrigger.NoEditTriggers)
self.setDragEnabled(True)
self.top_level_items = dict()
self.clear_modules()
if modules is not None:
self.set_modules(sorted(modules))
@property
def modules(self) -> List[str]:
modules = list()
for base, top_item in self.top_level_items.items():
items = [top_item.child(index) for index in range(top_item.childCount())]
modules.extend(f'{base}.{it.text(1)}' for it in items)
return modules
[docs]
def set_modules(self, modules: Iterable[str]) -> None:
# Clear all modules
self._clear_modules()
# Add new modules
for module in modules:
self._add_module(module)
# Resize columns
self.resize_columns_to_content()
[docs]
def add_module(self, module: str) -> None:
self._add_module(module)
self.resize_columns_to_content()
def _add_module(self, module: str) -> None:
base, module_class = module.split('.', 1)
item = QtWidgets.QTreeWidgetItem()
item.setText(1, module_class)
item.setFlags(
QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsDragEnabled
)
self.top_level_items[base].addChild(item)
[docs]
def remove_module(self, module: str) -> None:
self._remove_module(module)
self.resize_columns_to_content()
def _remove_module(self, module: str) -> None:
base, module_class = module.split('.', 1)
top_level_item = self.top_level_items[base]
for index in range(top_level_item.childCount()):
child = top_level_item.child(index)
if child.text(1) == module_class:
top_level_item.removeChild(child)
break
def _clear_modules(self) -> None:
self.clear()
for disp_base in ('GUI', 'Logic', 'Hardware'):
item = QtWidgets.QTreeWidgetItem()
item.setFlags(QtCore.Qt.ItemIsEnabled)
item.setText(0, disp_base)
self.addTopLevelItem(item)
item.setExpanded(True)
self.top_level_items[disp_base.lower()] = item
[docs]
def resize_columns_to_content(self) -> None:
for i in range(self.columnCount()):
self.resizeColumnToContents(i)
[docs]
def mimeData(self, items: Sequence) -> QtCore.QMimeData:
"""Add text to mime data. This is the quick (but not necessarily dirty) way."""
texts = tuple(f'{it.parent().text(0).lower()}.{it.text(1)}' for it in items)
mime = super().mimeData(items)
mime.setText(';'.join(texts))
return mime
[docs]
class ConfigModulesTreeWidget(QtWidgets.QTreeWidget):
""" """
[docs]
def __init__(
self,
named_modules: Optional[Mapping[str, str]] = None,
unnamed_modules: Optional[Iterable[str]] = None,
parent: Optional[QtWidgets.QWidget] = None,
) -> None:
super().__init__(parent=parent)
self.setDragEnabled(False)
self.setDropIndicatorShown(False)
self.setAcceptDrops(True)
self.setColumnCount(3)
self.setHeaderLabels(['Base', 'Name', 'module.Class'])
self.setSelectionMode(self.SingleSelection)
self.setEditTriggers(self.EditTrigger.NoEditTriggers)
self.itemDoubleClicked.connect(self._edit_item_column)
self.top_level_items = dict()
self.set_modules(named_modules, unnamed_modules)
self._valid_foreground = next(iter(self.top_level_items.values())).foreground(0)
self._name_regex = compile(r'^[a-zA-Z_]+[a-zA-Z0-9_]*$')
@property
def modules(self) -> Tuple[Dict[str, str], List[str]]:
named_modules = dict()
unnamed_modules = list()
for base, top_item in self.top_level_items.items():
items = [top_item.child(index) for index in range(top_item.childCount())]
for it in items:
name = it.text(1)
module = f'{base}.{it.text(2)}'
if self.is_valid_name(name) and name not in named_modules:
named_modules[name] = module
else:
unnamed_modules.append(module)
return named_modules, unnamed_modules
[docs]
def set_modules(
self,
named_modules: Optional[Mapping[str, str]] = None,
unnamed_modules: Optional[Iterable[str]] = None,
) -> None:
if named_modules is None:
named_modules = dict()
if unnamed_modules is None:
unnamed_modules = list()
# Clear all modules
self._clear_modules()
# Add new modules
for name, module in named_modules.items():
self._add_module(module, name)
for module in unnamed_modules:
self._add_module(module)
# Resize columns
self.resize_columns_to_content()
[docs]
def add_module(self, module: str, name: Optional[str] = None) -> None:
self._add_module(module, name)
self.resize_columns_to_content()
def _add_module(self, module: str, name: Optional[str] = None) -> None:
base, module_class = module.split('.', 1)
item = QtWidgets.QTreeWidgetItem()
item.setText(1, '<enter unique name>' if name is None else name)
item.setText(2, module_class)
item.setFlags(
QtCore.Qt.ItemIsEnabled
| QtCore.Qt.ItemIsSelectable
| QtCore.Qt.ItemIsEditable
)
self.top_level_items[base].addChild(item)
[docs]
def remove_module(self, name: str) -> None:
self._remove_module(name)
self.resize_columns_to_content()
def _remove_module(self, name: str) -> None:
found = False
for base, top_item in self.top_level_items.items():
for index in range(top_item.childCount()):
child = top_item.child(index)
if child.text(1) == name:
top_item.removeChild(child)
found = True
break
if found:
break
def _clear_modules(self) -> None:
self.clear()
for disp_base in ['GUI', 'Logic', 'Hardware']:
item = QtWidgets.QTreeWidgetItem()
item.setFlags(QtCore.Qt.ItemIsEnabled)
item.setText(0, disp_base)
self.addTopLevelItem(item)
item.setExpanded(True)
self.top_level_items[disp_base.lower()] = item
[docs]
def resize_columns_to_content(self) -> None:
for i in range(self.columnCount()):
self.resizeColumnToContents(i)
[docs]
def is_valid_name(self, name: str) -> bool:
set_names = list()
for base, top_item in self.top_level_items.items():
items = [top_item.child(index) for index in range(top_item.childCount())]
set_names.extend(it.text(1) for it in items)
return self._name_regex.match(name) is not None and set_names.count(name) <= 1
@QtCore.Slot(QtWidgets.QTreeWidgetItem, int)
def _edit_item_column(self, item: QtWidgets.QTreeWidgetItem, column: int) -> None:
if item and column == 1 and item.parent() is not None:
self.editItem(item, column)
[docs]
class SelectedModulesTreeWidget(ConfigModulesTreeWidget):
""" """
[docs]
def dropEvent(self, event: QtGui.QDropEvent) -> None:
if isinstance(event.source(), AvailableModulesTreeWidget):
full_text = event.mimeData().text()
for module in full_text.split(';'):
self._add_module(module)
self.resize_columns_to_content()
event.accept()
else:
event.ignore()
[docs]
def keyPressEvent(self, event: QtGui.QKeyEvent) -> None:
if event.key() == QtCore.Qt.Key_Delete:
for item in self.selectedItems():
if item.parent() is not None:
item.parent().removeChild(item)
event.accept()
else:
super().keyPressEvent(event)