def __init__(self, parent=None, show_fullpath=True, fullpath_sorting=True, show_all_files=True, show_comments=True): QWidget.__init__(self, parent) self.treewidget = OutlineExplorerTreeWidget(self, show_fullpath=show_fullpath, fullpath_sorting=fullpath_sorting, show_all_files=show_all_files, show_comments=show_comments) self.visibility_action = create_action(self, _("Show/hide outline explorer"), icon='outline_explorer_vis.png', toggled=self.toggle_visibility) self.visibility_action.setChecked(True) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) for btn in self.setup_buttons(): btn_layout.addWidget(btn) layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addLayout(btn_layout) layout.addWidget(self.treewidget) self.setLayout(layout)
class JumpList(QWidget): """List of buttons to jump to reactions on map""" def __init__(self, parent): QWidget.__init__(self) self.parent = parent self.layout = QHBoxLayout() self.layout.setAlignment(Qt.AlignLeft) def clear(self): for i in reversed(range(self.layout.count())): self.layout.itemAt(i).widget().setParent(None) def add(self, name: str): if self.layout.count() == 0: label = QLabel("Jump to reaction on map:") self.layout.addWidget(label) jb = JumpButton(self, name) policy = QSizePolicy() policy.ShrinkFlag = True jb.setSizePolicy(policy) self.layout.addWidget(jb) self.setLayout(self.layout) jb.jumpToMap.connect(self.parent.emit_jump_to_map) @ Slot(str) def emit_jump_to_map(self: JumpButton, name: str): self.parent.emit_jump_to_map(name) jumpToMap = Signal(str)
def __init__(self, parent, name_filters=[], show_all=True, show_hscrollbar=True, options_button=None): QWidget.__init__(self, parent) self.name_filters = name_filters self.show_all = show_all self.show_hscrollbar = show_hscrollbar self.treewidget = ExplorerTreeWidget(self, self.show_hscrollbar) self.treewidget.setup(name_filters=self.name_filters, show_all=self.show_all) self.treewidget.setup_view() self.treewidget.hide() self.emptywidget = ExplorerTreeWidget(self) if options_button: btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout) else: layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.emptywidget) layout.addWidget(self.treewidget) self.setLayout(layout)
def add_level_to_table(self, level): row_count = self.levelsTable.rowCount() self.levelsTable.setRowCount(row_count + 1) checkbox_widget = QWidget(self.levelsTable) checkbox_widget.setStyleSheet("QWidget { background-color:none;}") checkbox = QCheckBox() checkbox.setStyleSheet( "QCheckBox::indicator { width: 15px; height: 15px;}") checkbox.setChecked(level.enabled) checkbox.clicked.connect(level.set_enabled) checkbox.clicked.connect(self.level_show_changed) checkbox.toggled.connect(level.set_enabled) checkbox_layout = QHBoxLayout() checkbox_layout.setAlignment(Qt.AlignCenter) checkbox_layout.setContentsMargins(0, 0, 0, 0) checkbox_layout.addWidget(checkbox) checkbox_widget.setLayout(checkbox_layout) self.levelsTable.setCellWidget(row_count, 0, checkbox_widget) self.levelsTable.setItem(row_count, 1, QTableWidgetItem(level.levelname)) self.levelsTable.resizeColumnToContents(1)
def _create_propty_layout(self, propty, width=6.0): """Return layout that handles a property according to 'propty_type'.""" layout = QHBoxLayout() not_enum = propty.endswith('-SP') pv2 = propty.replace('-SP', '-RB').replace('-Sel', '-Sts') style = 'min-width:{0}em; max-width:{0}em;\ min-height:1.29em;'.format(str(width)) if pv2 != propty: chan1 = self.get_pvname(propty) if not_enum: wid = SiriusSpinbox(self, init_channel=chan1) wid.showStepExponent = False wid.setAlignment(Qt.AlignCenter) else: wid = SiriusEnumComboBox(self, init_channel=chan1) wid.setStyleSheet(style) layout.addWidget(wid) label = SiriusLabel(parent=self, init_channel=self.get_pvname(pv2)) label.setStyleSheet(style) label.setAlignment(Qt.AlignCenter) label.setObjectName(pv2.replace('-', '')) label.showUnits = True layout.addWidget(label) layout.setAlignment(Qt.AlignVCenter) return layout
def __init__(self, parent, name_filters=[], show_hscrollbar=True, options_button=None, single_click_to_open=False): # TODO: Remove once Projects is Migrated self.CONF_SECTION = parent.CONF_SECTION QWidget.__init__(self, parent) self.name_filters = name_filters self.show_hscrollbar = show_hscrollbar self.treewidget = ExplorerTreeWidget(self, self.show_hscrollbar) self.treewidget.setup() self.treewidget.setup_view() self.treewidget.hide() self.treewidget.sig_open_file_requested.connect( self.sig_open_file_requested) self.emptywidget = ExplorerTreeWidget(self) if options_button: btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout) else: layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.emptywidget) layout.addWidget(self.treewidget) self.setLayout(layout)
def __init__(self, parent: QWidget, axis: int): super().__init__(parent=parent) self.axis = axis self.qt_dims = parent self.dims = parent.dims self.axis_label = None self.slider = None self.play_button = None self.curslice_label = QLineEdit(self) self.curslice_label.setToolTip( trans._('Current slice for axis {axis}', axis=axis)) # if we set the QIntValidator to actually reflect the range of the data # then an invalid (i.e. too large) index doesn't actually trigger the # editingFinished event (the user is expected to change the value)... # which is confusing to the user, so instead we use an IntValidator # that makes sure the user can only enter integers, but we do our own # value validation in self.change_slice self.curslice_label.setValidator(QIntValidator(0, 999999)) self.curslice_label.editingFinished.connect(self._set_slice_from_label) self.totslice_label = QLabel(self) self.totslice_label.setToolTip( trans._('Total slices for axis {axis}', axis=axis)) self.curslice_label.setObjectName('slice_label') self.totslice_label.setObjectName('slice_label') sep = QFrame(self) sep.setFixedSize(1, 14) sep.setObjectName('slice_label_sep') settings = get_settings() self._fps = settings.application.playback_fps connect_setattr_value(settings.application.events.playback_fps, self, "fps") self._minframe = None self._maxframe = None self._loop_mode = settings.application.playback_mode connect_setattr_value(settings.application.events.playback_mode, self, "loop_mode") layout = QHBoxLayout() self._create_axis_label_widget() self._create_range_slider_widget() self._create_play_button_widget() layout.addWidget(self.axis_label) layout.addWidget(self.play_button) layout.addWidget(self.slider, stretch=1) layout.addWidget(self.curslice_label) layout.addWidget(sep) layout.addWidget(self.totslice_label) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) layout.setAlignment(Qt.AlignVCenter) self.setLayout(layout) self.dims.events.axis_labels.connect(self._pull_label)
def __init__(self, parent=None, namespace=None, commands=[], message=None, exitfunc=None, profile=False, multithreaded=False): SpyderPluginWidget.__init__(self, parent) logger.info("Initializing...") self.dialog_manager = DialogManager() # Shell self.shell = InternalShell(parent, namespace, commands, message, self.get_option('max_line_count'), self.get_plugin_font(), exitfunc, profile, multithreaded) self.shell.status.connect(lambda msg: self.show_message.emit(msg, 0)) self.shell.go_to_error.connect(self.go_to_error) self.shell.focus_changed.connect(lambda: self.focus_changed.emit()) # Redirecting some signals: self.shell.redirect_stdio.connect( lambda state: self.redirect_stdio.emit(state)) # Initialize plugin self.initialize_plugin() # Find/replace widget self.find_widget = FindReplace(self) self.find_widget.set_editor(self.shell) self.find_widget.hide() self.register_widget_shortcuts(self.find_widget) # Main layout btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(self.options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout) layout.addWidget(self.shell) layout.addWidget(self.find_widget) self.setLayout(layout) # Parameters self.shell.toggle_wrap_mode(self.get_option('wrap')) # Accepting drops self.setAcceptDrops(True) # Traceback MessageBox self.error_dlg = None self.error_traceback = "" self.dismiss_error = False
def set_layout(self): """Set layout for default widgets.""" # Icon if self.show_icon: self._icon = self.get_icon() self._pixmap = None self._icon_size = QSize(16, 16) # Should this be adjustable? self.label_icon = QLabel() self.set_icon() # Label if self.show_label: self.label_value = QLabel() self.set_value('') # See spyder-ide/spyder#9044. self.text_font = QFont(QFont().defaultFamily(), weight=QFont.Normal) self.label_value.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.label_value.setFont(self.text_font) # Custom widget if self.CUSTOM_WIDGET_CLASS: if not issubclass(self.CUSTOM_WIDGET_CLASS, QWidget): raise SpyderAPIError( 'Any custom status widget must subclass QWidget!') self.custom_widget = self.CUSTOM_WIDGET_CLASS(self._parent) # Spinner if self.show_spinner: self.spinner = create_waitspinner(size=14, parent=self) self.spinner.hide() # Layout setup layout = QHBoxLayout(self) layout.setSpacing(0) # Reduce space between icon and label if self.show_icon: layout.addWidget(self.label_icon) if self.show_label: layout.addWidget(self.label_value) if self.custom_widget: layout.addWidget(self.custom_widget) if self.show_spinner: layout.addWidget(self.spinner) if is_dark_interface(): layout.addSpacing(0) else: layout.addSpacing(10) layout.setContentsMargins(0, 0, 0, 0) layout.setAlignment(Qt.AlignVCenter) # Setup self.update_tooltip()
def _create_prop_widget(self, name, parent, wids, align_ver=True): pwid = QWidget(parent) vbl = QVBoxLayout(pwid) lab = QLabel(name) lab.setAlignment(Qt.AlignCenter) vbl.addWidget(lab) hbl = QHBoxLayout() hbl.setAlignment(Qt.AlignCenter) vbl.addItem(hbl) for wid in wids: wid.setParent(pwid) hbl.addWidget(wid) return pwid
def __init__(self, parent=None, namespace=None, commands=[], message=None, exitfunc=None, profile=False, multithreaded=False): SpyderPluginWidget.__init__(self, parent) logger.info("Initializing...") self.dialog_manager = DialogManager() # Shell light_background = self.get_option('light_background') self.shell = InternalShell(parent, namespace, commands, message, self.get_option('max_line_count'), self.get_plugin_font(), exitfunc, profile, multithreaded, light_background=light_background) self.shell.status.connect(lambda msg: self.show_message.emit(msg, 0)) self.shell.go_to_error.connect(self.go_to_error) self.shell.focus_changed.connect(lambda: self.focus_changed.emit()) # Redirecting some signals: self.shell.redirect_stdio.connect(lambda state: self.redirect_stdio.emit(state)) # Initialize plugin self.initialize_plugin() # Find/replace widget self.find_widget = FindReplace(self) self.find_widget.set_editor(self.shell) self.find_widget.hide() self.register_widget_shortcuts(self.find_widget) # Main layout btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(self.options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout) layout.addWidget(self.shell) layout.addWidget(self.find_widget) self.setLayout(layout) # Parameters self.shell.toggle_wrap_mode(self.get_option('wrap')) # Accepting drops self.setAcceptDrops(True) # Traceback MessageBox self.error_dlg = None self.error_traceback = "" self.dismiss_error = False
class ButtonsWidget(QWidget): """Widget with titlebar buttons""" def __init__(self, parent): super(ButtonsWidget, self).__init__(parent) self.setObjectName("buttonsWidget") self.setContentsMargins(0, 0, 0, 0) sizepolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) sizepolicy.setHorizontalStretch(0) sizepolicy.setVerticalStretch(0) sizepolicy.setHeightForWidth(self.sizePolicy().hasHeightForWidth()) self.setSizePolicy(sizepolicy) self.setStyleSheet("padding: 0px;") self.buttonsLayout = QHBoxLayout(self) self.buttonsLayout.setContentsMargins(0, 0, 0, 0) self.buttonsLayout.setSpacing(0) self.buttonsLayout.setAlignment(Qt.AlignVCenter) self.setLayout(self.buttonsLayout) if qrainbowstyle.USE_DARWIN_BUTTONS: self.btnMinimize = MinimizeDarwinButton(self) self.btnMaximize = MaximizeDarwinButton(self) self.btnRestore = RestoreDarwinButton(self) self.btnClose = CloseDarwinButton(self) else: self.btnMinimize = MinimizeWindowsButton(self) self.btnMaximize = MaximizeWindowsButton(self) self.btnRestore = RestoreWindowsButton(self) self.btnClose = CloseWindowsButton(self) if qrainbowstyle.ALIGN_BUTTONS_LEFT: self.buttonsLayout.addWidget(self.btnClose) if not qrainbowstyle.USE_DARWIN_BUTTONS: self.buttonsLayout.addWidget(self.btnRestore) self.buttonsLayout.addWidget(self.btnMinimize) self.buttonsLayout.addWidget(self.btnMaximize) else: self.buttonsLayout.addWidget(self.btnMinimize) self.buttonsLayout.addWidget(self.btnMaximize) if not qrainbowstyle.USE_DARWIN_BUTTONS: self.buttonsLayout.addWidget(self.btnRestore) self.buttonsLayout.addWidget(self.btnClose) self.btnMinimize.setObjectName("btnMinimize") self.btnMaximize.setObjectName("btnMaximize") if not qrainbowstyle.USE_DARWIN_BUTTONS: self.btnRestore.setObjectName("btnRestore") self.btnClose.setObjectName("btnClose") if qrainbowstyle.USE_DARWIN_BUTTONS: self.buttonsLayout.setSpacing(8)
def _ctrlModeWidget(self): self._led_ctrlmode = PyDMLed( self, self.dev_pref.substitute(propty='IsRemote-Mon')) self._led_ctrlmode.offColor = PyDMLed.Red self._led_ctrlmode.onColor = PyDMLed.LightGreen self._lb_ctrlmode = PyDMLabel( self, self.dev_pref.substitute(propty='Interface-Mon')) gbox_ctrlmode = QGroupBox('Control Mode') lay_ctrlmode = QHBoxLayout(gbox_ctrlmode) lay_ctrlmode.setAlignment(Qt.AlignCenter) lay_ctrlmode.addWidget(self._led_ctrlmode) lay_ctrlmode.addWidget(self._lb_ctrlmode) return gbox_ctrlmode
class QtAbout(QDialog): def __init__(self): super().__init__() self.layout = QVBoxLayout() # Description title_label = QLabel( "<b>napari: a multi-dimensional image viewer for python</b>") title_label.setTextInteractionFlags(Qt.TextSelectableByMouse) self.layout.addWidget(title_label) # Add information self.infoTextBox = QTextEdit() self.infoTextBox.setTextInteractionFlags(Qt.TextSelectableByMouse) self.infoTextBox.setLineWrapMode(QTextEdit.NoWrap) # Add text copy button self.infoCopyButton = QtCopyToClipboardButton(self.infoTextBox) self.info_layout = QHBoxLayout() self.info_layout.addWidget(self.infoTextBox, 1) self.info_layout.addWidget(self.infoCopyButton, 0, Qt.AlignTop) self.info_layout.setAlignment(Qt.AlignTop) self.layout.addLayout(self.info_layout) self.infoTextBox.setText(sys_info(as_html=True)) self.infoTextBox.setMinimumSize( self.infoTextBox.document().size().width() + 19, self.infoTextBox.document().size().height() + 10, ) self.layout.addWidget(QLabel('<b>citation information:</b>')) self.citationTextBox = QTextEdit(citation_text) self.citationTextBox.setFixedHeight(64) self.citationCopyButton = QtCopyToClipboardButton(self.citationTextBox) self.citation_layout = QHBoxLayout() self.citation_layout.addWidget(self.citationTextBox, 1) self.citation_layout.addWidget(self.citationCopyButton, 0, Qt.AlignTop) self.layout.addLayout(self.citation_layout) self.setLayout(self.layout) @staticmethod def showAbout(qt_viewer): d = QtAbout() d.setObjectName('QtAbout') d.setStyleSheet(qt_viewer.styleSheet()) d.setWindowTitle('About') d.setWindowModality(Qt.ApplicationModal) d.exec_()
def get_level_show_checkbox(self, level): checkbox_widget = QWidget(self.table) checkbox_widget.setStyleSheet("QWidget { background-color:none;}") checkbox = QCheckBox() checkbox.setStyleSheet( "QCheckBox::indicator { width: 15px; height: 15px;}") checkbox.setChecked(level.enabled) checkbox_layout = QHBoxLayout() checkbox_layout.setAlignment(Qt.AlignCenter) checkbox_layout.setContentsMargins(0, 0, 0, 0) checkbox_layout.addWidget(checkbox) checkbox_widget.setLayout(checkbox_layout) return checkbox_widget
def __init__(self, parent, data, readonly=False, xlabels=None, ylabels=None): QWidget.__init__(self, parent) self.data = data print(type(self.data)) self.old_data_shape = None if len(self.data.shape) == 1: self.old_data_shape = self.data.shape self.data.shape = (self.data.shape[0], 1) elif len(self.data.shape) == 0: self.old_data_shape = self.data.shape self.data.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') self.model = ArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = ArrayView(self, self.model, data.dtype, data.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton("Format") # disable format button for int type btn.setEnabled(is_float(data.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton("Resize") btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox('Background color') bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
def __init__(self, appdata: CnaData): QWidget.__init__(self) self.appdata = appdata self.last_selected = None self.reaction_counter = 1 self.add_button = QPushButton("Add new reaction") self.add_button.setIcon(QIcon.fromTheme("list-add")) policy = QSizePolicy() policy.ShrinkFlag = True self.add_button.setSizePolicy(policy) self.reaction_list = DragableTreeWidget() self.reaction_list.setDragEnabled(True) self.reaction_list.setHeaderLabels(["Id", "Name", "Flux"]) self.reaction_list.setSortingEnabled(True) for r in self.appdata.project.cobra_py_model.reactions: self.add_reaction(r) self.reaction_mask = ReactionMask(self) self.reaction_mask.hide() self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) l = QHBoxLayout() l.setAlignment(Qt.AlignRight) l.addWidget(self.add_button) self.splitter = QSplitter() self.splitter.setOrientation(Qt.Vertical) self.splitter.addWidget(self.reaction_list) self.splitter.addWidget(self.reaction_mask) self.layout.addItem(l) self.layout.addWidget(self.splitter) self.setLayout(self.layout) self.reaction_list.currentItemChanged.connect(self.reaction_selected) self.reaction_mask.reactionChanged.connect( self.handle_changed_reaction) self.reaction_mask.reactionDeleted.connect( self.handle_deleted_reaction) self.reaction_mask.jumpToMap.connect(self.emit_jump_to_map) self.reaction_mask.jumpToMetabolite.connect( self.emit_jump_to_metabolite) self.add_button.clicked.connect(self.add_new_reaction)
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 __init__(self, parent, name_filters=[], show_hscrollbar=True, options_button=None, single_click_to_open=False): QWidget.__init__(self, parent) self.name_filters = name_filters self.show_hscrollbar = show_hscrollbar self.treewidget = ExplorerTreeWidget(self, self.show_hscrollbar) options = { 'date_column': False, 'kind_column': True, 'size_column': False, 'name_filters': name_filters, 'show_hidden': False, 'single_click_to_open': single_click_to_open, 'file_associations': {}, } self.treewidget.setup(options) self.treewidget.setup_view() self.treewidget.hide() self.treewidget.sig_open_file_requested.connect( self.sig_open_file_requested) self.emptywidget = ExplorerTreeWidget(self) if options_button: btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout) else: layout = QVBoxLayout() layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.emptywidget) layout.addWidget(self.treewidget) self.setLayout(layout)
def _create_groupwidget(self, title, sp_wids, rb_wids, aux_wids=None): hbox_sp = QHBoxLayout() hbox_sp.setAlignment(Qt.AlignCenter) hbox_sp.setContentsMargins(0, 0, 0, 0) for wid in sp_wids: hbox_sp.addWidget(wid) hbox_rb = QHBoxLayout() hbox_rb.setAlignment(Qt.AlignCenter) hbox_rb.setContentsMargins(0, 0, 0, 0) for wid in rb_wids: hbox_rb.addWidget(wid) box = QGroupBox(title, self) if title else QWidget(self) lay = QVBoxLayout(box) lay.setAlignment(Qt.AlignCenter) if not isinstance(box, QGroupBox): lay.setContentsMargins(0, 0, 0, 0) lay.addLayout(hbox_sp) lay.addLayout(hbox_rb) if aux_wids: hbox_aux = QHBoxLayout() hbox_aux.setAlignment(Qt.AlignCenter) hbox_aux.setContentsMargins(0, 0, 0, 0) for wid in aux_wids: hbox_aux.addWidget(wid) lay.addLayout(hbox_aux) return box
class EntryWidget(QWidget): def __init__(self, parent=None): super(EntryWidget, self).__init__(parent) self.textQVBoxLayout = QVBoxLayout() self.allQHBoxLayout = QHBoxLayout() self.allQHBoxLayout.setAlignment(Qt.AlignCenter) self.titleLabel = QLabel() boldFont = QFont() boldFont.setBold(True) self.titleLabel.setFont(boldFont) self.descriptionLabel = QLabel() self.textQVBoxLayout.addWidget(self.titleLabel) self.textQVBoxLayout.addWidget(self.descriptionLabel) self.dateLabel = QLabel() self.iconLabel = QLabel() self.allQHBoxLayout.addWidget(self.iconLabel, 0) self.allQHBoxLayout.addLayout(self.textQVBoxLayout, 1) self.allQHBoxLayout.addWidget(self.dateLabel, 0) self.setLayout(self.allQHBoxLayout) def setTitle(self, text): self.titleLabel.setText(text) def setDescription(self, text): self.descriptionLabel.setText(text) def setDate(self, text): self.dateLabel.setText(text) def setIcon(self, icon, iconsize=32): self.iconLabel.setPixmap(icon.pixmap(QSize(iconsize, iconsize))) def setStyle(self, style): self.dateLabel.setStyleSheet(style)
def __init__(self, parent, data, readonly=False, xlabels=None, ylabels=None): QWidget.__init__(self, parent) self.data = data self.old_data_shape = None if len(self.data.shape) == 1: self.old_data_shape = self.data.shape self.data.shape = (self.data.shape[0], 1) elif len(self.data.shape) == 0: self.old_data_shape = self.data.shape self.data.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.dtype.name, '%s') self.model = ArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = ArrayView(self, self.model, data.dtype, data.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton(_( "Format")) # disable format button for int type btn.setEnabled(is_float(data.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton(_( "Resize")) btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox(_( 'Background color')) bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
def __init__(self, appdata): QWidget.__init__(self) self.appdata = appdata self.current = 0 self.scenario = {} self.setFixedHeight(70) self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.clear_button = QPushButton() self.clear_button.setIcon(QIcon.fromTheme("edit-delete")) self.clear_button.setToolTip("clear modes") self.prev_button = QPushButton("<") self.next_button = QPushButton(">") self.label = QLabel() l1 = QHBoxLayout() self.title = QLabel("Mode Navigation") l12 = QHBoxLayout() l12.setAlignment(Qt.AlignRight) l12.addWidget(self.clear_button) l1.addWidget(self.title) l1.addLayout(l12) l2 = QHBoxLayout() l2.addWidget(self.prev_button) l2.addWidget(self.label) l2.addWidget(self.next_button) self.layout.addLayout(l1) self.layout.addLayout(l2) self.setLayout(self.layout) self.prev_button.clicked.connect(self.prev) self.next_button.clicked.connect(self.next) self.clear_button.clicked.connect(self.clear)
def __init__(self): super().__init__() self.labeledSlider = VLabeledSlider() self._qscene = QGraphicsScene() self._qscene.setSceneRect(-self._size / 2, -self._size / 2, self._size, self._size) self._qview = BlockDiagramView(scene=self._qscene, parent=self) layout = QHBoxLayout() layout.addWidget(self.labeledSlider, 0) layout.addWidget(self._qview, 1) layout.setAlignment(Qt.AlignCenter) self.widgetLayout = layout groupBox = QGroupBox() groupBox.setTitle("Block diagram") groupBox.setLayout(layout) parentLayout = QVBoxLayout() parentLayout.addWidget(groupBox) self.setLayout(parentLayout)
def __init__(self, parent, options_button=None): QWidget.__init__(self, parent) self.setWindowTitle("Breakpoints") self.dictwidget = BreakpointTableView(self, self._load_all_breakpoints()) if options_button: btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn_layout.addStretch() btn_layout.addWidget(options_button, Qt.AlignRight) layout = create_plugin_layout(btn_layout, self.dictwidget) else: layout = QVBoxLayout() layout.addWidget(self.dictwidget) self.setLayout(layout) self.dictwidget.clear_all_breakpoints.connect( lambda: self.clear_all_breakpoints.emit()) self.dictwidget.clear_breakpoint.connect( lambda s1, lino: self.clear_breakpoint.emit(s1, lino)) self.dictwidget.edit_goto.connect( lambda s1, lino, s2: self.edit_goto.emit(s1, lino, s2)) self.dictwidget.set_or_edit_conditional_breakpoint.connect( lambda: self.set_or_edit_conditional_breakpoint.emit())
def fill_eline_table(self, table_contents): self._table_contents = copy.deepcopy(table_contents) self._enable_events = False self.tbl_elines.clearContents() # Clear the list of checkboxes for cb in self.cb_sel_list: cb.stateChanged.connect(self.cb_eline_state_changed) self.cb_sel_list = [] self.tbl_elines.setRowCount(len(table_contents)) for nr, row in enumerate(table_contents): sel_status = row["sel_status"] row_data = [ None, row["z"], row["eline"], row["energy"], row["peak_int"], row["rel_int"], row["cs"] ] for nc, entry in enumerate(row_data): label = self.tbl_labels[nc] # Set alternating background colors for the table rows # Make background for editable items a little brighter brightness = 240 if label in self.tbl_cols_editable else 220 if nr % 2: rgb_bckg = (255, brightness, brightness) # Light-red else: rgb_bckg = (brightness, 255, brightness) # Light-green if nc == 0: item = QWidget() cb = CheckBoxNamed(name=f"{nr}") item_hbox = QHBoxLayout(item) item_hbox.addWidget(cb) item_hbox.setAlignment(Qt.AlignCenter) item_hbox.setContentsMargins(0, 0, 0, 0) css1 = get_background_css(rgb_bckg, widget="QCheckbox", editable=False) css2 = get_background_css(rgb_bckg, widget="QWidget", editable=False) item.setStyleSheet(css2 + css1) cb.setChecked(Qt.Checked if sel_status else Qt.Unchecked) cb.stateChanged.connect(self.cb_eline_state_changed) cb.setStyleSheet("QCheckBox {color: black;}") self.cb_sel_list.append(cb) self.tbl_elines.setCellWidget(nr, nc, item) else: s = None # The case when the value (Rel. Int.) is limited from the bottom # We don't want to print very small numbers here if label in self.tbl_value_min: v = self.tbl_value_min[label] if isinstance(entry, (float, np.float64)) and (entry < v): s = f"<{v:.2f}" if s is None: if isinstance(entry, (float, np.float64)): s = f"{entry:.2f}" if entry else "-" else: s = f"{entry}" if entry else "-" item = QTableWidgetItem(s) item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) # Set all columns not editable (unless needed) if label not in self.tbl_cols_editable: item.setFlags(item.flags() & ~Qt.ItemIsEditable) brush = QBrush(QColor(*rgb_bckg)) item.setBackground(brush) self.tbl_elines.setItem(nr, nc, item) self._enable_events = True # Update the rest of the widgets self._update_widgets_based_on_table_state()
class Titlebar(QFrame): """Titlebar for frameless windows.""" minimizeClicked = Signal() maximizeClicked = Signal() restoreClicked = Signal() closeClicked = Signal() def __init__(self, parent=None): super(Titlebar, self).__init__(parent) self.setObjectName("titlebar") self.setMouseTracking(True) self.menus = [] self.setAutoFillBackground(True) self.setFixedHeight(30) self.setContentsMargins(0, 0, 0, 0) self.setBackgroundRole(QPalette.Highlight) self.layout = QHBoxLayout(self) self.layout.setAlignment(Qt.AlignVCenter) self.layout.setAlignment(Qt.AlignLeft) self.layout.setContentsMargins(8, 0, 0, 0) self.layout.setSpacing(0) self.setLayout(self.layout) self.appLogoLabel = AppLogo(self) if qrainbowstyle.APP_ICON_PATH: self.appLogoLabel.setPixmap(QPixmap(qrainbowstyle.APP_ICON_PATH)) self.layout.setContentsMargins(2, 0, 0, 0) self.layout.addWidget(self.appLogoLabel) if qrainbowstyle.ALIGN_BUTTONS_LEFT: self.appLogoLabel.setVisible(False) self.layout.setContentsMargins(8, 0, 0, 0) self.layout.insertStretch(50) self.buttonsWidget = ButtonsWidget(self) if qrainbowstyle.ALIGN_BUTTONS_LEFT: self.layout.insertWidget(0, self.buttonsWidget) else: self.layout.addWidget(self.buttonsWidget) # auto connect signals QMetaObject.connectSlotsByName(self) if self.window().parent() is not None: self.buttonsWidget.btnRestore.setVisible(False) self.buttonsWidget.btnMaximize.setVisible(False) self.buttonsWidget.btnMinimize.setVisible(False) def setWindowIcon(self, icon: QIcon): self.appLogoLabel.setPixmap(icon.pixmap()) # connecting buttons signals @Slot() def on_btnClose_clicked(self): self.closeClicked.emit() @Slot() def on_btnRestore_clicked(self): self.showRestoreButton(False) self.showMaximizeButton(True) self.window().showNormal() self.restoreClicked.emit() @Slot() def on_btnMaximize_clicked(self): if qrainbowstyle.USE_DARWIN_BUTTONS: if self.window().isMaximized(): self.window().showNormal() else: self.window().showMaximized() else: self.showRestoreButton(True) self.showMaximizeButton(False) self.window().showMaximized() self.maximizeClicked.emit() @Slot() def on_btnMinimize_clicked(self): self.window().showMinimized() def showLogo(self, value: bool): """Show or hide app logo label""" self.appLogoLabel.setVisible(value) def showRestoreButton(self, value): if self.window().parent() is None: self.buttonsWidget.btnRestore.setVisible(value) def showMaximizeButton(self, value): if self.window().parent() is None: self.buttonsWidget.btnMaximize.setVisible(value) def showMinimizeButton(self, value): if self.window().parent() is None: self.buttonsWidget.btnMinimize.setVisible(value) def addMenu(self, menu: QMenu): menuButton = MenuButton(self) menuButton.setMenu(menu) menuButton.setText(menu.title()) self.layout.insertWidget(len(self.menus) + 1, menuButton) self.menus.append(menuButton) def setTitlebarHeight(self, height: int): self.setFixedHeight(height) def mouseOverTitlebar(self, x, y): if self.childAt(QPoint(x, y)): return False else: return QRect(self.appLogoLabel.width(), 0, self.width() - self.appLogoLabel.width(), self.height()).contains(QPoint(x, y))
class QtLayerProperties(QFrame): def __init__(self, layer): super().__init__() self.layer = layer layer.events.select.connect(lambda v: self.setSelected(True)) layer.events.deselect.connect(lambda v: self.setSelected(False)) layer.events.name.connect(self._on_layer_name_change) layer.events.blending.connect(self._on_blending_change) layer.events.opacity.connect(self._on_opacity_change) layer.events.visible.connect(self._on_visible_change) layer.events.thumbnail.connect(self._on_thumbnail_change) self.setObjectName('layer') self.vbox_layout = QVBoxLayout() self.top = QFrame() self.top_layout = QHBoxLayout() self.grid = QFrame() self.grid_layout = QGridLayout() self.vbox_layout.addWidget(self.top) self.vbox_layout.addWidget(self.grid) self.vbox_layout.setSpacing(0) self.top.setFixedHeight(38) self.top_layout.setContentsMargins(0, 0, 0, 0) self.grid_layout.setContentsMargins(0, 0, 0, 0) self.top_layout.setAlignment(Qt.AlignCenter) self.top.setLayout(self.top_layout) self.grid.setLayout(self.grid_layout) self.setLayout(self.vbox_layout) self.name_column = 0 self.property_column = 1 cb = QCheckBox(self) cb.setObjectName('visibility') cb.setToolTip('Layer visibility') cb.setChecked(self.layer.visible) cb.setProperty('mode', 'visibility') cb.stateChanged.connect(lambda state=cb: self.changeVisible(state)) self.visibleCheckBox = cb self.top_layout.addWidget(cb) tb = QLabel(self) tb.setObjectName('thumbmnail') tb.setToolTip('Layer thumbmnail') self.thumbnail_label = tb self._on_thumbnail_change(None) self.top_layout.addWidget(tb) textbox = QLineEdit(self) textbox.setText(layer.name) textbox.home(False) textbox.setToolTip('Layer name') textbox.setAcceptDrops(False) textbox.setEnabled(True) textbox.editingFinished.connect(self.changeText) self.nameTextBox = textbox self.top_layout.addWidget(textbox) pb = QPushButton(self) pb.setToolTip('Expand properties') pb.clicked.connect(self.changeExpanded) pb.setObjectName('expand') self.expand_button = pb self.top_layout.addWidget(pb) row = self.grid_layout.rowCount() sld = QSlider(Qt.Horizontal, self) sld.setFocusPolicy(Qt.NoFocus) sld.setMinimum(0) sld.setMaximum(100) sld.setSingleStep(1) sld.setValue(self.layer.opacity * 100) sld.valueChanged[int].connect( lambda value=sld: self.changeOpacity(value)) self.opacitySilder = sld row = self.grid_layout.rowCount() self.grid_layout.addWidget(QLabel('opacity:'), row, self.name_column) self.grid_layout.addWidget(sld, row, self.property_column) row = self.grid_layout.rowCount() blend_comboBox = QComboBox() for blend in Blending: blend_comboBox.addItem(str(blend)) index = blend_comboBox.findText(self.layer.blending, Qt.MatchFixedString) blend_comboBox.setCurrentIndex(index) blend_comboBox.activated[str].connect( lambda text=blend_comboBox: self.changeBlending(text)) self.blendComboBox = blend_comboBox self.grid_layout.addWidget(QLabel('blending:'), row, self.name_column) self.grid_layout.addWidget(blend_comboBox, row, self.property_column) msg = 'Click to select\nDrag to rearrange\nDouble click to expand' self.setToolTip(msg) self.setExpanded(False) self.setFixedWidth(250) self.grid_layout.setColumnMinimumWidth(0, 100) self.grid_layout.setColumnMinimumWidth(1, 100) self.setSelected(self.layer.selected) def setSelected(self, state): self.setProperty('selected', state) self.nameTextBox.setEnabled(state) self.style().unpolish(self) self.style().polish(self) def changeOpacity(self, value): with self.layer.events.blocker(self._on_opacity_change): self.layer.opacity = value / 100 def changeVisible(self, state): if state == Qt.Checked: self.layer.visible = True else: self.layer.visible = False def changeText(self): self.layer.name = self.nameTextBox.text() def changeBlending(self, text): self.layer.blending = text def mouseReleaseEvent(self, event): event.ignore() def mousePressEvent(self, event): event.ignore() def mouseMoveEvent(self, event): event.ignore() def mouseDoubleClickEvent(self, event): self.setExpanded(not self.expanded) def changeExpanded(self): self.setExpanded(not self.expanded) def setExpanded(self, bool): if bool: self.expanded = True self.expand_button.setProperty('expanded', True) rows = self.grid_layout.rowCount() self.setFixedHeight(38 + 30 * rows) self.grid.show() else: self.expanded = False self.expand_button.setProperty('expanded', False) self.setFixedHeight(60) self.grid.hide() self.expand_button.style().unpolish(self.expand_button) self.expand_button.style().polish(self.expand_button) def _on_layer_name_change(self, event): with self.layer.events.name.blocker(): self.nameTextBox.setText(self.layer.name) self.nameTextBox.home(False) def _on_opacity_change(self, event): with self.layer.events.opacity.blocker(): self.opacitySilder.setValue(self.layer.opacity * 100) def _on_blending_change(self, event): with self.layer.events.blending.blocker(): index = self.blendComboBox.findText(self.layer.blending, Qt.MatchFixedString) self.blendComboBox.setCurrentIndex(index) def _on_visible_change(self, event): with self.layer.events.visible.blocker(): self.visibleCheckBox.setChecked(self.layer.visible) def _on_thumbnail_change(self, event): thumbnail = self.layer.thumbnail # Note that QImage expects the image width followed by height image = QImage( thumbnail, thumbnail.shape[1], thumbnail.shape[0], QImage.Format_RGBA8888, ) self.thumbnail_label.setPixmap(QPixmap.fromImage(image))
def _setupUi(self): # Layout to enter matype_label = QLabel('Choose a magnet: ', self) self._matype_cb = QComboBox(self) self._matype_items = MASearch.get_pwrsupply_manames() self._matype_cb.addItem('Select...') self._matype_cb.addItems(self._matype_items) self._matype_cb.setEditable(True) self._matype_cb.setMaxVisibleItems(10) self._matype_cb.currentIndexChanged.connect( self._fill_normalizer_layout) hlmatype = QHBoxLayout() hlmatype.setAlignment(Qt.AlignLeft) hlmatype.addWidget(matype_label) hlmatype.addWidget(self._matype_cb) # Layout to enter normalizer data self._lb_current = QLabel('Current [A]: ') lb_arrow = QLabel('ā', self, alignment=Qt.AlignCenter) lb_arrow.setStyleSheet('min-width:1.2em; max-width:1.2em;') self._lb_strength = QLabel('Strength: ') self._lb_energy = QLabel('Dipole Energy [GeV]: ') self._lb_energy.setVisible(False) self._lb_quadfam_kl = QLabel('Family KL [1/m]: ') self._lb_quadfam_kl.setVisible(False) for name in ['_sb_current', '_sb_strength', '_sb_energy', '_sb_quadfam_kl']: setattr(self, name, QDoubleSpinBoxPlus()) sb = getattr(self, name) sb.setObjectName(name) sb.setValue(0) sb.setMinimum(-100000) sb.setMaximum(100000) sb.setDecimals(4) sb.setStyleSheet("min-width:8em; max-width:8em;") sb.editingFinished.connect(self._update_inputs) if name in ['_sb_current', '_sb_strength']: sb.setEnabled(False) else: sb.setVisible(False) norm_lay = QGridLayout() norm_lay.setAlignment(Qt.AlignLeft) norm_lay.setHorizontalSpacing(5) norm_lay.addItem( QSpacerItem(15, 1, QSzPlcy.Fixed, QSzPlcy.Ignored), 1, 0) norm_lay.addWidget(self._lb_current, 0, 1) norm_lay.addWidget(self._sb_current, 1, 1) norm_lay.addWidget(lb_arrow, 0, 2, 2, 1) norm_lay.addWidget(self._lb_strength, 0, 3) norm_lay.addWidget(self._sb_strength, 1, 3) norm_lay.addItem( QSpacerItem(15, 1, QSzPlcy.Fixed, QSzPlcy.Ignored), 1, 4) norm_lay.addWidget(self._lb_energy, 0, 5) norm_lay.addWidget(self._sb_energy, 1, 5) norm_lay.addItem( QSpacerItem(15, 1, QSzPlcy.Fixed, QSzPlcy.Ignored), 1, 6) norm_lay.addWidget(self._lb_quadfam_kl, 0, 7) norm_lay.addWidget(self._sb_quadfam_kl, 1, 7) self.norm_gb = QGroupBox('', self) self.norm_gb.setLayout(norm_lay) # General layout layout = QVBoxLayout() layout.setSpacing(20) layout.addWidget( QLabel('<h2>Offline Strength/Current Converter</h2>', self, alignment=Qt.AlignCenter)) layout.addLayout(hlmatype) layout.addWidget(self.norm_gb) cw = QWidget(self) cw.setObjectName('central_widget') cw.setStyleSheet("""#central_widget{min-width:42em;}""") cw.setLayout(layout) self.setCentralWidget(cw) self.setFocusPolicy(Qt.StrongFocus)
def __init__(self, parent, data: _Quantity, readonly=False, xlabels=None, ylabels=None, qdata=None): QWidget.__init__(self, parent) self.parent = parent self.qdata = data self.data = data self.old_data_shape = None if len(self.data.m.shape) == 1: self.old_data_shape = self.data.m.shape self.data.m.shape = (self.data.m.shape[0], 1) elif len(self.data.m.shape) == 0: self.old_data_shape = self.data.m.shape self.data.m.shape = (1, 1) format = SUPPORTED_FORMATS.get(data.m.dtype.name, '%s') self.model = QuantityArrayModel(self.data, format=format, xlabels=xlabels, ylabels=ylabels, readonly=readonly, parent=self) self.view = QuantityArrayView(self, self.model, data.m.dtype, data.m.shape) btn_layout = QHBoxLayout() btn_layout.setAlignment(Qt.AlignLeft) btn = QPushButton("Format") # disable format button for int type btn.setEnabled(is_float(data.m.dtype)) btn_layout.addWidget(btn) btn.clicked.connect(self.change_format) btn = QPushButton("Resize") btn_layout.addWidget(btn) btn.clicked.connect(self.view.resize_to_contents) bgcolor = QCheckBox('Background color') bgcolor.setChecked(self.model.bgcolor_enabled) bgcolor.setEnabled(self.model.bgcolor_enabled) bgcolor.stateChanged.connect(self.model.bgcolor) btn_layout.addWidget(bgcolor) self.unitCombo = QComboBox() self.add_units_to_combo() i = self.unitCombo.findData(self.data.u) self.unitCombo.setCurrentIndex(i) self.unitCombo.currentIndexChanged.connect(self.handle_unit_change) btn_layout.addWidget(self.unitCombo) layout = QVBoxLayout() layout.addWidget(self.view) layout.addLayout(btn_layout) self.setLayout(layout)
class MirrorScreen(Display): def __init__(self, parent=None, args=None, macros=None): super(MirrorScreen, self).__init__(parent=parent, args=args, macros=macros) if macros != None: self.x_stop = EpicsSignal(macros['XAXIS'] + ".STOP") self.y_stop = EpicsSignal(macros['YAXIS'] + ".STOP") self.p_stop = EpicsSignal(macros['PITCH'] + ".STOP") self.alarm_box_x = QHBoxLayout() self.alarm_box_y = QHBoxLayout() self.alarm_box_p = QHBoxLayout() self.alarm_box_gantry_x = QHBoxLayout() self.alarm_box_gantry_y = QHBoxLayout() self.ghost = QHBoxLayout() self.alarm_x = TyphosAlarmCircle() self.alarm_x.channel = "ca://" + macros['XAXIS'] self.alarm_x.setMaximumHeight(35) self.alarm_x.setMaximumWidth(35) self.alarm_y = TyphosAlarmCircle() self.alarm_y.channel = "ca://" + macros['YAXIS'] self.alarm_y.setMaximumHeight(35) self.alarm_y.setMaximumWidth(35) self.alarm_p = TyphosAlarmCircle() self.alarm_p.channel = "ca://" + macros['PITCH'] self.alarm_p.setMaximumHeight(35) self.alarm_p.setMaximumWidth(35) label_font = QFont() label_font.setBold(True) self.x_label = QLabel("x") #self.x_label.setFont(label_font) self.y_label = QLabel("y") #self.y_label.setFont(label_font) self.p_label = QLabel("pitch") #self.p_label.setFont(label_font) self.gantry_x_label = QLabel("gantry x") #self.gantry_x_label.setFont(label_font) self.gantry_y_label = QLabel("gantry y") #self.gantry_y_label.setFont(label_font) self.alarm_gantry_x = TyphosAlarmCircle() self.alarm_gantry_x.channel = "ca://" + macros['MIRROR'] + ":HOMS:ALREADY_COUPLED_X_RBV" self.alarm_gantry_x.setMaximumHeight(35) self.alarm_gantry_x.setMaximumWidth(35) self.alarm_gantry_y = TyphosAlarmCircle() self.alarm_gantry_y.channel = "ca://" + macros['MIRROR'] + ":HOMS:ALREADY_COUPLED_Y_RBV" self.alarm_gantry_y.setMaximumHeight(35) self.alarm_gantry_y.setMaximumWidth(35) self.alarm_box_x.addWidget(self.x_label) self.alarm_box_x.setAlignment(self.x_label, Qt.AlignCenter) self.alarm_box_x.addWidget(self.alarm_x) self.alarm_box_y.addWidget(self.y_label) self.alarm_box_y.setAlignment(self.y_label, Qt.AlignCenter) self.alarm_box_y.addWidget(self.alarm_y) self.alarm_box_p.addWidget(self.p_label) self.alarm_box_p.setAlignment(self.p_label, Qt.AlignCenter) self.alarm_box_p.addWidget(self.alarm_p) self.alarm_box_gantry_x.addWidget(self.gantry_x_label) self.alarm_box_gantry_x.setAlignment(self.gantry_x_label, Qt.AlignCenter) self.alarm_box_gantry_x.addWidget(self.alarm_gantry_x) self.alarm_box_gantry_y.addWidget(self.gantry_y_label) self.alarm_box_gantry_y.setAlignment(self.gantry_y_label, Qt.AlignCenter) self.alarm_box_gantry_y.addWidget(self.alarm_gantry_y) self.stop_button = PyDMPushButton(label="Stop") self.stop_button.setMaximumHeight(120) self.stop_button.setMaximumWidth(120) self.stop_button.clicked.connect(self.stop_motors) self.stop_button.setStyleSheet("background: rgb(255,0,0)") self.advanced_button = TyphosRelatedSuiteButton() self.advanced_button.happi_names = [macros['MIRROR'].lower() + "_homs"] self.advanced_button.setText("Advanced") self.ui.horizontalLayout_8.addLayout(self.alarm_box_x) self.ui.horizontalLayout_8.addLayout(self.alarm_box_y) self.ui.horizontalLayout_8.addLayout(self.alarm_box_p) self.ui.horizontalLayout_8.addLayout(self.alarm_box_gantry_x) self.ui.horizontalLayout_8.addLayout(self.alarm_box_gantry_y) #self.ui.horizontalLayout_8.addWidget(self.alarm_x) #self.ui.horizontalLayout_8.addWidget(self.alarm_y) #self.ui.horizontalLayout_8.addWidget(self.alarm_p) #self.ui.horizontalLayout_8.addWidget(self.stop_button) #self.ui.verticalLayout_6.setAlignment(Qt.AlignCenter) self.ui.horizontalLayout_14.addWidget(self.advanced_button) self.ui.horizontalLayout_14.addSpacing(50) self.ui.horizontalLayout_14.addWidget(self.stop_button) self.ui.horizontalLayout_14.addSpacing(160) self.ui.setGeometry(QtCore.QRect(0,0, 360, 385)) def stop_motors(self): self.x_stop.put(1) self.y_stop.put(1) self.p_stop.put(1) def ui_filename(self): # Point to our UI file return 'mirrorScreen.ui' def ui_filepath(self): # Return the full path to the UI file return path.join(path.dirname(path.realpath(__file__)), self.ui_filename())
class PyDMChartingDisplay(Display): def __init__(self, parent=None, args=[], macros=None): """ Create all the widgets, including any child dialogs. Parameters ---------- parent : QWidget The parent widget of the charting display args : list The command parameters macros : str Macros to modify the UI parameters at runtime """ super(PyDMChartingDisplay, self).__init__(parent=parent, args=args, macros=macros) self.channel_map = dict() self.setWindowTitle("PyDM Charting Tool") self.main_layout = QVBoxLayout() self.body_layout = QVBoxLayout() self.pv_layout = QHBoxLayout() self.pv_name_line_edt = QLineEdit() self.pv_name_line_edt.setAcceptDrops(True) self.pv_name_line_edt.installEventFilter(self) self.pv_protocol_cmb = QComboBox() self.pv_protocol_cmb.addItems(["ca://", "archive://"]) self.pv_connect_push_btn = QPushButton("Connect") self.pv_connect_push_btn.clicked.connect(self.add_curve) self.tab_panel = QTabWidget() self.tab_panel.setMaximumWidth(450) self.curve_settings_tab = QWidget() self.chart_settings_tab = QWidget() self.charting_layout = QHBoxLayout() self.chart = PyDMTimePlot(plot_by_timestamps=False, plot_display=self) self.chart.setPlotTitle("Time Plot") self.splitter = QSplitter() self.curve_settings_layout = QVBoxLayout() self.curve_settings_layout.setAlignment(Qt.AlignTop) self.curve_settings_layout.setSizeConstraint(QLayout.SetMinAndMaxSize) self.curve_settings_layout.setSpacing(5) self.crosshair_settings_layout = QVBoxLayout() self.crosshair_settings_layout.setAlignment(Qt.AlignTop) self.crosshair_settings_layout.setSpacing(5) self.enable_crosshair_chk = QCheckBox("Enable Crosshair") self.cross_hair_coord_lbl = QLabel() self.curve_settings_inner_frame = QFrame() self.curve_settings_inner_frame.setLayout(self.curve_settings_layout) self.curve_settings_scroll = QScrollArea() self.curve_settings_scroll.setVerticalScrollBarPolicy( Qt.ScrollBarAsNeeded) self.curve_settings_scroll.setWidget(self.curve_settings_inner_frame) self.curves_tab_layout = QHBoxLayout() self.curves_tab_layout.addWidget(self.curve_settings_scroll) self.enable_crosshair_chk.setChecked(False) self.enable_crosshair_chk.clicked.connect( self.handle_enable_crosshair_checkbox_clicked) self.enable_crosshair_chk.clicked.emit(False) self.chart_settings_layout = QVBoxLayout() self.chart_settings_layout.setAlignment(Qt.AlignTop) self.chart_layout = QVBoxLayout() self.chart_panel = QWidget() self.chart_control_layout = QHBoxLayout() self.chart_control_layout.setAlignment(Qt.AlignHCenter) self.chart_control_layout.setSpacing(10) self.view_all_btn = QPushButton("View All") self.view_all_btn.clicked.connect(self.handle_view_all_button_clicked) self.view_all_btn.setEnabled(False) self.auto_scale_btn = QPushButton("Auto Scale") self.auto_scale_btn.clicked.connect(self.handle_auto_scale_btn_clicked) self.auto_scale_btn.setEnabled(False) self.reset_chart_btn = QPushButton("Reset") self.reset_chart_btn.clicked.connect( self.handle_reset_chart_btn_clicked) self.reset_chart_btn.setEnabled(False) self.resume_chart_text = "Resume" self.pause_chart_text = "Pause" self.pause_chart_btn = QPushButton(self.pause_chart_text) self.pause_chart_btn.clicked.connect( self.handle_pause_chart_btn_clicked) self.title_settings_layout = QVBoxLayout() self.title_settings_layout.setSpacing(10) self.title_settings_grpbx = QGroupBox() self.title_settings_grpbx.setFixedHeight(150) self.import_data_btn = QPushButton("Import Data...") self.import_data_btn.clicked.connect( self.handle_import_data_btn_clicked) self.export_data_btn = QPushButton("Export Data...") self.export_data_btn.clicked.connect( self.handle_export_data_btn_clicked) self.chart_title_lbl = QLabel(text="Chart Title") self.chart_title_line_edt = QLineEdit() self.chart_title_line_edt.setText(self.chart.getPlotTitle()) self.chart_title_line_edt.textChanged.connect( self.handle_title_text_changed) self.chart_change_axis_settings_btn = QPushButton( text="Change Axis Settings...") self.chart_change_axis_settings_btn.clicked.connect( self.handle_change_axis_settings_clicked) self.update_datetime_timer = QTimer(self) self.update_datetime_timer.timeout.connect( self.handle_update_datetime_timer_timeout) self.chart_sync_mode_layout = QVBoxLayout() self.chart_sync_mode_layout.setSpacing(5) self.chart_sync_mode_grpbx = QGroupBox("Data Sampling Mode") self.chart_sync_mode_grpbx.setFixedHeight(80) self.chart_sync_mode_sync_radio = QRadioButton("Synchronous") self.chart_sync_mode_async_radio = QRadioButton("Asynchronous") self.chart_sync_mode_async_radio.setChecked(True) self.graph_drawing_settings_layout = QVBoxLayout() self.chart_redraw_rate_lbl = QLabel("Redraw Rate (Hz)") self.chart_redraw_rate_spin = QSpinBox() self.chart_redraw_rate_spin.setRange(MIN_REDRAW_RATE_HZ, MAX_REDRAW_RATE_HZ) self.chart_redraw_rate_spin.setValue(DEFAULT_REDRAW_RATE_HZ) self.chart_redraw_rate_spin.valueChanged.connect( self.handle_redraw_rate_changed) self.chart_data_sampling_rate_lbl = QLabel( "Asynchronous Data Sampling Rate (Hz)") self.chart_data_async_sampling_rate_spin = QSpinBox() self.chart_data_async_sampling_rate_spin.setRange( MIN_DATA_SAMPLING_RATE_HZ, MAX_DATA_SAMPLING_RATE_HZ) self.chart_data_async_sampling_rate_spin.setValue( DEFAULT_DATA_SAMPLING_RATE_HZ) self.chart_data_async_sampling_rate_spin.valueChanged.connect( self.handle_data_sampling_rate_changed) self.chart_data_sampling_rate_lbl.hide() self.chart_data_async_sampling_rate_spin.hide() self.chart_limit_time_span_layout = QHBoxLayout() self.chart_limit_time_span_layout.setSpacing(5) self.limit_time_plan_text = "Limit Time Span" self.chart_limit_time_span_chk = QCheckBox(self.limit_time_plan_text) self.chart_limit_time_span_chk.hide() self.chart_limit_time_span_lbl = QLabel("Hours : Minutes : Seconds") self.chart_limit_time_span_hours_line_edt = QLineEdit() self.chart_limit_time_span_minutes_line_edt = QLineEdit() self.chart_limit_time_span_seconds_line_edt = QLineEdit() self.chart_limit_time_span_activate_btn = QPushButton("Apply") self.chart_limit_time_span_activate_btn.setDisabled(True) self.chart_ring_buffer_size_lbl = QLabel("Ring Buffer Size") self.chart_ring_buffer_size_edt = QLineEdit() self.chart_ring_buffer_size_edt.installEventFilter(self) self.chart_ring_buffer_size_edt.textChanged.connect( self.handle_buffer_size_changed) self.chart_ring_buffer_size_edt.setText(str(DEFAULT_BUFFER_SIZE)) self.show_legend_chk = QCheckBox("Show Legend") self.show_legend_chk.setChecked(self.chart.showLegend) self.show_legend_chk.clicked.connect( self.handle_show_legend_checkbox_clicked) self.graph_background_color_layout = QFormLayout() self.background_color_lbl = QLabel("Graph Background Color ") self.background_color_btn = QPushButton() self.background_color_btn.setStyleSheet( "background-color: " + self.chart.getBackgroundColor().name()) self.background_color_btn.setContentsMargins(10, 0, 5, 5) self.background_color_btn.setMaximumWidth(20) self.background_color_btn.clicked.connect( self.handle_background_color_button_clicked) self.axis_settings_layout = QVBoxLayout() self.axis_settings_layout.setSpacing(5) self.show_x_grid_chk = QCheckBox("Show x Grid") self.show_x_grid_chk.setChecked(self.chart.showXGrid) self.show_x_grid_chk.clicked.connect( self.handle_show_x_grid_checkbox_clicked) self.show_y_grid_chk = QCheckBox("Show y Grid") self.show_y_grid_chk.setChecked(self.chart.showYGrid) self.show_y_grid_chk.clicked.connect( self.handle_show_y_grid_checkbox_clicked) self.axis_color_lbl = QLabel("Axis and Grid Color") self.axis_color_lbl.setEnabled(False) self.axis_color_btn = QPushButton() self.axis_color_btn.setStyleSheet("background-color: " + DEFAULT_CHART_AXIS_COLOR.name()) self.axis_color_btn.setContentsMargins(10, 0, 5, 5) self.axis_color_btn.setMaximumWidth(20) self.axis_color_btn.clicked.connect( self.handle_axis_color_button_clicked) self.axis_color_btn.setEnabled(False) self.grid_opacity_lbl = QLabel("Grid Opacity") self.grid_opacity_lbl.setEnabled(False) self.grid_opacity_slr = QSlider(Qt.Horizontal) self.grid_opacity_slr.setFocusPolicy(Qt.StrongFocus) self.grid_opacity_slr.setRange(0, 10) self.grid_opacity_slr.setValue(5) self.grid_opacity_slr.setTickInterval(1) self.grid_opacity_slr.setSingleStep(1) self.grid_opacity_slr.setTickPosition(QSlider.TicksBelow) self.grid_opacity_slr.valueChanged.connect( self.handle_grid_opacity_slider_mouse_release) self.grid_opacity_slr.setEnabled(False) self.reset_chart_settings_btn = QPushButton("Reset Chart Settings") self.reset_chart_settings_btn.clicked.connect( self.handle_reset_chart_settings_btn_clicked) self.curve_checkbox_panel = QWidget() self.graph_drawing_settings_grpbx = QGroupBox() self.graph_drawing_settings_grpbx.setFixedHeight(270) self.axis_settings_grpbx = QGroupBox() self.axis_settings_grpbx.setFixedHeight(180) self.app = QApplication.instance() self.setup_ui() self.curve_settings_disp = None self.axis_settings_disp = None self.chart_data_export_disp = None self.chart_data_import_disp = None self.grid_alpha = 5 self.time_span_limit_hours = None self.time_span_limit_minutes = None self.time_span_limit_seconds = None self.data_sampling_mode = ASYNC_DATA_SAMPLING def minimumSizeHint(self): """ The minimum recommended size of the main window. """ return QSize(1490, 800) def ui_filepath(self): """ The path to the UI file created by Qt Designer, if applicable. """ # No UI file is being used return None def ui_filename(self): """ The name the UI file created by Qt Designer, if applicable. """ # No UI file is being used return None def setup_ui(self): """ Initialize the widgets and layouts. """ self.setLayout(self.main_layout) self.pv_layout.addWidget(self.pv_protocol_cmb) self.pv_layout.addWidget(self.pv_name_line_edt) self.pv_layout.addWidget(self.pv_connect_push_btn) QTimer.singleShot(0, self.pv_name_line_edt.setFocus) self.curve_settings_tab.setLayout(self.curves_tab_layout) self.chart_settings_tab.setLayout(self.chart_settings_layout) self.setup_chart_settings_layout() self.tab_panel.addTab(self.curve_settings_tab, "Curves") self.tab_panel.addTab(self.chart_settings_tab, "Chart") self.tab_panel.hide() self.crosshair_settings_layout.addWidget(self.enable_crosshair_chk) self.crosshair_settings_layout.addWidget(self.cross_hair_coord_lbl) self.chart_control_layout.addWidget(self.auto_scale_btn) self.chart_control_layout.addWidget(self.view_all_btn) self.chart_control_layout.addWidget(self.reset_chart_btn) self.chart_control_layout.addWidget(self.pause_chart_btn) self.chart_control_layout.addLayout(self.crosshair_settings_layout) self.chart_control_layout.addWidget(self.import_data_btn) self.chart_control_layout.addWidget(self.export_data_btn) self.chart_control_layout.setStretch(4, 15) self.chart_control_layout.insertSpacing(5, 350) self.chart_layout.addWidget(self.chart) self.chart_layout.addLayout(self.chart_control_layout) self.chart_panel.setLayout(self.chart_layout) self.splitter.addWidget(self.chart_panel) self.splitter.addWidget(self.tab_panel) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) self.charting_layout.addWidget(self.splitter) self.body_layout.addLayout(self.pv_layout) self.body_layout.addLayout(self.charting_layout) self.body_layout.addLayout(self.chart_control_layout) self.main_layout.addLayout(self.body_layout) self.enable_chart_control_buttons(False) def setup_chart_settings_layout(self): self.chart_sync_mode_sync_radio.toggled.connect( partial(self.handle_sync_mode_radio_toggle, self.chart_sync_mode_sync_radio)) self.chart_sync_mode_async_radio.toggled.connect( partial(self.handle_sync_mode_radio_toggle, self.chart_sync_mode_async_radio)) self.title_settings_layout.addWidget(self.chart_title_lbl) self.title_settings_layout.addWidget(self.chart_title_line_edt) self.title_settings_layout.addWidget(self.show_legend_chk) self.title_settings_layout.addWidget( self.chart_change_axis_settings_btn) self.title_settings_grpbx.setLayout(self.title_settings_layout) self.chart_settings_layout.addWidget(self.title_settings_grpbx) self.chart_sync_mode_layout.addWidget(self.chart_sync_mode_sync_radio) self.chart_sync_mode_layout.addWidget(self.chart_sync_mode_async_radio) self.chart_sync_mode_grpbx.setLayout(self.chart_sync_mode_layout) self.chart_settings_layout.addWidget(self.chart_sync_mode_grpbx) self.chart_settings_layout.addWidget(self.chart_sync_mode_grpbx) self.chart_limit_time_span_layout.addWidget( self.chart_limit_time_span_lbl) self.chart_limit_time_span_layout.addWidget( self.chart_limit_time_span_hours_line_edt) self.chart_limit_time_span_layout.addWidget( self.chart_limit_time_span_minutes_line_edt) self.chart_limit_time_span_layout.addWidget( self.chart_limit_time_span_seconds_line_edt) self.chart_limit_time_span_layout.addWidget( self.chart_limit_time_span_activate_btn) self.chart_limit_time_span_lbl.hide() self.chart_limit_time_span_hours_line_edt.hide() self.chart_limit_time_span_minutes_line_edt.hide() self.chart_limit_time_span_seconds_line_edt.hide() self.chart_limit_time_span_activate_btn.hide() self.chart_limit_time_span_hours_line_edt.textChanged.connect( self.handle_time_span_edt_text_changed) self.chart_limit_time_span_minutes_line_edt.textChanged.connect( self.handle_time_span_edt_text_changed) self.chart_limit_time_span_seconds_line_edt.textChanged.connect( self.handle_time_span_edt_text_changed) self.chart_limit_time_span_chk.clicked.connect( self.handle_limit_time_span_checkbox_clicked) self.chart_limit_time_span_activate_btn.clicked.connect( self.handle_chart_limit_time_span_activate_btn_clicked) self.chart_limit_time_span_activate_btn.installEventFilter(self) self.graph_background_color_layout.addRow(self.background_color_lbl, self.background_color_btn) self.graph_drawing_settings_layout.addLayout( self.graph_background_color_layout) self.graph_drawing_settings_layout.addWidget( self.chart_redraw_rate_lbl) self.graph_drawing_settings_layout.addWidget( self.chart_redraw_rate_spin) self.graph_drawing_settings_layout.addWidget( self.chart_data_sampling_rate_lbl) self.graph_drawing_settings_layout.addWidget( self.chart_data_async_sampling_rate_spin) self.graph_drawing_settings_layout.addWidget( self.chart_limit_time_span_chk) self.graph_drawing_settings_layout.addLayout( self.chart_limit_time_span_layout) self.graph_drawing_settings_layout.addWidget( self.chart_ring_buffer_size_lbl) self.graph_drawing_settings_layout.addWidget( self.chart_ring_buffer_size_edt) self.graph_drawing_settings_grpbx.setLayout( self.graph_drawing_settings_layout) self.axis_settings_layout.addWidget(self.show_x_grid_chk) self.axis_settings_layout.addWidget(self.show_y_grid_chk) self.axis_settings_layout.addWidget(self.axis_color_lbl) self.axis_settings_layout.addWidget(self.axis_color_btn) self.axis_settings_layout.addWidget(self.grid_opacity_lbl) self.axis_settings_layout.addWidget(self.grid_opacity_slr) self.axis_settings_grpbx.setLayout(self.axis_settings_layout) self.chart_settings_layout.addWidget(self.graph_drawing_settings_grpbx) self.chart_settings_layout.addWidget(self.axis_settings_grpbx) self.chart_settings_layout.addWidget(self.reset_chart_settings_btn) self.chart_sync_mode_async_radio.toggled.emit(True) self.update_datetime_timer.start(1000) def eventFilter(self, obj, event): """ Handle key and mouse events for any applicable widget. Parameters ---------- obj : QWidget The current widget that accepts the event event : QEvent The key or mouse event to handle Returns ------- True if the event was handled successfully; False otherwise """ if obj == self.pv_name_line_edt and event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: self.add_curve() return True elif obj == self.chart_limit_time_span_activate_btn and event.type( ) == QEvent.KeyPress: if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return: self.handle_chart_limit_time_span_activate_btn_clicked() return True elif obj == self.chart_ring_buffer_size_edt: if event.type() == QEvent.KeyPress and (event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return) or \ event.type() == QEvent.FocusOut: try: buffer_size = int(self.chart_ring_buffer_size_edt.text()) if buffer_size < MINIMUM_BUFFER_SIZE: self.chart_ring_buffer_size_edt.setText( str(MINIMUM_BUFFER_SIZE)) except ValueError: display_message_box(QMessageBox.Critical, "Invalid Values", "Only integer values are accepted.") return True return super(PyDMChartingDisplay, self).eventFilter(obj, event) def add_curve(self): """ Add a new curve to the chart. """ pv_name = self._get_full_pv_name(self.pv_name_line_edt.text()) color = random_color() for k, v in self.channel_map.items(): if color == v.color: color = random_color() self.add_y_channel(pv_name=pv_name, curve_name=pv_name, color=color) def handle_enable_crosshair_checkbox_clicked(self, is_checked): self.chart.enableCrosshair(is_checked) self.cross_hair_coord_lbl.setVisible(is_checked) def add_y_channel(self, pv_name, curve_name, color, line_style=Qt.SolidLine, line_width=2, symbol=None, symbol_size=None): if pv_name in self.channel_map: logger.error("'{0}' has already been added.".format(pv_name)) return curve = self.chart.addYChannel(y_channel=pv_name, name=curve_name, color=color, lineStyle=line_style, lineWidth=line_width, symbol=symbol, symbolSize=symbol_size) self.channel_map[pv_name] = curve self.generate_pv_controls(pv_name, color) self.enable_chart_control_buttons() self.app.establish_widget_connections(self) def generate_pv_controls(self, pv_name, curve_color): """ Generate a set of widgets to manage the appearance of a curve. The set of widgets includes: 1. A checkbox which shows the curve on the chart if checked, and hide the curve if not checked 2. Two buttons -- Modify... and Remove. Modify... will bring up the Curve Settings dialog. Remove will delete the curve from the chart This set of widgets will be hidden initially, until the first curve is plotted. Parameters ---------- pv_name: str The name of the PV the current curve is being plotted for curve_color : QColor The color of the curve to paint for the checkbox label to help the user track the curve to the checkbox """ checkbox = QCheckBox() checkbox.setObjectName(pv_name) palette = checkbox.palette() palette.setColor(QPalette.Active, QPalette.WindowText, curve_color) checkbox.setPalette(palette) display_name = pv_name.split("://")[1] if len(display_name) > MAX_DISPLAY_PV_NAME_LENGTH: # Only display max allowed number of characters of the PV Name display_name = display_name[:int(MAX_DISPLAY_PV_NAME_LENGTH / 2) - 1] + "..." + \ display_name[-int(MAX_DISPLAY_PV_NAME_LENGTH / 2) + 2:] checkbox.setText(display_name) data_text = QLabel() data_text.setObjectName(pv_name) data_text.setPalette(palette) checkbox.setChecked(True) checkbox.clicked.connect( partial(self.handle_curve_chkbox_toggled, checkbox)) curve_btn_layout = QHBoxLayout() modify_curve_btn = QPushButton("Modify...") modify_curve_btn.setObjectName(pv_name) modify_curve_btn.setMaximumWidth(100) modify_curve_btn.clicked.connect( partial(self.display_curve_settings_dialog, pv_name)) focus_curve_btn = QPushButton("Focus") focus_curve_btn.setObjectName(pv_name) focus_curve_btn.setMaximumWidth(100) focus_curve_btn.clicked.connect(partial(self.focus_curve, pv_name)) annotate_curve_btn = QPushButton("Annotate...") annotate_curve_btn.setObjectName(pv_name) annotate_curve_btn.setMaximumWidth(100) annotate_curve_btn.clicked.connect( partial(self.annotate_curve, pv_name)) remove_curve_btn = QPushButton("Remove") remove_curve_btn.setObjectName(pv_name) remove_curve_btn.setMaximumWidth(100) remove_curve_btn.clicked.connect(partial(self.remove_curve, pv_name)) curve_btn_layout.addWidget(modify_curve_btn) curve_btn_layout.addWidget(focus_curve_btn) curve_btn_layout.addWidget(annotate_curve_btn) curve_btn_layout.addWidget(remove_curve_btn) individual_curve_layout = QVBoxLayout() individual_curve_layout.addWidget(checkbox) individual_curve_layout.addWidget(data_text) individual_curve_layout.addLayout(curve_btn_layout) size_policy = QSizePolicy() size_policy.setVerticalPolicy(QSizePolicy.Fixed) individual_curve_grpbx = QGroupBox() individual_curve_grpbx.setSizePolicy(size_policy) individual_curve_grpbx.setObjectName(pv_name) individual_curve_grpbx.setLayout(individual_curve_layout) self.curve_settings_layout.addWidget(individual_curve_grpbx) self.tab_panel.show() def handle_curve_chkbox_toggled(self, checkbox): """ Handle a checkbox's checked and unchecked events. If a checkbox is checked, find the curve from the channel map. If found, re-draw the curve with its previous appearance settings. If a checkbox is unchecked, remove the curve from the chart, but keep the cached data in the channel map. Parameters ---------- checkbox : QCheckBox The current checkbox being toggled """ pv_name = self._get_full_pv_name(checkbox.text()) if checkbox.isChecked(): curve = self.channel_map.get(pv_name, None) if curve: self.chart.addLegendItem(curve, pv_name, self.show_legend_chk.isChecked()) curve.show() else: curve = self.chart.findCurve(pv_name) if curve: curve.hide() self.chart.removeLegendItem(pv_name) def display_curve_settings_dialog(self, pv_name): """ Bring up the Curve Settings dialog to modify the appearance of a curve. Parameters ---------- pv_name : str The name of the PV the curve is being plotted for """ self.curve_settings_disp = CurveSettingsDisplay(self, pv_name) self.curve_settings_disp.show() def focus_curve(self, pv_name): curve = self.chart.findCurve(pv_name) if curve: self.chart.plotItem.setYRange(curve.minY, curve.maxY, padding=0) def annotate_curve(self, pv_name): curve = self.chart.findCurve(pv_name) if curve: annot = TextItem( html= '<div style="text-align: center"><span style="color: #FFF;">This is the' '</span><br><span style="color: #FF0; font-size: 16pt;">PEAK</span></div>', anchor=(-0.3, 0.5), border='w', fill=(0, 0, 255, 100)) annot = TextItem("test", anchor=(-0.3, 0.5)) self.chart.annotateCurve(curve, annot) def remove_curve(self, pv_name): """ Remove a curve from the chart permanently. This will also clear the channel map cache from retaining the removed curve's appearance settings. Parameters ---------- pv_name : str The name of the PV the curve is being plotted for """ curve = self.chart.findCurve(pv_name) if curve: self.chart.removeYChannel(curve) del self.channel_map[pv_name] self.chart.removeLegendItem(pv_name) widgets = self.findChildren( (QCheckBox, QLabel, QPushButton, QGroupBox), pv_name) for w in widgets: w.deleteLater() if len(self.chart.getCurves()) < 1: self.enable_chart_control_buttons(False) self.show_legend_chk.setChecked(False) def handle_title_text_changed(self, new_text): self.chart.setPlotTitle(new_text) def handle_change_axis_settings_clicked(self): self.axis_settings_disp = AxisSettingsDisplay(self) self.axis_settings_disp.show() def handle_limit_time_span_checkbox_clicked(self, is_checked): self.chart_limit_time_span_lbl.setVisible(is_checked) self.chart_limit_time_span_hours_line_edt.setVisible(is_checked) self.chart_limit_time_span_minutes_line_edt.setVisible(is_checked) self.chart_limit_time_span_seconds_line_edt.setVisible(is_checked) self.chart_limit_time_span_activate_btn.setVisible(is_checked) self.chart_ring_buffer_size_lbl.setDisabled(is_checked) self.chart_ring_buffer_size_edt.setDisabled(is_checked) if not is_checked: self.chart_limit_time_span_chk.setText(self.limit_time_plan_text) def handle_time_span_edt_text_changed(self, new_text): try: self.time_span_limit_hours = int( self.chart_limit_time_span_hours_line_edt.text()) self.time_span_limit_minutes = int( self.chart_limit_time_span_minutes_line_edt.text()) self.time_span_limit_seconds = int( self.chart_limit_time_span_seconds_line_edt.text()) except ValueError as e: self.time_span_limit_hours = None self.time_span_limit_minutes = None self.time_span_limit_seconds = None if self.time_span_limit_hours is not None and self.time_span_limit_minutes is not None and \ self.time_span_limit_seconds is not None: self.chart_limit_time_span_activate_btn.setEnabled(True) else: self.chart_limit_time_span_activate_btn.setEnabled(False) def handle_chart_limit_time_span_activate_btn_clicked(self): if self.time_span_limit_hours is None or self.time_span_limit_minutes is None or \ self.time_span_limit_seconds is None: display_message_box( QMessageBox.Critical, "Invalid Values", "Hours, minutes, and seconds expect only integer values.") else: timeout_milliseconds = (self.time_span_limit_hours * 3600 + self.time_span_limit_minutes * 60 + self.time_span_limit_seconds) * 1000 self.chart.setTimeSpan(timeout_milliseconds / 1000.0) self.chart_ring_buffer_size_edt.setText( str(self.chart.getBufferSize())) def handle_buffer_size_changed(self, new_buffer_size): try: if new_buffer_size and int(new_buffer_size) > MINIMUM_BUFFER_SIZE: self.chart.setBufferSize(new_buffer_size) except ValueError: display_message_box(QMessageBox.Critical, "Invalid Values", "Only integer values are accepted.") def handle_redraw_rate_changed(self, new_redraw_rate): self.chart.maxRedrawRate = new_redraw_rate def handle_data_sampling_rate_changed(self, new_data_sampling_rate): # The chart expects the value in milliseconds sampling_rate_seconds = 1 / new_data_sampling_rate self.chart.setUpdateInterval(sampling_rate_seconds) def handle_background_color_button_clicked(self): selected_color = QColorDialog.getColor() self.chart.setBackgroundColor(selected_color) self.background_color_btn.setStyleSheet("background-color: " + selected_color.name()) def handle_axis_color_button_clicked(self): selected_color = QColorDialog.getColor() self.chart.setAxisColor(selected_color) self.axis_color_btn.setStyleSheet("background-color: " + selected_color.name()) def handle_grid_opacity_slider_mouse_release(self): self.grid_alpha = float(self.grid_opacity_slr.value()) / 10.0 self.chart.setShowXGrid(self.show_x_grid_chk.isChecked(), self.grid_alpha) self.chart.setShowYGrid(self.show_y_grid_chk.isChecked(), self.grid_alpha) def handle_show_x_grid_checkbox_clicked(self, is_checked): self.chart.setShowXGrid(is_checked, self.grid_alpha) self.axis_color_lbl.setEnabled(is_checked or self.show_y_grid_chk.isChecked()) self.axis_color_btn.setEnabled(is_checked or self.show_y_grid_chk.isChecked()) self.grid_opacity_lbl.setEnabled(is_checked or self.show_y_grid_chk.isChecked()) self.grid_opacity_slr.setEnabled(is_checked or self.show_y_grid_chk.isChecked()) def handle_show_y_grid_checkbox_clicked(self, is_checked): self.chart.setShowYGrid(is_checked, self.grid_alpha) self.axis_color_lbl.setEnabled(is_checked or self.show_x_grid_chk.isChecked()) self.axis_color_btn.setEnabled(is_checked or self.show_x_grid_chk.isChecked()) self.grid_opacity_lbl.setEnabled(is_checked or self.show_x_grid_chk.isChecked()) self.grid_opacity_slr.setEnabled(is_checked or self.show_x_grid_chk.isChecked()) def handle_show_legend_checkbox_clicked(self, is_checked): self.chart.setShowLegend(is_checked) def handle_export_data_btn_clicked(self): self.chart_data_export_disp = ChartDataExportDisplay(self) self.chart_data_export_disp.show() def handle_import_data_btn_clicked(self): open_file_info = QFileDialog.getOpenFileName(self, caption="Save File", filter="*." + IMPORT_FILE_FORMAT) open_file_name = open_file_info[0] if open_file_name: importer = SettingsImporter(self) importer.import_settings(open_file_name) def handle_sync_mode_radio_toggle(self, radio_btn): if radio_btn.isChecked(): if radio_btn.text() == "Synchronous": self.data_sampling_mode = SYNC_DATA_SAMPLING self.chart_data_sampling_rate_lbl.hide() self.chart_data_async_sampling_rate_spin.hide() self.chart.resetTimeSpan() self.chart_limit_time_span_chk.setChecked(False) self.chart_limit_time_span_chk.clicked.emit(False) self.chart_limit_time_span_chk.hide() self.graph_drawing_settings_grpbx.setFixedHeight(180) self.chart.setUpdatesAsynchronously(False) elif radio_btn.text() == "Asynchronous": self.data_sampling_mode = ASYNC_DATA_SAMPLING self.chart_data_sampling_rate_lbl.show() self.chart_data_async_sampling_rate_spin.show() self.chart_limit_time_span_chk.show() self.graph_drawing_settings_grpbx.setFixedHeight(270) self.chart.setUpdatesAsynchronously(True) self.app.establish_widget_connections(self) def handle_auto_scale_btn_clicked(self): self.chart.resetAutoRangeX() self.chart.resetAutoRangeY() def handle_view_all_button_clicked(self): self.chart.getViewBox().autoRange() def handle_pause_chart_btn_clicked(self): if self.chart.pausePlotting(): self.pause_chart_btn.setText(self.pause_chart_text) else: self.pause_chart_btn.setText(self.resume_chart_text) def handle_reset_chart_btn_clicked(self): self.chart.getViewBox().setXRange(DEFAULT_X_MIN, 0) self.chart.resetAutoRangeY() @Slot() def handle_reset_chart_settings_btn_clicked(self): self.chart_ring_buffer_size_edt.setText(str(DEFAULT_BUFFER_SIZE)) self.chart_redraw_rate_spin.setValue(DEFAULT_REDRAW_RATE_HZ) self.chart_data_async_sampling_rate_spin.setValue( DEFAULT_DATA_SAMPLING_RATE_HZ) self.chart_data_sampling_rate_lbl.hide() self.chart_data_async_sampling_rate_spin.hide() self.chart_sync_mode_async_radio.setChecked(True) self.chart_sync_mode_async_radio.toggled.emit(True) self.chart_limit_time_span_chk.setChecked(False) self.chart_limit_time_span_chk.setText(self.limit_time_plan_text) self.chart_limit_time_span_chk.clicked.emit(False) self.chart.setUpdatesAsynchronously(True) self.chart.resetTimeSpan() self.chart.resetUpdateInterval() self.chart.setBufferSize(DEFAULT_BUFFER_SIZE) self.chart.setBackgroundColor(DEFAULT_CHART_BACKGROUND_COLOR) self.background_color_btn.setStyleSheet( "background-color: " + DEFAULT_CHART_BACKGROUND_COLOR.name()) self.chart.setAxisColor(DEFAULT_CHART_AXIS_COLOR) self.axis_color_btn.setStyleSheet("background-color: " + DEFAULT_CHART_AXIS_COLOR.name()) self.grid_opacity_slr.setValue(5) self.show_x_grid_chk.setChecked(False) self.show_x_grid_chk.clicked.emit(False) self.show_y_grid_chk.setChecked(False) self.show_y_grid_chk.clicked.emit(False) self.show_legend_chk.setChecked(False) self.chart.setShowXGrid(False) self.chart.setShowYGrid(False) self.chart.setShowLegend(False) def enable_chart_control_buttons(self, enabled=True): self.auto_scale_btn.setEnabled(enabled) self.view_all_btn.setEnabled(enabled) self.reset_chart_btn.setEnabled(enabled) self.pause_chart_btn.setText(self.pause_chart_text) self.pause_chart_btn.setEnabled(enabled) self.export_data_btn.setEnabled(enabled) def _get_full_pv_name(self, pv_name): """ Append the protocol to the PV Name. Parameters ---------- pv_name : str The name of the PV the curve is being plotted for """ if pv_name and "://" not in pv_name: pv_name = ''.join([self.pv_protocol_cmb.currentText(), pv_name]) return pv_name def handle_update_datetime_timer_timeout(self): current_label = self.chart.getBottomAxisLabel() new_label = "Current Time: " + PyDMChartingDisplay.get_current_datetime( ) if X_AXIS_LABEL_SEPARATOR in current_label: current_label = current_label[current_label. find(X_AXIS_LABEL_SEPARATOR) + len(X_AXIS_LABEL_SEPARATOR):] new_label += X_AXIS_LABEL_SEPARATOR + current_label self.chart.setLabel("bottom", text=new_label) def update_curve_data(self, curve): """ Determine if the PV is active. If not, disable the related PV controls. If the PV is active, update the PV controls' states. Parameters ---------- curve : PlotItem A PlotItem, i.e. a plot, to draw on the chart. """ pv_name = curve.name() max_x = self.chart.getViewBox().viewRange()[1][0] max_y = self.chart.getViewBox().viewRange()[1][1] current_y = curve.data_buffer[1, -1] widgets = self.findChildren((QCheckBox, QLabel, QPushButton), pv_name) for w in widgets: if np.isnan(current_y): if isinstance(w, QCheckBox): w.setChecked(False) else: if isinstance(w, QCheckBox) and not w.isEnabled(): w.setChecked(True) if isinstance(w, QLabel): w.clear() w.setText( "(yMin = {0:.3f}, yMax = {1:.3f}) y = {2:.3f}".format( max_x, max_y, current_y)) w.show() w.setEnabled(not np.isnan(current_y)) if isinstance(w, QPushButton) and w.text() == "Remove": # Enable the Remove button to make removing inactive PVs possible anytime w.setEnabled(True) def show_mouse_coordinates(self, x, y): self.cross_hair_coord_lbl.clear() self.cross_hair_coord_lbl.setText("x = {0:.3f}, y = {1:.3f}".format( x, y)) @staticmethod def get_current_datetime(): current_date = datetime.datetime.now().strftime("%b %d, %Y") current_time = datetime.datetime.now().strftime("%H:%M:%S") current_datetime = current_time + ' (' + current_date + ')' return current_datetime @property def gridAlpha(self): return self.grid_alpha