diff --git a/src/pymodaq_gui/examples/icons.py b/src/pymodaq_gui/examples/icons.py index 22a5a050..9cf1747e 100644 --- a/src/pymodaq_gui/examples/icons.py +++ b/src/pymodaq_gui/examples/icons.py @@ -1,8 +1,8 @@ import sys -from qtpy.QtWidgets import (QApplication, QGridLayout, QPushButton, QStyle, +from PyQt6.QtWidgets import (QApplication, QGridLayout, QPushButton, QStyle, QWidget, QVBoxLayout) -from qtpy.QtGui import QIcon +from PyQt6.QtGui import QIcon class Icons(): diff --git a/src/pymodaq_gui/managers/action_manager.py b/src/pymodaq_gui/managers/action_manager.py index ad35b369..16ba2eb5 100644 --- a/src/pymodaq_gui/managers/action_manager.py +++ b/src/pymodaq_gui/managers/action_manager.py @@ -61,7 +61,7 @@ def __repr__(self): def addaction(name: str = '', icon_name: Union[str, Path, QtGui.QIcon]= '', tip='', checkable=False, checked=False, slot: Callable = None, toolbar: QtWidgets.QToolBar = None, - menu: QtWidgets.QMenu = None, visible=True, shortcut=None, + menu: QtWidgets.QMenu = None, visible=True, shortcut: Union[str, QtCore.Qt.Key]=None, enabled=True): """Create a new action and add it eventually to a toolbar and a menu @@ -87,7 +87,7 @@ def addaction(name: str = '', icon_name: Union[str, Path, QtGui.QIcon]= '', tip= a menu where action should be added. visible: bool display or not the action in the toolbar/menu - shortcut: str + shortcut: str or Qt.Key a string defining a shortcut for this action enabled: bool set the enabled state @@ -240,12 +240,12 @@ def __init__(self, toolbar: QtWidgets.QToolBar = None, menu: QtWidgets.QMenu = N @property def _menu(self) -> QtWidgets.QMenu: """Get the default menu (backward compatibility)""" - return self._menus.get('_default') + return self._menus.get('_default', None) @property def _toolbar(self) -> QtWidgets.QToolBar: """Get the default toolbar (backward compatibility)""" - return self._toolbars.get('_default') + return self._toolbars.get('_default', None) def setup_actions(self): """Method where to create actions to be subclassed. Mandatory @@ -266,8 +266,9 @@ def setup_actions(self): def add_action(self, short_name: str = '', name: str = '', icon_name: Union[str, Path, QtGui.QIcon] = '', tip='', checkable=False, - checked=False, toolbar: Union[str, QtWidgets.QToolBar, None]=None, menu: Union[str, QtWidgets.QMenu, None] = None, - visible=True, shortcut=None, auto_toolbar=True, auto_menu=True, + checked=False, toolbar: Union[str, QtWidgets.QToolBar, None]=None, + menu: Union[str, QtWidgets.QMenu, None] = None, + visible=True, shortcut: Union[str, QtCore.Qt.Key]=None, auto_toolbar=True, auto_menu=True, enabled=True): """Create a new action and add it to toolbar and menu diff --git a/src/pymodaq_gui/managers/parameter_manager.py b/src/pymodaq_gui/managers/parameter_manager.py index 39e86b8d..1a9cf0be 100644 --- a/src/pymodaq_gui/managers/parameter_manager.py +++ b/src/pymodaq_gui/managers/parameter_manager.py @@ -231,7 +231,6 @@ def __init__( settings_name: Optional[str] = None, action_list: tuple = ("search", "save", "update", "load"), tree: ParameterTree = None - ): self._current_filter_text = "" if settings_name is None: @@ -268,12 +267,12 @@ def __init__( ) @property - def settings_tree(self): + def settings_tree(self) -> QtWidgets.QWidget: """QWidget: The main widget containing the parameter tree and toolbar.""" return self._settings_tree.widget @property - def tree(self): + def tree(self) -> ParameterTree: """ParameterTree: The underlying parameter tree widget.""" return self._settings_tree.tree diff --git a/src/pymodaq_gui/parameter/ioxml.py b/src/pymodaq_gui/parameter/ioxml.py index 73263e86..e0934c1d 100644 --- a/src/pymodaq_gui/parameter/ioxml.py +++ b/src/pymodaq_gui/parameter/ioxml.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from typing import Union from pathlib import Path @@ -13,6 +15,9 @@ from pyqtgraph.parametertree.Parameter import PARAM_TYPES, PARAM_NAMES +VALID_FOR_CONFIGURATION = 'valid_for_configuration' + + def walk_parameters_to_xml(parent_elt=None, param=None): """ To convert a parameter object (and children) to xml data tree. @@ -166,6 +171,9 @@ def dict_from_param(param): readonly = '0' opts.update(dict(readonly=readonly)) + if VALID_FOR_CONFIGURATION in param.opts: + opts.update({VALID_FOR_CONFIGURATION: '1' if param.opts[VALID_FOR_CONFIGURATION] else '0'}) + # if 'limits' in param.opts: # values = str(param.opts['limits']) # opts.update(dict(values=values)) @@ -258,12 +266,22 @@ def elt_to_dict(el): readonly = bool(int(el.get('readonly'))) param.update(dict(readonly=readonly)) + if VALID_FOR_CONFIGURATION in el.attrib.keys(): + valid = bool(int(el.get(VALID_FOR_CONFIGURATION))) + param.update({VALID_FOR_CONFIGURATION: valid}) + if 'show_pb' in el.attrib.keys(): show_pb = bool(int(el.get('show_pb'))) else: show_pb = False param.update(dict(show_pb=show_pb)) + if 'readonly' not in el.attrib.keys(): + readonly = False + else: + readonly = bool(int(el.get('readonly'))) + param.update(dict(readonly=readonly)) + if 'filetype' in el.attrib.keys(): filetype = bool(int(el.get('filetype'))) param.update(dict(filetype=filetype)) @@ -423,6 +441,7 @@ def walk_xml_to_parameter(params=[], XML_elt=None): raise e return params + def set_dict_from_el(el): """Convert an element into a dict ---------- @@ -513,49 +532,43 @@ def XML_file_to_parameter(file_name: Union[str, Path]) -> list: def XML_string_to_parameter(xml_string): + """ Convert a xml string into a list of dict for initialize pyqtgraph parameter object. """ - Convert a xml string into a list of dict for initialize pyqtgraph parameter object. - - =============== =========== ================================ - **Parameters** **Type** **Description** - - xml_string string the xml string to be converted - =============== =========== ================================ + root = ET.fromstring(xml_string) + params = walk_xml_to_parameter(params=[], XML_elt=root) + return params - Returns - ------- - params: a parameter list of dict to init a parameter - See Also - -------- - walk_parameters_to_xml - - Examples - -------- +def xml_string_to_parameter_dict(xml_string) -> dict: + """ + Convert a xml string into a dict to initialize pyqtgraph parameter object. """ root = ET.fromstring(xml_string) tree = ET.ElementTree(root) - # tree.write('test.xml') - params = walk_xml_to_parameter(params=[], XML_elt=root) + param_dict = set_dict_from_el(root) + if len(root) > 0: + param_dict['children'] = walk_xml_to_parameter(params=[], XML_elt=root) - return params + return param_dict + +def xml_string_to_parameter(xml_string) -> Parameter: + return Parameter.create(**xml_string_to_parameter_dict(xml_string)) def XML_string_to_pobject(xml_string) -> Parameter: """ - return a Parameter object from its *translated* version as a XML string + return a Parameter object from its deserialized version from a XML string + + Deprecated as not symetric with parameter_to_xml_string + Parameters ---------- xml_string: (str) string representation of a Parameter Object - Returns - ------- - Parameter - - See Also - -------- - parameter_to_xml_string """ return Parameter.create(name='settings', type='group', children=XML_string_to_parameter(xml_string)) + + + diff --git a/src/pymodaq_gui/parameter/utils.py b/src/pymodaq_gui/parameter/utils.py index e286e5be..12afb773 100644 --- a/src/pymodaq_gui/parameter/utils.py +++ b/src/pymodaq_gui/parameter/utils.py @@ -1,9 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, List, Tuple, Any, Union -from dataclasses import Field, fields +from typing import List, Tuple, Union import numpy as np from collections import OrderedDict -from dataclasses import dataclass from pymodaq_utils.utils import find_keys_from_val from pymodaq_utils.serialize.factory import SerializableFactory, SerializableBase from pymodaq_gui.parameter import ioxml @@ -75,8 +73,8 @@ def deserialize(cls, """ path, remaining_bytes = ser_factory.get_apply_deserializer(bytes_str, False) param_as_xml, remaining_bytes = ser_factory.get_apply_deserializer(remaining_bytes, False) - param_dict = ioxml.XML_string_to_parameter(param_as_xml) - param_obj = Parameter.create(**param_dict[0]) + param_dict = ioxml.xml_string_to_parameter_dict(param_as_xml) + param_obj = Parameter.create(**param_dict) return ParameterWithPath(param_obj, path), remaining_bytes @@ -472,3 +470,4 @@ def scroll_linear(scroll_val, min_val, max_val): d['readonly'] = False print(parent[0]['children'][1]['children']) + diff --git a/src/pymodaq_gui/utils/custom_app.py b/src/pymodaq_gui/utils/custom_app.py index 59168968..0375ea1b 100644 --- a/src/pymodaq_gui/utils/custom_app.py +++ b/src/pymodaq_gui/utils/custom_app.py @@ -8,6 +8,7 @@ from pymodaq_gui.utils.dock import DockArea, Dock from pymodaq_gui.managers.action_manager import ActionManager from pymodaq_gui.managers.parameter_manager import ParameterManager +from pymodaq_gui.parameter import ParameterTree class CustomApp(QObject, ActionManager, ParameterManager): @@ -38,10 +39,11 @@ class CustomApp(QObject, ActionManager, ParameterManager): log_signal = QtCore.Signal(str) params = [] - def __init__(self, parent: Union[DockArea, QtWidgets.QMainWindow, QtWidgets.QWidget] = None): + def __init__(self, parent: Union[DockArea, QtWidgets.QMainWindow, QtWidgets.QWidget] = None, + tree: ParameterTree = None): QObject.__init__(self) ActionManager.__init__(self) - ParameterManager.__init__(self) + ParameterManager.__init__(self, tree=tree) if not (isinstance(parent, DockArea) or isinstance(parent, QtWidgets.QMainWindow) or @@ -82,6 +84,18 @@ def setup_ui(self): self.connect_things() + self.do_things_after_ui_setup() + + def quit_fun(self): + """Method to be subclassed in order to define a custom quit function + """ + self.mainwindow.close() + + def do_things_after_ui_setup(self): + """Non mandatory method to be subclassed in order to do things after the UI setup + """ + pass + def setup_docks(self): """Mandatory method to be subclassed to setup the docks layout diff --git a/src/pymodaq_gui/utils/widgets/qled.py b/src/pymodaq_gui/utils/widgets/qled.py index 37e604af..3b0d0338 100644 --- a/src/pymodaq_gui/utils/widgets/qled.py +++ b/src/pymodaq_gui/utils/widgets/qled.py @@ -60,3 +60,7 @@ def LED_Clicked(self): else: self.set_as_true() self.value_changed.emit(not self.state) + + def setReadOnly(self, isreadonly: bool): + self.clickable = not isreadonly + diff --git a/src/pymodaq_gui/utils/widgets/table.py b/src/pymodaq_gui/utils/widgets/table.py index 353c1e72..852e5e88 100644 --- a/src/pymodaq_gui/utils/widgets/table.py +++ b/src/pymodaq_gui/utils/widgets/table.py @@ -189,7 +189,8 @@ def insert_data(self, row, data): def insertRows(self, row, count, parent): self.beginInsertRows(QtCore.QModelIndex(), row, row + count - 1) for ind in range(count): - self._data.insert(row + ind, self.data_tmp) + self._data.insert(row + ind, self.data_tmp[ind] if + (hasattr(self.data_tmp, '__len__') and len(self.data_tmp) == count) else self.data_tmp) self._checked.insert(row + ind, False) self.endInsertRows() return True