Source code for qudi.tools.config_editor.custom_widgets
# -*- coding: utf-8 -*-
"""
QWidgets serving as editors for custom configuration entries.
Copyright (c) 2021, the qudi developers. See the AUTHORS.md file at the top-level directory of this
distribution and on <https://github.com/Ulm-IQO/qudi-core/>
This file is part of qudi.
Qudi is free software: you can redistribute it and/or modify it under the terms of
the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
Qudi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with qudi.
If not, see <https://www.gnu.org/licenses/>.
"""
__all__ = ['CustomItemsWidget', 'CustomOptionsWidget', 'CustomConnectorsWidget']
import os
from PySide2 import QtCore, QtWidgets, QtGui
from typing import Optional, Mapping, Union, Dict, Iterable, Any
from qudi.util.paths import get_artwork_dir
[docs]
class CustomItemsWidget(QtWidgets.QWidget):
"""
"""
[docs]
def __init__(self,
forbidden_names: Optional[Iterable[str]] = None,
allowed_values: Optional[Iterable[str]] = None,
config: Optional[Mapping[str, str]] = None,
parent: Optional[QtWidgets.QWidget] = None
) -> None:
super().__init__(parent=parent)
self._forbidden_names = frozenset() if forbidden_names is None else frozenset(
forbidden_names)
self._allowed_values = [val.strip() for val in allowed_values] if allowed_values else None
layout = QtWidgets.QGridLayout()
layout.setColumnStretch(2, 1)
self.setLayout(layout)
icons_dir = os.path.join(get_artwork_dir(), 'icons')
self.add_item_button = QtWidgets.QToolButton()
self.add_item_button.setIcon(QtGui.QIcon(os.path.join(icons_dir, 'list-add')))
self.add_item_button.clicked.connect(self._add_item_clicked)
self.item_name_lineedit = QtWidgets.QLineEdit()
layout.addWidget(self.add_item_button, 0, 0, 1, 1)
layout.addWidget(self.item_name_lineedit, 0, 1, 1, 2)
# Remove icons reused for each custom item
self._remove_icon = QtGui.QIcon(os.path.join(icons_dir, 'list-remove'))
# Keep track of custom item widgets
self._item_widgets = dict()
# Set config if given
self.set_config(config)
@property
def config(self) -> Dict[str, str]:
if self._allowed_values is None:
config = {
name: widgets[2].text().strip() for name, widgets in self._item_widgets.items()
}
else:
config = {
name: widgets[2].currentText() for name, widgets in self._item_widgets.items()
}
return config
def set_config(self, config: Union[None, Mapping[str, str]]) -> None:
self.clear_items()
if config:
for name, value in config.items():
self.add_item(name, value)
def add_item(self, name: str, value: Optional[str] = None) -> None:
if not name:
raise ValueError('Item name must be non-empty string')
if name in self._forbidden_names:
raise ValueError(f'Item name to add "{name}" is one of the forbidden names:\n'
f'{set(self._forbidden_names)}')
if name in self._item_widgets:
raise ValueError(f'Item name to add "{name}" is already present')
label = QtWidgets.QLabel(f'{name}:')
label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter)
label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
label.setFocusPolicy(QtCore.Qt.FocusPolicy.NoFocus)
if self._allowed_values:
editor = QtWidgets.QComboBox()
editor.addItem('')
editor.addItems(self._allowed_values)
editor.setSizeAdjustPolicy(QtWidgets.QComboBox.AdjustToContents)
else:
editor = QtWidgets.QLineEdit()
remove_button = QtWidgets.QToolButton()
remove_button.setIcon(self._remove_icon)
remove_button.clicked.connect(lambda: self.remove_item(name))
row = len(self._item_widgets) + 1
layout = self.layout()
layout.addWidget(remove_button, row, 0)
layout.addWidget(label, row, 1)
layout.addWidget(editor, row, 2)
self._item_widgets[name] = (remove_button, label, editor)
def remove_item(self, name: str) -> None:
if name not in self._item_widgets:
return
layout = self.layout()
# Remove all widgets from layout
for button, label, editor in reversed(list(self._item_widgets.values())):
layout.removeWidget(button)
layout.removeWidget(label)
layout.removeWidget(editor)
# Delete widgets for row to remove
button, label, editor = self._item_widgets.pop(name)
button.clicked.disconnect()
button.setParent(None)
label.setParent(None)
editor.setParent(None)
button.deleteLater()
label.deleteLater()
editor.deleteLater()
# Add all remaining widgets to layout again
for row, (button, label, editor) in enumerate(self._item_widgets.values(), 1):
layout.addWidget(button, row, 0)
layout.addWidget(label, row, 1)
layout.addWidget(editor, row, 2)
def clear_items(self) -> None:
layout = self.layout()
widgets = list(self._item_widgets.values())
self._item_widgets.clear()
for button, label, editor in reversed(widgets):
layout.removeWidget(button)
layout.removeWidget(label)
layout.removeWidget(editor)
button.setParent(None)
label.setParent(None)
editor.setParent(None)
button.deleteLater()
label.deleteLater()
editor.deleteLater()
@QtCore.Slot()
def _add_item_clicked(self) -> None:
name = self.item_name_lineedit.text().strip()
try:
self.add_item(name)
self.item_name_lineedit.clear()
except ValueError:
pass
[docs]
class CustomOptionsWidget(CustomItemsWidget):
"""
"""
[docs]
def __init__(self,
forbidden_names: Optional[Iterable[str]] = None,
config: Optional[Mapping[str, Any]] = None,
parent: Optional[QtWidgets.QWidget] = None
) -> None:
super().__init__(forbidden_names=forbidden_names, config=config, parent=parent)
self.add_item_button.setToolTip('Add custom ConfigOption with given name.')
self.item_name_lineedit.setPlaceholderText('Enter custom ConfigOption name')
@property
def config(self) -> Dict[str, Any]:
cfg = super().config
for name, value in cfg.items():
if value == '':
cfg[name] = None
else:
try:
cfg[name] = eval(value)
except:
pass
return cfg
def set_config(self, config: Union[None, Mapping[str, Any]]) -> None:
if config:
config = {name: '' if val is None else repr(val) for name, val in config.items()}
return super().set_config(config)
def add_item(self, name: str, value: Optional[str] = None) -> None:
super().add_item(name=name, value=value)
self._item_widgets[name][2].setPlaceholderText('text parsed by eval()')
[docs]
class CustomConnectorsWidget(CustomItemsWidget):
"""
"""
[docs]
def __init__(self,
forbidden_names: Optional[Iterable[str]] = None,
module_names: Optional[Iterable[str]] = None,
config: Optional[Mapping[str, Any]] = None,
parent: Optional[QtWidgets.QWidget] = None
) -> None:
super().__init__(forbidden_names=forbidden_names,
allowed_values=module_names,
config=config,
parent=parent)
self.add_item_button.setToolTip('Add custom Connector with given name.')
self.item_name_lineedit.setPlaceholderText('Enter custom Connector name')