def __init__(self, param_dict: dict, t_name: str): super(Param2levelDialog, self).__init__('Parameters required', '') g_layout = QGridLayout() self.layout.addLayout(g_layout) self.params = {} input_widgets = {} g_layout.addWidget(CustomLabel(t_name, primary=True), 0, 0, 1, 0) count = 1 # Event connection def activation_f(key: str): return lambda: self.update_param(key, input_widgets[key].text()) for name, val in param_dict.items(): input_widgets[name] = CustomTextBox(str(val['value'])) input_widgets[name].setValidator(ArithmeticValidator.FLOAT) g_layout.addWidget(CustomLabel(name), count, 0) g_layout.addWidget(input_widgets[name], count, 1) self.params[name] = val['value'] input_widgets[name].textChanged.connect(activation_f(name)) count += 1 # Buttons self.cancel_btn.clicked.connect(lambda: self.reset(param_dict)) self.render_layout()
def __init__(self): super().__init__('Dataset - additional parameters', '') g_layout = QGridLayout() self.layout.addLayout(g_layout) self.params = {'target_idx': 0, 'data_type': float, 'delimiter': ','} target_label = CustomLabel('Target index') target_edit = CustomTextBox() target_edit.setValidator(ArithmeticValidator.INT) target_edit.textChanged. \ connect(lambda: self.update_dict('target_idx', target_edit.text())) g_layout.addWidget(target_label, 0, 0) g_layout.addWidget(target_edit, 0, 1) data_type_label = CustomLabel('Data type') data_type_edit = CustomComboBox() data_type_edit.addItems(['float', 'int']) data_type_edit.activated. \ connect(lambda: self.update_dict('data_type', data_type_edit.currentText())) g_layout.addWidget(data_type_label, 1, 0) g_layout.addWidget(data_type_edit, 1, 1) delimiter_label = CustomLabel('Delimiter character') delimiter_edit = CustomTextBox(',') delimiter_edit.textChanged. \ connect(lambda: self.update_dict('delimiter', delimiter_edit.text())) g_layout.addWidget(delimiter_label, 2, 0) g_layout.addWidget(delimiter_edit, 2, 1) self.cancel_btn.clicked.connect(self.reset) self.render_layout()
def __init__(self, message: str, ok_fun: Callable): super().__init__('', message) title_label = CustomLabel('Message', alignment=Qt.AlignCenter) # Set content label mess_label = CustomLabel(f"\n{self.content}\n", alignment=Qt.AlignCenter) # Connect function self.ok_btn.clicked.connect(lambda: ok_fun) self.layout.addWidget(title_label) self.layout.addWidget(mess_label) self.render_layout()
def __init__(self, block_id: str, p_type: str): super().__init__(block_id) self.property_type = p_type self.pre_condition = True self.smt_string = "" if p_type == "Generic SMT": self.label_string = "-" elif p_type == "Polyhedral": self.label_string = "Ax - b <= 0" self.condition_label = CustomLabel("PRE") self.property_label = CustomLabel(self.label_string) self.variables = [] self.init_layout() self.init_context_menu()
def show_section(label: CustomLabel, buttons: dict): """ This method shows the given dictionary of buttons with its label. Parameters ---------- label: CustomLabel buttons: dict """ label.show() label.setStyleSheet(style.NODE_LABEL_STYLE) for tool in buttons.values(): tool.show()
def hide_section(label: CustomLabel, buttons: dict): """ This method hides the given dictionary of buttons with its label. Parameters ---------- label: CustomLabel buttons: dict """ label.hide() label.setStyleSheet(style.HIDDEN_LABEL_STYLE) for tool in buttons.values(): tool.hide() if type(tool) is NodeButton: tool.setStyleSheet(style.HIDDEN_LABEL_STYLE)
def __init__(self, title, message): super().__init__(title, message) # Set title label title_label = CustomLabel(self.title, primary=True) # Set message label mess_label = CustomLabel(f"\n{self.content}\n", alignment=Qt.AlignCenter) self.confirm = False # Add buttons to close the dialog self.ok_btn.clicked.connect(self.ok) self.cancel_btn.clicked.connect(self.deny) # Compose widgets self.layout.addWidget(title_label) self.layout.addWidget(mess_label) self.render_layout()
def init_layout(self) -> None: """ This method sets up the the property block main_layout with the property parameters. """ # Override title label self.title_label.setText(self.property_type) self.title_label.setStyleSheet(style.PROPERTY_TITLE_STYLE) self.condition_label.setStyleSheet(style.PROPERTY_CONDITION_STYLE) self.main_layout.addWidget(self.title_label) self.main_layout.addWidget(self.condition_label) self.init_grid() formula_label = CustomLabel("Formula") formula_label.setStyleSheet(style.PAR_NODE_STYLE) self.property_label.setStyleSheet(style.DIM_NODE_STYLE) self.content_layout.addWidget(formula_label, 1, 0) self.content_layout.addWidget(self.property_label, 1, 1)
def __init__(self): super(MainWindow, self).__init__() # Init window appearance self.SYSNAME = "NeVer 2" self.setWindowTitle(self.SYSNAME) self.setWindowIcon(QtGui.QIcon(ROOT_DIR + '/res/icons/logo.png')) self.setStyleSheet("background-color: " + style.GREY_1) # Navigation menu self.nav_menu_bar = self.menuBar() self.init_nav_menu_bar() # Blocks toolbar self.toolbar = BlocksToolbar(ROOT_DIR + '/res/json/blocks.json') # Parameters toolbar self.parameters = ParamToolbar() # Drawing Canvas self.canvas = Canvas(self.toolbar.blocks) # Status bar self.status_bar = QStatusBar() self.status_bar.setSizeGripEnabled(False) self.setStatusBar(self.status_bar) self.status_bar.setStyleSheet(style.STATUS_BAR_STYLE) self.status_bar_mode_label = CustomLabel() self.status_bar_mode_label.setStyleSheet(style.STATUS_BAR_WIDGET_STYLE) self.status_bar_selections_label = CustomLabel() self.status_bar_selections_label.setStyleSheet(style.STATUS_BAR_WIDGET_STYLE) self.status_bar.addPermanentWidget(self.status_bar_selections_label) self.status_bar.addPermanentWidget(self.status_bar_mode_label) # And adding them to the window self.addDockWidget(Qt.RightDockWidgetArea, self.parameters, Qt.Vertical) self.addToolBar(QtCore.Qt.ToolBarArea.LeftToolBarArea, self.toolbar) self.setCentralWidget(self.canvas.view) self.connect_events()
def __init__(self, message): super().__init__("", message) # Set title label title_label = CustomLabel("Input required") title_label.setStyleSheet(style.NODE_LABEL_STYLE) title_label.setAlignment(Qt.AlignCenter) # Set message label mess_label = CustomLabel("\n" + self.content + "\n") mess_label.setAlignment(Qt.AlignCenter) # Set input reading self.input = None input_line = CustomTextBox() input_line.setValidator(QRegExpValidator(QRegExp( ArithmeticValidator.TENSOR.regExp().pattern() + "|" + ArithmeticValidator.TENSOR_LIST.regExp().pattern()))) # Add buttons to close the dialog confirm_button = CustomButton("Ok") confirm_button.clicked.connect(self.save_input) cancel_button = CustomButton("Cancel") cancel_button.clicked.connect(self.cancel) buttons = QWidget() buttons_layout = QHBoxLayout() buttons_layout.addWidget(confirm_button) buttons_layout.addWidget(cancel_button) buttons.setLayout(buttons_layout) # Compose widgets self.layout.addWidget(title_label) self.layout.addWidget(mess_label) self.layout.addWidget(input_line) self.layout.addWidget(buttons) self.render_layout()
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 __init__(self, message: str, message_type: MessageType): super().__init__('', message) # Set the dialog stile depending on message_type if message_type == MessageType.MESSAGE: title_label = CustomLabel('Message', alignment=Qt.AlignCenter) title_label.setStyleSheet(style.NODE_LABEL_STYLE) else: title_label = CustomLabel('Error', alignment=Qt.AlignCenter) title_label.setStyleSheet(style.ERROR_LABEL_STYLE) # Set content label mess_label = CustomLabel(f"\n{self.content}\n", alignment=Qt.AlignCenter) # Compose widgets self.layout.addWidget(title_label) self.layout.addWidget(mess_label) self.render_layout()
def __init__(self, message: str): super().__init__("", message) # Override window title self.setWindowTitle("Wait...") # Set content label message_label = CustomLabel(self.content) message_label.setStyleSheet(style.LOADING_LABEL_STYLE) # Set loading bar progress_bar = ProgressBar(self, minimum=0, maximum=0, textVisible=False, objectName="ProgressBar") progress_bar.setStyleSheet(style.PROGRESS_BAR_STYLE) # Compose widgets self.layout.addWidget(message_label) self.layout.addWidget(progress_bar) self.render_layout() # Disable the dialog frame and close button self.setWindowFlag(QtCore.Qt.WindowCloseButtonHint, False) self.setWindowFlags(Qt.FramelessWindowHint)
def init_layout(self): """ Initialize the dialog layout with two ListViews and two buttons in the middle for the composition """ # Setup layouts tr_layout = QHBoxLayout() left_layout = QVBoxLayout() label1 = CustomLabel('Available', primary=True) left_layout.addWidget(label1) left_layout.addWidget(self.available) mid_layout = QVBoxLayout() mid_layout.addWidget(self.add_btn) mid_layout.addWidget(self.rm_btn) mid_layout.setAlignment(Qt.AlignCenter) right_layout = QVBoxLayout() label2 = CustomLabel('Selected', primary=True) right_layout.addWidget(label2) right_layout.addWidget(self.selected) tr_layout.addLayout(left_layout) tr_layout.addLayout(mid_layout) tr_layout.addLayout(right_layout) self.layout.addLayout(tr_layout) transform = utility.read_json(ROOT_DIR + '/res/json/transform.json') for t in transform.keys(): self.available.addItem(t) if transform[t]: self.params_2level[t] = {} for k, v in transform[t].items(): self.params_2level[t][k] = v
def __init__(self, block_id: str): super(GraphicBlock, self).__init__() self.block_id = block_id self.main_layout = QVBoxLayout() self.main_layout.setSpacing(0) self.title_label = CustomLabel("Graphic block") self.content_layout = QGridLayout() self.rect = None self.proxy_control = None self.context_actions = dict() self.setLayout(self.main_layout) # Set style and transparent background for the rounded corners self.setAttribute(Qt.WA_TranslucentBackground) self.setStyleSheet(style.GRAPHIC_BLOCK_STYLE)
def __init__(self): super().__init__('Mixed Verification', '') g_layout = QGridLayout() self.layout.addLayout(g_layout) self.n_neurons = 0 target_label = CustomLabel('Neurons number') target_edit = CustomTextBox() target_edit.textChanged.connect(lambda: self.update_neurons(target_edit.text())) target_edit.setValidator(ArithmeticValidator.INT) g_layout.addWidget(target_label, 0, 0) g_layout.addWidget(target_edit, 0, 1) # Buttons self.cancel_btn.clicked.connect(self.reset) self.render_layout()
def __init__(self, node_file_path: str): super().__init__() self.NODE_FILE_PATH = node_file_path self.setContextMenuPolicy(Qt.PreventContextMenu) self.blocks = dict() self.properties = dict() # Graphic buttons self.f_buttons = dict() self.b_buttons = dict() self.p_buttons = dict() # Labels self.toolbar_tools_label = CustomLabel("Tools") self.toolbar_blocks_label = CustomLabel("Nodes") self.toolbar_properties_label = CustomLabel("Properties") self.isToolbar_tools_label_visible = True self.isToolbar_blocks_label_visible = True self.isToolbar_properties_label_visible = True # Setup view self.__display_tools() self.__init_blocks() self.__display_blocks() self.__init_properties() self.__display_properties() # Toolbar style for item in self.children(): if type(item) is QToolButton: item.setStyleSheet("background-color: " + style.GREEN_2) self.setStyleSheet(style.TOOLBAR_STYLE) self.setOrientation(Qt.Vertical) self.setMovable(False) self.setFloatable(False)
class MainWindow(QtWidgets.QMainWindow): """ This class is the main window of the program, containing all the graphics objects such as the toolbar, the state bar, the menu and the canvas scene. Attributes ---------- SYSNAME : str The application name displayed in the window. nav_menu_bar : QMenuBar Menu bar of the window, containing different menus. status_bar : QStatusBar Status bar of the window. toolbar : BlocksToolbar Toolbar appearing on the left of the window, showing several buttons to add elements to the canvas. parameters : ParamToolbar Fixed toolbar on the right of the window, displaying details about a certain block. canvas : Canvas Central view of the window, containing a blank space in which the blocks appear. Methods ---------- connect_events() Connects to all signals of the elements. init_menu_bar() Sets all menus of the menu bar and their actions. update_status() Changes the status bar displaying on it the canvas mode and the selected items. change_draw_mode(DrawingMode) Changes the drawing mode of the canvas. create_from(NodeButton) Draws in the canvas the block corresponding to the button pressed. reset() Clears both graphical and logical network. open() Procedure to open an existing network. save(bool) Saves the current network in a new file or in the opened one. """ def __init__(self): super(MainWindow, self).__init__() # Init window appearance self.SYSNAME = "NeVer 2" self.setWindowTitle(self.SYSNAME) self.setWindowIcon(QtGui.QIcon(ROOT_DIR + '/res/icons/logo.png')) self.setStyleSheet("background-color: " + style.GREY_1) # Navigation menu self.nav_menu_bar = self.menuBar() self.init_nav_menu_bar() # Blocks toolbar self.toolbar = BlocksToolbar(ROOT_DIR + '/res/json/blocks.json') # Parameters toolbar self.parameters = ParamToolbar() # Drawing Canvas self.canvas = Canvas(self.toolbar.blocks) # Status bar self.status_bar = QStatusBar() self.status_bar.setSizeGripEnabled(False) self.setStatusBar(self.status_bar) self.status_bar.setStyleSheet(style.STATUS_BAR_STYLE) self.status_bar_mode_label = CustomLabel() self.status_bar_mode_label.setStyleSheet(style.STATUS_BAR_WIDGET_STYLE) self.status_bar_selections_label = CustomLabel() self.status_bar_selections_label.setStyleSheet(style.STATUS_BAR_WIDGET_STYLE) self.status_bar.addPermanentWidget(self.status_bar_selections_label) self.status_bar.addPermanentWidget(self.status_bar_mode_label) # And adding them to the window self.addDockWidget(Qt.RightDockWidgetArea, self.parameters, Qt.Vertical) self.addToolBar(QtCore.Qt.ToolBarArea.LeftToolBarArea, self.toolbar) self.setCentralWidget(self.canvas.view) self.connect_events() def connect_events(self): """ Associate the various events coming from button signals and other graphical objects to the correct actions. """ # Block buttons for b in itertools.chain(self.toolbar.b_buttons.values(), self.toolbar.p_buttons.values()): b.clicked.connect(self.create_from(b)) # Draw line button self.toolbar.f_buttons["draw_line"].clicked \ .connect(lambda: self.change_draw_mode(DrawingMode.DRAW_LINE)) # Insert block button self.toolbar.f_buttons["insert_block"].clicked \ .connect(lambda: self.change_draw_mode(DrawingMode.DRAW_BLOCK)) # Parameters box appearing self.canvas.param_requested \ .connect(lambda: self.parameters.display(self.canvas.block_to_show)) # State bar updating self.canvas.scene.has_changed_mode \ .connect(lambda: self.update_status()) self.canvas.scene.selectionChanged \ .connect(lambda: self.update_status()) def init_nav_menu_bar(self): """ This method creates the navigation bar by adding the menus and corresponding actions. """ self.nav_menu_bar.setStyleSheet(style.MENU_BAR_STYLE) self.setContextMenuPolicy(Qt.PreventContextMenu) actions_dict = dict() with open(ROOT_DIR + '/res/json/menu.json') as json_menu: menu = json.loads(json_menu.read()) for menu_item, actions in menu.items(): entry = self.nav_menu_bar.addMenu(menu_item) entry.setStyleSheet(style.MENU_BAR_STYLE) for a, v in actions.items(): action_item = QAction(a, self) if "Shortcut" in v.keys(): action_item.setShortcut(v["Shortcut"]) if v["checkable"] == "True": action_item.setCheckable(True) action_item.setChecked(True) entry.addAction(action_item) actions_dict[f"{menu_item}:{a}"] = action_item # Triggers connection actions_dict["File:New..."].triggered.connect(lambda: self.reset()) actions_dict["File:Open..."].triggered.connect(lambda: self.open()) actions_dict["File:Load property..."].triggered.connect(lambda: self.canvas.project.open_property()) actions_dict["File:Save"].triggered.connect(lambda: self.save(False)) actions_dict["File:Save as..."].triggered.connect(lambda: self.save()) actions_dict["File:Exit"].triggered.connect(lambda: self.close()) actions_dict["Edit:Copy"].triggered.connect(lambda: self.canvas.copy_selected()) actions_dict["Edit:Paste"].triggered.connect(lambda: self.canvas.paste_selected()) actions_dict["Edit:Cut"].triggered.connect(lambda: self.canvas.cut_selected()) actions_dict["Edit:Delete"].triggered.connect(lambda: self.canvas.delete_selected()) actions_dict["Edit:Clear canvas"].triggered.connect(lambda: self.clear()) actions_dict["Edit:Draw connection"].triggered.connect(lambda: self.change_draw_mode(DrawingMode.DRAW_LINE)) actions_dict["Edit:Edit node"].triggered.connect( lambda: self.canvas.scene.edit_node(self.edit_action_validation())) actions_dict["View:Zoom in"].triggered.connect(lambda: self.canvas.zoom_in()) actions_dict["View:Zoom out"].triggered.connect(lambda: self.canvas.zoom_out()) actions_dict["View:Dimensions"].toggled.connect(lambda: self.canvas.scene.switch_dim_visibility()) actions_dict["View:Tools"].toggled.connect(lambda: self.toolbar.change_tools_mode()) actions_dict["View:Blocks"].toggled.connect(lambda: self.toolbar.change_blocks_mode()) actions_dict["View:Parameters"].triggered.connect( lambda: self.canvas.show_parameters(self.parameters_action_validation())) actions_dict["Learning:Train..."].triggered.connect(lambda: self.canvas.train_network()) actions_dict["Learning:Prune..."].triggered.connect(lambda: self.temp_window()) actions_dict["Verification:Verify..."].triggered.connect(lambda: self.canvas.verify_network()) actions_dict["Verification:Repair..."].triggered.connect(lambda: self.temp_window()) actions_dict["Help:Show guide"].triggered.connect(lambda: self.show_help()) @staticmethod def temp_window(): dialog = MessageDialog("Work in progress...", MessageType.MESSAGE) dialog.exec() def create_from(self, button: QPushButton): """ This method draws on the canvas the block corresponding to the pressed BlockButton. Parameters ---------- button : CustomButton The pressed button. """ def pressed(): if isinstance(button, NodeButton): self.canvas.draw_node(button.node_type) elif isinstance(button, PropertyButton): if self.canvas.project.network.nodes: self.canvas.draw_property(button.name) return pressed def update_status(self): """ This method updates the widget in the status bar, displaying the items selected and the current drawing mode. """ # Show the canvas drawing mode if self.canvas.scene.mode == DrawingMode.DRAW_LINE: self.status_bar_mode_label.setText("GraphicLine drawing") elif self.canvas.scene.mode == DrawingMode.DRAW_BLOCK: self.status_bar_mode_label.setText("Block insertion") else: self.status_bar_mode_label.setText("") # Show the selected items, if any if not self.canvas.scene.selectedItems(): self.status_bar_selections_label.setText("") else: selections = "" semicolons = ["; " for _ in range(len(self.canvas.scene.selectedItems()))] semicolons[-1] = "" # No semicolon for the last element in the selections list for counter, item in enumerate(self.canvas.scene.selectedItems()): if type(item) is QGraphicsRectItem: # If the item is a rect, prev_node_id is written selections += self.canvas.scene.blocks[item].block_id selections += semicolons[counter] elif type(item) is GraphicLine: # If the item is a line, origin and destination ids are written origin = self.canvas.scene.blocks[item.origin].block_id destination = self.canvas.scene.blocks[item.destination].block_id selections += origin + "->" + destination selections += semicolons[counter] self.status_bar_selections_label.setText(selections) def change_draw_mode(self, newmode: DrawingMode = None): """ This method changes the drawing mode of the canvas when the user clicks on the corresponding button. The mode changes depending on the previous one. Parameters ---------- newmode : DrawingMode, optional Specifies the new DrawingMode to use. (Default: None) """ if newmode is None: self.canvas.scene.set_mode(DrawingMode.IDLE) else: curmode = self.canvas.scene.mode if newmode == curmode: self.canvas.scene.set_mode(DrawingMode.IDLE) else: self.canvas.scene.set_mode(newmode) def clear(self): """ Utility for deleting the content of the window. Before taking effect, it prompts the user to confirm. """ if self.canvas.num_nodes > 0: alert_dialog = ConfirmDialog("Clear workspace", "The network will be erased and your work will be lost.\n" "Do you wish to continue?") alert_dialog.exec() if alert_dialog.confirm: self.canvas.clear_scene() self.canvas.scene.has_changed_mode.connect(lambda: self.update_status()) self.canvas.scene.selectionChanged.connect(lambda: self.update_status()) self.update_status() self.setWindowTitle(self.SYSNAME) else: self.canvas.clear_scene() self.canvas.scene.has_changed_mode.connect(lambda: self.update_status()) self.canvas.scene.selectionChanged.connect(lambda: self.update_status()) self.update_status() self.setWindowTitle(self.SYSNAME) def reset(self): """ This method clears the scene and the network, stops to work on the file and restarts from scratch. """ self.clear() self.canvas.project.file_name = ("", "") self.setWindowTitle(self.SYSNAME) def open(self): """ This method handles the opening of a file. """ if self.canvas.renderer.disconnected_network: # If there is already a network in the canvas, it is asked to the # user if continuing with opening. confirm_dialog = ConfirmDialog("Open network", "A new network will be opened " "cancelling the current nodes.\n" "Do you wish to continue?") confirm_dialog.exec() # If the user clicks on "yes", the canvas is cleaned, a net is # opened and the window title is updated. if confirm_dialog is not None: if confirm_dialog.confirm: # The canvas is cleaned self.canvas.clear_scene() self.canvas.scene.has_changed_mode.connect(lambda: self.update_status()) self.canvas.scene.selectionChanged.connect(lambda: self.update_status()) self.update_status() # A file is opened self.canvas.project.open() if self.canvas.project.network is not None: self.setWindowTitle(self.SYSNAME + " - " + self.canvas.project.network.identifier) else: # If the canvas was already empty, the opening function is directly # called self.canvas.project.open() if self.canvas.project.network is not None: self.setWindowTitle(self.SYSNAME + " - " + self.canvas.project.network.identifier) def save(self, _as: bool = True): """ This method saves the current network if the format is correct Parameters ---------- _as : bool, optional This attribute distinguishes between "save" and "save as". If _as is True the network will be saved in a new file, while if _as is False the network will overwrite the current one. (Default: True) """ if len(self.canvas.renderer.NN.nodes) == 0 or \ len(self.canvas.renderer.NN.edges) == 0: # Limit case: one disconnected node -> new network with one node if len(self.canvas.renderer.disconnected_network) == 1: for node in self.canvas.renderer.disconnected_network: try: self.canvas.renderer.add_node_to_nn(node) self.canvas.project.save(_as) self.setWindowTitle(self.SYSNAME + " - " + self.canvas.project.network.identifier) except Exception as e: error_dialog = MessageDialog(str(e), MessageType.ERROR) error_dialog.exec() # More than one disconnected nodes cannot be saved elif len(self.canvas.renderer.disconnected_network) > 1: not_sequential_dialog = MessageDialog("The network is not sequential, and " "cannot be saved.", MessageType.ERROR) not_sequential_dialog.exec() else: # Network is empty message = MessageDialog("The network is empty!", MessageType.MESSAGE) message.exec() elif self.canvas.renderer.is_nn_sequential(): # If there are logical nodes, the network is sequential every_node_connected = True # every node has to be in the nodes dictionary for node in self.canvas.renderer.disconnected_network: if node not in self.canvas.project.network.nodes: every_node_connected = False break if every_node_connected: self.canvas.project.save(_as) if self.canvas.project.network is not None: self.setWindowTitle(self.SYSNAME + " - " + self.canvas.project.network.identifier) else: # If there are disconnected nodes, a message is displayed to the # user to choose if saving only the connected network confirm_dialog = ConfirmDialog("Save network", "All the nodes outside the " "sequential network will lost.\n" "Do you wish to continue?") confirm_dialog.exec() if confirm_dialog.confirm: self.canvas.project.save(_as) if self.canvas.project.network is not None: self.setWindowTitle(self.SYSNAME + " - " + self.canvas.project.network.identifier) else: # If the network is not sequential, it cannot be saved. not_sequential_dialog = MessageDialog("The network is not sequential and " "cannot be saved.", MessageType.ERROR) not_sequential_dialog.exec() def edit_action_validation(self) -> Optional[NodeBlock]: """ This method performs a check on the object on which the edit action is called, in order to prevent unwanted operations. Returns ---------- NodeBlock The graphic wrapper of the NetworkNode selected, if present. """ if self.canvas.scene.selectedItems(): if type(self.canvas.scene.selectedItems()[0]) is QGraphicsRectItem: # Return block graphic object return self.canvas.scene.blocks[self.canvas.scene.selectedItems()[0]] elif type(self.canvas.scene.selectedItems()[0]) is GraphicLine: msg_dialog = MessageDialog("Can't edit edges, please select a block instead.", MessageType.ERROR) msg_dialog.exec() else: err_dialog = MessageDialog("No block selected.", MessageType.MESSAGE) err_dialog.exec() def parameters_action_validation(self) -> Optional[NodeBlock]: """ This method performs a check on the object on which the parameters action is called, in order to prevent unwanted operations. Returns ---------- NodeBlock The graphic wrapper of the NetworkNode selected, if present. """ if self.canvas.scene.selectedItems(): if type(self.canvas.scene.selectedItems()[0]) is QGraphicsRectItem: # Return block graphic object return self.canvas.scene.blocks[self.canvas.scene.selectedItems()[0]] elif type(self.canvas.scene.selectedItems()[0]) is GraphicLine: msg_dialog = MessageDialog("No parameters available for connections.", MessageType.ERROR) msg_dialog.exec() else: err_dialog = MessageDialog("No block selected.", MessageType.MESSAGE) err_dialog.exec() @staticmethod def show_help(): help_dialog = HelpDialog() help_dialog.exec()
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 __init__(self, node_block: NodeBlock): super().__init__(node_block.node.name, "") self.layout = QGridLayout() # Connect node self.node = node_block self.parameters = dict() self.edited_data = dict() self.has_edits = False # Build main_layout title_label = CustomLabel("Edit parameters") title_label.setStyleSheet(style.NODE_LABEL_STYLE) title_label.setAlignment(Qt.AlignCenter) self.layout.addWidget(title_label, 0, 0, 1, 2) # Input box in_dim_label = CustomLabel("Input") in_dim_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) in_dim_label.setAlignment(Qt.AlignRight) self.layout.addWidget(in_dim_label, 1, 0) in_dim_box = CustomTextBox(','.join(map(str, node_block.in_dim))) in_dim_box.setValidator(ArithmeticValidator.TENSOR) self.layout.addWidget(in_dim_box, 1, 1) self.parameters["in_dim"] = in_dim_box if not node_block.is_head: in_dim_box.setReadOnly(True) # Display parameters if present counter = 2 if node_block.node.param: counter = self.append_node_params(node_block.node, node_block.block_data) # "Apply" button which saves changes apply_button = CustomButton("Apply") apply_button.clicked.connect(self.save_data) self.layout.addWidget(apply_button, counter, 0) # "Cancel" button which closes the dialog without saving cancel_button = CustomButton("Cancel") cancel_button.clicked.connect(self.close) self.layout.addWidget(cancel_button, counter, 1) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 1) self.render_layout()
def __init__(self, node_block: NodeBlock): super().__init__(node_block.node.name, "") self.layout = QGridLayout() # Connect node self.node = node_block self.new_in_dim = ','.join(map(str, node_block.in_dim)) self.in_dim_box = CustomTextBox() self.has_edits = False # Build main_layout title_label = CustomLabel("Edit network input") title_label.setStyleSheet(style.NODE_LABEL_STYLE) title_label.setAlignment(Qt.AlignCenter) self.layout.addWidget(title_label, 0, 0, 1, 2) # Input box in_dim_label = CustomLabel("Input shape") in_dim_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) in_dim_label.setAlignment(Qt.AlignRight) self.layout.addWidget(in_dim_label, 1, 0) self.in_dim_box.setText(self.new_in_dim) self.in_dim_box.setValidator(ArithmeticValidator.TENSOR) self.layout.addWidget(self.in_dim_box, 1, 1) if not node_block.is_head: self.in_dim_box.setReadOnly(True) # "Apply" button which saves changes apply_button = CustomButton("Apply") apply_button.clicked.connect(self.save_data) self.layout.addWidget(apply_button, 2, 0) # "Cancel" button which closes the dialog without saving cancel_button = CustomButton("Cancel") cancel_button.clicked.connect(self.close) self.layout.addWidget(cancel_button, 2, 1) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 1) self.render_layout()
def __init__(self, property_block: PropertyBlock): super().__init__("Edit property", "") self.property_block = property_block self.has_edits = False self.property_list = [] self.viewer = CustomTextArea() self.viewer.setReadOnly(True) self.viewer.setFixedHeight(100) grid = QGridLayout() # Build main_layout title_label = CustomLabel("Polyhedral property") title_label.setStyleSheet(style.NODE_LABEL_STYLE) title_label.setAlignment(Qt.AlignCenter) grid.addWidget(title_label, 0, 0, 1, 3) # Labels var_label = CustomLabel("Variable") var_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) var_label.setAlignment(Qt.AlignRight) grid.addWidget(var_label, 1, 0) relop_label = CustomLabel("Operator") relop_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) relop_label.setAlignment(Qt.AlignCenter) grid.addWidget(relop_label, 1, 1) value_label = CustomLabel("Value") value_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) value_label.setAlignment(Qt.AlignLeft) grid.addWidget(value_label, 1, 2) self.var_cb = CustomComboBox() for v in property_block.variables: self.var_cb.addItem(v) grid.addWidget(self.var_cb, 2, 0) self.op_cb = CustomComboBox() operators = ["<=", "<", ">", ">="] for o in operators: self.op_cb.addItem(o) grid.addWidget(self.op_cb, 2, 1) self.val = CustomTextBox() self.val.setValidator(ArithmeticValidator.FLOAT) grid.addWidget(self.val, 2, 2) # "Add" button which adds the constraint add_button = CustomButton("Add") add_button.clicked.connect( lambda: self.add_entry(str(self.var_cb.currentText()), str(self.op_cb.currentText()), self.val.text())) grid.addWidget(add_button, 3, 0) # "Save" button which saves the state save_button = CustomButton("Save") save_button.clicked.connect(self.save_property) grid.addWidget(save_button, 3, 1) # "Cancel" button which closes the dialog without saving cancel_button = CustomButton("Cancel") cancel_button.clicked.connect(self.close) grid.addWidget(cancel_button, 3, 2) grid.setColumnStretch(0, 1) grid.setColumnStretch(1, 1) grid.setColumnStretch(2, 1) self.layout.addLayout(grid) self.layout.addWidget(self.viewer, 3) self.render_layout()
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
def init_layout(self) -> None: """ This method sets up the the node block main_layout with attributes and values. """ self.init_grid() # Iterate and display parameters, count rows par_labels = dict() count = 1 for name, param in self.node.param.items(): # Set the label par_labels[name] = CustomLabel(name) par_labels[name].setAlignment(Qt.AlignLeft) par_labels[name].setStyleSheet(style.PAR_NODE_STYLE) self.dim_labels[name] = CustomLabel() self.dim_labels[name].setAlignment(Qt.AlignCenter) self.dim_labels[name].setStyleSheet(style.DIM_NODE_STYLE) self.content_layout.addWidget(par_labels[name], count, 0) self.content_layout.addWidget(self.dim_labels[name], count, 1) count += 1 # Init block data with default values if "default" in param.keys() and param["default"] == "None": self.block_data[name] = None elif param["type"] == "Tensor": if "shape" in param.keys(): shape = self.text_to_tuple(param["shape"]) self.block_data[name] = Tensor(shape=shape, buffer=np.random.normal(size=shape)) else: self.block_data[name] = Tensor(shape=(1, 1), buffer=np.random.normal(size=(1, 1))) elif param["type"] == "int": if "default" in param.keys(): if param["default"] == "rand": self.block_data[name] = 1 else: self.block_data[name] = int(param["default"]) else: self.block_data[name] = 1 elif param["type"] == "list of ints": if "default" in param.keys(): self.block_data[name] = tuple(map(int, param["default"].split(', '))) else: self.block_data[name] = (1, 1) elif param["type"] == "float": if "default" in param.keys(): if param["default"] == "rand": self.block_data[name] = 1 else: self.block_data[name] = float(param["default"]) else: self.block_data[name] = 0.1 elif param["type"] == "boolean": if "default" in param.keys(): self.block_data[name] = bool(param["default"]) else: self.block_data[name] = False self.update_labels() self.edited.connect(lambda: self.update_labels())
class BlocksToolbar(QToolBar): """ This class defines a toolbar containing all the blocks available to draw a neural network: each type of block is read from a file in order to build a list of NetworkNode objects to be displayed. Attributes ---------- NODE_FILE_PATH : str Path of the JSON file containing all information about the implemented nodes available to displaying. blocks : dict Dictionary of blocks connecting each block name with a NetworkNode. properties : dict Dictionary of properties connecting each property name with a NetworkProperty. f_buttons : dict Dictionary of buttons connecting a function name to a CustomButton. b_buttons : dict Dictionary connecting each block name to a CustomButton, which makes the corresponding node appear. p_buttons : dict Dictionary connecting each property name to a CustomButton, which makes the corresponding property appear. toolbar_tools_label : CustomLabel Label of the toolbar introducing tools. toolbar_blocks_label : CustomLabel Label of the toolbar introducing blocks types. toolbar_properties_label : CustomLabel Label of the toolbar introducing property names. isToolbar_tools_label_visible : bool Tells if the tools button are visible in the toolbar. isToolbar_blocks_label_visible : bool Tells if the blocks button are visible in the toolbar. isToolbar_properties_label_visible : bool Tells if the properties button are visible in the toolbar. Methods ---------- __display_tools() This method displays on the toolbar the CustomButtons related to available tools. __init_blocks() This method reads from file all types of blocks storing them. __display_blocks() This method displays on the toolbar all buttons related to types of blocks. change_tools_mode() This method changes the visibility of the tools section of the toolbar. change_blocks_mode() This method changes the visibility of the blocks section of the toolbar. show_section(CustomLabel, dict) This method shows the given objects. hide_section(CustomLabel, dict) This method hides the given objects. """ def __init__(self, node_file_path: str): super().__init__() self.NODE_FILE_PATH = node_file_path self.setContextMenuPolicy(Qt.PreventContextMenu) self.blocks = dict() self.properties = dict() # Graphic buttons self.f_buttons = dict() self.b_buttons = dict() self.p_buttons = dict() # Labels self.toolbar_tools_label = CustomLabel("Tools") self.toolbar_blocks_label = CustomLabel("Nodes") self.toolbar_properties_label = CustomLabel("Properties") self.isToolbar_tools_label_visible = True self.isToolbar_blocks_label_visible = True self.isToolbar_properties_label_visible = True # Setup view self.__display_tools() self.__init_blocks() self.__display_blocks() self.__init_properties() self.__display_properties() # Toolbar style for item in self.children(): if type(item) is QToolButton: item.setStyleSheet("background-color: " + style.GREEN_2) self.setStyleSheet(style.TOOLBAR_STYLE) self.setOrientation(Qt.Vertical) self.setMovable(False) self.setFloatable(False) def __init_blocks(self): """ Uploading blocks from a JSON file storing them in a dictionary of NetworkNode objects. """ with open(self.NODE_FILE_PATH) as json_file: blocks_dict = json.loads(json_file.read()) for k, b in blocks_dict.items(): self.blocks[k] = NetworkNode(k, b["name"], b["input"], b["parameters"], b["output"], b["description"]) button = NodeButton(k, self.blocks[k]) button.setToolTip(b["description"]) self.b_buttons[k] = button def __init_properties(self): props = ("Generic SMT", "Polyhedral") for k in props: button = PropertyButton(k) button.setToolTip(k) self.p_buttons[k] = button def __display_tools(self): """ This method adds to the toolbar all buttons related to available tools, displaying them in rows in order to have a flexible main_layout in every position of the toolbar. """ # Setting the label of the toolbar self.addWidget(self.toolbar_tools_label) self.toolbar_tools_label.setAlignment(Qt.AlignCenter) self.toolbar_tools_label.setStyleSheet(style.NODE_LABEL_STYLE) # Setting the first row with horizontal main_layout row_1 = QWidget() row_1_layout = QHBoxLayout() row_1_layout.setSpacing(0) row_1_layout.setContentsMargins(0, 0, 0, 0) row_1_layout.setAlignment(Qt.AlignCenter) row_1.setLayout(row_1_layout) # DrawLine mode button draw_line_button = CustomButton() draw_line_button.setIcon(QIcon(ROOT_DIR + "/res/icons/line.png")) draw_line_button.setFixedSize(40, 40) draw_line_button.setToolTip("Draw line") # Insert block button insert_block_button = CustomButton() insert_block_button.setIcon(QIcon(ROOT_DIR + "/res/icons/insert.png")) insert_block_button.setFixedSize(40, 40) insert_block_button.setToolTip("Insert block in edge") # Save in a dictionary button references and add the rows to the # toolbar row_1_layout.addWidget(draw_line_button) row_1_layout.addWidget(insert_block_button) self.f_buttons["draw_line"] = draw_line_button self.f_buttons["insert_block"] = insert_block_button self.addWidget(row_1) self.addSeparator() def __display_blocks(self): """ Graphical blocks are displayed in a vertical main_layout, which is put in a movable toolbar of fixed size. """ # Labels self.toolbar_blocks_label.setAlignment(Qt.AlignCenter) self.toolbar_blocks_label.setStyleSheet(style.NODE_LABEL_STYLE) # Buttons self.addWidget(self.toolbar_blocks_label) for b in self.b_buttons.values(): self.addWidget(b) def __display_properties(self): """ Graphical properties are displayed in a vertical main_layout, which is put in a movable toolbar of fixed size. """ # Labels self.toolbar_properties_label.setAlignment(Qt.AlignCenter) self.toolbar_properties_label.setStyleSheet(style.NODE_LABEL_STYLE) # Buttons self.addWidget(self.toolbar_properties_label) for p in self.p_buttons.values(): self.addWidget(p) def change_tools_mode(self): """ This method handles the visibility of the tool section of the toolbar. If all sections are not visible, then the toolbar itself is hidden, while if the toolbar was hidden, it is shown with the tools section. """ if not self.isToolbar_tools_label_visible: self.show_section(self.toolbar_tools_label, self.f_buttons) self.isToolbar_tools_label_visible = True if not self.isToolbar_blocks_label_visible: self.show() else: self.hide_section(self.toolbar_tools_label, self.f_buttons) self.isToolbar_tools_label_visible = False if not self.isToolbar_blocks_label_visible: self.hide() def change_blocks_mode(self): """ This method handles the visibility of the blocks section of the toolbar. If all sections are not visible, then the toolbar itself is hidden, while if the toolbar was hidden, it is shown with the blocks section. """ if not self.isToolbar_blocks_label_visible: self.show_section(self.toolbar_blocks_label, self.b_buttons) self.isToolbar_blocks_label_visible = True if not self.isToolbar_tools_label_visible: self.show() else: self.hide_section(self.toolbar_blocks_label, self.b_buttons) self.isToolbar_blocks_label_visible = False if not self.isToolbar_tools_label_visible: self.hide() @staticmethod def show_section(label: CustomLabel, buttons: dict): """ This method shows the given dictionary of buttons with its label. Parameters ---------- label: CustomLabel buttons: dict """ label.show() label.setStyleSheet(style.NODE_LABEL_STYLE) for tool in buttons.values(): tool.show() @staticmethod def hide_section(label: CustomLabel, buttons: dict): """ This method hides the given dictionary of buttons with its label. Parameters ---------- label: CustomLabel buttons: dict """ label.hide() label.setStyleSheet(style.HIDDEN_LABEL_STYLE) for tool in buttons.values(): tool.hide() if type(tool) is NodeButton: tool.setStyleSheet(style.HIDDEN_LABEL_STYLE)
class PropertyBlock(GraphicBlock): """ This class represents the widget associated to a SMTLIB property in CoCoNet. Attributes ---------- property_type : str The property type (SMT, Polyhedral...). smt_string : str The SMT-LIB expression of the property. property_label : CustomLabel The visible label of the property. condition_label : CustomLabel The POST or PRE label of the property. variables : list The list of admissible variables for the property. """ def __init__(self, block_id: str, p_type: str): super().__init__(block_id) self.property_type = p_type self.pre_condition = True self.smt_string = "" if p_type == "Generic SMT": self.label_string = "-" elif p_type == "Polyhedral": self.label_string = "Ax - b <= 0" self.condition_label = CustomLabel("PRE") self.property_label = CustomLabel(self.label_string) self.variables = [] self.init_layout() self.init_context_menu() def init_layout(self) -> None: """ This method sets up the the property block main_layout with the property parameters. """ # Override title label self.title_label.setText(self.property_type) self.title_label.setStyleSheet(style.PROPERTY_TITLE_STYLE) self.condition_label.setStyleSheet(style.PROPERTY_CONDITION_STYLE) self.main_layout.addWidget(self.title_label) self.main_layout.addWidget(self.condition_label) self.init_grid() formula_label = CustomLabel("Formula") formula_label.setStyleSheet(style.PAR_NODE_STYLE) self.property_label.setStyleSheet(style.DIM_NODE_STYLE) self.content_layout.addWidget(formula_label, 1, 0) self.content_layout.addWidget(self.property_label, 1, 1) def init_context_menu(self): """ This method sets up the context menu actions that are available for the block. """ block_actions = dict() block_actions["Define"] = QAction("Define...", self) self.set_context_menu(block_actions) def set_label(self): self.property_label.setText(self.label_string) def set_smt_label(self): self.property_label.setText(self.smt_string) def update_condition_label(self): if self.pre_condition: self.condition_label.setText("PRE") else: self.condition_label.setText("POST")
def __init__(self, nn: NeuralNetwork): super().__init__("Train Network") # Training elements self.nn = nn self.is_nn_trained = False self.dataset_path = "" self.dataset_params = dict() self.dataset_transform = tr.Compose([]) self.params = utility.read_json(ROOT_DIR + '/res/json/training.json') self.gui_params = dict() self.loss_f = '' self.metric = '' self.grid_layout = QGridLayout() # Dataset dt_label = CustomLabel("Dataset") dt_label.setAlignment(Qt.AlignCenter) dt_label.setStyleSheet(style.NODE_LABEL_STYLE) self.layout.addWidget(dt_label) dataset_layout = QHBoxLayout() self.widgets["dataset"] = CustomComboBox() self.widgets["dataset"].addItems( ["MNIST", "Fashion MNIST", "Custom data source..."]) self.widgets["dataset"].setCurrentIndex(-1) self.widgets["dataset"].activated \ .connect(lambda: self.setup_dataset(self.widgets["dataset"].currentText())) dataset_layout.addWidget(CustomLabel("Dataset")) dataset_layout.addWidget(self.widgets["dataset"]) self.layout.addLayout(dataset_layout) transform_layout = QHBoxLayout() self.widgets["transform"] = CustomComboBox() self.widgets["transform"].addItems([ "No transform", "Convolutional MNIST", "Fully Connected MNIST", "Custom..." ]) self.widgets["transform"].activated \ .connect(lambda: self.setup_transform(self.widgets["transform"].currentText())) transform_layout.addWidget(CustomLabel("Dataset transform")) transform_layout.addWidget(self.widgets["transform"]) self.layout.addLayout(transform_layout) # Separator sep_label = CustomLabel("Training parameters") sep_label.setAlignment(Qt.AlignCenter) sep_label.setStyleSheet(style.NODE_LABEL_STYLE) self.layout.addWidget(sep_label) # Main body # Activation functions for dynamic widgets def activation_combo(key: str): return lambda: self.update_grid_view( f"{key}:{self.widgets[key].currentText()}") def activation_line(key: str): return lambda: self.update_dict_value(key, "", self.widgets[key]. text()) body_layout = self.create_widget_layout(self.params, activation_combo, activation_line) body_layout.addLayout(self.grid_layout) self.grid_layout.setAlignment(Qt.AlignTop) self.layout.addLayout(body_layout) # Buttons btn_layout = QHBoxLayout() self.train_btn = CustomButton("Train network") self.train_btn.clicked.connect(self.train_network) self.cancel_btn = CustomButton("Cancel") self.cancel_btn.clicked.connect(self.close) btn_layout.addWidget(self.train_btn) btn_layout.addWidget(self.cancel_btn) self.layout.addLayout(btn_layout) self.render_layout()
class BlockInspector(QWidget): """ This class contains the widget for displaying the description of a network block in the ParamToolbar. Each block attribute is labeled and wrapped in a drop-down box. Attributes ---------- layout : QVBoxLayout Vertical main_layout of the widget. title_label : CustomLabel Title of the widget. description_label : CustomLabel Description of the block. parameters : QWidget Container of parameters. parameters_label : CustomLabel Label of parameters. parameters_layout : QVBoxLayout Vertical main_layout of parameters. inputs : QWidget Container of inputs. inputs_label : CustomLabel Label of inputs. inputs_layout : QVBoxLayout Vertical main_layout of inputs. outputs : QWidget Container of outputs. outputs_label : CustomLabel Label of outputs. outputs_layout : QVBoxLayout Vertical main_layout of outputs. """ def __init__(self, block: NodeBlock): super().__init__() self.setStyleSheet(style.BLOCK_BOX_STYLE) self.layout = QVBoxLayout() self.layout.setSpacing(0) self.layout.setContentsMargins(0, 0, 10, 0) self.setLayout(self.layout) self.adjustSize() self.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Maximum) # Widget title self.title_label = CustomLabel() self.title_label.setStyleSheet(style.TITLE_LABEL_STYLE) self.title_label.setText(block.block_id + ":" + block.node.name) # NodeBlock description self.description_label = CustomLabel() self.description_label.setStyleSheet(style.DESCRIPTION_STYLE) self.description_label.setWordWrap(True) self.description_label.setAlignment(Qt.AlignLeft) self.description_label.setText(block.node.descr) self.description_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) # Parameters section if block.node.param: self.parameters_label = CustomLabel("Parameters") self.parameters_label.setStyleSheet(style.NODE_LABEL_STYLE) self.parameters = QWidget() self.parameters_layout = QVBoxLayout() self.parameters_layout.setSpacing(0) self.parameters.setLayout(self.parameters_layout) self.parameters.setStyleSheet("padding: 0px") for par, values in block.node.param.items(): self.parameters_layout.addWidget(DropDownLabel(par, values)) # Inputs section if block.node.input: self.inputs_label = CustomLabel("Input") self.inputs_label.setStyleSheet(style.NODE_LABEL_STYLE) self.inputs = QWidget() self.inputs_layout = QVBoxLayout() self.inputs_layout.setSpacing(0) self.inputs.setLayout(self.inputs_layout) self.inputs.setStyleSheet("padding: 0px") for par, values in block.node.input.items(): self.inputs_layout.addWidget(DropDownLabel(par, values)) # Outputs section if block.node.output: self.outputs_label = CustomLabel("Output") self.outputs_label.setStyleSheet(style.NODE_LABEL_STYLE) self.outputs = QWidget() self.outputs_layout = QVBoxLayout() self.outputs_layout.setSpacing(0) self.outputs.setLayout(self.outputs_layout) self.outputs.setStyleSheet("padding: 0px") for par, values in block.node.output.items(): self.outputs_layout.addWidget(DropDownLabel(par, values)) # Compose widget self.layout.addWidget(self.title_label) self.layout.addWidget(self.description_label) if block.node.param: self.layout.addWidget(self.parameters_label) self.layout.addWidget(self.parameters) if block.node.input: self.layout.addWidget(self.inputs_label) self.layout.addWidget(self.inputs) if block.node.output: self.layout.addWidget(self.outputs_label) self.layout.addWidget(self.outputs)
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 __init__(self, property_block: PropertyBlock): super().__init__("Edit property", "") self.property_block = property_block self.new_property = self.property_block.smt_string self.has_edits = False self.layout = QGridLayout() # Build main_layout title_label = CustomLabel("SMT property") title_label.setStyleSheet(style.NODE_LABEL_STYLE) title_label.setAlignment(Qt.AlignCenter) self.layout.addWidget(title_label, 0, 0, 1, 2) # Input box smt_label = CustomLabel("SMT-LIB definition") smt_label.setStyleSheet(style.IN_DIM_LABEL_STYLE) smt_label.setAlignment(Qt.AlignRight) self.layout.addWidget(smt_label, 1, 0) self.smt_box = CustomTextArea() self.smt_box.insertPlainText(self.new_property) self.layout.addWidget(self.smt_box, 1, 1) # "Apply" button which saves changes apply_button = CustomButton("Apply") apply_button.clicked.connect(self.save_data) self.layout.addWidget(apply_button, 2, 0) # "Cancel" button which closes the dialog without saving cancel_button = CustomButton("Cancel") cancel_button.clicked.connect(self.close) self.layout.addWidget(cancel_button, 2, 1) self.layout.setColumnStretch(0, 1) self.layout.setColumnStretch(1, 1) self.render_layout()