Source code for qudi.tools.config_editor.global_widgets

# -*- coding: utf-8 -*-

"""
QWidget serving as editor for the default global configuration section

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__ = ['GlobalConfigWidget', 'GlobalOptionsWidget', 'RemoteServerWidget', 'CustomOptionsWidget']

from typing import Dict, Union, Mapping, Optional, Any
from PySide2 import QtCore, QtWidgets

from qudi.util.widgets.separator_lines import HorizontalLine
from qudi.util.widgets.path_line_edit import PathLineEdit
from qudi.tools.config_editor.custom_widgets import CustomOptionsWidget
from qudi.core.config.schema import config_schema


[docs] class RemoteServerWidget(QtWidgets.QWidget): """Remote modules server configuration editor widget. """
[docs] def __init__(self, config: Optional[Mapping[str, Union[int, str]]] = None, parent: Optional[QtWidgets.QWidget] = None ) -> None: super().__init__(parent=parent) # Create main layout layout = QtWidgets.QGridLayout() layout.setColumnStretch(1, 1) self.setLayout(layout) # server enable flag label = QtWidgets.QLabel('Remote modules server:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.enable_checkbox = QtWidgets.QCheckBox() self.enable_checkbox.setChecked(True) self.enable_checkbox.setToolTip( 'Whether qudi should start a remote modules server at all.\nYou will not be able to ' 'communicate with remote qudi instances if this is disabled.' ) self.enable_checkbox.toggled.connect(self._toggle_editors) layout.addWidget(label, 0, 0) layout.addWidget(self.enable_checkbox, 0, 1) # Create server config editors in their own layout self._server_layout = QtWidgets.QGridLayout() self._server_layout.setColumnStretch(1, 1) layout.addLayout(self._server_layout, 1, 0, 1, 2) label = QtWidgets.QLabel('Host address:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.host_lineedit = QtWidgets.QLineEdit() self.host_lineedit.setToolTip( 'The host address to share qudi modules with other qudi instances' ) self._server_layout.addWidget(label, 0, 0) self._server_layout.addWidget(self.host_lineedit, 0, 1) label = QtWidgets.QLabel('Port:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.port_spinbox = QtWidgets.QSpinBox() self.port_spinbox.setToolTip('Port number for the remote modules server to bind to') self.port_spinbox.setRange(0, 65535) self.port_spinbox.setValue(12345) self._server_layout.addWidget(label, 1, 0) self._server_layout.addWidget(self.port_spinbox, 1, 1) label = QtWidgets.QLabel('Certificate file:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.certfile_lineedit = PathLineEdit(dialog_caption='Select SSL Certificate File', follow_symlinks=True) self.certfile_lineedit.setPlaceholderText('No certificate') self.certfile_lineedit.setToolTip('SSL certificate file path for the remote module server') self._server_layout.addWidget(label, 2, 0) self._server_layout.addWidget(self.certfile_lineedit, 2, 1) label = QtWidgets.QLabel('Key file:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.keyfile_lineedit = PathLineEdit(dialog_caption='Select SSL Key File', follow_symlinks=True) self.keyfile_lineedit.setPlaceholderText('No key') self.keyfile_lineedit.setToolTip('SSL key file path for the remote module server') self._server_layout.addWidget(label, 3, 0) self._server_layout.addWidget(self.keyfile_lineedit, 3, 1) self.set_config(config)
@property def config(self) -> Union[None, Dict[str, Union[int, str]]]: if self.enable_checkbox.isChecked(): host = self.host_lineedit.text().strip() cfg = {'address': host if host else 'localhost', 'port' : self.port_spinbox.value()} try: cfg['certfile'] = self.certfile_lineedit.paths[0] except IndexError: pass try: cfg['keyfile'] = self.keyfile_lineedit.paths[0] except IndexError: pass return cfg return None def set_config(self, config: Union[None, Mapping[str, Union[None, int, str]]]) -> None: if config is None: self.enable_checkbox.setChecked(False) else: self.enable_checkbox.setChecked(True) host = config.get('address', None) port = config.get('port', None) certfile = config.get('certfile', None) keyfile = config.get('keyfile', None) if host is None: host = 'localhost' if port is None: port = 12345 if certfile is None or keyfile is None: certfile = keyfile = '' self.host_lineedit.setText(host) self.port_spinbox.setValue(port) self.certfile_lineedit.setText(certfile) self.keyfile_lineedit.setText(keyfile) @QtCore.Slot(bool) def _toggle_editors(self, enabled: bool) -> None: widget_iterator = ( self._server_layout.itemAt(ii).widget() for ii in range(self._server_layout.count()) ) for widget in widget_iterator: widget.setVisible(enabled)
[docs] class GlobalOptionsWidget(QtWidgets.QWidget): """ """
[docs] def __init__(self, config: Optional[Mapping[str, Any]] = None, parent: Optional[QtWidgets.QWidget] = None ) -> None: super().__init__(parent=parent) # Create main layout layout = QtWidgets.QGridLayout() layout.setColumnStretch(1, 1) self.setLayout(layout) # Create module server editor self.remote_server_editor = RemoteServerWidget() layout.addWidget(self.remote_server_editor, 0, 0, 1, 2) # Add separator layout.addWidget(HorizontalLine(), 1, 0, 1, 2) # Create local module server port editor label = QtWidgets.QLabel('Namespace server port:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.namespace_port_spinbox = QtWidgets.QSpinBox() self.namespace_port_spinbox.setToolTip('Port number for the local namespace server') self.namespace_port_spinbox.setRange(0, 65535) layout.addWidget(label, 2, 0) layout.addWidget(self.namespace_port_spinbox, 2, 1) # Create flag editor to enforce remote calls by value label = QtWidgets.QLabel('Force remote calls by value:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.force_calls_by_value_checkbox = QtWidgets.QCheckBox() self.force_calls_by_value_checkbox.setToolTip( 'Will force all arguments from remote calls to qudi API methods to pass by value\n' '(serialized -> sent to qudi -> de-serialized).' ) layout.addWidget(label, 3, 0) layout.addWidget(self.force_calls_by_value_checkbox, 3, 1) # Create flag editor to hide manager window upon startup label = QtWidgets.QLabel('Hide manager window:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.hide_manager_window_checkbox = QtWidgets.QCheckBox() self.hide_manager_window_checkbox.setToolTip( 'Whether to suppress the qudi module manager window at startup.' ) layout.addWidget(label, 4, 0) layout.addWidget(self.hide_manager_window_checkbox, 4, 1) # Create flag editor to auomatically create a data sub-directory for each day label = QtWidgets.QLabel('Create daily data directories:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.daily_data_dirs_checkbox = QtWidgets.QCheckBox() self.daily_data_dirs_checkbox.setToolTip( 'Whether to automatically create daily sub-directories in the data directory for file ' 'based data storage facilities' ) layout.addWidget(label, 5, 0) layout.addWidget(self.daily_data_dirs_checkbox, 5, 1) # Create default data path editor label = QtWidgets.QLabel('Default data directory:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.data_directory_lineedit = PathLineEdit(dialog_caption='Select Default Data Directory', select_directory=True) self.data_directory_lineedit.setPlaceholderText('Default "<UserHome>/qudi/Data/"') self.data_directory_lineedit.setToolTip('Default data directory for qudi modules to save ' 'measurement data into.') layout.addWidget(label, 6, 0) layout.addWidget(self.data_directory_lineedit, 6, 1) # Create startup modules editor label = QtWidgets.QLabel('Startup Modules:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.startup_lineedit = QtWidgets.QLineEdit() self.startup_lineedit.setPlaceholderText('No startup modules') self.startup_lineedit.setToolTip('Modules to be automatically activated on qudi startup.\n' 'Separate multiple module names with commas.') layout.addWidget(label, 7, 0) layout.addWidget(self.startup_lineedit, 7, 1) # Create stylesheet file path editor label = QtWidgets.QLabel('Stylesheet:') label.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignVCenter) self.stylesheet_lineedit = PathLineEdit(dialog_caption='Select QSS Stylesheet', filters='Stylesheets (*.qss)', follow_symlinks=True) self.stylesheet_lineedit.setPlaceholderText('Platform dependent Qt default') self.stylesheet_lineedit.setToolTip( 'File path for qudi QSS stylesheet to use.\nIf just a file name is given, the file ' 'must be found in the qudi artwork resources.' ) layout.addWidget(label, 8, 0) layout.addWidget(self.stylesheet_lineedit, 8, 1) # Get default config from JSON schema global_props = config_schema()['properties']['global']['properties'] self._config_defaults = { name: prop.get('default', None) for name, prop in global_props.items() } # Fixme: Remove deprecated option manually del self._config_defaults['extension_paths'] self.set_config(config)
@property def config(self) -> Dict[str, Any]: config = { 'remote_modules_server' : self.remote_server_editor.config, 'namespace_server_port' : self.namespace_port_spinbox.value(), 'force_remote_calls_by_value': self.force_calls_by_value_checkbox.isChecked(), 'hide_manager_window' : self.hide_manager_window_checkbox.isChecked(), 'daily_data_dirs' : self.daily_data_dirs_checkbox.isChecked(), 'startup_modules' : [mod.strip() for mod in self.startup_lineedit.text().split(',') if mod.strip()], } try: config['default_data_dir'] = self.data_directory_lineedit.paths[0] except IndexError: config['default_data_dir'] = self._config_defaults['default_data_dir'] try: config['stylesheet'] = self.stylesheet_lineedit.paths[0] except IndexError: config['stylesheet'] = self._config_defaults['stylesheet'] return config def set_config(self, config: Union[None, Mapping[str, Any]]): if config is None: config = dict() config = { name: config.get(name, default) for name, default in self._config_defaults.items() } self.remote_server_editor.set_config(config['remote_modules_server']) self.namespace_port_spinbox.setValue(config['namespace_server_port']) self.force_calls_by_value_checkbox.setChecked(config['force_remote_calls_by_value']) self.hide_manager_window_checkbox.setChecked(config['hide_manager_window']) self.daily_data_dirs_checkbox.setChecked(config['daily_data_dirs']) default_data_dir = config['default_data_dir'] self.data_directory_lineedit.setText('' if default_data_dir is None else default_data_dir) stylesheet = config['stylesheet'] self.stylesheet_lineedit.setText('' if stylesheet is None else stylesheet) self.startup_lineedit.setText(','.join(config['startup_modules']))
[docs] class GlobalConfigWidget(QtWidgets.QWidget): """ """
[docs] def __init__(self, config: Optional[Mapping[str, Any]] = None, parent: Optional[QtWidgets.QWidget] = None ) -> None: super().__init__(parent=parent) # Create main layout layout = QtWidgets.QVBoxLayout() self.setLayout(layout) # Create Caption label = QtWidgets.QLabel('Global Configuration') label.setAlignment(QtCore.Qt.AlignCenter) font = label.font() font.setBold(True) font.setPointSize(font.pointSize() + 4) label.setFont(font) layout.addWidget(label) # Create default config editor self.default_options_widget = GlobalOptionsWidget() layout.addWidget(self.default_options_widget) # Remember default global config option names self._default_option_names = frozenset(self.default_options_widget.config) # Add separator layout.addWidget(HorizontalLine()) # Create custom option editor self.custom_options_widget = CustomOptionsWidget(forbidden_names=self._default_option_names) layout.addWidget(self.custom_options_widget) layout.addStretch(1) self.set_config(config)
@property def config(self) -> Dict[str, Any]: config = self.default_options_widget.config config.update(self.custom_options_widget.config) return config def set_config(self, config: Union[None, Mapping[str, Any]]) -> None: if config is None: self.default_options_widget.set_config(None) self.custom_options_widget.set_config(None) else: default_config = {name: value for name, value in config.items() if name in self._default_option_names} custom_config = {name: value for name, value in config.items() if name not in self._default_option_names} self.default_options_widget.set_config(default_config) self.custom_options_widget.set_config(custom_config)