class SnapshotCompareWidget(QWidget): pvs_filtered = QtCore.pyqtSignal(set) restore_requested = QtCore.pyqtSignal(list) rgx_icon = None def __init__(self, snapshot, common_settings, parent=None, **kw): super().__init__(parent, **kw) self.snapshot = snapshot self.common_settings = common_settings # ----------- PV Table ------------- # PV table consist of: # self.model: holding the data, values, being updated by PV callbacks, etc # self._proxy: a proxy model implementing the filter functionality # self.view: visual representation of the PV table self.view = SnapshotPvTableView(self) self.view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.view.restore_requested.connect(self._handle_restore_request) self.model = SnapshotPvTableModel(snapshot, self) self.model.file_parse_errors.connect(self._show_snapshot_parse_errors) self._proxy = SnapshotPvFilterProxyModel(self) self._proxy.setSourceModel(self.model) self._proxy.filtered.connect(self.pvs_filtered) # Build model and set default visualization on view (column widths, etc) self.model.set_pvs(snapshot.pvs.values()) self.view.setModel(self._proxy) # ---------- Filter control elements --------------- # - text input to filter by name # - drop down to filter by compare status # - check box to select if showing pvs with incomplete data if SnapshotCompareWidget.rgx_icon is None: SnapshotCompareWidget.rgx_icon = QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), "images/rgx.png")) # #### PV name filter pv_filter_label = QLabel("Filter:", self) pv_filter_label.setAlignment(Qt.AlignCenter | Qt.AlignRight) self.pv_filter_sel = QComboBox(self) self.pv_filter_sel.setEditable(True) self.pv_filter_sel.setIconSize(QtCore.QSize(35, 15)) self.pv_filter_inp = self.pv_filter_sel.lineEdit() self.pv_filter_inp.setPlaceholderText("Filter by PV name") policy = self.pv_filter_sel.sizePolicy() policy.setHorizontalPolicy(policy.Expanding) self.pv_filter_sel.setSizePolicy(policy) self.pv_filter_sel.currentIndexChanged.connect( self._predefined_filter_selected) self.pv_filter_inp.textChanged.connect(self._create_name_filter) self._populate_filter_list() # Prepare pallets to color the pv name filter input if rgx not valid self._inp_palette_ok = self.pv_filter_inp.palette() self._inp_palette_err = QPalette() self._inp_palette_err.setColor(QPalette.Base, QColor("#F39292")) # Create a PV name filter layout and add items pv_filter_layout = QHBoxLayout() pv_filter_layout.setSpacing(10) pv_filter_layout.addWidget(pv_filter_label) pv_filter_layout.addWidget(self.pv_filter_sel) # #### Regex selector self.regex = QCheckBox("Regex", self) self.regex.stateChanged.connect(self._handle_regex_change) # #### Selector for comparison filter self.compare_filter_inp = QComboBox(self) self.compare_filter_inp.addItems( ["Show all", "Different only", "Equal only"]) self.compare_filter_inp.currentIndexChanged.connect( self._proxy.set_eq_filter) self.compare_filter_inp.setMaximumWidth(200) # ### Show disconnected selector self.show_disconn_inp = QCheckBox("Show disconnected PVs.", self) self.show_disconn_inp.setChecked(True) self.show_disconn_inp.stateChanged.connect( self._proxy.set_disconn_filter) self.show_disconn_inp.setMaximumWidth(500) # Tolerance setting tol_label = QLabel("Tolerance:") tol = QSpinBox() tol.setRange(1, 1000000) tol.setValue(1) tol.valueChanged[int].connect(self.model.change_tolerance) self.model.change_tolerance(tol.value()) # ### Put all tolerance and filter selectors in one layout filter_layout = QHBoxLayout() filter_layout.addWidget(tol_label) filter_layout.addWidget(tol) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addLayout(pv_filter_layout) filter_layout.addWidget(self.regex) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addWidget(self.compare_filter_inp) filter_layout.addWidget(self.show_disconn_inp) filter_layout.setAlignment(Qt.AlignLeft) filter_layout.setSpacing(10) # ------- Build main layout --------- layout = QVBoxLayout(self) layout.setContentsMargins(10, 10, 10, 10) layout.addLayout(filter_layout) layout.addWidget(self.view) self.setLayout(layout) def _populate_filter_list(self): predefined_filters = self.common_settings['predefined_filters'] self.pv_filter_sel.blockSignals(True) self.pv_filter_sel.clear() self.pv_filter_sel.addItem(None) for rgx in predefined_filters.get('rgx-filters', list()): self.pv_filter_sel.addItem(SnapshotCompareWidget.rgx_icon, rgx) self.pv_filter_sel.addItems(predefined_filters.get('filters', list())) self.pv_filter_sel.blockSignals(False) def _handle_regex_change(self, state): txt = self.pv_filter_inp.text() if state and txt.strip() == '': self.pv_filter_inp.setText('.*') elif not state and txt.strip() == '.*': self.pv_filter_inp.setText('') else: self._create_name_filter(txt) def _create_name_filter(self, txt): if self.regex.isChecked(): try: srch_filter = re.compile(txt) self.pv_filter_inp.setPalette(self._inp_palette_ok) except: # Syntax error (happens a lot during typing an expression). In such cases make compiler which will # not match any pv name srch_filter = re.compile("") self.pv_filter_inp.setPalette(self._inp_palette_err) else: srch_filter = txt self.pv_filter_inp.setPalette(self._inp_palette_ok) self._proxy.set_name_filter(srch_filter) def _show_snapshot_parse_errors(self, errors): show_snapshot_parse_errors(self, errors) def new_selected_files(self, selected_files): self.model.clear_snap_files() self.model.add_snap_files(selected_files) self._proxy.apply_filter() def clear_snap_files(self): self.model.clear_snap_files() def handle_new_snapshot_instance(self, snapshot): self.snapshot = snapshot self.model.snapshot = snapshot self.model.set_pvs(snapshot.pvs.values()) self.view.sortByColumn(0, Qt.AscendingOrder) # default sorting self._populate_filter_list() def _handle_restore_request(self, pvs_list): self.restore_requested.emit(pvs_list) def _predefined_filter_selected(self, idx): txt = self.pv_filter_inp.text() if idx == 0: # First empty option; the menu is always reset to this. return if not self.pv_filter_sel.itemIcon(idx).isNull(): # Set back to first index, to get rid of the icon. Set to regex and # pass text of filter to the input self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(True) self.pv_filter_inp.setText(txt) else: # Imitate same behaviour self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(False) self.pv_filter_inp.setText(txt) def filter_update(self): self._proxy.apply_filter()
class Window(QDialog): def __init__(self): super(Window, self).__init__() #self.createIconGroupBox() #self.createMessageGroupBox() self.durationLabel = QLabel("Duration:") self.iconLabel = QLabel("Icon:") self.iconComboBox = QComboBox() self.iconComboBox.addItem(QIcon(':/images/bad.png'), "Bad") self.iconComboBox.addItem(QIcon(':/images/heart.png'), "Heart") self.iconComboBox.addItem(QIcon(':/images/trash.png'), "Trash") self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width()) self.createActions() self.createTrayIcon() """ self.showMessageButton.clicked.connect(self.showMessage) self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible) self.iconComboBox.currentIndexChanged.connect(self.setIcon) """ self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) #mainLayout = QVBoxLayout() #self.setLayout(mainLayout) self.iconComboBox.setCurrentIndex(1) self.setIcon(1) self.trayIcon.show() self.setWindowTitle("Systray") self.resize(400, 300) def setIcon(self, index): icon = self.iconComboBox.itemIcon(index) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip(self.iconComboBox.itemText(index)) def createActions(self): self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.quitAction = QAction("&Quit", self, triggered=QApplication.instance().quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) def iconActivated(self, reason): sys.stdout.write("ouch\n") if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): self.iconComboBox.setCurrentIndex( (self.iconComboBox.currentIndex() + 1) % self.iconComboBox.count()) elif reason == QSystemTrayIcon.MiddleClick: self.showMessage() def messageClicked(self): QMessageBox.information( None, "Systray", "Sorry, I already gave what help I could.\nMaybe you should " "try asking a human?")