class AlgorithmProgressWidget(QWidget): """ Widget consisting of a progress bar and a button. """ def __init__(self, parent=None): super(AlgorithmProgressWidget, self).__init__(parent) self.progress_bar = None self.details_button = QPushButton('Details') self.details_button.clicked.connect(self.show_dialog) self.layout = QHBoxLayout() self.layout.addStretch() self.layout.addWidget(self.details_button) self.setLayout(self.layout) self.presenter = AlgorithmProgressPresenter(self) def show_progress_bar(self): if self.progress_bar is None: self.progress_bar = QProgressBar() self.progress_bar.setAlignment(Qt.AlignHCenter) self.layout.insertWidget(0, self.progress_bar) self.layout.removeItem(self.layout.takeAt(1)) def hide_progress_bar(self): if self.progress_bar is not None: self.layout.insertStretch(0) self.layout.removeWidget(self.progress_bar) self.progress_bar.close() self.progress_bar = None def show_dialog(self): dialog = AlgorithmMonitorDialog(self, self.presenter.model) dialog.show()
class ConvertFluxUnitGUI(QDialog): """ GUI for unit conversions """ def __init__(self, controller, parent=None): super(ConvertFluxUnitGUI, self).__init__(parent=parent) self.setWindowFlags(self.windowFlags() | Qt.Tool) self.title = "Unit Conversion" self.setMinimumSize(400, 270) self.cubeviz_layout = controller.cubeviz_layout self._hub = self.cubeviz_layout.session.hub self.controller = controller self.data = controller.data self.controller_components = controller._components self.current_unit = None self._init_ui() def _init_ui(self): # LINE 1: Data component drop down self.component_prompt = QLabel("Data Component:") self.component_prompt.setWordWrap(True) # Add the data component labels to the drop down, with the ComponentID # set as the userData: if self.parent is not None and hasattr(self.parent, 'data_components'): self.label_data = [(str(cid), cid) for cid in self.parent.data_components] else: self.label_data = [(str(cid), cid) for cid in self.data.visible_components] default_index = 0 self.component_combo = QComboBox() self.component_combo.setFixedWidth(200) update_combobox(self.component_combo, self.label_data, default_index=default_index) self.component_combo.currentIndexChanged.connect(self.update_unit_layout) # hbl is short for Horizontal Box Layout hbl1 = QHBoxLayout() hbl1.addWidget(self.component_prompt) hbl1.addWidget(self.component_combo) hbl1.addStretch(1) # LINE 2: Unit conversion layout # This layout is filled by CubeVizUnit self.unit_layout = QHBoxLayout() # this is hbl2 # LINE 3: Message box self.message_box = QLabel("") hbl3 = QHBoxLayout() hbl3.addWidget(self.message_box) hbl3.addStretch(1) # Line 4: Buttons self.okButton = QPushButton("Convert Units") self.okButton.clicked.connect(self.call_main) self.okButton.setDefault(True) self.cancelButton = QPushButton("Cancel") self.cancelButton.clicked.connect(self.cancel) hbl4 = QHBoxLayout() hbl4.addStretch(1) hbl4.addWidget(self.cancelButton) hbl4.addWidget(self.okButton) vbl = QVBoxLayout() vbl.addLayout(hbl1) vbl.addLayout(self.unit_layout) vbl.addLayout(hbl3) vbl.addLayout(hbl4) self.setLayout(vbl) self.vbl = vbl self.update_unit_layout(default_index) self.show() def update_unit_layout(self, index): """ Call back for component selection drop down. """ component_id = str(self.component_combo.currentData()) # STEP1: Clean up widgets from last component widgets = (self.unit_layout.itemAt(i) for i in range(self.unit_layout.count())) for w in widgets: if isinstance(w, QSpacerItem): self.unit_layout.removeItem(w) continue elif isinstance(w, QWidgetItem): w = w.widget() if hasattr(w, "deleteLater"): w.deleteLater() self.message_box.setText("") if self.current_unit: self.current_unit.reset_widgets() # STEP2: Add now component and connect to CubeVizUnit # so that new widgets are populated. if component_id in self.controller_components: cubeviz_unit = self.controller_components[component_id] self.current_unit = cubeviz_unit cubeviz_unit.set_message_box(self.message_box) cubeviz_unit.populate_unit_layout(self.unit_layout, self) if cubeviz_unit.is_convertible: self.okButton.setEnabled(True) else: self.okButton.setEnabled(False) else: self.current_unit = None default_message = "CubeViz can not convert this unit." default_label = QLabel(default_message) self.unit_layout.addWidget(default_label) self.okButton.setEnabled(False) self.unit_layout.update() self.vbl.update() def call_main(self): """ Calls CubeVizUnit.change_units to finalize conversions. Updates plots with new units. :return: """ success = self.current_unit.change_units() if not success: # Todo: Warning should pop up return component_id = self.component_combo.currentData() self.data.get_component(component_id).units = self.current_unit.unit_string msg = FluxUnitsUpdateMessage(self, self.current_unit.unit, component_id) self._hub.broadcast(msg) self.close() def cancel(self): self.close()
class ConvertFluxUnitGUI(QDialog): """ GUI for unit conversions """ def __init__(self, controller, parent=None, convert_data=False): super(ConvertFluxUnitGUI, self).__init__(parent=parent) self.setWindowFlags(self.windowFlags() | Qt.Tool) self.title = "Unit Conversion" self.setMinimumSize(400, 270) self.convert_data = convert_data self.cubeviz_layout = controller.cubeviz_layout self._hub = self.cubeviz_layout.session.hub self.controller = controller self.data = controller.data self.controller_components = controller._components self.current_unit = None self.current_layout = None self._init_ui() def _init_ui(self): # LINE 1: Data component drop down self.component_prompt = QLabel("Data Component:") self.component_prompt.setWordWrap(True) # Add the data component labels to the drop down, with the ComponentID # set as the userData: if self.parent is not None and hasattr(self.parent, 'data_components'): self.label_data = [(str(cid), cid) for cid in self.parent.data_components] else: self.label_data = [(str(cid), cid) for cid in self.data.visible_components] default_index = 0 self.component_combo = QComboBox() self.component_combo.setFixedWidth(200) update_combobox(self.component_combo, self.label_data, default_index=default_index) self.component_combo.currentIndexChanged.connect(self.update_unit_layout) # hbl is short for Horizontal Box Layout hbl1 = QHBoxLayout() hbl1.addWidget(self.component_prompt) hbl1.addWidget(self.component_combo) hbl1.addStretch(1) # LINE 2: Unit conversion layout # This layout is filled by CubeVizUnit self.unit_layout = QHBoxLayout() # this is hbl2 # LINE 3: Message box self.message_box = QLabel("") hbl3 = QHBoxLayout() hbl3.addWidget(self.message_box) hbl3.addStretch(1) # Line 4: Buttons ok_text = "Convert Data" if self.convert_data else "Convert Displayed Units" ok_function = self.convert_data_units if self.convert_data else self.convert_displayed_units self.okButton = QPushButton(ok_text) self.okButton.clicked.connect(ok_function) self.okButton.setDefault(True) self.cancelButton = QPushButton("Cancel") self.cancelButton.clicked.connect(self.cancel) hbl4 = QHBoxLayout() hbl4.addStretch(1) hbl4.addWidget(self.cancelButton) hbl4.addWidget(self.okButton) vbl = QVBoxLayout() vbl.addLayout(hbl1) vbl.addLayout(self.unit_layout) vbl.addLayout(hbl3) vbl.addLayout(hbl4) self.setLayout(vbl) self.vbl = vbl self.update_unit_layout(default_index) self.show() def update_unit_layout(self, index): """ Call back for component selection drop down. """ component_id = self.component_combo.currentData() # STEP1: Clean up widgets from last component widgets = (self.unit_layout.itemAt(i) for i in range(self.unit_layout.count())) for w in widgets: if isinstance(w, QSpacerItem): self.unit_layout.removeItem(w) continue elif isinstance(w, QWidgetItem): w = w.widget() if hasattr(w, "deleteLater"): w.deleteLater() self.message_box.setText("") if self.current_layout: self.current_layout.reset_widgets() # STEP2: Add now component and connect to CubeVizUnit # so that new widgets are populated. if component_id in self.controller_components: cubeviz_unit = self.controller_components[component_id] self.current_unit = cubeviz_unit wave = self.controller.wave pixel_area = self.controller.pixel_area layout = assign_cubeviz_unit_layout(cubeviz_unit, wave=wave, pixel_area=pixel_area) layout.set_message_box(self.message_box) layout.populate_unit_layout(self.unit_layout, self) self.current_layout = layout if ASTROPY_CubeVizUnit == cubeviz_unit.type: self.okButton.setEnabled(True) else: self.okButton.setEnabled(False) else: self.current_unit = None default_message = "CubeViz can not convert this unit." default_label = QLabel(default_message) self.unit_layout.addWidget(default_label) self.okButton.setEnabled(False) self.unit_layout.update() self.vbl.update() def convert_displayed_units(self): """ Calls CubeVizUnit.change_units to finalize conversions. Updates plots with new units. :return: """ success = self.current_layout.change_units() if not success: info = QMessageBox.critical(self, "Error", "Conversion failed.") return new_unit = self.current_layout.new_unit self.current_unit.unit = new_unit self.current_unit.unit_string = str(new_unit) component_id = self.component_combo.currentData() self.data.get_component(component_id).units = self.current_unit.unit_string msg = FluxUnitsUpdateMessage(self, self.current_unit, component_id) self._hub.broadcast(msg) self.close() def convert_data_units(self): """ Calls CubeVizUnit.change_units to finalize conversions. Updates plots with new units. :return: """ success = self.current_layout.change_units() if not success: info = QMessageBox.critical(self, "Error", "Conversion failed.") return new_unit = self.current_layout.new_unit self.current_unit.unit = new_unit self.current_unit.unit_string = str(new_unit) component_id = self.component_combo.currentData() component = component_id.parent.get_component(component_id) old_array = component._data.copy() old_array.flags.writeable = True wavelengths = self.controller.construct_3d_wavelengths(old_array) new_array = self.current_unit.convert_value(old_array, wave=wavelengths) component._data = new_array self.current_unit = self.controller.add_component_unit(component_id, new_unit) component.units = self.current_unit.unit_string msg = FluxUnitsUpdateMessage(self, self.current_unit, component_id) self._hub.broadcast(msg) self.close() def cancel(self): self.close()
class CodePreviewWidget(QWidget): def __init__(self, main_window, flow): super(CodePreviewWidget, self).__init__() self.codes = { # structure: # # <node>: { # <object>: { (the node instance or some widget instance) # 'title': str, (for faster widget building) # 'original': str, (original code comes from Node.__class_codes__) # 'edited': str(None), (initially None) # } # } } self.node = None # currently displayed node self.current_obj = None # reference to the currently shown/edited object self.radio_buttons = [] # the radio buttons to select a source flow.nodes_selection_changed.connect(self.set_selected_nodes) self.text_edit = CodeEditorWidget(main_window.theme) self.setup_ui() self.set_node(None) def setup_ui(self): secondary_layout = QHBoxLayout() # class radio buttons widget self.class_selection_layout = QHBoxLayout() # QGridLayout() secondary_layout.addLayout(self.class_selection_layout) self.class_selection_layout.setAlignment(Qt.AlignLeft) # secondary_layout.setAlignment(self.class_selection_layout, Qt.AlignLeft) # edit source code buttons self.edit_code_button = QPushButton('edit') self.edit_code_button.setProperty('class', 'small_button') self.edit_code_button.setMaximumWidth(100) self.edit_code_button.clicked.connect(self.edit_code_button_clicked) self.override_code_button = QPushButton('override') self.override_code_button.setProperty('class', 'small_button') self.override_code_button.setMaximumWidth(100) self.override_code_button.setEnabled(False) self.override_code_button.clicked.connect( self.override_code_button_clicked) self.reset_code_button = QPushButton('reset') self.reset_code_button.setProperty('class', 'small_button') self.reset_code_button.setMaximumWidth(206) self.reset_code_button.setEnabled(False) self.reset_code_button.clicked.connect(self.reset_code_button_clicked) edit_buttons_layout = QHBoxLayout() # edit_buttons_layout.addWidget(self.highlight_code_button) edit_buttons_layout.addWidget(self.edit_code_button) edit_buttons_layout.addWidget(self.override_code_button) edit_buttons_layout.addWidget(self.reset_code_button) # edit_buttons_layout.addWidget(self.highlight_code_button) secondary_layout.addLayout(edit_buttons_layout) edit_buttons_layout.setAlignment(Qt.AlignRight) main_layout = QVBoxLayout() main_layout.addLayout(secondary_layout) main_layout.addWidget(self.text_edit) self.setLayout(main_layout) def set_selected_nodes(self, nodes): if len(nodes) == 0: self.set_node(None) else: self.set_node(nodes[-1]) def set_node(self, node): self.node = node if node is None or node.__class_codes__ is None: # no node selected / only imported nodes have __class_codes__ # clear view self.text_edit.set_code('') self.edit_code_button.setEnabled(False) self.override_code_button.setEnabled(False) self.reset_code_button.setEnabled(False) self.clear_class_layout() return if node not in self.codes: # node not yet registered # save class sources for all objects (node, main_widget, custom input widgets) # saving it in this structure enables easy view updates and src code overrides node_objects = { node: { 'title': 'node', 'original cls': node.__class_codes__['node cls'], 'original mod': node.__class_codes__['node mod'], 'modified cls': None, }, } if node.main_widget(): node_objects[node.main_widget()] = { 'title': 'main widget', 'original cls': node.__class_codes__['main widget cls'], 'original mod': node.__class_codes__['main widget mod'], 'modified cls': None } for i in range(len(node.inputs)): iw = node.item.inputs[i].widget if iw: # find code code = '' for name, cls in node.input_widget_classes.items(): if cls == iw.__class__: code = node.__class_codes__[ 'custom input widgets'][name]['cls'] break else: # no break -> no custom widget (builtin widget) -> ignore continue node_objects[iw] = { 'title': f'inp {i}', 'original cls': code, 'modified cls': None, } self.codes[node] = node_objects self.rebuild_class_selection(node) self.update_edit_statuses() self.edit_code_button.setEnabled(True) self.update_code(node, node) def update_code(self, node, obj): """ Updates the 'modified' field in the nodes dict """ self.disable_editing() self.text_edit.disable_highlighting() self.current_obj = obj orig = self.codes[node][obj]['original cls'] modf = self.codes[node][obj]['modified cls'] if modf: code = modf self.reset_code_button.setEnabled(True) else: code = orig self.reset_code_button.setEnabled(False) self.text_edit.set_code(code) def rebuild_class_selection(self, node): self.clear_class_layout() self.radio_buttons.clear() for obj, d in self.codes[node].items(): rb = LinkedRadioButton(d['title'], node, obj) rb.toggled.connect(self.class_RB_toggled) self.class_selection_layout.addWidget(rb) self.radio_buttons.append(rb) self.radio_buttons[0].setChecked(True) def clear_class_layout(self): # clear layout for i in range(self.class_selection_layout.count()): item = self.class_selection_layout.itemAt(0) widget = item.widget() widget.hide() self.class_selection_layout.removeItem(item) def update_edit_statuses(self): """ Draws radio buttons referring to modified objects bold """ for b in self.radio_buttons: if self.codes[b.node][b.obj]['modified cls']: # o.setStyleSheet('color: #3B9CD9;') f = b.font() f.setBold(True) b.setFont(f) else: # o.setStyleSheet('color: white;') f = b.font() f.setBold(False) b.setFont(f) def class_RB_toggled(self, checked): if checked: self.update_code(self.sender().node, self.sender().obj) def edit_code_button_clicked(self): if not EditSrcCodeInfoDialog.dont_show_again: info_dialog = EditSrcCodeInfoDialog(self) accepted = info_dialog.exec_() if not accepted: return self.enable_editing() def enable_editing(self): self.text_edit.enable_editing() self.override_code_button.setEnabled(True) def disable_editing(self): self.text_edit.disable_editing() self.override_code_button.setEnabled(False) def override_code_button_clicked(self): new_code = self.text_edit.get_code() SrcCodeUpdater.override_code( obj=self.current_obj, new_class_src=new_code, ) self.codes[self.node][self.current_obj]['modified cls'] = new_code self.disable_editing() self.reset_code_button.setEnabled(True) self.update_edit_statuses() def reset_code_button_clicked(self): code = self.codes[self.node][self.current_obj]['original cls'] SrcCodeUpdater.override_code(obj=self.current_obj, new_class_src=code) self.codes[self.node][self.current_obj]['modified cls'] = None self.update_code(self.node, self.current_obj) self.update_edit_statuses()