Source code for visiannot.configuration.ConfigurationWindow

# -*- coding: utf-8 -*-
#
# Copyright Université Rennes 1 / INSERM
# Contributor: Raphael Weber
#
# Under CeCILL license
# http://www.cecill.info

"""
Module defining class :class:`.ConfigurationWindow`
"""


from PyQt5 import QtWidgets, QtCore, QtGui
import os
from configobj import ConfigObj
import numpy as np
from ..tools import ToolsPyQt
from collections import OrderedDict
from .Configuration import Configuration


[docs]class ConfigurationWindow(): def __init__(self, path=""): """ Class creating the window for configuring :class:`.ViSiAnnoTLongRec` :param path: path to the configuration file to load when creating the window :type path: str The window is composed of 5 main widgets: - Video configuration - Signal configuration - Eevent annotation configuration - General configuration - Group box with load/save/done push buttons Each one of the video configuration, signal configuration and event annotation configuration is contained in an instance of :class:`Configuration`. The signal configuration has several configuration children (Interval, Threshold and YRange), each one displayed in a separate window. All the instances of :class:`Configuration` are stored in the dictionary :attr:`.meta_dict`. On the one hand, the attribute :attr:`ConfigurationWindow.lay` refers to the first level of QGridLayout, meaning the layout filling the whole window. On the other hand, the second level of QGridLayout fills the first level layout ConfigurationWindow.lay. These layouts are the configuration widgets (general => ConfigurationWindow.general_lay, video => ConfigurationWindow.meta_dict["Video"].lay, ...). The different types of configuration are stored in 7 dictionaries: - :attr:`ConfigurationWindow.general_dict` - ``ConfigurationWindow.meta_dict["Video"].dict`` - ``ConfigurationWindow.meta_dict["Signal"].dict`` - ``ConfigurationWindow.meta_dict["Interval"].dict`` - ``ConfigurationWindow.meta_dict["Threshold"].dict`` - ``ConfigurationWindow.meta_dict["AnnotEvent"].dict`` - ``ConfigurationWindow.meta_dict["AnnotImage"].dict`` When values are modified manually in the window, these dictionaries are not updated. The method :meth:`.setConfigurationDictionaries` must be called to do so. """ #: (*dict*) Each value is an instances of :class:`Configuration` #: contained in the window #: #: The keys are: #: #: - Video #: - Signal #: - Interval #: - Threshold #: - YRange #: - AnnotEvent #: - AnnotImage #: #: See :attr:`Configuration.dict` for details. self.meta_dict = {} #: (*list*) Types of configuration, each element is a string and must #: be a key of :attr:`.meta_dict` self.config_type_list = [ "Video", "Signal", "Interval", "Threshold", "YRange", "AnnotEvent", "AnnotImage" ] #: (*dict*) Same keys as :attr:`.general_dict`, #: value is the list of positions of the corresponding widgets in the #: general configuration widget #: (:attr:`.general_lay`) self.general_config_position_dict = {} #: (*QtWidgets.QGridLayout*) Layout of the general configuration widget self.general_lay = None #: (*QtWidgets.QGroupBox*) Group box of the general configuration #: widget self.general_group_box = None #: (*QtWidgets.QPushButton*) Push button for changing the directory of #: annotations self.general_push_button = None # *********************** create window ***************************** # #: (*QtWidgets.QApplication*) Display initializer self.app = ToolsPyQt.initializeDisplay() # set default font self.app.setFont(QtGui.QFont("Times", 12)) #: (*QtWidgets.QWidget*) Container of main configuration window self.win = None #: (*QtWidgets.QGridLayout*) Layout of the main configuration window self.lay = None # create window self.win, self.lay = ToolsPyQt.createWindow( title="ViSiAnnoT configuration" ) # create scroll area scroll_lay, _ = ToolsPyQt.addScrollArea(self.lay, (0, 0)) # ******************** create video widget ************************** # self.meta_dict["Video"] = Configuration( self.lay, (0, 0), "Video", 1, [ ("edit", 1, {}), ("edit", 1, {}), ("edit", 1, {}), ("spin", 1, {"minimum": -2}), ("edit", 1, {}) ], ['', '*.mp4', '_', 0, '%Y-%m-%dT%H-%M-%S'], help_text="1. Directory\ 2. File pattern\ 3. Timestamp delimiter\ 4. Timestamp position\ 5. Timestamp format", config_base_name="vid_", pos_dir=0, flag_dir_identical=True ) # add configuration group box to scroll area scroll_lay.addWidget(self.meta_dict["Video"].group_box) # ******************** create signal widget ************************* # self.meta_dict["Signal"] = Configuration( self.lay, (1, 0), "Signal", 2, [ ("edit", 1, {}), ("edit", 1, {}), ("edit_freq", 1, {}), ("edit", 1, {}), ("edit", 1, {}), ("spin", 1, {"minimum": -2}), ("edit", 1, {}), ("edit_literal", 1, {}) ], [['', '', 0, '*', '_', 0, '%Y-%m-%dT%H-%M-%S', None]], help_text="1. Signal widget name\ 2. Directory\ 3. Key to access the data (for .mat or .h5), used as legend\ 4. Frequency (0 if 2D data with timestamp, -1 if same as video)\ 5. File pattern\ 6. Timestamp delimiter\ 7. Timestamp position\ 8. Timestamp format\ 9. Plot style (default is None)", pos_dir=0, flag_dir_identical=True, flag_key=True, config_base_name="sig_", children_config_name_list=["Interval", "Threshold", "YRange"] ) # add configuration group box to scroll area scroll_lay.addWidget(self.meta_dict["Signal"].group_box) # listen to callback self.meta_dict["Signal"].button_group_children.buttonClicked[int].connect( self.showChildWindow ) # **************** create annotEvent widget ************************* # self.meta_dict["AnnotEvent"] = Configuration( self.lay, (2, 0), "AnnotEvent", 1, [ ("spin", 4, [ {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 100} ]) ], (0, 0, 0, 50), help_text="1. Label\ 2. Color (RGBA)", flag_key=True, config_base_name="label_" ) # add configuration group box to scroll area scroll_lay.addWidget(self.meta_dict["AnnotEvent"].group_box) # **************** create annotImage widget ************************* # self.meta_dict["AnnotImage"] = Configuration( self.lay, (3, 0), "AnnotImage", 1, [("edit", 1, {})], '', help_text="1. Label", config_base_name="label_" ) # add configuration group box to scroll area scroll_lay.addWidget(self.meta_dict["AnnotImage"].group_box) # ******************* create general widget ************************* # self.createWidgetGeneral((4, 0)) # add group box to scroll area scroll_lay.addWidget(self.general_group_box) # listen to callback self.general_push_button.clicked.connect(self.setDirectoryAnnotation) # ***************** create load/save/done widget ******************** # #: (*QtWidgets.QButtonGroup*) Group of buttons with the push buttons #: Load/Save/Done self.button_group_lsd = None _, group_box_lsd, self.button_group_lsd = \ ToolsPyQt.addWidgetButtonGroup( self.lay, (5, 0), ["Load", "Save", "Done"], button_type="push", box_title="Configuration file", width=120 ) # set size group_box_lsd.setMaximumHeight(80) # listen to callback self.button_group_lsd.buttonClicked[int].connect(self.callLSD) # ******************* create children windows ********************** # #: (*list*) Instances of QtWidgets.QWidget containing the children #: configuration windows self.children_win_list = [] # interval configuration self.createChildConfigurationWindow( self.meta_dict["Signal"], "Interval", 2, [ ("edit", 1, {}), ("edit", 1, {}), ("edit_freq", 1, {}), ("edit", 1, {}), ("edit", 1, {}), ("edit", 1, {}), ("edit", 1, {}), ("spin", 4, [ {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 100} ]) ], [['', '', 0, '', '_', 0, '%Y-%m-%dT%H-%M-%S', (0, 0, 0, 50)]], help_text="1. Directory\ 2. Key to access the data (for .mat or .h5)\ 3. Frequency (0 if timestamps, -1 if same as signal)\ 4. File pattern\ 5. Timestamp delimiter\ 6. Timestamp position\ 7. Timestamp format\ 8. Color (RGBA)", pos_dir=0, flag_dir_identical=True, win_size=(1200, 400) ) # threshold configuration self.createChildConfigurationWindow( self.meta_dict["Signal"], "Threshold", 2, [ ("edit_float", 1, {}), ("spin", 4, [ {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 255}, {"minimum": 0, "maximum": 100} ]) ], [[0, (0, 0, 0, 50)]], help_text="1. Threhsold value\ 2. Color (RGBA)", win_size=(600, 400) ) # Y range configuration self.createChildConfigurationWindow( self.meta_dict["Signal"], "YRange", 1, [("edit_float", 1, {}), ("edit_float", 1, {})], [0, 0], help_text="1. Min value\ 2. Max value", win_size=(500, 400) ) # ************** initialize general configuration ******************* # #: (*dict*) Default general configuration, see :attr:`.general_dict` #: for details self.general_dict_default = { "flag_synchro": False, "flag_pause_status": True, "layout_mode": 0, "zoom_factor": 2, "down_freq": 500, "max_points": 5000, "nb_ticks": 10, "trunc_duration": [0, 0], "time_zone": "Europe/Paris", "annot_dir_base": "Annotations", "from_cursor_list": [], "ticks_size": 12, "ticks_color": (93, 91, 89), "ticks_offset": 5, "font_name": "Times", "font_size": 12, "font_size_title": 16, "font_color": (0, 0, 0), "nb_table_annot": 5, "bg_color": (244, 244, 244), "bg_color_plot": (255, 255, 255), "height_widget_signal": 150 } # load configuration file if it exists if os.path.isfile(path): self.load(path) self.resetDisplay() print("Configuration file loaded") # create default configuration (only general configuration) else: #: (*dict*) General configuration #: #: The keys and values are: #: #: - ``"flag_synchro"``: (*bool*) specify if the signals are #: synchronized with video #: - ``"flag_pause_status"``: (*bool*) specify if the video is #: paused at launching #: - ``"layout_mode"``: (*int*) either 0, 1 or 2 #: - ``"zoom_factor"``: (*int*) #: - ``"down_freq"``: (*float*) #: - ``"max_points"``: (*int*) #: - ``"nb_ticks"``: (*int*) #: - ``"trunc_duration"``: (*list*) length 2 *(minute, second)* #: - ``"time_zone"``: (*str*) time zone (complying with pytz #: package) #: - ``"annot_dir_base"``: (*str*) base directory of the #: annotations #: - ``"from_cursor_list"``: (*list*) each element is a list of #: length 2 *(minute, second)* #: - ``"ticks_size"``: (*int*) size of the ticks text in signal #: plots #: - ``"ticks_color"``: (*list*) RGB color of ticks #: - ``"ticks_offset"``: (*int*) space in pixels between ticks and #: associated values #: - ``"font_name"``: (*str*) font of the text in ViSiAnnoT #: - ``"font_size"``: (*int*) font size in ViSiAnnoT #: - ``"font_size_title"``: (*int*) font size in ViSiAnnoT (title #: of video widget and progress bar widget) #: - ``"font_color"``: (*list*) RGB color of font in ViSiAnnoT #: - ``"nb_table_annot"``: (*int*) maximum number of labels in one #: row in annotation widgets #: - ``"bg_color"``: (*list*) RGB color of background in ViSiAnnoT #: - ``"bg_color_plot"``: (*list*) RGB color of background color of #: signal plots self.general_dict = self.general_dict_default.copy() # display general configuration values self.setGeneralConfiguration(True) # infinite loop ToolsPyQt.infiniteLoopDisplay(self.app) # *********************************************************************** # # ******************** ConfigurationWindow methods ********************** # # *********************************************************************** # # *********************************************************************** # # Group: Methods for widget creation # *********************************************************************** #
[docs] def createWidgetGeneral(self, widget_position): """ Adds a widget with the general configuration to the layout :attr:`ConfigurationWindow.lay` The widget is contained in an instance of QtWidgets.QGroupBox. This method sets the following attributes: - :attr:`.general_config_position_dict`. - :attr:`.general_group_box`. - :attr:`.general_lay`, - :attr:`.general_push_button`. :param widget_position: position of the group box in the parent grid layout, length 2 ``(row, col)`` or 4 ``(row, col, rowspan, colspan)`` :type widget_position: tuple of integers """ # create general configuration group box self.general_lay, self.general_group_box = ToolsPyQt.addGroupBox( self.lay, widget_position, "General" ) # minimum height of widgets in the group box height = 30 # list of elements to add in the group box # # each element is a tuple of length 5: # - text to display next to the widget element # - corresponding key in the general configuration dictionary # - type of widget element ("check_box", "spin", "spin_double" or # "edit") # - number of widget elements # - dictionary with keyword arguments to pass to the widget constructor elt_list = [ ("Signals synchronized", "flag_synchro", "check_box", 1, {}), ( "Video paused at launch", "flag_pause_status", "check_box", 1, {} ), ( "Layout mode (1, 2 or 3)", "layout_mode", "spin", 1, {"minimum": 1, "maximum": 3} ), ("Time zone", "time_zone", "edit", 1, {}), ( "Max nb of points to display", "max_points", "spin", 1, {"minimum": 1, "maximum": 100000} ), ( "Max signal frequency (above downsampling)", "down_freq", "spin_double", 1, {"maximum": 100000} ), ( "Minimum height in pixels of the signal widgets", "height_widget_signal", "spin", 1, {"minimum": 1, "maximum": 100000} ), ( "Trunc duration (min, sec)", "trunc_duration", "spin", 2, {"minimum": 0, "maximum": 59} ), ("Zoom factor", "zoom_factor", "spin", 1, {"minimum": 1}), ("Temporal ticks nb", "nb_ticks", "spin", 1, {"minimum": 1}), ( "Ticks color", "ticks_color", "spin", 3, {"minimum": 0, "maximum": 255} ), ("Ticks size", "ticks_size", "spin", 1, {"minimum": 1}), ("Ticks offset", "ticks_offset", "spin", 1, {}), ("Font name", "font_name", "edit", 1, {}), ("Font size", "font_size", "spin", 1, {"minimum": 1}), ( "Font size (title)", "font_size_title", "spin", 1, {"minimum": 1} ), ( "Font color", "font_color", "spin", 3, {"minimum": 0, "maximum": 255} ), ( "Maximum number of labels in a row", "nb_table_annot", "spin", 1, {"minimum": 1} ), ( "Background color", "bg_color", "spin", 3, {"minimum": 0, "maximum": 255} ), ( "Background color (signal plots)", "bg_color_plot", "spin", 3, {"minimum": 0, "maximum": 255} ), ("Annotations base directory", "annot_dir_base", "edit", 1, {}) ] # get number of rows in the widget nb_rows = len(elt_list) # get maximum number of columns for an element nb_cols_max = max([n for _, _, _, n, _ in elt_list]) # loop on elements to add in the widget for i, (label, key, elt_type, elt_nb, elt_params) in \ enumerate(elt_list): # add label q_label = QtWidgets.QLabel(label) q_label.setAlignment(QtCore.Qt.AlignRight) q_label.setAlignment(QtCore.Qt.AlignVCenter) self.general_lay.addWidget(q_label, i, 0) # get widget position wid_pos = (i, 1) # add key to configuration position dictionary self.general_config_position_dict[key] = [] # loop on columns for j in range(elt_nb): # check element type if elt_type == "check_box": widget = QtWidgets.QCheckBox(**elt_params) elif elt_type == "edit": widget = QtWidgets.QLineEdit(**elt_params) elif elt_type == "spin_double": widget = QtWidgets.QDoubleSpinBox(**elt_params) else: widget = QtWidgets.QSpinBox(**elt_params) # set widget height widget.setMinimumHeight(height) # check number of columns if elt_nb == 1: # update widget position wid_pos += (1, nb_cols_max) else: # update widget position wid_pos = (i, j + 1) # update configuration position dictionary self.general_config_position_dict[key].append(wid_pos[:2]) # add widget ToolsPyQt.addWidgetToLayout(self.general_lay, widget, wid_pos) # get index of row with annotation base directory i = [key for _, key, _, _, _ in elt_list].index("annot_dir_base") # create push button used to change the directory of anntotations self.general_push_button = ToolsPyQt.addPushButton( self.general_lay, (i, nb_cols_max + 1), "Change directory", width=200 ) self.general_push_button.setMinimumHeight(height) # create list of "from cursor" durations ToolsPyQt.addSpinBoxTable( self.general_lay, (0, nb_cols_max + 2, nb_rows, 1), 10, 2, "'from cursor' durations (min, sec)", {"minimum": 0, "maximum": 59} ) self.general_config_position_dict["from_cursor_list"] = \ [(0, nb_cols_max + 2)]
[docs] def createChildConfigurationWindow( self, parent_config, *args, win_size=(0, 0), **kwargs ): """ Creates a window for a child configuration This method can be used when a configuration type is linked to another one so that the keys in Configuration.dict must be the same. For example, this applies to the signal configuration. We can specify some threshold values to plot on the signal widgets. The thresholds must be defined for an existing signal, so the keys in the configuration dictionary are the same as in the signal configuration. As a consequence, when a configuration grid is added in the child configuration window, it has automatically the same key as in the signal configuration and it cannot be changed. If the whole configuration grid for a given key is deleted in the signal configuration, then it is also deleted in the threshold configuration. This method sets the following attributes: - :attr:`.additional_win_list`. - :attr:`.additional_lay_list`. - :attr:`.meta_dict`. :param parent_config: parent configuration :type parent_config: :class:`.Configuration` :param args: positional arguments of :class:`.Configuration` constructor, starting from the third position :param win_size: window size :math:`(width, height)`, set one value to 0 to maximize the window :type win_size: tuple :param kwargs: keyword arguments of :class:`.Configuration` constructor (excepted ``parent_config`` which is the first positional argument of this method) """ # create configuration window win, lay = ToolsPyQt.createWindow( size=win_size, title="%s configuration" % args[0], flag_show=False ) # create scroll area scroll_lay, scroll_area = ToolsPyQt.addScrollArea(lay, (0, 0)) # set size scroll_area.setMinimumSize(win_size[0], win_size[1]) # create configuration config = Configuration( lay, (0, 0), *args, **kwargs, parent_config=parent_config ) # add configuration group box to scroll area scroll_lay.addWidget(config.group_box) # add child to parent configuration parent_config.addChildConfiguration(config) # create push button for hiding configuration window push_button_ok = ToolsPyQt.addPushButton(lay, (1, 0), "Ok", width=80) # listen to callback push_button_ok.clicked.connect(win.hide) # update attributes self.children_win_list.append(win) self.meta_dict[args[0]] = config
# *********************************************************************** # # End group # *********************************************************************** # # *********************************************************************** # # Group: Methods for the management of callbacks # *********************************************************************** #
[docs] def showChildWindow(self, button_id): """ Callback method for opening child configuration window related to signal configuration Connected to the signal ``buttonClicked[int]`` of ``ConfigurationWindow.meta_dict["Signal"].button_group_children``. The group button ``ConfigurationWindow.meta_dict["Signal"].button_group_children`` has as much buttons as children configurations (see :attr:`.Configuration.children_configuration_list` of ``ConfigurationWindow.meta_dict["Signal"]``). :param button_id: index of the button that has been pushed :type button_id: int """ self.children_win_list[button_id].show()
[docs] def setDirectoryAnnotation(self): """ Callback method for annotation directory selection Connected to the signal ``clicked`` of :attr:`.general_push_button`. """ # get text edit pos = self.general_config_position_dict["annot_dir_base"][0] edit_dir = self.general_lay.itemAtPosition(pos[0], pos[1]).widget() # open dialog window directory = QtWidgets.QFileDialog.getExistingDirectory( None, "Select video directory", edit_dir.text(), QtWidgets.QFileDialog.ShowDirsOnly ) if directory != "": # set text edit edit_dir.setText(directory)
[docs] def callLSD(self, button_id): """ Callback method for Load/Save/Done push buttons Connected to the signal ``buttonClicked[int]`` of the button group :attr:`.button_group_lsd` The group button ConfigurationWindow.button_group_lsd has 3 buttons: - button_id == 0 => load configuration file - button_id == 1 => save configuration file - button_id == 2 => set configuration dictionaries and close main window :param button_id: index of the button that has been pushed :type button_id: int """ # load if button_id == 0: # open a dialog window in order to get the configuration file name win = QtWidgets.QWidget() path = QtWidgets.QFileDialog.getOpenFileName( win, "Load configuration file", os.getcwd(), "*.ini" )[0] # load configuration file if path != "": self.load(path) self.resetDisplay() print("Configuration file loaded") # save elif button_id == 1: # open dialog window in order to get where to save the # configuration file path = QtWidgets.QFileDialog.getSaveFileName( None, "Save configuration file", os.getcwd(), "*.ini" )[0] # write configuration file self.writeConfigObjFile(path) print("Configuration file written") # done elif button_id == 2: # set configuration dictionaries self.setConfigurationDictionaries() # close window(s) self.win.close() for win in self.children_win_list: win.close() print("Done with configuration")
# *********************************************************************** # # End group # *********************************************************************** # # *********************************************************************** # # Group: Methods for loading/saving configuration # *********************************************************************** #
[docs] @staticmethod def loadConfigFile( path, key_dict={ "General": 0, "Video": 1, "Signal": 2, "Interval": 2, "Threshold": 2, "YRange": 1, "AnnotEvent": 1, "AnnotImage": 0 } ): """ Loads a configuration file :param path: path to the configuration file to load :type path: str :param key_dict: each item corresponds to a configuration type to retrieve, key is the configuration key, value is the number of nesting levels to apply for converting a nested dictionary (that may be retrieved by **ConfigObj**) to a nested list (equivalent to :attr:`.nb_level` of corresponding instance of :class:`.Configuration`) :type key_dict: dict :returns: configuration dictionary, with the same keys as ``key_dict`` :rtype: dict """ # load file config = ConfigObj(path, unrepr=True) # initialize output dictionary config_dict = {} # loop on configuration keys for key, convert_level in key_dict.items(): if key in config.keys(): if convert_level == 0: config_dict[key] = config[key] else: config_dict[key] = OrderedDict() for key_sub, dict_tmp in config[key].items(): config_dict[key][key_sub] = \ ConfigurationWindow.convertDictToListNested( dict_tmp, level=convert_level ) return config_dict
[docs] def load(self, path): """ Loads a configuration file and store it directly in the configuration dictionaries It calls the static method :meth:`.loadConfigFile` and then sets the following attributes: - :attr:`.general_dict` - ``ConfigurationWindow.meta_dict[config_type].dict`` for config_type in :attr:`.config_type_list` :param path: path to the configuration file to load :type path: str """ # load configuration file config_dict = ConfigurationWindow.loadConfigFile(path) # get general configuration if "General" in config_dict: self.general_dict = config_dict["General"] # check if missing keys in general configuration for key, default_value in self.general_dict_default.items(): if key not in self.general_dict.keys(): self.general_dict[key] = default_value else: # get default general configuration self.general_dict = self.general_dict_default # update meta_dict attribute for config_type in self.config_type_list: if config_type in config_dict.keys(): self.meta_dict[config_type].dict = config_dict[config_type]
[docs] def resetDisplay(self): """ Deletes the whole configuration display and replaces it with the current values of the attribute dictionaries """ # reset general self.setGeneralConfiguration(True) # reset configurations for config_type in self.config_type_list: self.meta_dict[config_type].resetDisplay()
[docs] def writeConfigObjFile(self, path): """ Creates a .ini configuration file with the current configuration Before writing the file, the method calls :meth:`.setConfigurationDictionaries`. :param path: path to the configuration file to create :type path: str """ # set configuration dictionaries self.setConfigurationDictionaries() # remove configuration file if necessary if os.path.isfile(path): os.remove(path) # create config object config = ConfigObj(path, unrepr=True) # convert general dictionary to config object config["General"] = self.general_dict # convert configuration dictionaries to config object for config_type in self.config_type_list: config[config_type] = self.meta_dict[config_type].dict # write configuration file config.write()
[docs] def setConfigurationDictionaries(self): """ Sets the configuration dictionaries with the values filled in the GUI The configuration dictionaries are: - :attr:`.general_dict` - ``ConfigurationWindow.meta_dict[config_type].dict`` for config_type in :attr:`.config_type_list` """ self.setGeneralConfiguration(False) for config_type in self.config_type_list: self.meta_dict[config_type].setDictionary()
[docs] def setGeneralConfiguration(self, flag_display): """ Displays the values in the general configuration widget from :attr:`.general_dict` or sets the values of :attr:`.general_dict` from the values filled in the general configuration widget (i.e. :attr:`.general_lay`) :param flag_display: specify if displaying values in the general configuration widget, if ``False`` then it sets the values of :attr:`.general_dict` :type flag_display: bool """ # loop on elements for key, pos_list in self.general_config_position_dict.items(): # check mode if not flag_display: # reset key in configuration dictionary self.general_dict[key] = [] # loop on columns for ite_col, pos in enumerate(pos_list): # get widget widget = self.general_lay.itemAtPosition( pos[0], pos[1] ).widget() # check element type if isinstance(widget, QtWidgets.QCheckBox): # get method name method_0 = "setChecked" method_1 = "isChecked" elif isinstance(widget, QtWidgets.QSpinBox) or \ isinstance(widget, QtWidgets.QDoubleSpinBox): # get method name method_0 = "setValue" method_1 = "value" elif isinstance(widget, QtWidgets.QLineEdit): # get method name method_0 = "setText" method_1 = "text" else: method_0, method_1 = None, None # check method name if method_0 is not None: # check mode if flag_display: # check number of columns if len(pos_list) > 1: # set widget value getattr(widget, method_0)( self.general_dict[key][ite_col] ) else: # set widget value getattr(widget, method_0)(self.general_dict[key]) else: # update configuration dictionary self.general_dict[key].append( getattr(widget, method_1)() ) # check mode if method_0 is not None and not flag_display: # check if single column if len(pos_list) == 1: # update configuration dictionary self.general_dict[key] = self.general_dict[key][0] # deal with list of list of spin boxes (trunc duration) # get grid key = "from_cursor_list" pos = self.general_config_position_dict[key][0] grid = self.general_lay.itemAtPosition( pos[0], pos[1] ).widget().layout() # get value list value_list = self.general_dict[key] # list of list of spin boxes value_list = ToolsPyQt.setSpinBoxTable( grid, value_list, flag_display ) if not flag_display: # delete empty durations (full of zeros) value_array = np.array(value_list) value_array = np.delete( value_array, np.where(value_array.sum(axis=1) == 0), axis=0 ) value_list = [] for value in value_array: value_list.append(list(value)) # update configuration dictionary self.general_dict[key] = value_list
# *********************************************************************** # # End group # *********************************************************************** # # *********************************************************************** # # Group: Static methods for converting dictionary to list # *********************************************************************** #
[docs] @staticmethod def convertDictToList(dic): """ Puts the values of a dictionary in a list :param dic: dictionary :type dic: dict If dic is not a dictionary, then it returns ``dic``. :returns: list :rtype: list """ if isinstance(dic, dict): return list(dic.values()) else: return dic
[docs] @staticmethod def convertDictToListNested(dic, level=1): """ Puts the values of a nested dictionary in a nested list :param dic: dictionary :type dic: dict :returns: list of lists :rtype: list """ dic_list = ConfigurationWindow.convertDictToList(dic) if level == 1 or not isinstance(dic_list, list): return dic_list else: output = [] for dic_tmp in dic_list: output.append( ConfigurationWindow.convertDictToListNested( dic_tmp, level=level - 1 ) ) return output
# *********************************************************************** # # End group # *********************************************************************** #