def create_widget_layout(self, widget_dict: dict, cb_f: Callable = None, line_f: Callable = None) -> QHBoxLayout: """ This method sets up the parameters layout by reading the JSON-based dict of params and building the corresponding graphic objects. Parameters ---------- widget_dict : dict The dictionary of widgets to build. cb_f : Callable, optional The activation function for combo boxes. line_f : Callable, optional The activation function for text boxes. Returns ---------- QHBoxLayout The layout with all the widgets loaded. """ widget_layout = QHBoxLayout() left_layout = QGridLayout() left_layout.setAlignment(Qt.AlignTop) counter = 0 for first_level in widget_dict.keys(): sub_key = next(iter(widget_dict[first_level])) if type(widget_dict[first_level][sub_key]) == dict: self.widgets[first_level] = CustomComboBox() for second_level in widget_dict[first_level].keys(): self.widgets[first_level].addItem(second_level) self.widgets[first_level].setCurrentIndex(-1) if cb_f is not None: self.widgets[first_level].activated.connect( cb_f(first_level)) else: if widget_dict[first_level]["type"] == "bool": self.widgets[first_level] = CustomComboBox() self.widgets[first_level].addItems([ str(widget_dict[first_level]["value"]), str(not widget_dict[first_level]["value"]) ]) else: self.widgets[first_level] = CustomTextBox() self.widgets[first_level].setText( str(widget_dict[first_level].get("value", ""))) if line_f is not None: self.widgets[first_level].textChanged.connect( line_f(first_level)) if widget_dict[first_level]["type"] == "int": self.widgets[first_level].setValidator( ArithmeticValidator.INT) elif widget_dict[first_level]["type"] == "float": self.widgets[first_level].setValidator( ArithmeticValidator.FLOAT) elif widget_dict[first_level]["type"] == "tensor" or \ widget_dict[first_level]["type"] == "tuple": self.widgets[first_level].setValidator( ArithmeticValidator.TENSOR) w_label = CustomLabel(first_level) if 'optional' not in widget_dict[first_level].keys(): w_label.setText(first_level + '*') w_label.setToolTip(widget_dict[first_level].get("description")) left_layout.addWidget(w_label, counter, 0) left_layout.addWidget(self.widgets[first_level], counter, 1) counter += 1 widget_layout.addLayout(left_layout) return widget_layout
def append_node_params(self, node: NetworkNode, current_data: dict) -> int: """ This method adds to the dialog layer the editable parameters of the node, and returns the last row counter for the grid main_layout. Attributes ---------- node: NetworkNode The node whose parameters are displayed. current_data: dict The node current data. Returns ---------- int The last row counter. """ # Init column counter counter = 2 # Display parameter labels for param, value in node.param.items(): param_label = CustomLabel(param) if node.param[param]["editable"] == "false": param_label.setStyleSheet(style.UNEDITABLE_PARAM_LABEL_STYLE) param_label.setAlignment(Qt.AlignRight) # Set the tooltip of the input with the description param_label.setToolTip("<" + value["type"] + ">: " + value["description"]) self.layout.addWidget(param_label, counter, 0) # Display parameter values if value["type"] == "boolean": line = CustomComboBox() line.addItem("True") line.addItem("False") line.setPlaceholderText(str(current_data[param])) else: line = CustomTextBox() if node.param[param]["editable"] == "false": line.setReadOnly(True) if isinstance(current_data[param], Tensor) or isinstance(current_data[param], np.ndarray): line.setText("(" + ','.join(map(str, current_data[param].shape)) + ")") elif isinstance(current_data[param], tuple): line.setText(','.join(map(str, current_data[param]))) else: line.setText(str(current_data[param])) # Set type validator validator = None if value["type"] == "int": validator = ArithmeticValidator.INT elif value["type"] == "float": validator = ArithmeticValidator.FLOAT elif value["type"] == "Tensor" or value["type"] == "list of ints": validator = ArithmeticValidator.TENSOR elif value["type"] == "list of Tensors": validator = ArithmeticValidator.TENSOR_LIST line.setValidator(validator) if node.param[param]["editable"] == "false": line.setStyleSheet(style.UNEDITABLE_VALUE_LABEL_STYLE) self.layout.addWidget(line, counter, 1) # Keep trace of CustomTextBox objects self.parameters[param] = line counter += 1 return counter
def show_layout(self, name: str) -> None: """ This method displays a grid layout initialized by the dictionary of parameters and default values. Parameters ---------- name : str The name of the main parameter to which the dictionary is related. """ title = CustomLabel(name.replace(':', ': ')) title.setAlignment(Qt.AlignCenter) self.grid_layout.addWidget(title, 0, 0, 1, 2) widgets_2level = dict() count = 1 for k, v in self.gui_params[name].items(): # Activation functions for dynamic widgets def activation_combo(super_key: str, key: str): return lambda: self.update_dict_value( name, key, widgets_2level[f"{super_key}:{key}"][1]. currentText()) def activation_line(super_key: str, key: str): return lambda: self.update_dict_value( name, key, widgets_2level[f"{super_key}:{key}"][1].text()) w_label = CustomLabel(k) w_label.setToolTip(v.get("description")) if v["type"] == "bool": cb = CustomComboBox() cb.addItems([str(v["value"]), str(not v["value"])]) widgets_2level[f"{name}:{k}"] = (w_label, cb) widgets_2level[f"{name}:{k}"][1].activated.connect( activation_combo(name, k)) elif "allowed" in v.keys(): cb = CustomComboBox() cb.addItems(v["allowed"]) widgets_2level[f"{name}:{k}"] = (w_label, cb) widgets_2level[f"{name}:{k}"][1].activated.connect( activation_combo(name, k)) else: widgets_2level[f"{name}:{k}"] = (w_label, CustomTextBox(str( v["value"]))) widgets_2level[f"{name}:{k}"][1].textChanged.connect( activation_line(name, k)) if v["type"] == "int": widgets_2level[f"{name}:{k}"][1].setValidator( ArithmeticValidator.INT) elif v["type"] == "float": widgets_2level[f"{name}:{k}"][1].setValidator( ArithmeticValidator.FLOAT) elif v["type"] == "tensor" or \ v["type"] == "tuple": widgets_2level[f"{name}:{k}"][1].setValidator( ArithmeticValidator.TENSOR) self.grid_layout.addWidget(widgets_2level[f"{name}:{k}"][0], count, 0) self.grid_layout.addWidget(widgets_2level[f"{name}:{k}"][1], count, 1) count += 1
class DropDownLabel(QWidget): """ This widget displays a generic parameter name and value. It features a drop-down arrow button that shows or hides the description. Attributes ---------- layout : QVBoxLayout Vertical main_layout of the widget. top : QWidget First part of the widget with name, type and default value of the parameter. top_layout : QHBoxLayout Horizontal main_layout of the top of the widget. name_label : CustomLabel Id and type of the object. type_label : CustomLabel Object type. default_label : CustomLabel Eventual default value. down_button : CustomButton Arrow button to show/hide the description of the parameter. description : CustomLabel Description of the parameter. Methods ---------- change_description_mode() This method shows or hides the description of the parameter. """ def __init__(self, name: str, parameters: dict): super().__init__() self.layout = QVBoxLayout() self.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.setStyleSheet(style.DROPDOWN_STYLE) self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Expanding) # Top bar with short info self.top = QWidget() self.top.setContentsMargins(0, 0, 0, 0) self.top.setStyleSheet(style.DROPDOWN_TOP_STYLE) self.top_layout = QHBoxLayout() self.top_layout.setContentsMargins(0, 0, 0, 0) self.top.setLayout(self.top_layout) self.name_label = CustomLabel(name) self.name_label.setAlignment(Qt.AlignLeft) self.name_label.setStyleSheet(style.DROPDOWN_NAME_STYLE) self.top_layout.addWidget(self.name_label, Qt.AlignLeft) self.type_label = CustomLabel(parameters["type"]) self.type_label.setAlignment(Qt.AlignLeft) self.type_label.setToolTip("Type") self.type_label.setStyleSheet(style.DROPDOWN_TYPE_STYLE) self.top_layout.addWidget(self.type_label, Qt.AlignLeft) if "default" in parameters: self.default_label = CustomLabel(parameters["default"]) self.default_label.setStyleSheet(style.DROPDOWN_DEFAULT_STYLE) self.default_label.setToolTip("Default value") self.default_label.setAlignment(Qt.AlignCenter) self.top_layout.addWidget(self.default_label, Qt.AlignRight) self.down_button = CustomButton("\u25bc") self.down_button.setStyleSheet(style.DROPDOWN_ARROW_STYLE) self.down_button.clicked.connect(lambda: self.__toggle_visibility()) self.top_layout.addWidget(self.down_button, Qt.AlignRight) self.layout.addWidget(self.top) self.description = CustomLabel(parameters["description"]) self.description.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) self.description.setWordWrap(True) self.description.setStyleSheet(style.DESCRIPTION_STYLE) self.description.hide() self.layout.addWidget(self.description) def __toggle_visibility(self): """ This method toggles the visibility of the parameter description changing the arrow icon of the button. """ if self.description.isHidden(): self.description.show() self.down_button.setText("\u25b2") else: self.description.hide() self.down_button.setText("\u25bc")