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
[docs] 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)
[docs] 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)
[docs] 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)
[docs] 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
[docs] 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)
[docs] 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')