class PickChannelsDialog(QDialog): def __init__(self, parent, channels, selected=None, title="Pick channels"): super().__init__(parent) self.setWindowTitle(title) if selected is None: selected = [] self.initial_selection = selected vbox = QVBoxLayout(self) self.channels = QListWidget() self.channels.insertItems(0, channels) self.channels.setSelectionMode(QListWidget.ExtendedSelection) for i in range(self.channels.count()): if self.channels.item(i).data(0) in selected: self.channels.item(i).setSelected(True) vbox.addWidget(self.channels) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) vbox.addWidget(self.buttonbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.channels.itemSelectionChanged.connect(self.toggle_buttons) self.toggle_buttons() # initialize OK button state @Slot() def toggle_buttons(self): """Toggle OK button. """ selected = [item.data(0) for item in self.channels.selectedItems()] if selected != self.initial_selection: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False)
class AcceptFiles(QDialog): def __init__(self, files): super().__init__() self.ok = QPushButton("Add", self) self.ok.clicked.connect(self.accept) discard = QPushButton("Discard", self) discard.clicked.connect(self.close) self.files = QListWidget(self) self.files.setSelectionMode(QAbstractItemView.ExtendedSelection) for file_name in files: self.files.addItem(file_name) for i in range(self.files.count()): self.files.item(i).setSelected(True) self.ok.setDefault(True) self.ok.setAutoDefault(True) layout = QVBoxLayout() layout.addWidget(QLabel("Found {} files".format(len(files)))) layout.addWidget(self.files) butt_layout = QHBoxLayout() butt_layout.addWidget(discard) butt_layout.addStretch() butt_layout.addWidget(self.ok) layout.addLayout(butt_layout) self.setLayout(layout) def selection_changed(self): if self.files.selectedItems().count() == 0: self.ok.setDisabled(True) else: self.ok.setEnabled(True) def get_files(self): return [str(item.text()) for item in self.files.selectedItems()]
class CaseList(QWidget): def __init__(self, facade: LibresFacade, notifier: ErtNotifier): self.facade = facade self.notifier = notifier QWidget.__init__(self) addHelpToWidget(self, "init/case_list") layout = QVBoxLayout() self._list = QListWidget(self) self._list.setMinimumHeight(100) self._list.setMaximumHeight(250) self._default_selection_mode = self._list.selectionMode() self.setSelectable(False) layout.addWidget(QLabel("Available cases:")) layout.addWidget(self._list) self._addRemoveWidget = AddRemoveWidget(self.addItem, self.removeItem, horizontal=True) self._addRemoveWidget.enableRemoveButton(False) layout.addWidget(self._addRemoveWidget) self._title = "New keyword" self._description = "Enter name of keyword:" self.setLayout(layout) notifier.ertChanged.connect(self.updateList) self.updateList() def setSelectable(self, selectable): if selectable: self._list.setSelectionMode(self._default_selection_mode) else: self._list.setSelectionMode(QAbstractItemView.NoSelection) def addItem(self): dialog = ValidatedDialog("New case", "Enter name of new case:", getAllCases(self.facade)) new_case_name = dialog.showAndTell() if not new_case_name == "": self.facade.select_or_create_new_case(new_case_name) self.notifier.ertChanged.emit() def removeItem(self): message = "Support for removal of items has not been implemented!" QMessageBox.information(self, "Not implemented!", message) def updateList(self): """Retrieves data from the model and inserts it into the list""" case_list = getAllCases(self.facade) self._list.clear() for case in case_list: self._list.addItem(case)
class AppendDialog(QDialog): def __init__(self, parent, compatibles, title="Append data"): super().__init__(parent) self.setWindowTitle(title) vbox = QVBoxLayout(self) grid = QGridLayout() grid.addWidget(QLabel("Source"), 0, 0, Qt.AlignCenter) grid.addWidget(QLabel("Destination"), 0, 2, Qt.AlignCenter) source = QListWidget(self) source.setAcceptDrops(True) source.setDragEnabled(True) source.setSelectionMode(QAbstractItemView.ExtendedSelection) source.setDefaultDropAction(Qt.DropAction.MoveAction) source.insertItems(0, [d["name"] for d in compatibles]) grid.addWidget(source, 1, 0) grid.addWidget(QLabel("->"), 1, 1, Qt.AlignHCenter) self.destination = QListWidget(self) self.destination.setAcceptDrops(True) self.destination.setDragEnabled(True) self.destination.setSelectionMode(QAbstractItemView.ExtendedSelection) self.destination.setDefaultDropAction(Qt.DropAction.MoveAction) grid.addWidget(self.destination, 1, 2) vbox.addLayout(grid) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) vbox.addWidget(self.buttonbox) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) self.destination.model().rowsInserted.connect(self.toggle_buttons) self.destination.model().rowsRemoved.connect(self.toggle_buttons) self.toggle_buttons() @property def names(self): names = [] for it in range(self.destination.count()): names.append(self.destination.item(it).text()) return names @Slot() def toggle_buttons(self): """Toggle OK button. """ if self.destination.count() > 0: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False)
class MontageDialog(QDialog): def __init__(self, parent, montages, selected=None): super().__init__(parent) self.setWindowTitle("Set montage") vbox = QVBoxLayout(self) self.montages = QListWidget() self.montages.insertItems(0, montages) self.montages.setSelectionMode(QListWidget.SingleSelection) if selected is not None: for i in range(self.montages.count()): if self.montages.item(i).data(0) == selected: self.montages.item(i).setSelected(True) vbox.addWidget(self.montages) hbox = QHBoxLayout() self.view_button = QPushButton("View") self.view_button.clicked.connect(self.view_montage) hbox.addWidget(self.view_button) hbox.addStretch() self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) hbox.addWidget(self.buttonbox) vbox.addLayout(hbox) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) self.montages.itemSelectionChanged.connect(self.toggle_buttons) self.toggle_buttons() # initialize OK and View buttons state @Slot() def toggle_buttons(self): """Toggle OK and View buttons. """ if self.montages.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) self.view_button.setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) self.view_button.setEnabled(False) def view_montage(self): name = self.montages.selectedItems()[0].data(0) montage = make_standard_montage(name) fig = montage.plot(show_names=True, show=False) win = fig.canvas.manager.window win.setWindowModality(Qt.WindowModal) win.setWindowTitle("Montage") win.findChild(QStatusBar).hide() win.findChild(QToolBar).hide() fig.show()
class PopInDialog(QDialog): pop_in_tabs = Signal(list) def __init__(self, parent, loggers): super().__init__(parent) self.loggers = loggers self.setupUi() def setupUi(self): self.resize(200, 320) self.vbox = QVBoxLayout(self) self.listWidget = QListWidget(self) self.listWidget.setSelectionMode(self.listWidget.MultiSelection) self.listWidget.selectionModel().reset() self.vbox.addWidget(self.listWidget) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, self) self.vbox.addWidget(self.buttonBox) self.buttonBox.accepted.connect(self.accept) self.listWidget.doubleClicked.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.fill_logger_list() def fill_logger_list(self): for logger in self.loggers: if logger.popped_out: self.listWidget.addItem(logger.name) self.listWidget.setCurrentRow(0) def accept(self, index=None): names = [] if index is not None: item = self.listWidget.itemFromIndex(index) names.append(item.text()) else: for item in self.listWidget.selectedItems(): names.append(item.text()) if len(names) > 0: self.pop_in_tabs.emit(names) self.done(0) def reject(self): self.done(0)
class LoadRecentFiles(QDialog): def __init__(self, settings: BaseSettings, parent=None): super().__init__(parent) self.settings = settings self.file_list = QListWidget() self.file_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.cancel_btn = QPushButton("Cancel", clicked=self.reject) self.load_btn = QPushButton("Load", clicked=self.accept) for name_list, method in settings.get_last_files_multiple(): entry = f"{name_list[0]} {method}" item = QListWidgetItem(entry, self.file_list) item.setData(Qt.UserRole, (name_list, method)) last_set = {(tuple(x), y) for x, y in settings.get_last_files_multiple()} for name_list, method in settings.get_last_files(): if (tuple(name_list), method) in last_set: continue entry = f"{name_list[0]} {method}" item = QListWidgetItem(entry, self.file_list) item.setData(Qt.UserRole, (name_list, method)) layout = QGridLayout() layout.addWidget(QLabel("Select files")) layout.addWidget(self.file_list, 1, 0, 1, 2) layout.addWidget(self.cancel_btn, 2, 0) layout.addWidget(self.load_btn, 2, 1) self.setLayout(layout) self.resize( *self.settings.get_from_profile("multiple_files_dialog_size", ( self.size().width(), self.size().height()))) def get_files(self) -> List[Tuple[List[str], str]]: return [ item.data(Qt.UserRole) for item in self.file_list.selectedItems() ] def accept(self) -> None: self.settings.set_in_profile( "multiple_files_dialog_size", (self.size().width(), self.size().height())) super().accept()
class RemoveLasersWindow(QWidget): def __init__(self, main_window): super().__init__() self.main_window = main_window lasers = self.main_window.lasers self.setWindowTitle("Remove laser") layout = QVBoxLayout() self.setLayout(layout) self.laser_list = QListWidget() self.laser_list.setSelectionMode(QAbstractItemView.ExtendedSelection) layout.addWidget(self.laser_list) names = [] for laser in lasers: settings = laser.get_settings() names.append(settings['name']) print(names) self.laser_list.addItems(names) self.removeButton = QPushButton("Remove") layout.addWidget(self.removeButton) self._createActions() self._connectActions() def _createActions(self): self.removeAction = QAction(self) self.removeAction.setText("Remove") def _connectActions(self): self.removeButton.clicked.connect(self.removeAction.trigger) self.removeAction.triggered.connect(self.remove_lasers) def remove_lasers(self): selected_rows = [x.row() for x in self.laser_list.selectedIndexes()] print(selected_rows) self.main_window.remove_lasers(selected_rows)
def __init__(self, parent, compatibles, title="Append data"): super().__init__(parent) self.setWindowTitle(title) vbox = QVBoxLayout(self) grid = QGridLayout() grid.addWidget(QLabel("Source"), 0, 0, Qt.AlignCenter) grid.addWidget(QLabel("Destination"), 0, 2, Qt.AlignCenter) source = QListWidget(self) source.setAcceptDrops(True) source.setDragEnabled(True) source.setSelectionMode(QAbstractItemView.ExtendedSelection) source.setDefaultDropAction(Qt.DropAction.MoveAction) source.insertItems(0, [d["name"] for d in compatibles]) grid.addWidget(source, 1, 0) grid.addWidget(QLabel("->"), 1, 1, Qt.AlignHCenter) self.destination = QListWidget(self) self.destination.setAcceptDrops(True) self.destination.setDragEnabled(True) self.destination.setSelectionMode(QAbstractItemView.ExtendedSelection) self.destination.setDefaultDropAction(Qt.DropAction.MoveAction) grid.addWidget(self.destination, 1, 2) vbox.addLayout(grid) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) vbox.addWidget(self.buttonbox) vbox.setSizeConstraint(QVBoxLayout.SetFixedSize) self.destination.model().rowsInserted.connect(self.toggle_buttons) self.destination.model().rowsRemoved.connect(self.toggle_buttons) self.toggle_buttons()
class RemoveRPWindow(QWidget): def __init__(self, main_window): super().__init__() self.main_window = main_window rps = self.main_window.rps self.setWindowTitle("Remove RedPitaya") layout = QVBoxLayout() self.setLayout(layout) self.laser_list = QListWidget() self.laser_list.setSelectionMode(QAbstractItemView.ExtendedSelection) layout.addWidget(self.laser_list) names = [] self.laser_list.addItems(rps) self.removeButton = QPushButton("Remove") layout.addWidget(self.removeButton) self._createActions() self._connectActions() def _createActions(self): self.removeAction = QAction(self) self.removeAction.setText("Remove") def _connectActions(self): self.removeButton.clicked.connect(self.removeAction.trigger) self.removeAction.triggered.connect(self.remove_lasers) def remove_lasers(self): selected_rows = [x.row() for x in self.laser_list.selectedIndexes()] print(selected_rows) self.main_window.remove_rps(selected_rows)
class EpochDialog(QDialog): def __init__(self, parent, events): super().__init__(parent) self.setWindowTitle("Create Epochs") grid = QGridLayout(self) label = QLabel("Events:") label.setAlignment(Qt.AlignTop) grid.addWidget(label, 0, 0, 1, 1) self.events = QListWidget() self.events.insertItems(0, unique(events[:, 2]).astype(str)) self.events.setSelectionMode(QListWidget.ExtendedSelection) grid.addWidget(self.events, 0, 1, 1, 2) grid.addWidget(QLabel("Interval around events:"), 1, 0, 1, 1) self.tmin = QDoubleSpinBox() self.tmin.setMinimum(-10000) self.tmin.setValue(-0.2) self.tmin.setSingleStep(0.1) self.tmin.setAlignment(Qt.AlignRight) self.tmax = QDoubleSpinBox() self.tmax.setMinimum(-10000) self.tmax.setValue(0.5) self.tmax.setSingleStep(0.1) self.tmax.setAlignment(Qt.AlignRight) grid.addWidget(self.tmin, 1, 1, 1, 1) grid.addWidget(self.tmax, 1, 2, 1, 1) self.baseline = QCheckBox("Baseline Correction:") self.baseline.setChecked(True) self.baseline.stateChanged.connect(self.toggle_baseline) grid.addWidget(self.baseline, 2, 0, 1, 1) self.a = QDoubleSpinBox() self.a.setMinimum(-10000) self.a.setValue(-0.2) self.a.setSingleStep(0.1) self.a.setAlignment(Qt.AlignRight) self.b = QDoubleSpinBox() self.b.setMinimum(-10000) self.b.setValue(0) self.b.setSingleStep(0.1) self.b.setAlignment(Qt.AlignRight) grid.addWidget(self.a, 2, 1, 1, 1) grid.addWidget(self.b, 2, 2, 1, 1) self.buttonbox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttonbox.accepted.connect(self.accept) self.buttonbox.rejected.connect(self.reject) grid.addWidget(self.buttonbox, 3, 0, 1, -1) self.events.itemSelectionChanged.connect(self.toggle_ok) self.toggle_ok() grid.setSizeConstraint(QGridLayout.SetFixedSize) @Slot() def toggle_ok(self): if self.events.selectedItems(): self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(True) else: self.buttonbox.button(QDialogButtonBox.Ok).setEnabled(False) @Slot() def toggle_baseline(self): if self.baseline.isChecked(): self.a.setEnabled(True) self.b.setEnabled(True) else: self.a.setEnabled(False) self.b.setEnabled(False)
class CheckList(QWidget): def __init__(self, model, label="", help_link="", custom_filter_button=None): """ :param custom_filter_button: if needed, add a button that opens a custom filter menu. Useful when search alone isn't enough to filter the list. :type custom_filter_button: QToolButton """ QWidget.__init__(self) self._model = model if help_link != "": addHelpToWidget(self, help_link) layout = QVBoxLayout() self._createCheckButtons() self._list = QListWidget() self._list.setContextMenuPolicy(Qt.CustomContextMenu) self._list.setSelectionMode(QAbstractItemView.ExtendedSelection) self._search_box = SearchBox() check_button_layout = QHBoxLayout() check_button_layout.setContentsMargins(0, 0, 0, 0) check_button_layout.setSpacing(0) check_button_layout.addWidget(QLabel(label)) check_button_layout.addStretch(1) check_button_layout.addWidget(self._checkAllButton) check_button_layout.addWidget(self._uncheckAllButton) layout.addLayout(check_button_layout) layout.addWidget(self._list) """ Inserts the custom filter button, if provided. The caller is responsible for all related actions. """ if custom_filter_button is not None: search_bar_layout = QHBoxLayout() search_bar_layout.addWidget(self._search_box) search_bar_layout.addWidget(custom_filter_button) layout.addLayout(search_bar_layout) else: layout.addWidget(self._search_box) self.setLayout(layout) self._checkAllButton.clicked.connect(self.checkAll) self._uncheckAllButton.clicked.connect(self.uncheckAll) self._list.itemChanged.connect(self.itemChanged) self._search_box.filterChanged.connect(self.filterList) self._list.customContextMenuRequested.connect(self.showContextMenu) self._model.selectionChanged.connect(self.modelChanged) self._model.modelChanged.connect(self.modelChanged) self.modelChanged() def _createCheckButtons(self): self._checkAllButton = QToolButton() self._checkAllButton.setIcon(resourceIcon("check.svg")) self._checkAllButton.setIconSize(QSize(16, 16)) self._checkAllButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self._checkAllButton.setAutoRaise(True) self._checkAllButton.setToolTip("Select all") self._uncheckAllButton = QToolButton() self._uncheckAllButton.setIcon(resourceIcon("checkbox_outline.svg")) self._uncheckAllButton.setIconSize(QSize(16, 16)) self._uncheckAllButton.setToolButtonStyle(Qt.ToolButtonIconOnly) self._uncheckAllButton.setAutoRaise(True) self._uncheckAllButton.setToolTip("Unselect all") def itemChanged(self, item): """@type item: QListWidgetItem""" if item.checkState() == Qt.Checked: self._model.selectValue(str(item.text())) elif item.checkState() == Qt.Unchecked: self._model.unselectValue(str(item.text())) else: raise AssertionError("Unhandled checkstate!") def modelChanged(self): self._list.clear() items = self._model.getList() for item in items: list_item = QListWidgetItem(item) list_item.setFlags(list_item.flags() | Qt.ItemIsUserCheckable) if self._model.isValueSelected(item): list_item.setCheckState(Qt.Checked) else: list_item.setCheckState(Qt.Unchecked) self._list.addItem(list_item) self.filterList(self._search_box.filter()) def setSelectionEnabled(self, enabled): self.setEnabled(enabled) self._checkAllButton.setEnabled(enabled) self._uncheckAllButton.setEnabled(enabled) def filterList(self, filter): filter = filter.lower() for index in range(0, self._list.count()): item = self._list.item(index) text = str(item.text()).lower() if filter == "": item.setHidden(False) elif filter in text: item.setHidden(False) else: item.setHidden(True) def checkAll(self): """ Checks all visible items in the list. """ for index in range(0, self._list.count()): item = self._list.item(index) if not item.isHidden(): self._model.selectValue(str(item.text())) def uncheckAll(self): """ Unchecks all items in the list, visible or not """ self._model.unselectAll() def checkSelected(self): items = [] for item in self._list.selectedItems(): items.append(str(item.text())) for item in items: self._model.selectValue(item) def uncheckSelected(self): items = [] for item in self._list.selectedItems(): items.append(str(item.text())) for item in items: self._model.unselectValue(item) def showContextMenu(self, point): p = self._list.mapToGlobal(point) menu = QMenu() check_selected = menu.addAction("Check selected") uncheck_selected = menu.addAction("Uncheck selected") menu.addSeparator() clear_selection = menu.addAction("Clear selection") selected_item = menu.exec_(p) if selected_item == check_selected: self.checkSelected() elif selected_item == uncheck_selected: self.uncheckSelected() elif selected_item == clear_selection: self._list.clearSelection()
class MainWindow(QMainWindow): def selectFileToOpen(self): def getPreProcessingChoice(self, filename, filestructure): items = ("Choose the longest", "Merge all") item, okPressed = QInputDialog.getItem( self, "Multiple tracks/segments", "File '" + filename + "' contains more than one track/segment\n\n" + infos + "\nWhat to do?", items, 0, False) if okPressed and item: return items.index(item) else: return 0 # Try to recover the last used directory old_directory = self.settings.value("lastdirectory", str) # Check if the setting exists if old_directory is not None: # Check if it's not empty if old_directory: old_directory = old_directory else: old_directory = bombo.TRACKS_FOLDER else: old_directory = bombo.TRACKS_FOLDER # Open the dialog box fullfilename_list = QFileDialog.getOpenFileNames( self, 'Open .gpx', old_directory, "GPX files (*.gpx)") if os.environ['QT_API'] == 'pyqt': pass elif os.environ['QT_API'] == 'pyqt5': fullfilename_list = fullfilename_list[0] # Process every selected file for i, fullfilename in enumerate(fullfilename_list): # Process filename directory, filename = os.path.split(str(fullfilename)) filename, fileextension = os.path.splitext(filename) # Save the new directory in the application settings (it only # needs to be done once) if i == 0: # print "New directory to be saved: {}\n".format(directory) if os.environ['QT_API'] == 'pyqt': self.settings.setValue("lastdirectory", str(directory)) elif os.environ['QT_API'] == 'pyqt5': self.settings.setValue("lastdirectory", QtCore.QVariant(str(directory))) # Open file and inspect what's inside gpxraw, longest_traseg, Ntracks, Nsegments, infos = bombo.LoadGPX( fullfilename) # If there's more than one track or segment, ask how to proceed if (Ntracks > 1) or (Nsegments > 1): preprocessingchoice = getPreProcessingChoice( self, filename, infos) if preprocessingchoice == 0: preprocessedgpx = bombo.SelectOneTrackAndSegmentFromGPX( gpxraw, longest_traseg[0], longest_traseg[1]) listname = filename + " (longest)" elif preprocessingchoice == 1: preprocessedgpx = bombo.MergeAllTracksAndSegmentsFromGPX( gpxraw) listname = filename + " (merged)" else: preprocessedgpx = gpxraw listname = filename # Append the list of open GPX files using the next available color (that's the size of the list -1) self.gpxlist.append(preprocessedgpx) self.gpxnamelist.append(listname) newitem = QListWidgetItem(listname) newitem.setBackground( QtGui.QColor(self.palette[len(self.gpxlist) - 1])) self.tracklist.addItem(newitem) return def Go(self): if len(self.gpxselectedlist) > 0: # Temporarily change cursor QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) # Clear up global variables self.proc_coords = [] self.proc_measurements = [] self.proc_state_means = [] self.proc_state_vars = [] self.proc_new_coords = [] self.proc_new_gpx = [] self.proc_coords_to_plot = [] self.proc_coords_to_plot2 = [] self.proc_balloondata = [] # For every GPX file that is selected self.textWarningConsole.clear() for i, currentgpx in enumerate(self.gpxselectedlist): # Parse the GPX file gpx, coords, dinfos_before, warnings = bombo.ParseGPX( currentgpx, track_nr=0, segment_nr=0, use_srtm_elevation=bool(self.checkUseSRTM.isChecked())) self.textWarningConsole.append(warnings) # Kalman processing coords, measurements, state_means, state_vars, dinfos_during = bombo.ApplyKalmanFilter( coords, gpx, method=self.comboBoxProcessingMethod.currentIndex(), use_acceleration=self.checkUseAcceleration.isChecked(), extra_smooth=self.checkExtraSmooth.isChecked(), debug_plot=False) # Save data in GPX structure to compute speed and elevations new_coords, new_gpx, dinfos_after = bombo.SaveDataToCoordsAndGPX( coords, state_means) # Update GUI with the computed stats parent = QtGui.QStandardItem(self.gpxselectednamelist[i]) parent_beforeprocessing = QtGui.QStandardItem("Raw GPX stats") parent_beforeprocessing.appendRow([ QtGui.QStandardItem("Total distance"), QtGui.QStandardItem(dinfos_before['total_distance']) ]) parent_beforeprocessing_moving = QtGui.QStandardItem("Moving") parent_beforeprocessing_moving.appendRow([ QtGui.QStandardItem("Time"), QtGui.QStandardItem(dinfos_before['moving_time']) ]) parent_beforeprocessing_moving.appendRow([ QtGui.QStandardItem("Distance"), QtGui.QStandardItem(dinfos_before['moving_distance']) ]) parent_beforeprocessing.appendRow( parent_beforeprocessing_moving) parent_beforeprocessing_idle = QtGui.QStandardItem("Idle") parent_beforeprocessing_idle.appendRow([ QtGui.QStandardItem("Time"), QtGui.QStandardItem(dinfos_before['idle_time']) ]) parent_beforeprocessing_idle.appendRow([ QtGui.QStandardItem("Distance"), QtGui.QStandardItem(dinfos_before['idle_distance']) ]) parent_beforeprocessing.appendRow(parent_beforeprocessing_idle) parent_beforeprocessing.appendRow([ QtGui.QStandardItem("Elevation"), QtGui.QStandardItem(dinfos_before['elevation']) ]) parent_beforeprocessing.appendRow([ QtGui.QStandardItem("Climb"), QtGui.QStandardItem(dinfos_before['climb']) ]) parent.appendRow(parent_beforeprocessing) parent.appendRow([ QtGui.QStandardItem("Samples"), QtGui.QStandardItem(dinfos_during['nsamples']) ]) parent.appendRow([ QtGui.QStandardItem("Total distance"), QtGui.QStandardItem(dinfos_after['total_distance']) ]) parent_moving = QtGui.QStandardItem("Moving") parent_moving.appendRow([ QtGui.QStandardItem("Time"), QtGui.QStandardItem(dinfos_after['moving_time']) ]) parent_moving.appendRow([ QtGui.QStandardItem("Distance"), QtGui.QStandardItem(dinfos_after['moving_distance']) ]) parent.appendRow(parent_moving) parent_idle = QtGui.QStandardItem("Idle") parent_idle.appendRow([ QtGui.QStandardItem("Time"), QtGui.QStandardItem(dinfos_after['idle_time']) ]) parent_idle.appendRow([ QtGui.QStandardItem("Distance"), QtGui.QStandardItem(dinfos_after['idle_distance']) ]) parent.appendRow(parent_idle) parent.appendRow([ QtGui.QStandardItem("Elevation"), QtGui.QStandardItem(dinfos_after['elevation']) ]) parent.appendRow([ QtGui.QStandardItem("Climb"), QtGui.QStandardItem(dinfos_after['climb']) ]) self.treemodel.appendRow(parent) # Create balloondata for the html plot balloondata = { 'distance': np.cumsum( bombo.HaversineDistance(np.asarray(new_coords['lat']), np.asarray( new_coords['lon']))), 'elevation': np.asarray(new_coords['ele']), 'speed': None } # Create extra data for the html plot (fully implemented in bombo, not here) """ data = np.ones((len(lat_cleaned),2)) data[:,0] = h_filtered / np.max(h_filtered) * 0.0004 data[:,1] = np.hstack((np.asarray([0]), speed_h)) / np.max(np.hstack((np.asarray([0]), speed_h))) * 0.0004 tangentdata = {'data': data, 'sides': (0, 1), 'palette': ('blue','red')} """ # Save relevant output in global variables self.proc_coords.append(coords) self.proc_measurements.append(measurements) self.proc_state_means.append(state_means) self.proc_state_vars.append(state_vars) self.proc_new_coords.append(new_coords) self.proc_new_gpx.append(new_gpx) self.proc_coords_to_plot.append( np.vstack((new_coords['lat'], new_coords['lon'])).T) self.proc_coords_to_plot2.append( np.vstack((coords['lat'], coords['lon'])).T) self.proc_balloondata.append(balloondata) # Restore original cursor QApplication.restoreOverrideCursor() # Generate embedded plots if len(self.gpxselectedlist) == 1: self.plotEmbeddedElevationAndSpeed.update_figure( measurements, state_means, new_gpx.tracks[0].segments[0]) self.plotEmbeddedDetails.update_figure( measurements, state_means, state_vars, new_gpx.tracks[0].segments[0]) else: # Commentato per adesso # self.plotEmbeddedElevationAndSpeed.update_figure_multiple_tracks(self.proc_measurements, self.proc_state_means, self.proc_new_gpx) self.plotEmbeddedElevationAndSpeed.clear_figure() self.plotEmbeddedDetails.clear_figure() # Generate html plot, if only one track is selected, proceed with the complete output, otherwise just plot the traces if len(self.gpxselectedlist) is 1: bombo.PlotOnMap( coords_array_list=self.proc_coords_to_plot, coords_array2_list=self.proc_coords_to_plot2, coords_palette=self.selectedpalette, tangentdata=None, balloondata_list=self.proc_balloondata, rdp_reduction=self.checkUseRDP.isChecked(), showmap=bool(self.check2DMapInExternalBrowser.isChecked())) else: bombo.PlotOnMap( coords_array_list=self.proc_coords_to_plot, coords_array2_list=None, coords_palette=self.selectedpalette, tangentdata=None, balloondata_list=self.proc_balloondata, rdp_reduction=self.checkUseRDP.isChecked(), showmap=bool(self.check2DMapInExternalBrowser.isChecked())) self.map2d.load(QtCore.QUrl(bombo.MAP_2D_FILENAME)) self.map2d.show() # Generate 3D plot, only with one track for the moment if len(self.gpxselectedlist) == 1: if self.check3DMapSelection.isChecked(): tile_selection = 'auto' else: tile_selection = self.text3DMapName.text() terrain, track, warnings = bombo.Generate3DMap( new_coords['lat'], new_coords['lon'], tile_selection=tile_selection, margin=self.spinbox3DMargin.value(), elevation_scale=self.spinbox3DElevationScale.value(), mapping='coords', use_osm_texture=True, texture_type='osm', texture_zoom=self.spinbox3DOSMZoom.value(), texture_invert=self.check3DOSMInvert.isChecked(), use_proxy=self.use_proxy, proxy_data=self.proxy_config, verbose=False) self.textWarningConsole.append(warnings) if terrain is not None: self.map3d.update_plot(terrain, track) else: self.textWarningConsole.setText( "You need to open a .gpx file before!") return def PlotSpecificAreaDialog(self): def PlotSpecificArea(): # Save coordinates for the next time if os.environ['QT_API'] == 'pyqt': self.settings.setValue("last_point_coord_lat", self.spinboxLatDec.value()) self.settings.setValue("last_point_coord_lon", self.spinboxLonDec.value()) elif os.environ['QT_API'] == 'pyqt5': self.settings.setValue( "last_point_coord_lat", QtCore.QVariant(self.spinboxLatDec.value())) self.settings.setValue( "last_point_coord_lon", QtCore.QVariant(self.spinboxLonDec.value())) # Select the 3D Map tab self.tab.setCurrentIndex(2) # Plot if self.check3DMapSelection.isChecked(): tile_selection = 'auto' else: tile_selection = self.text3DMapName.text() terrain, track, warnings = bombo.Generate3DMap( [self.spinboxLatDec.value()], [self.spinboxLonDec.value()], tile_selection=tile_selection, margin=self.spinbox3DMargin.value(), elevation_scale=self.spinbox3DElevationScale.value(), mapping='coords', use_osm_texture=True, texture_type='osm', texture_zoom=self.spinbox3DOSMZoom.value(), texture_invert=self.check3DOSMInvert.isChecked(), use_proxy=self.use_proxy, proxy_data=self.proxy_config, verbose=False) self.textWarningConsole.append(warnings) if terrain is not None: self.map3d.update_plot(terrain, track) d.done(0) def Convert(): try: dd = bombo.parse_dms(self.textLatLonGMS.text()) self.spinboxLatDec.setValue(dd[0]) self.spinboxLonDec.setValue(dd[1]) except: pass d = QDialog() grid = QGridLayout() hBox_coordsGMS = QHBoxLayout() hBox_coordsGMS.setSpacing(5) label = QLabel('Coordinates (gms)') grid.addWidget(label, 0, 0) self.textLatLonGMS = QLineEdit() self.textLatLonGMS.setText("") grid.addWidget(self.textLatLonGMS, 0, 1, 1, 2) button1 = QPushButton("Convert to decimal") button1.clicked.connect(Convert) grid.addWidget(button1, 0, 3) label = QLabel('Coordinates (decimal)') grid.addWidget(label, 1, 0) self.spinboxLatDec = QDoubleSpinBox() self.spinboxLatDec.setRange(-90, +90) self.spinboxLatDec.setSingleStep(0.0000001) self.spinboxLatDec.setDecimals(7) grid.addWidget(self.spinboxLatDec, 1, 1) self.spinboxLonDec = QDoubleSpinBox() self.spinboxLonDec.setRange(-180, +180) self.spinboxLonDec.setSingleStep(0.0000001) self.spinboxLonDec.setDecimals(7) grid.addWidget(self.spinboxLonDec, 1, 2) # Try to recover the last used points try: old_lat = self.settings.value("last_point_coord_lat", type=float) old_lon = self.settings.value("last_point_coord_lon", type=float) self.spinboxLatDec.setValue(old_lat) self.spinboxLonDec.setValue(old_lon) except: # Coordinates of Mt. Rinjani in Indonesia self.spinboxLatDec.setValue(-8.4166000) self.spinboxLonDec.setValue(116.4666000) button2 = QPushButton("Show 3D map") button2.clicked.connect(PlotSpecificArea) grid.addWidget(button2, 1, 3) d.setWindowTitle("Show point on 3D map") d.setLayout(grid) d.setWindowModality(QtCore.Qt.ApplicationModal) d.exec_() def ProxyDialog(self): def SetProxy(): self.use_proxy = bool(self.checkUseProxy.isChecked()) self.proxy_config = self.textProxyConfig.text() if os.environ['QT_API'] == 'pyqt': self.settings.setValue("use_proxy", self.use_proxy) self.settings.setValue("proxy_config", str(self.proxy_config)) elif os.environ['QT_API'] == 'pyqt5': self.settings.setValue("use_proxy", QtCore.QVariant(self.use_proxy)) self.settings.setValue("proxy_config", QtCore.QVariant(str(self.proxy_config))) d.done(0) d = QDialog() box = QVBoxLayout() hBox_proxy = QHBoxLayout() hBox_proxy.setSpacing(5) label = QLabel('Proxy') hBox_proxy.addWidget(label) self.textProxyConfig = QLineEdit() try: self.textProxyConfig.setText( self.settings.value('proxy_config', str)) except: self.textProxyConfig.setText(bombo.PROXY_DATA) self.textProxyConfig.setMinimumWidth(200) hBox_proxy.addWidget(self.textProxyConfig) box.addLayout(hBox_proxy) self.checkUseProxy = QCheckBox("Use proxy") try: self.checkUseProxy.setChecked( self.settings.value('use_proxy', bool)) except: self.checkUseProxy.setChecked(bool(bombo.USE_PROXY)) box.addWidget(self.checkUseProxy) button = QPushButton("Save configuration") button.clicked.connect(SetProxy) box.addWidget(button) d.setWindowTitle("Proxy configuration") d.setLayout(box) d.setWindowModality(QtCore.Qt.ApplicationModal) d.exec_() def __init__(self, parent=None): super(MainWindow, self).__init__() self.initVariables() self.initUI() def initVariables(self): self.gpxlist = list() self.gpxnamelist = list() self.gpxselectedlist = list() self.gpxselectednamelist = list() self.palette = bombo.GeneratePalette(N=10) * 5 # replicated 5 times #self.palette = ["#0000FF", "#00FF00", "#00FFFF", "#FF0000", "#FF00FF", "#FFFF00", "#FFFFFF"] # test palette self.selectedpalette = list() self.proc_coords = list() self.proc_measurements = list() self.proc_state_means = list() self.proc_state_vars = list() self.proc_new_coords = list() self.proc_new_gpx = list() self.proc_coords_to_plot = list() self.proc_coords_to_plot2 = list() self.proc_balloondata = list() def initUI(self): def selection_changed(): # Retrieve selected items # selecteditems = self.tracklist.selectedItems() selectedindexes = self.tracklist.selectedIndexes() # Adding the selected items to the processing list self.gpxselectedlist[:] = [] self.gpxselectednamelist[:] = [] self.selectedpalette[:] = [] for i in selectedindexes: # print str(i.text()) self.gpxselectedlist.append(self.gpxlist[i.row()]) self.gpxselectednamelist.append(self.gpxnamelist[i.row()]) self.selectedpalette.append(self.palette[i.row()]) def ClearStats(): """ # Some other code that could be used in the future index = self.treemodel.indexFromItem(parent1) self.tree.expand(index) selmod = self.tree.selectionModel() index2 = self.treemodel.indexFromItem(child2) selmod.select(index2, QtCore.QItemSelectionModel.Select|QtCore.QItemSelectionModel.Rows) root = self.treemodel.invisibleRootItem() (item.parent() or root).removeChild(item) """ # Returns a list of indexes. In our case, for each row there are 2 indexes, cos there are 2 columns. for index in self.tree.selectedIndexes(): # Consider only the first columns if index.column() == 0: # Need to check if it's a top item (i.e. track), otherwise if a subitem (i.e. distance or time) is selected, the result might be buggy parent = index.parent() parent_item = self.treemodel.itemFromIndex(parent) if parent_item is None: self.treemodel.removeRow(index.row()) # Application Settings QtCore.QCoreApplication.setOrganizationName("Ste") QtCore.QCoreApplication.setOrganizationDomain( "https://github.com/stesalati/sport/") QtCore.QCoreApplication.setApplicationName("TrackAnalyser") # Config settings self.settings = QtCore.QSettings(self) # Proxy settings try: self.use_proxy = self.settings.value('use_proxy', bool) self.proxy_config = self.settings.value('proxy_config', str) except: self.use_proxy = bombo.USE_PROXY self.proxy_config = bombo.PROXY_DATA # Actions openfile = QAction(QtGui.QIcon("icons/openfile.png"), "Open .gpx", self) openfile.setShortcut("Ctrl+O") openfile.setStatusTip("Open file") openfile.triggered.connect(self.selectFileToOpen) go = QAction(QtGui.QIcon("icons/go.png"), "Go!", self) go.setShortcut("Ctrl+R") go.setStatusTip("Run analysis") go.triggered.connect(self.Go) clearstats = QAction(QtGui.QIcon("icons/clear.png"), "Clear stats", self) clearstats.setShortcut("Ctrl+C") clearstats.setStatusTip("Clear stats") clearstats.triggered.connect(ClearStats) sep1 = QAction(self) sep1.setSeparator(True) showpoint = QAction(QtGui.QIcon("icons/point.png"), "Show point", self) showpoint.setShortcut("Ctrl+P") showpoint.setStatusTip("Show point") showpoint.triggered.connect(self.PlotSpecificAreaDialog) sep2 = QAction(self) sep2.setSeparator(True) quitapp = QAction(QtGui.QIcon("icons/quit.png"), "Quit", self) quitapp.setShortcut("Ctrl+Q") quitapp.setStatusTip("Quit application") quitapp.triggered.connect(qApp.quit) configs = QAction(QtGui.QIcon("icons/configs.png"), "Configs", self) configs.setStatusTip("Configs") configs.triggered.connect(self.ProxyDialog) # Menubar mainMenu = self.menuBar() configMenu = mainMenu.addMenu('&Config') configMenu.addAction(configs) # Toolbar toolbar = self.addToolBar('My tools') toolbar.addAction(openfile) toolbar.addAction(go) toolbar.addAction(clearstats) toolbar.addAction(sep1) toolbar.addAction(showpoint) toolbar.addAction(sep2) toolbar.addAction(quitapp) toolbar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon) toolbar.setIconSize(QtCore.QSize(30, 30)) # Status bar self.statusBar().show() # Main widget (everything that's not toolbar, statusbar or menubar must be in this widget) self.scatola = QWidget() # Main horizontal impagination hBox = QHBoxLayout() hBox.setSpacing(5) # Vertical left column vBox_left = QVBoxLayout() vBox_left.setSpacing(5) # 1st vertical box, a list self.tracklist = QListWidget() vBox_left.addWidget(self.tracklist) self.tracklist.setSelectionMode(QAbstractItemView.ExtendedSelection) self.tracklist.itemSelectionChanged.connect(selection_changed) self.tracklist.setMaximumHeight(120) # 2nd vertical box, containing several horizontal boxes, one for each setting vBox2 = QVBoxLayout() vBox2.setSpacing(5) # Just the group label labelSettings = QLabel('Settings') vBox2.addWidget(labelSettings) # Use/don't use corrected altitude self.checkUseSRTM = QCheckBox( "Use SRTM corrected elevation (needs Internet)") self.checkUseSRTM.setChecked(False) vBox2.addWidget(self.checkUseSRTM) # Choose processing method + use/don't use acceleration hBoxProcessingMethod = QHBoxLayout() labelProcessingMethod = QLabel('Processing method') hBoxProcessingMethod.addWidget(labelProcessingMethod) self.comboBoxProcessingMethod = QComboBox() self.comboBoxProcessingMethod.addItem("Just use available data") self.comboBoxProcessingMethod.addItem( "Fill all gaps at T=1s (resample)") self.comboBoxProcessingMethod.addItem("Fill only smaller gaps at T=1s") hBoxProcessingMethod.addWidget(self.comboBoxProcessingMethod) self.checkUseAcceleration = QCheckBox("Use acceleration") self.checkUseAcceleration.setChecked(False) hBoxProcessingMethod.addWidget(self.checkUseAcceleration) vBox2.addLayout(hBoxProcessingMethod) # Use/don't use variance smooth self.checkExtraSmooth = QCheckBox("Extra smooth") self.checkExtraSmooth.setChecked(False) vBox2.addWidget(self.checkExtraSmooth) # 2D interactive map settings hBox2DMap = QHBoxLayout() self.checkUseRDP = QCheckBox("Use RDP to reduce points") self.checkUseRDP.setChecked(False) hBox2DMap.addWidget(self.checkUseRDP) self.check2DMapInExternalBrowser = QCheckBox( "Show in external browser") self.check2DMapInExternalBrowser.setChecked(False) hBox2DMap.addWidget(self.check2DMapInExternalBrowser) vBox2.addLayout(hBox2DMap) # Settings for the 3D map line3DViewSettings = QFrame() #line3DViewSettings.setGeometry(QtCore.QRect(320, 150, 118, 3)) line3DViewSettings.setFrameShape(QFrame.HLine) line3DViewSettings.setFrameShadow(QFrame.Sunken) vBox2.addWidget(line3DViewSettings) label3DViewSettings = QLabel('3D view settings') vBox2.addWidget(label3DViewSettings) hBox3DMapSelection = QHBoxLayout() self.check3DMapSelection = QCheckBox( "Select elevation tiles automatically, otherwise") self.check3DMapSelection.setChecked(True) hBox3DMapSelection.addWidget(self.check3DMapSelection) self.text3DMapName = QLineEdit() self.text3DMapName.setText("Iceland.tif") hBox3DMapSelection.addWidget(self.text3DMapName) vBox2.addLayout(hBox3DMapSelection) hBox3D = QHBoxLayout() label3DMargin = QLabel('Margin') hBox3D.addWidget(label3DMargin) self.spinbox3DMargin = QSpinBox() self.spinbox3DMargin.setRange(50, 1000) self.spinbox3DMargin.setValue(100) self.spinbox3DMargin.setSingleStep(10) hBox3D.addWidget(self.spinbox3DMargin) labelSpace = QLabel(' ') hBox3D.addWidget(labelSpace) label3DElevationScale = QLabel('Elev. scale') hBox3D.addWidget(label3DElevationScale) self.spinbox3DElevationScale = QDoubleSpinBox() self.spinbox3DElevationScale.setRange(1, 50) self.spinbox3DElevationScale.setSingleStep(0.1) hBox3D.addWidget(self.spinbox3DElevationScale) hBox3D.addWidget(labelSpace) label3DOSMZoom = QLabel('Zoom') hBox3D.addWidget(label3DOSMZoom) self.spinbox3DOSMZoom = QSpinBox() self.spinbox3DOSMZoom.setRange(8, 15) self.spinbox3DOSMZoom.setValue(13) self.spinbox3DOSMZoom.setSingleStep(1) hBox3D.addWidget(self.spinbox3DOSMZoom) hBox3D.addWidget(labelSpace) self.check3DOSMInvert = QCheckBox("Invert") self.check3DOSMInvert.setChecked(False) hBox3D.addWidget(self.check3DOSMInvert) vBox2.addLayout(hBox3D) vBox_left.addLayout(vBox2) # 3rd stats tree lineTree = QFrame() lineTree.setFrameShape(QFrame.HLine) lineTree.setFrameShadow(QFrame.Sunken) vBox2.addWidget(lineTree) labelTree = QLabel('Track stats') vBox2.addWidget(labelTree) self.tree = QTreeView() self.tree.setSelectionBehavior(QAbstractItemView.SelectRows) self.treemodel = QtGui.QStandardItemModel() self.treemodel.setHorizontalHeaderLabels(['Name', 'Value']) self.tree.setModel(self.treemodel) self.tree.setUniformRowHeights(True) self.tree.setColumnWidth(0, 200) vBox_left.addWidget(self.tree) # 4th text, containing text messages/errors self.textWarningConsole = QTextEdit() self.textWarningConsole.setReadOnly(True) self.textWarningConsole.setFont(QtGui.QFont("Courier New", FONTSIZE)) self.textWarningConsole.clear() self.textWarningConsole.setMaximumHeight(50) vBox_left.addWidget(self.textWarningConsole) # I put "vBox_left" inside a widget and then the widget inside "hBox" # instead of just doing "hBox.addLayout(vBox_left) so I can set its # maximum width. vBox_left_widget = QWidget() vBox_left_widget.setLayout(vBox_left) vBox_left_widget.setMinimumWidth(400) vBox_left_widget.setMaximumWidth(500) hBox.addWidget(vBox_left_widget) # Vertical right column self.tab = QTabWidget() # Tab 1: Summary: elevation and speed tab1 = QWidget() # The tab layout vBox_tab = QVBoxLayout() vBox_tab.setSpacing(5) # Plot area self.plotEmbeddedElevationAndSpeed = EmbeddedPlot_ElevationSpeed( width=5, height=4, dpi=100) self.plotEmbeddedElevationAndSpeed.setMinimumWidth(800) # Add toolbar to the plot self.mpl_toolbar1 = NavigationToolbar( self.plotEmbeddedElevationAndSpeed, self.scatola) # Add widgets to the layout vBox_tab.addWidget(self.plotEmbeddedElevationAndSpeed) vBox_tab.addWidget(self.mpl_toolbar1) # Associate the layout to the tab tab1.setLayout(vBox_tab) # Tab 2: html 2D map tab2 = QWidget() # The tab layout vBox_tab = QVBoxLayout() vBox_tab.setSpacing(5) # Area self.map2d = QtWebEngineWidgets.QWebEngineView() # Add widgets to the layout vBox_tab.addWidget(self.map2d) # Associate the layout to the tab tab2.setLayout(vBox_tab) # Tab 3: 3D plot tab3 = QWidget() # The tab layout vBox_tab = QVBoxLayout() vBox_tab.setSpacing(5) # Area self.map3d = MayaviQWidget() # Add widgets to the layout vBox_tab.addWidget(self.map3d) # Associate the layout to the tab tab3.setLayout(vBox_tab) # Tab 4: Details tab4 = QWidget() # The tab layout vBox_tab = QVBoxLayout() vBox_tab.setSpacing(5) # Plot area self.plotEmbeddedDetails = EmbeddedPlot_Details(width=5, height=4, dpi=100) self.plotEmbeddedDetails.setMinimumWidth(800) # Add toolbar to the plot self.mpl_toolbar2 = NavigationToolbar(self.plotEmbeddedDetails, self.scatola) # Add widgets to the layout vBox_tab.addWidget(self.plotEmbeddedDetails) vBox_tab.addWidget(self.mpl_toolbar2) # Associate the layout to the tab tab4.setLayout(vBox_tab) # Associate tabs self.tab.addTab(tab1, "Summary") self.tab.addTab(tab2, "2D Map") self.tab.addTab(tab3, "3D Map") self.tab.addTab(tab4, "Details") hBox.addWidget(self.tab) # Setting hBox as main box self.scatola.setLayout(hBox) self.setCentralWidget(self.scatola) # Application settings self.setWindowTitle('TrackAnalyser') self.setWindowIcon((QtGui.QIcon('icons/app.png'))) self.setGeometry(100, 100, 1200, 700) self.show()
class MergeDialog(QDialog): # name of src tab, names of dst tabs, whether to keep connections alive or not merge_tabs_signal = Signal(str, list, bool) def __init__(self, parent, loggers): super().__init__(parent) self.loggers = loggers self.merge_list = [] # all tabs to be merged self.merge_dst = None # tab to merge the rest of merge_list into self.setupUi() def setupUi(self): self.resize(340, 320) self.gridLayout = QGridLayout(self) self.dstComboBox = QComboBox(self) self.gridLayout.addWidget(self.dstComboBox, 1, 2, 1, 2) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Cancel | QDialogButtonBox.Ok, self) self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 4) self.loggerList = QListWidget(self) self.loggerList.setDefaultDropAction(Qt.IgnoreAction) self.loggerList.setSelectionMode(QAbstractItemView.MultiSelection) self.gridLayout.addWidget(self.loggerList, 1, 0, 4, 2) self.keepAliveCheckBox = QCheckBox("Keep connections alive", self) self.keepAliveCheckBox.setChecked(True) self.gridLayout.addWidget(self.keepAliveCheckBox, 2, 2, 1, 2) self.srcsLabel = QLabel("All loggers:", self) self.gridLayout.addWidget(self.srcsLabel, 0, 0, 1, 2) self.dstLabel = QLabel("Merge all into:", self) self.gridLayout.addWidget(self.dstLabel, 0, 2, 1, 2) spacerItem = QSpacerItem(20, 169, QSizePolicy.Minimum, QSizePolicy.Expanding) self.gridLayout.addItem(spacerItem, 4, 2, 1, 2) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.loggerList.selectionModel().selectionChanged.connect(self.merge_list_changed) self.dstComboBox.currentTextChanged.connect(self.merge_dst_changed) self.ok_button = self.buttonBox.button(QDialogButtonBox.Ok) self.ok_button.setEnabled(False) self.keepAliveCheckBox.setToolTip("If disabled then only the destination connection " "will still be alive after merging.") self.fill_logger_list() def fill_logger_list(self): for logger_name in self.loggers.keys(): LoggerListItem(self.loggerList, logger_name) def merge_list_changed(self, sel, desel): sel = sel.indexes() desel = desel.indexes() for index in sel: sel_item = self.loggerList.itemFromIndex(index) self.merge_list.append(sel_item) self.dstComboBox.addItem(sel_item.name) self.ok_button.setEnabled(True) for index in desel: desel_item = self.loggerList.itemFromIndex(index) self.merge_list.remove(desel_item) row = self.dstComboBox.findText(desel_item.name) self.dstComboBox.removeItem(row) if self.dstComboBox.count() == 0: self.ok_button.setEnabled(False) def merge_dst_changed(self, text): self.merge_dst = text def accept(self): name_list = [item.name for item in self.merge_list] name_list.remove(self.merge_dst) self.merge_tabs_signal.emit(self.merge_dst, name_list, self.keepAliveCheckBox.isChecked()) self.done(0) def reject(self): self.done(0)
class HeaderEditDialog(QDialog): # name of the current preset; whether to set this preset as default; list of Columns header_changed = Signal(str, bool, list) def __init__(self, parent, table_header): super().__init__(parent) self.table_header = table_header self.default_preset_name = None self.preset_name = table_header.preset_name self.columns = deepcopy(table_header.columns) self.setupUi() self.update_output() def setupUi(self): self.resize(240, 400) self.vbox = QVBoxLayout(self) self.presetLabel = QLabel(self) self.columnList = QListWidget(self) self.setAsDefaultCheckbox = QCheckBox("Set as default preset", self) self.vbox.addWidget(self.presetLabel) self.vbox.addWidget(self.columnList) self.vbox.addWidget(self.setAsDefaultCheckbox) self.columnList.setDragDropMode(QListWidget.InternalMove) self.columnList.setDefaultDropAction(Qt.MoveAction) self.columnList.setSelectionMode(QListWidget.ExtendedSelection) self.columnList.setAlternatingRowColors(True) self.columnList.installEventFilter(self) self.columnList.setContextMenuPolicy(Qt.CustomContextMenu) self.columnList.customContextMenuRequested.connect(self.open_menu) self.columnList.model().rowsMoved.connect(self.read_columns_from_list) # for a dumb qss hack to make selected checkboxes not white on a light theme self.columnList.setObjectName("ColumnList") buttons = QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Cancel self.buttonBox = QDialogButtonBox(buttons, self) self.vbox.addWidget(self.buttonBox) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.resetButton = self.buttonBox.button(QDialogButtonBox.Reset) self.resetButton.clicked.connect(self.reset_to_stock) def eventFilter(self, object, event): if event.type() == QEvent.KeyPress: if event.key() == Qt.Key_Space or event.key() == Qt.Key_Return: self.toggle_selected_columns() return True if event.key() == Qt.Key_Delete: self.delete_selected() return True return False def update_output(self): self.presetLabel.setText("Preset: {}".format(self.preset_name)) self.setAsDefaultCheckbox.setChecked( CONFIG['default_header_preset'] == self.preset_name) self.columnList.clear() for column in self.columns: ColumnListItem(self.columnList, column) def accept(self): self.read_columns_from_list() self.header_changed.emit(self.preset_name, self.setAsDefaultCheckbox.isChecked(), self.columns) self.done(0) def reject(self): self.done(0) def reset_to_stock(self): self.columns = deepcopy(DEFAULT_COLUMNS) self.update_output() def read_columns_from_list(self): new_columns = [] for i in range(self.columnList.count()): item = self.columnList.item(i) new_columns.append(item.column) self.columns = new_columns def toggle_selected_columns(self): selected = self.columnList.selectedItems() for item in selected: value_now = item.data(Qt.CheckStateRole) item.setData(Qt.CheckStateRole, not value_now) self.columnList.reset( ) # @Improvement: is there a better way to update QListWidget? def open_menu(self, position): menu = QMenu(self) preset_menu = menu.addMenu('Presets') preset_menu.addAction('New preset', self.new_preset_dialog) preset_menu.addSeparator() preset_names = CONFIG.get_header_presets() if len(preset_names) == 0: action = preset_menu.addAction('No presets') action.setEnabled(False) else: delete_menu = menu.addMenu('Delete preset') for name in preset_names: preset_menu.addAction(name, partial(self.load_preset, name)) delete_menu.addAction(name, partial(self.delete_preset, name)) menu.addSeparator() menu.addAction('New column...', self.create_new_column_dialog) if len(self.columnList.selectedIndexes()) > 0: menu.addAction('Delete selected', self.delete_selected) menu.popup(self.columnList.viewport().mapToGlobal(position)) def load_preset(self, name): new_columns = CONFIG.load_header_preset(name) if not new_columns: return self.columns = new_columns self.preset_name = name self.update_output() def new_preset_dialog(self): d = QInputDialog(self) d.setLabelText('Enter the new name for the new preset:') d.setWindowTitle('Create new preset') d.textValueSelected.connect(self.create_new_preset) d.open() def create_new_preset(self, name): if name in CONFIG.get_header_presets(): show_warning_dialog( self, "Preset creation error", 'Preset named "{}" already exists.'.format(name)) return if len(name.strip()) == 0: show_warning_dialog( self, "Preset creation error", 'This preset name is not allowed.'.format(name)) return self.preset_name = name self.update_output() CONFIG.save_header_preset(name, self.columns) def delete_preset(self, name): CONFIG.delete_header_preset(name) if name == self.preset_name: self.columns = deepcopy(DEFAULT_COLUMNS) self.update_output() def create_new_column_dialog(self): d = CreateNewColumnDialog(self) d.add_new_column.connect(self.add_new_column) d.setWindowTitle('Create new column') d.open() def add_new_column(self, name, title): new_column = Column(name, title) # if the last column is message, insert this column before it (I think that makes sense?) if len(self.columns) == 0: self.columns.append(new_column) elif self.columns[-1].name in ('message', 'msg'): self.columns.insert(-1, new_column) else: self.columns.append(new_column) self.update_output() def delete_selected(self): selected = self.columnList.selectedItems() for item in selected: self.columnList.takeItem(self.columnList.row(item)) self.read_columns_from_list() self.update_output()
class CycleWindow(SiriusMainWindow): """Power supplies cycle window.""" def __init__(self, parent=None, checked_accs=(), adv_mode=False): """Constructor.""" super().__init__(parent) self.setObjectName('ASApp') cor = get_appropriate_color(section='AS') self.setWindowIcon(qta.icon('mdi.recycle', color=cor)) self._is_adv_mode = adv_mode # Data structs self._psnames = get_psnames(isadv=self._is_adv_mode) self._timing = Timing() self._ps2cycle = list() self._ps_ready = list() self._ps_failed = list() self._checked_accs = checked_accs # Flags self._is_preparing = '' self._prepared_init_vals = { 'timing': False, 'ps_sofbmode': False, 'ps_om_slowref': False, 'ps_current': False, 'ps_params': False, 'ps_om_cycle': False, 'trims': True } self._prepared = self._prepared_init_vals.copy() self._icon_check = qta.icon('fa5s.check') self._pixmap_check = self._icon_check.pixmap( self._icon_check.actualSize(QSize(16, 16))) self._icon_not = qta.icon('fa5s.times') self._pixmap_not = self._icon_not.pixmap( self._icon_not.actualSize(QSize(16, 16))) # Tasks self._step_2_task = { 'save_timing': SaveTiming, 'timing': PrepareTiming, 'ps_sofbmode': PreparePSSOFBMode, 'ps_om_slowref': PreparePSOpModeSlowRef, 'ps_current': PreparePSCurrentZero, 'ps_params': PreparePSParams, 'ps_om_cycle': PreparePSOpModeCycle, 'trims': CycleTrims, 'cycle': Cycle, 'restore_timing': RestoreTiming, } # Setup UI self._needs_update_setup = False self._setup_ui() self._update_setup_timer = QTimer(self) self._update_setup_timer.timeout.connect(self._update_setup) self._update_setup_timer.setInterval(250) self._update_setup_timer.start() self.setWindowTitle('PS Cycle') def _setup_ui(self): # central widget self.central_widget = QWidget() self.setCentralWidget(self.central_widget) # tree gb_tree = QGroupBox('Select power supplies:') self.pwrsupplies_tree = PVNameTree(self._psnames, ('sec', 'mag_group'), tuple(), self) self.pwrsupplies_tree.tree.setHeaderHidden(True) self.pwrsupplies_tree.tree.setColumnCount(1) glay_tree = QVBoxLayout(gb_tree) glay_tree.addWidget(self.pwrsupplies_tree) # commands lb_prep_ti = QLabel('<h4>Prepare Timing</h4>', self, alignment=Qt.AlignCenter) ti_ch = [ PVName(name).substitute(prefix=VACA_PREFIX) for name in self._timing.get_pvnames_by_psnames() ] self.ticonn_led = PyDMLedMultiConn(self, channels=ti_ch) self.save_timing_bt = QPushButton('1. Save Timing Initial State', self) self.save_timing_bt.setToolTip( 'Save timing current state as initial state.') self.save_timing_bt.clicked.connect( _part(self._run_task, 'save_timing')) self.save_timing_bt.clicked.connect(self._set_lastcomm) self.prepare_timing_bt = QPushButton('2. Prepare Timing', self) self.prepare_timing_bt.setToolTip('Prepare EVG, triggers and events') self.prepare_timing_bt.clicked.connect(_part(self._run_task, 'timing')) self.prepare_timing_bt.clicked.connect(self._set_lastcomm) self.prepare_timing_lb = QLabel(self) self.prepare_timing_lb.setPixmap(self._pixmap_not) lb_prep_ps = QLabel('<h4>Prepare PS</h4>', self, alignment=Qt.AlignCenter) self.psconn_led = PyDMLedMultiConn(self) self.set_ps_sofbmode_off_bt = QPushButton('3. Turn off PS SOFBMode', self) self.set_ps_sofbmode_off_bt.setToolTip( 'Turn off power supplies SOFBMode.') self.set_ps_sofbmode_off_bt.clicked.connect( _part(self._run_task, 'ps_sofbmode')) self.set_ps_sofbmode_off_bt.clicked.connect(self._set_lastcomm) self.set_ps_sofbmode_off_lb = QLabel(self) self.set_ps_sofbmode_off_lb.setPixmap(self._pixmap_not) self.set_ps_opmode_slowref_bt = QPushButton( '4. Set PS OpMode to SlowRef', self) self.set_ps_opmode_slowref_bt.setToolTip( 'Set power supplies OpMode to SlowRef.') self.set_ps_opmode_slowref_bt.clicked.connect( _part(self._run_task, 'ps_om_slowref')) self.set_ps_opmode_slowref_bt.clicked.connect(self._set_lastcomm) self.set_ps_opmode_slowref_lb = QLabel(self) self.set_ps_opmode_slowref_lb.setPixmap(self._pixmap_not) self.set_ps_current_zero_bt = QPushButton('5. Set PS current to zero', self) self.set_ps_current_zero_bt.setToolTip( 'Set power supplies current to zero.') self.set_ps_current_zero_bt.clicked.connect( _part(self._run_task, 'ps_current')) self.set_ps_current_zero_bt.clicked.connect(self._set_lastcomm) self.set_ps_current_zero_lb = QLabel(self) self.set_ps_current_zero_lb.setPixmap(self._pixmap_not) self.prepare_ps_params_bt = QPushButton('6. Prepare PS Parameters', self) self.prepare_ps_params_bt.setToolTip( 'Check power supplies OpMode in SlowRef, check\n' 'current is zero and configure cycle parameters.') self.prepare_ps_params_bt.clicked.connect( _part(self._run_task, 'ps_params')) self.prepare_ps_params_bt.clicked.connect(self._set_lastcomm) self.prepare_ps_params_lb = QLabel(self) self.prepare_ps_params_lb.setPixmap(self._pixmap_not) self.prepare_ps_opmode_bt = QPushButton('7. Prepare PS OpMode', self) self.prepare_ps_opmode_bt.setToolTip( 'Set power supplies OpMode to Cycle.') self.prepare_ps_opmode_bt.clicked.connect( _part(self._run_task, 'ps_om_cycle')) self.prepare_ps_opmode_bt.clicked.connect(self._set_lastcomm) self.prepare_ps_opmode_lb = QLabel(self) self.prepare_ps_opmode_lb.setPixmap(self._pixmap_not) lb_cycle = QLabel('<h4>Cycle</h4>', self, alignment=Qt.AlignCenter) self.cycle_trims_bt = QPushButton('8. Cycle Trims', self) self.cycle_trims_bt.setToolTip( 'Cycle trims:\nStep 1) CH, QS and QTrims\nStep 2) CV') self.cycle_trims_bt.clicked.connect(_part(self._run_task, 'trims')) self.cycle_trims_bt.clicked.connect(self._set_lastcomm) self.cycle_trims_bt.setVisible(False) self.cycle_trims_lb = QLabel(self) self.cycle_trims_lb.setPixmap(self._pixmap_check) self.cycle_trims_lb.setVisible(False) self.cycle_bt = QPushButton('8. Cycle', self) self.cycle_bt.setToolTip( 'Check all configurations,\nenable triggers and run cycle.') self.cycle_bt.clicked.connect(_part(self._run_task, 'cycle')) self.cycle_bt.clicked.connect(self._set_lastcomm) self.cycle_bt.setEnabled(False) lb_rest_ti = QLabel('<h4>Restore Timing</h4>', self, alignment=Qt.AlignCenter) self.restore_timing_bt = QPushButton('9. Restore Timing Initial State', self) self.restore_timing_bt.setToolTip('Restore timing initial state.') self.restore_timing_bt.clicked.connect( _part(self._run_task, 'restore_timing')) self.restore_timing_bt.clicked.connect(self._set_lastcomm) self._prepared_labels = { 'timing': self.prepare_timing_lb, 'ps_sofbmode': self.set_ps_sofbmode_off_lb, 'ps_om_slowref': self.set_ps_opmode_slowref_lb, 'ps_current': self.set_ps_current_zero_lb, 'ps_params': self.prepare_ps_params_lb, 'ps_om_cycle': self.prepare_ps_opmode_lb, 'trims': self.cycle_trims_lb } gb_commsts = QGroupBox() gb_commsts.setStyleSheet(""" QPushButton{min-height:1.5em;} QLabel{qproperty-alignment: AlignCenter;}""") lay_commsts = QGridLayout(gb_commsts) lay_commsts.addItem( QSpacerItem(1, 1, QSzPlcy.Ignored, QSzPlcy.Expanding), 0, 0, 1, 2) lay_commsts.addWidget(lb_prep_ti, 1, 0) lay_commsts.addWidget(self.ticonn_led, 1, 1) lay_commsts.addWidget(self.save_timing_bt, 2, 0) lay_commsts.addWidget(self.prepare_timing_bt, 3, 0) lay_commsts.addWidget(self.prepare_timing_lb, 3, 1) lay_commsts.addItem( QSpacerItem(1, 1, QSzPlcy.Ignored, QSzPlcy.Expanding), 4, 0) lay_commsts.addWidget(lb_prep_ps, 5, 0) lay_commsts.addWidget(self.psconn_led, 5, 1) lay_commsts.addWidget(self.set_ps_sofbmode_off_bt, 6, 0) lay_commsts.addWidget(self.set_ps_sofbmode_off_lb, 6, 1) lay_commsts.addWidget(self.set_ps_opmode_slowref_bt, 7, 0) lay_commsts.addWidget(self.set_ps_opmode_slowref_lb, 7, 1) lay_commsts.addWidget(self.set_ps_current_zero_bt, 8, 0) lay_commsts.addWidget(self.set_ps_current_zero_lb, 8, 1) lay_commsts.addWidget(self.prepare_ps_params_bt, 9, 0) lay_commsts.addWidget(self.prepare_ps_params_lb, 9, 1) lay_commsts.addWidget(self.prepare_ps_opmode_bt, 10, 0) lay_commsts.addWidget(self.prepare_ps_opmode_lb, 10, 1) lay_commsts.addItem( QSpacerItem(1, 1, QSzPlcy.Ignored, QSzPlcy.Expanding), 11, 0) lay_commsts.addWidget(lb_cycle, 12, 0) lay_commsts.addWidget(self.cycle_trims_bt, 13, 0) lay_commsts.addWidget(self.cycle_trims_lb, 13, 1) lay_commsts.addWidget(self.cycle_bt, 14, 0) lay_commsts.addItem( QSpacerItem(1, 1, QSzPlcy.Ignored, QSzPlcy.Expanding), 15, 0) lay_commsts.addWidget(lb_rest_ti, 16, 0) lay_commsts.addWidget(self.restore_timing_bt, 17, 0) lay_commsts.addItem( QSpacerItem(1, 1, QSzPlcy.Ignored, QSzPlcy.Expanding), 18, 0) lay_commsts.setColumnStretch(0, 10) lay_commsts.setColumnStretch(1, 1) lay_commsts.setVerticalSpacing(12) lay_commsts.setHorizontalSpacing(6) self.label_lastcomm = QLabel('Last Command: ', self) self.clearhist_bt = QPushButton('Clear', self) self.clearhist_bt.clicked.connect(self._clear_lastcomm) lay_lc = QHBoxLayout() lay_lc.setContentsMargins(0, 0, 0, 0) lay_lc.addWidget(self.label_lastcomm, alignment=Qt.AlignLeft) lay_lc.addWidget(self.clearhist_bt, alignment=Qt.AlignRight) lay_lc.setStretch(0, 10) lay_lc.setStretch(1, 1) self.progress_list = QListWidget(self) self.progress_list.setObjectName('progresslist') self.progress_list.setStyleSheet('#progresslist{min-width:20em;}') self.progress_list.itemDoubleClicked.connect(self._open_ps_detail) self.progress_list.setSelectionMode(QAbstractItemView.MultiSelection) self.progress_list.setToolTip( 'Select rows and press Ctrl+C to copy and Esc to deselect.') self.progress_bar = MyProgressBar(self) lay_log = QVBoxLayout() lay_log.addLayout(lay_lc) lay_log.addWidget(self.progress_list) lay_log.addWidget(self.progress_bar) # connect tree signals self.pwrsupplies_tree.tree.doubleClicked.connect(self._open_ps_detail) self.pwrsupplies_tree.tree.itemChanged.connect( self._handle_checked_items_changed) self.pwrsupplies_tree.check_requested_levels(self._checked_accs) # layout layout = QGridLayout() layout.setVerticalSpacing(10) layout.setHorizontalSpacing(10) layout.addWidget( QLabel('<h3>PS Cycle</h3>', self, alignment=Qt.AlignCenter), 0, 0, 1, 3) layout.addWidget(gb_tree, 1, 0) layout.addWidget(gb_commsts, 1, 1) layout.addLayout(lay_log, 1, 2) layout.setRowStretch(0, 1) layout.setRowStretch(1, 15) layout.setColumnStretch(0, 5) layout.setColumnStretch(1, 4) layout.setColumnStretch(2, 8) self.central_widget.setLayout(layout) # --- handle tasks --- def _run_task(self, control=''): if not self._check_connected(control): return pwrsupplies = self._get_ps_list() if not pwrsupplies: return if 'ps' in control and not self._verify_ps(pwrsupplies): return if control in self._step_2_task: task_class = self._step_2_task[control] else: raise NotImplementedError( "Task not defined for control '{}'".format(control)) self._is_preparing = control self._handle_buttons_enabled(False) self.progress_list.clear() task = task_class(parent=self, psnames=pwrsupplies, timing=self._timing, isadv=self._is_adv_mode) task.updated.connect(self._update_progress) duration = task.duration() self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(duration) self.progress_bar.setValue(0) pal = self.progress_bar.palette() pal.setColor(QPalette.Highlight, self.progress_bar.default_color) self.progress_bar.setPalette(pal) self.update_bar = UpdateProgressBar(duration, self) self.update_bar.increment.connect(self.progress_bar.increment) task.start() self.update_bar.start() def _update_progress(self, text, done, warning=False, error=False): """Update automated cycle progress list and bar.""" if done: last_item = self.progress_list.item(self.progress_list.count() - 1) curr_text = last_item.text() last_item.setText(curr_text + ' done.') elif 'Remaining time' in text: last_item = self.progress_list.item(self.progress_list.count() - 1) if 'Remaining time' in last_item.text(): last_item.setText(text) else: self.progress_list.addItem(text) self.progress_list.scrollToBottom() elif 'Sent ' in text: last_item = self.progress_list.item(self.progress_list.count() - 1) if 'Sent ' in last_item.text(): last_item.setText(text) else: self.progress_list.addItem(text) self.progress_list.scrollToBottom() elif 'Successfully checked ' in text: last_item = self.progress_list.item(self.progress_list.count() - 1) if 'Successfully checked ' in last_item.text(): last_item.setText(text) else: self.progress_list.addItem(text) self.progress_list.scrollToBottom() elif 'Created connections ' in text: last_item = self.progress_list.item(self.progress_list.count() - 1) if 'Created connections ' in last_item.text(): last_item.setText(text) else: self.progress_list.addItem(text) self.progress_list.scrollToBottom() else: item = QListWidgetItem(text) if error: item.setForeground(errorcolor) self.update_bar.exit_task() pal = self.progress_bar.palette() pal.setColor(QPalette.Highlight, self.progress_bar.warning_color) self.progress_bar.setPalette(pal) if self._is_preparing in self._prepared.keys(): self._prepared[self._is_preparing] = False cycle = all(self._prepared.values()) self._handle_buttons_enabled(True, cycle=cycle) elif warning: item.setForeground(warncolor) elif 'finished' in text: self.update_bar.exit_task() self.progress_bar.setValue(self.progress_bar.maximum()) if self._is_preparing == 'cycle': self._prepared = {k: False for k in self._prepared.keys()} if not self.cycle_trims_bt.isVisible(): self._prepared['trims'] = True cycle = False else: if self._is_preparing in self._prepared.keys(): self._prepared[self._is_preparing] = True cycle = all(self._prepared.values()) self._handle_buttons_enabled(True, cycle=cycle) self._handle_stslabels_content() self.progress_list.addItem(item) self.progress_list.scrollToBottom() def _handle_buttons_enabled(self, enable, cycle=False): self.save_timing_bt.setEnabled(enable) self.prepare_timing_bt.setEnabled(enable) self.set_ps_sofbmode_off_bt.setEnabled(enable) self.set_ps_opmode_slowref_bt.setEnabled(enable) self.set_ps_current_zero_bt.setEnabled(enable) self.prepare_ps_params_bt.setEnabled(enable) self.prepare_ps_opmode_bt.setEnabled(enable) self.cycle_trims_bt.setEnabled(enable) self.cycle_bt.setEnabled(cycle) self.restore_timing_bt.setEnabled(enable) self.clearhist_bt.setEnabled(enable) self.pwrsupplies_tree.setEnabled(enable) def _handle_stslabels_content(self): for prep, value in self._prepared.items(): pixmap = self._pixmap_check if value else self._pixmap_not self._prepared_labels[prep].setPixmap(pixmap) def _set_lastcomm(self): sender_text = self.sender().text() self.label_lastcomm.setText('Last Command: ' + sender_text) def _clear_lastcomm(self): self.progress_bar.setValue(0) self.progress_list.clear() self.label_lastcomm.setText('Last Command: ') # --- handle ps selection --- def _get_ps_list(self): """Return list of power supplies to cycle.""" # Get power supplies list pwrsupplies = self.pwrsupplies_tree.checked_items() if not pwrsupplies: QMessageBox.critical(self, 'Message', 'No power supply selected!') return False sections = get_sections(pwrsupplies) if 'BO' in sections and len(sections) > 1: QMessageBox.critical(self, 'Error', 'Can not cycle Booster with other sectors!') return False create_task = CreateCyclers(parent=self, psnames=pwrsupplies) dlg = ProgressDialog('Creating cycles...', create_task, self) ret = dlg.exec_() if ret == dlg.Rejected: return False return pwrsupplies def _handle_checked_items_changed(self, item): psname = PVName(item.data(0, Qt.DisplayRole)) if not _re.match('.*-.*:.*-.*', psname): return if not self._is_adv_mode and psname.sec == 'SI' and \ not psname.dev.startswith('FC'): psname2check = Filter.process_filters(self._psnames, filters={ 'sec': 'SI', 'dev': '(?!FC)' }) psname2check.remove(psname) state2set = item.checkState(0) self.pwrsupplies_tree.tree.blockSignals(True) for psn in psname2check: item2check = self.pwrsupplies_tree._item_map[psn] if item2check.checkState(0) != state2set: item2check.setData(0, Qt.CheckStateRole, state2set) self.pwrsupplies_tree.tree.blockSignals(False) else: if (psname.sec in ['BO', 'SI'] and psname.dev in ['B', 'B1B2']): psname2check = PSSearch.get_psnames({ 'sec': psname.sec, 'dev': 'B.*' }) psname2check.remove(psname) item2check = self.pwrsupplies_tree._item_map[psname2check[0]] state2set = item.checkState(0) state2change = item2check.checkState(0) if state2change != state2set: self.pwrsupplies_tree.tree.blockSignals(True) item2check.setData(0, Qt.CheckStateRole, state2set) self.pwrsupplies_tree.tree.blockSignals(False) self._prepared.update(self._prepared_init_vals) self._needs_update_setup = True def _update_setup(self): if not self._needs_update_setup: return self._needs_update_setup = False # update leds psnames = self.pwrsupplies_tree.checked_items() ti_ch = [ PVName(name).substitute(prefix=VACA_PREFIX) for name in self._timing.get_pvnames_by_psnames(psnames) ] self.ticonn_led.set_channels(ti_ch) ps_ch = list() for name in psnames: ps_ch.append( PVName(name).substitute(prefix=VACA_PREFIX, propty='PwrState-Sts')) self.psconn_led.set_channels(ps_ch) # update buttons and self._prepared dict if not in advanced mode if not self._is_adv_mode: has_si = False for psn in PSSearch.get_psnames({'sec': 'SI', 'dis': 'PS'}): if psn not in self.pwrsupplies_tree._item_map: continue item = self.pwrsupplies_tree._item_map[psn] has_si |= item.checkState(0) != 0 if not has_si: self.cycle_bt.setText('8. Cycle') self.restore_timing_bt.setText( '9. Restore Timing Initial State') self.cycle_trims_bt.setVisible(False) self.cycle_trims_lb.setVisible(False) self._prepared['trims'] = True else: self.cycle_bt.setText('9. Cycle') self.restore_timing_bt.setText( '10. Restore Timing Initial State') self.cycle_trims_bt.setVisible(True) self.cycle_trims_lb.setVisible(True) self._prepared['trims'] = False self._handle_stslabels_content() self._handle_buttons_enabled(True) # --- auxiliary checks --- def _check_connected(self, control): if control in ['trims', 'cycle']: leds = [self.ticonn_led, self.psconn_led] elif 'timing' in control: leds = [ self.ticonn_led, ] else: leds = [ self.psconn_led, ] for led in leds: pvs_disconnected = set() for ch, v in led.channels2conn.items(): if not v: pvs_disconnected.add(ch) if pvs_disconnected: sttr = '' for item in pvs_disconnected: sttr += item + '\n' QMessageBox.information( self, 'Message', 'The following PVs are not connected:\n' + sttr) return False return True def _verify_ps(self, pwrsupplies): self._ps_failed = set() task = VerifyPS(parent=self, psnames=pwrsupplies) task.itemDone.connect(self._get_ps_not_ready_2_cycle) dlg = ProgressDialog('Verifying power supplies initial state...', task, self) ret = dlg.exec_() if ret == dlg.Rejected: self._handle_buttons_enabled(True) return False if self._ps_failed: text = 'Verify power state and interlocks' \ ' of the following power supplies' dlg = PSStatusDialog(self._ps_failed, text, self) dlg.exec_() self._handle_buttons_enabled(True) return False return True def _get_ps_not_ready_2_cycle(self, psname, status): if not status: self._ps_failed.add(psname) def _open_ps_detail(self, item): if self.sender() == self.progress_list: text_split = item.data(Qt.DisplayRole).split(' ') psname = '' for text in text_split: if _re.match('.*-.*:.*-.*', text): psname = text if not psname: return else: psname = item.data() if not _re.match('.*-.*:.*-.*', psname): if item.model().rowCount(item) == 1: psname = item.child(0, 0).data() else: return run_newprocess(['sirius-hla-as-ps-detail.py', psname]) # --- events --- def keyPressEvent(self, evt): """Implement keyPressEvent.""" if evt.matches(QKeySequence.Copy) and self.progress_list.underMouse(): items = self.progress_list.selectedItems() items = '\n'.join([i.text() for i in items]) QApplication.clipboard().setText(items) if evt.key() == Qt.Key_Escape and self.progress_list.underMouse(): items = self.progress_list.clearSelection() super().keyPressEvent(evt) def closeEvent(self, ev): self._update_setup_timer.stop() super().closeEvent(ev)
class PSTestWindow(SiriusMainWindow): """PS test window.""" def __init__(self, parent=None, adv_mode=False): """Constructor.""" super().__init__(parent) self.setWindowTitle('PS/PU Test') self.setObjectName('ASApp') cor = get_appropriate_color(section='AS') self.setWindowIcon(qta.icon('mdi.test-tube', color=cor)) # auxiliar data for initializing SI Fam PS self._is_adv_mode = adv_mode self._si_fam_psnames = PSSearch.get_psnames(filters={ 'sec': 'SI', 'sub': 'Fam', 'dis': 'PS' }) # auxiliary data for SI fast correctors self._si_fastcorrs = PSSearch.get_psnames(filters={ 'sec': 'SI', 'dis': 'PS', 'dev': 'FC.*' }) self._needs_update_setup = False self._setup_ui() self._update_setup_timer = QTimer(self) self._update_setup_timer.timeout.connect(self._update_setup) self._update_setup_timer.setInterval(250) self._update_setup_timer.start() def _setup_ui(self): # setup central widget self.central_widget = QFrame() self.central_widget.setStyleSheet(""" #OkList { background-color: #eafaea; } #NokList { background-color: #ffebe6; } QLabel{ max-height: 1.29em; } QTabWidget::pane { border-left: 2px solid gray; border-bottom: 2px solid gray; border-right: 2px solid gray; }""") self.setCentralWidget(self.central_widget) self.tab = QTabWidget(self) self.tab.setObjectName('ASTab') # # PS self.ps_wid = QWidget(self) lay_ps = QGridLayout(self.ps_wid) lay_ps.setContentsMargins(0, 9, 0, 0) lay_ps.setHorizontalSpacing(0) # PS selection self.ps_tree = PVNameTree(items=self._get_ps_tree_names(), tree_levels=('sec', 'mag_group'), parent=self) self.ps_tree.tree.setHeaderHidden(True) self.ps_tree.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.ps_tree.tree.setColumnCount(1) self.ps_tree.tree.doubleClicked.connect(self._open_detail) self.ps_tree.tree.itemChanged.connect( self._handle_checked_items_changed) gbox_ps_select = QGroupBox('Select PS: ', self) gbox_ps_select.setObjectName('select') gbox_ps_select.setStyleSheet(""" #select{ border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; }""") lay_ps_select = QVBoxLayout(gbox_ps_select) lay_ps_select.addWidget(self.ps_tree) lay_ps.addWidget(gbox_ps_select, 0, 0) lay_ps.setColumnStretch(0, 1) # PS commands self.checkcomm_ps_bt = QPushButton('Check Communication', self) self.checkcomm_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.checkcomm_ps_bt.clicked.connect(self._check_comm) self.checkcomm_ps_bt.setToolTip( 'Check PS and DCLinks communication status (verify invalid alarms ' 'and, if LI, the value of Connected-Mon PV)') self.checkstatus_ps_bt = QPushButton('Show Status Summary', self) self.checkstatus_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.checkstatus_ps_bt.clicked.connect(_part(self._check_status, 'PS')) self.checkstatus_ps_bt.setToolTip( 'Check PS and DCLinks interlock status and, if powered on, ' 'check if it is following reference') self.dsbltrigger_ps_bt = QPushButton('Disable PS triggers', self) self.dsbltrigger_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.dsbltrigger_ps_bt.clicked.connect( _part(self._set_check_trigger_state, 'PS', 'dsbl')) self.setsofbmode_ps_bt = QPushButton('Turn Off SOFBMode', self) self.setsofbmode_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.setsofbmode_ps_bt.clicked.connect( _part(self._set_check_fbp_sofbmode, 'off')) self.setslowref_ps_bt = QPushButton('Set PS and DCLinks to SlowRef', self) self.setslowref_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.setslowref_ps_bt.clicked.connect(self._set_check_opmode_slowref) self.currzero_ps_bt1 = QPushButton('Set PS Current to zero', self) self.currzero_ps_bt1.clicked.connect(_part(self._set_lastcomm, 'PS')) self.currzero_ps_bt1.clicked.connect(self._set_zero_ps) self.reset_ps_bt = QPushButton('Reset PS and DCLinks', self) self.reset_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.reset_ps_bt.clicked.connect(_part(self._reset_intlk, 'PS')) self.prep_sidclink_bt = QPushButton('Prepare SI Fam DCLinks', self) self.prep_sidclink_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.prep_sidclink_bt.clicked.connect(self._prepare_sidclinks) self.prep_sidclink_bt.setVisible(False) self.init_sips_bt = QPushButton('Initialize SI Fam PS', self) self.init_sips_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.init_sips_bt.clicked.connect(self._set_check_pwrstateinit) self.init_sips_bt.setVisible(False) self.aux_label = QLabel('') self.aux_label.setVisible(False) self.turnon_dcl_bt = QPushButton('Turn DCLinks On', self) self.turnon_dcl_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.turnon_dcl_bt.clicked.connect( _part(self._set_check_pwrstate_dclinks, 'on')) self.checkctrlloop_dcl_bt = QPushButton('Check DCLinks CtrlLoop', self) self.checkctrlloop_dcl_bt.clicked.connect( _part(self._set_lastcomm, 'PS')) self.checkctrlloop_dcl_bt.clicked.connect( _part(self._set_check_ctrlloop, 'dclink')) self.setvolt_dcl_bt = QPushButton('Set DCLinks Voltage', self) self.setvolt_dcl_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.setvolt_dcl_bt.clicked.connect(self._set_check_dclinks_capvolt) self.turnon_ps_bt = QPushButton('Turn PS On', self) self.turnon_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.turnon_ps_bt.clicked.connect( _part(self._set_check_pwrstate, 'PS', 'on', True)) self.setcheckctrlloop_ps_bt = QPushButton('Check PS CtrlLoop', self) self.setcheckctrlloop_ps_bt.clicked.connect( _part(self._set_lastcomm, 'PS')) self.setcheckctrlloop_ps_bt.clicked.connect( _part(self._set_check_ctrlloop, 'pwrsupply')) self.test_ps_bt = QPushButton('Set PS Current to test value', self) self.test_ps_bt.clicked.connect(_part(self._set_lastcomm, 'PS')) self.test_ps_bt.clicked.connect(self._set_test_ps) self.currzero_ps_bt2 = QPushButton('Set PS Current to zero', self) self.currzero_ps_bt2.clicked.connect(_part(self._set_lastcomm, 'PS')) self.currzero_ps_bt2.clicked.connect(self._set_zero_ps) self.restoretrigger_ps_bt = QPushButton('Restore PS triggers', self) self.restoretrigger_ps_bt.clicked.connect( _part(self._set_lastcomm, 'PS')) self.restoretrigger_ps_bt.clicked.connect( _part(self._restore_triggers_state, 'PS')) gbox_ps_comm = QGroupBox('Commands', self) gbox_ps_comm.setObjectName('comm') gbox_ps_comm.setStyleSheet('#comm{border: 0px solid transparent;}') lay_ps_comm = QVBoxLayout(gbox_ps_comm) lay_ps_comm.setContentsMargins(20, 9, 20, 9) lay_ps_comm.addWidget(QLabel('')) lay_ps_comm.addWidget( QLabel('<h4>Check</h4>', self, alignment=Qt.AlignCenter)) lay_ps_comm.addWidget(self.checkcomm_ps_bt) lay_ps_comm.addWidget(self.checkstatus_ps_bt) lay_ps_comm.addWidget(QLabel('')) lay_ps_comm.addWidget( QLabel('<h4>Prepare</h4>', self, alignment=Qt.AlignCenter)) lay_ps_comm.addWidget(self.dsbltrigger_ps_bt) lay_ps_comm.addWidget(self.setsofbmode_ps_bt) lay_ps_comm.addWidget(self.setslowref_ps_bt) lay_ps_comm.addWidget(self.currzero_ps_bt1) lay_ps_comm.addWidget(self.reset_ps_bt) lay_ps_comm.addWidget(QLabel('')) lay_ps_comm.addWidget(self.prep_sidclink_bt) lay_ps_comm.addWidget(self.init_sips_bt) lay_ps_comm.addWidget(self.aux_label) lay_ps_comm.addWidget( QLabel('<h4>Config DCLinks</h4>', self, alignment=Qt.AlignCenter)) lay_ps_comm.addWidget(self.turnon_dcl_bt) lay_ps_comm.addWidget(self.checkctrlloop_dcl_bt) lay_ps_comm.addWidget(self.setvolt_dcl_bt) lay_ps_comm.addWidget(QLabel('')) lay_ps_comm.addWidget( QLabel('<h4>Test</h4>', self, alignment=Qt.AlignCenter)) lay_ps_comm.addWidget(self.turnon_ps_bt) lay_ps_comm.addWidget(self.setcheckctrlloop_ps_bt) lay_ps_comm.addWidget(self.test_ps_bt) lay_ps_comm.addWidget(self.currzero_ps_bt2) lay_ps_comm.addWidget(QLabel('')) lay_ps_comm.addWidget( QLabel('<h4>Restore</h4>', self, alignment=Qt.AlignCenter)) lay_ps_comm.addWidget(self.restoretrigger_ps_bt) lay_ps_comm.addWidget(QLabel('')) lay_ps.addWidget(gbox_ps_comm, 0, 1) lay_ps.setColumnStretch(1, 1) self.tab.addTab(self.ps_wid, 'PS') # # PU self.pu_wid = QWidget(self) lay_pu = QGridLayout(self.pu_wid) lay_pu.setContentsMargins(0, 9, 0, 0) lay_pu.setHorizontalSpacing(0) # PU selection self.pu_tree = PVNameTree(items=self._get_pu_tree_names(), tree_levels=('sec', ), parent=self) self.pu_tree.tree.setHeaderHidden(True) self.pu_tree.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred) self.pu_tree.tree.setColumnCount(1) self.pu_tree.tree.doubleClicked.connect(self._open_detail) gbox_pu_select = QGroupBox('Select PU: ', self) gbox_pu_select.setObjectName('select') gbox_pu_select.setStyleSheet(""" #select{ border-top: 0px solid transparent; border-left: 0px solid transparent; border-bottom: 0px solid transparent; }""") lay_pu_select = QVBoxLayout(gbox_pu_select) lay_pu_select.addWidget(self.pu_tree) lay_pu.addWidget(gbox_pu_select, 0, 0) lay_pu.setColumnStretch(0, 1) # PU commands self.checkstatus_pu_bt = QPushButton('Show Status Summary', self) self.checkstatus_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.checkstatus_pu_bt.clicked.connect(_part(self._check_status, 'PU')) self.checkstatus_pu_bt.setToolTip( 'Check PU interlock status and, if powered on, ' 'check if it is following voltage setpoint') self.voltzero_pu_bt1 = QPushButton('Set PU Voltage to zero', self) self.voltzero_pu_bt1.clicked.connect(_part(self._set_lastcomm, 'PU')) self.voltzero_pu_bt1.clicked.connect(_part(self._set_zero_pu, False)) self.reset_pu_bt = QPushButton('Reset PU', self) self.reset_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.reset_pu_bt.clicked.connect(_part(self._reset_intlk, 'PU')) self.turnon_pu_bt = QPushButton('Turn PU On', self) self.turnon_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.turnon_pu_bt.clicked.connect( _part(self._set_check_pwrstate, 'PU', 'on', True)) self.enblpulse_pu_bt = QPushButton('Enable PU Pulse', self) self.enblpulse_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.enblpulse_pu_bt.clicked.connect(_part(self._set_check_pulse, 'on')) self.enbltrigger_pu_bt = QPushButton('Enable PU triggers', self) self.enbltrigger_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.enbltrigger_pu_bt.clicked.connect( _part(self._set_check_trigger_state, 'PU', 'on')) self.test_pu_bt = QPushButton('Set PU Voltage to test value', self) self.test_pu_bt.clicked.connect(_part(self._set_lastcomm, 'PU')) self.test_pu_bt.clicked.connect(self._set_test_pu) self.voltzero_pu_bt2 = QPushButton('Set PU Voltage to zero', self) self.voltzero_pu_bt2.clicked.connect(_part(self._set_lastcomm, 'PU')) self.voltzero_pu_bt2.clicked.connect(_part(self._set_zero_pu, True)) self.restoretrigger_pu_bt = QPushButton('Restore PU triggers', self) self.restoretrigger_pu_bt.clicked.connect( _part(self._set_lastcomm, 'PU')) self.restoretrigger_pu_bt.clicked.connect( _part(self._restore_triggers_state, 'PU')) gbox_pu_comm = QGroupBox('Commands', self) gbox_pu_comm.setObjectName('comm') gbox_pu_comm.setStyleSheet('#comm{border: 0px solid transparent;}') lay_pu_comm = QVBoxLayout(gbox_pu_comm) lay_pu_comm.setContentsMargins(20, 9, 20, 9) lay_pu_comm.addWidget(QLabel('')) lay_pu_comm.addWidget( QLabel('<h4>Check</h4>', self, alignment=Qt.AlignCenter)) lay_pu_comm.addWidget(self.checkstatus_pu_bt) lay_pu_comm.addWidget(QLabel('')) lay_pu_comm.addWidget( QLabel('<h4>Prepare</h4>', self, alignment=Qt.AlignCenter)) lay_pu_comm.addWidget(self.voltzero_pu_bt1) lay_pu_comm.addWidget(self.reset_pu_bt) lay_pu_comm.addWidget(QLabel('')) lay_pu_comm.addWidget( QLabel('<h4>Test</h4>', self, alignment=Qt.AlignCenter)) lay_pu_comm.addWidget(self.turnon_pu_bt) lay_pu_comm.addWidget(self.enblpulse_pu_bt) lay_pu_comm.addWidget(self.enbltrigger_pu_bt) lay_pu_comm.addWidget(self.test_pu_bt) lay_pu_comm.addWidget(self.voltzero_pu_bt2) lay_pu_comm.addWidget(QLabel('')) lay_pu_comm.addWidget( QLabel('<h4>Restore</h4>', self, alignment=Qt.AlignCenter)) lay_pu_comm.addWidget(self.restoretrigger_pu_bt) lay_pu_comm.addWidget(QLabel('')) lay_pu.addWidget(gbox_pu_comm, 0, 1) lay_pu.setColumnStretch(1, 1) self.tab.addTab(self.pu_wid, 'PU') # lists self.label_lastcomm = QLabel('Last Command: ', self) self.ok_ps = QListWidget(self) self.ok_ps.setObjectName('OkList') self.ok_ps.doubleClicked.connect(self._open_detail) self.ok_ps.setSelectionMode(QAbstractItemView.MultiSelection) self.ok_ps.setToolTip( 'Select rows and press Ctrl+C to copy and Esc to deselect.') self.nok_ps = QListWidget(self) self.nok_ps.setObjectName('NokList') self.nok_ps.doubleClicked.connect(self._open_detail) self.nok_ps.setSelectionMode(QAbstractItemView.MultiSelection) self.nok_ps.setToolTip( 'Select rows and press Ctrl+C to copy and Esc to deselect.') self.clearlists_bt = QPushButton('Clear', self) self.clearlists_bt.clicked.connect(self._clear_lastcomm) self.ok_ps_aux_list = list() self.nok_ps_aux_list = list() hbox = QHBoxLayout() hbox.addWidget(self.label_lastcomm) hbox.addWidget(self.clearlists_bt, alignment=Qt.AlignRight) list_layout = QGridLayout() list_layout.setContentsMargins(0, 0, 0, 0) list_layout.setVerticalSpacing(6) list_layout.setHorizontalSpacing(9) list_layout.addLayout(hbox, 0, 0, 1, 2) list_layout.addWidget( QLabel('<h4>Ok</h4>', self, alignment=Qt.AlignCenter), 1, 0) list_layout.addWidget(self.ok_ps, 2, 0) list_layout.addWidget( QLabel('<h4>Failed</h4>', self, alignment=Qt.AlignCenter), 1, 1) list_layout.addWidget(self.nok_ps, 2, 1) # menu self.menu = self.menuBar() self.act_cycle = self.menu.addAction('Open Cycle Window') connect_newprocess(self.act_cycle, 'sirius-hla-as-ps-cycle.py', parent=self) self.aux_comm = self.menu.addMenu('Auxiliary commands') # # auxiliary PS self.ps_menu = self.aux_comm.addMenu('PS') self.act_turnoff_ps = self.ps_menu.addAction('Turn PS Off') self.act_turnoff_ps.triggered.connect(_part(self._set_lastcomm, 'PS')) self.act_turnoff_ps.triggered.connect( _part(self._set_check_pwrstate, 'PS', 'off', True)) self.act_turnoff_dclink = self.ps_menu.addAction('Turn DCLinks Off') self.act_turnoff_dclink.triggered.connect( _part(self._set_lastcomm, 'PS')) self.act_turnoff_dclink.triggered.connect( _part(self._set_check_pwrstate_dclinks, 'off')) self.act_setcurrent_ps = self.ps_menu.addAction('Set PS Current') self.act_setcurrent_ps.triggered.connect( _part(self._set_lastcomm, 'PS')) self.act_setcurrent_ps.triggered.connect(self._set_check_current) self.act_opmode_ps = self.ps_menu.addAction('Set PS OpMode') self.act_opmode_ps.triggered.connect(_part(self._set_lastcomm, 'PS')) self.act_opmode_ps.triggered.connect(self._set_check_opmode) # # auxiliary PU self.pu_menu = self.aux_comm.addMenu('PU') self.act_turnoff_pu = self.pu_menu.addAction('Turn PU Off') self.act_turnoff_pu.triggered.connect(_part(self._set_lastcomm, 'PU')) self.act_turnoff_pu.triggered.connect( _part(self._set_check_pwrstate, 'PU', 'off', True)) self.act_dsblpulse_pu = self.pu_menu.addAction('Disable PU Pulse') self.act_dsblpulse_pu.triggered.connect(_part(self._set_lastcomm, 'PU')) self.act_dsblpulse_pu.triggered.connect( _part(self._set_check_pulse, 'off')) # layout lay = QGridLayout() lay.setHorizontalSpacing(15) lay.setVerticalSpacing(5) lay.addWidget( QLabel('<h3>Power Supplies Test</h3>', self, alignment=Qt.AlignCenter), 0, 0, 1, 3) lay.addWidget(self.tab, 1, 0) lay.addLayout(list_layout, 1, 1) lay.setColumnStretch(0, 2) lay.setColumnStretch(1, 2) lay.setRowStretch(0, 2) lay.setRowStretch(1, 18) self.central_widget.setLayout(lay) # ---------- commands ------------ def _check_comm(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() if not devices: return dclinks = self._get_related_dclinks(devices, include_regatrons=True) devices.extend(dclinks) task0 = CreateTesters(devices, parent=self) task1 = CheckComm(devices, parent=self) task1.itemDone.connect(self._log) labels = [ 'Connecting to devices...', 'Checking PS and DCLinks Comm. Status...' ] tasks = [task0, task1] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _check_status(self, dev_type): self.ok_ps.clear() self.nok_ps.clear() if dev_type == 'PS': devices = self._get_selected_ps() if not devices: return dclinks = self._get_related_dclinks(devices, include_regatrons=True) devices.extend(dclinks) label1 = 'Reading PS and DCLinks Status...' elif dev_type == 'PU': devices = self._get_selected_pu() if not devices: return label1 = 'Reading PU Status...' task0 = CreateTesters(devices, parent=self) task1 = CheckStatus(devices, parent=self) task1.itemDone.connect(self._log) labels = ['Connecting to devices...', label1] tasks = [task0, task1] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_trigger_state(self, dev_type, state): self.ok_ps.clear() self.nok_ps.clear() if dev_type == 'PS': devices = self._get_selected_ps() elif dev_type == 'PU': devices = self._get_selected_pu() if not devices: return task1 = SetTriggerState(parent=self, dis=dev_type, state=state, devices=devices) task2 = CheckTriggerState(parent=self, dis=dev_type, state=state, devices=devices) task2.itemDone.connect(self._log) tasks = [task1, task2] labels = [ 'Disabling ' + dev_type + ' triggers...', 'Checking ' + dev_type + ' trigger states...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_opmode_slowref(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() if not devices: return dclinks = self._get_related_dclinks(devices) devices.extend(dclinks) devices = [dev for dev in devices if 'LI-' not in dev] task0 = CreateTesters(devices, parent=self) task1 = SetOpMode(devices, state=_PSC.OpMode.SlowRef, parent=self) task2 = CheckOpMode(devices, state=[ _PSC.States.SlowRef, _PSC.States.Off, _PSC.States.Interlock ], parent=self) task2.itemDone.connect(self._log) labels = [ 'Connecting to devices...', 'Setting PS OpMode to SlowRef...', 'Checking PS OpMode...' ] tasks = [task0, task1, task2] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _reset_intlk(self, dev_type): self.ok_ps.clear() self.nok_ps.clear() if dev_type == 'PS': devices = self._get_selected_ps() if not devices: return dclinks = self._get_related_dclinks(devices, include_regatrons=True) devices.extend(dclinks) dev_label = 'PS and DCLinks' elif dev_type == 'PU': devices = self._get_selected_pu() if not devices: return dev_label = 'PU' devices_not_rst = { dev for dev in devices if dev.sec != 'LI' and dev.dev not in ('FCH', 'FCV') } task0 = CreateTesters(devices, parent=self) task1 = ResetIntlk(devices_not_rst, parent=self) task2 = CheckIntlk(devices, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Reseting ' + dev_label + '...', 'Checking ' + dev_label + ' Interlocks...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_pwrstate(self, dev_type, state, show=True): self.ok_ps.clear() self.nok_ps.clear() if isinstance(dev_type, list): devices = list(dev_type) dev_type = devices[0].dis else: if dev_type == 'PS': devices = self._get_selected_ps() elif dev_type == 'PU': devices = self._get_selected_pu() if not devices: return if state == 'on' and dev_type == 'PS': dev2ctrl = list(set(devices) - set(self._si_fam_psnames)) else: dev2ctrl = devices task0 = CreateTesters(devices, parent=self) task1 = SetPwrState(dev2ctrl, state=state, parent=self) task2 = CheckPwrState(devices, state=state, is_test=True, parent=self) task2.itemDone.connect(_part(self._log, show=show)) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Turning ' + dev_type + ' ' + state + '...', 'Checking ' + dev_type + ' powered ' + state + '...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _prepare_sidclinks(self): self.ok_ps.clear() self.nok_ps.clear() selected = self._get_selected_ps() ps2check = [ps for ps in selected if ps in self._si_fam_psnames] dclinks = self._get_related_dclinks(ps2check, include_regatrons=True) if not ps2check: return # if power state is on, do nothing self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(ps2check, state='on', is_test=False, show=False) if set(self.ok_ps_aux_list) == set(ps2check): for dev in dclinks: self._log(dev, True) return ps2act = list(self.nok_ps_aux_list) # act in PS off dcl2act = self._get_related_dclinks(ps2act, include_regatrons=True) # print('act', ps2act, dcl2act) # if need initializing, check if DCLinks are turned off self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(dcl2act, state='off', is_test=False, show=False) if not self.nok_ps_aux_list: for dev in dclinks: self._log(dev, True) return dcl2ctrl = list(self.nok_ps_aux_list) # control DCLink on dcl_ok = set(dclinks) - set(dcl2ctrl) ps2ctrl = set() # get related psnames for dcl in dcl2ctrl: pss = PSSearch.conv_dclink_2_psname(dcl) ps2ctrl.update(pss) # print('ctrl', ps2ctrl, dcl2ctrl) # if some DCLink is on, make sure related PS are turned off self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(ps2ctrl, state='off', is_test=False, show=False) if self.nok_ps_aux_list: ps2ctrl = list(self.nok_ps_aux_list) self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._set_zero_ps(ps2ctrl, show=False) self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._set_check_pwrstate(dev_type=ps2ctrl, state='off', show=False) if self.nok_ps_aux_list: for dev in self.ok_ps_aux_list: self._log(dev, True) for dev in self.nok_ps_aux_list: self._log(dev, False) text = 'The listed PS seems to be taking too\n'\ 'long to turn off.\n'\ 'Try to execute this step once again.' QMessageBox.warning(self, 'Message', text) return # finally, turn DCLinks off self._set_check_pwrstate_dclinks(state='off', devices=dcl2ctrl, ps2check=ps2ctrl) # log DCLinks Ok for dev in dcl_ok: self._log(dev, True) def _set_check_pwrstateinit(self): self.ok_ps.clear() self.nok_ps.clear() selected = self._get_selected_ps() devices = [ps for ps in selected if ps in self._si_fam_psnames] if not devices: return # if power state is on, do nothing self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(devices, state='on', is_test=False, show=False) if len(self.ok_ps_aux_list) == len(devices): for dev in self.ok_ps_aux_list: self._log(dev, True) return # if need initializing, check if DCLinks are turned off before continue ps_ok = list(self.ok_ps_aux_list) ps2ctrl = list(self.nok_ps_aux_list) # check PS off dcl2check = self._get_related_dclinks(ps2ctrl, include_regatrons=True) # print('set_check_pwrstateinit', ps2ctrl) self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(dcl2check, state='off', is_test=False, show=False) if len(self.nok_ps_aux_list) > 0: for dev in self.ok_ps_aux_list: self._log(dev, True) for dev in self.nok_ps_aux_list: self._log(dev, False) QMessageBox.critical( self, 'Message', 'Make sure related DCLinks are turned\n' 'off before initialize SI Fam PS!') return # list in Ok PS already on for dev in ps_ok: self._log(dev, True) # then, initialize SI Fam PS task0 = CreateTesters(ps2ctrl, parent=self) task1 = SetPwrState(ps2ctrl, state='on', parent=self) task2 = CheckOpMode(ps2ctrl, state=_PSC.States.Initializing, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Initializing SI Fam PS...', 'Checking SI Fam PS initialized...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_pulse(self, state): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_pu() if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetPulse(devices, state=state, parent=self) task2 = CheckPulse(devices, state=state, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Turning PU Pulse ' + state + '...', 'Checking PU Pulse ' + state + '...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_pwrstate_dclinks(self, state, devices=list(), ps2check=list()): self.ok_ps.clear() self.nok_ps.clear() if not devices: pwrsupplies = self._get_selected_ps() if not pwrsupplies: return devices = self._get_related_dclinks(pwrsupplies, include_regatrons=True) ps2check = set() for dev in devices: ps2check.update(PSSearch.conv_dclink_2_psname(dev)) if not devices: return if state == 'off': self.ok_ps_aux_list.clear() self.nok_ps_aux_list.clear() self._check_pwrstate(ps2check, state='offintlk', show=False) if len(self.nok_ps_aux_list) > 0: for dev in self.ok_ps_aux_list: self._log(dev, True) for dev in self.nok_ps_aux_list: self._log(dev, False) QMessageBox.critical( self, 'Message', 'Make sure all related power supplies\n' 'are turned off before turning DCLinks off!') return task0 = CreateTesters(devices, parent=self) task1 = SetPwrState(devices, state=state, parent=self) task2 = CheckPwrState(devices, state=state, is_test=True, parent=self) tasks = [task0, task1, task2] labels = [ 'Connecting to DCLinks...', 'Turning DCLinks ' + state + '...', 'Checking DCLinks powered ' + state + '...' ] if state == 'on': task3 = CheckInitOk(devices, parent=self) task3.itemDone.connect(self._log) tasks.append(task3) labels.append('Wait DCLinks initialize...') else: task2.itemDone.connect(self._log) dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _check_pwrstate(self, devices, state, is_test=True, show=True): self.ok_ps.clear() self.nok_ps.clear() task0 = CreateTesters(devices, parent=self) if state == 'offintlk': text = 'off or interlock' task1 = CheckOpMode(devices, state=[_PSC.States.Off, _PSC.States.Interlock], parent=self) else: text = state task1 = CheckPwrState(devices, state=state, is_test=is_test, parent=self) task1.itemDone.connect(_part(self._log, show=show)) tasks = [task0, task1] labels = [ 'Connecting to devices...', 'Checking devices powered ' + text + '...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_ctrlloop(self, dev_type='pwrsupply'): self.ok_ps.clear() self.nok_ps.clear() pwrsupplies = self._get_selected_ps() if not pwrsupplies: return devices_set = {} if dev_type == 'pwrsupply': devices = {dev for dev in pwrsupplies if 'LI' not in dev} devices_set = {dev for dev in devices if dev.dev in ('FCH', 'FCV')} else: devices = self._get_related_dclinks(pwrsupplies) if not devices: return tasks = [ CreateTesters(devices, parent=self), ] labels = [ 'Connecting to devices...', ] if devices_set: task1 = SetCtrlLoop(devices_set, parent=self) task2 = CheckCtrlLoop(devices, parent=self) task2.itemDone.connect(self._log) tasks.extend([task1, task2]) labels.extend( ['Setting CtrlLoop...', 'Checking CtrlLoop state...']) else: task1 = CheckCtrlLoop(devices, parent=self) task1.itemDone.connect(self._log) tasks.append(task1) labels.append('Checking CtrlLoop state...') dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_dclinks_capvolt(self): self.ok_ps.clear() self.nok_ps.clear() pwrsupplies = self._get_selected_ps() if not pwrsupplies: return devices = self._get_related_dclinks(pwrsupplies, include_regatrons=True) dev_exc_regatrons = { dev for dev in devices if PSSearch.conv_psname_2_psmodel(dev) != 'REGATRON_DCLink' } if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetCapBankVolt(dev_exc_regatrons, parent=self) task2 = CheckCapBankVolt(devices, parent=self) task2.itemDone.connect(self._log) labels = [ 'Connecting to devices...', 'Setting capacitor bank voltage...', 'Checking capacitor bank voltage...' ] tasks = [task0, task1, task2] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_fbp_sofbmode(self, state): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() devices = [ dev for dev in devices if PSSearch.conv_psname_2_psmodel(dev) == 'FBP' ] if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetSOFBMode(devices, state=state, parent=self) task2 = CheckSOFBMode(devices, state=state, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Turning PS SOFBMode ' + state + '...', 'Checking PS SOFBMode ' + state + '...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_test_ps(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetCurrent(devices, is_test=True, parent=self) task2 = CheckCurrent(devices, is_test=True, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Testing PS... Setting current...', 'Testing PS... Checking current value...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_zero_ps(self, devices=list(), show=True): self.ok_ps.clear() self.nok_ps.clear() if not devices: devices = self._get_selected_ps() if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetCurrent(devices, parent=self) task2 = CheckCurrent(devices, parent=self) task2.itemDone.connect(_part(self._log, show=show)) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Setting current to zero...', 'Checking current value...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_current(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() if not devices: return value, ok = QInputDialog.getDouble(self, "Setpoint Input", "Insert current setpoint: ") if not ok: return task0 = CreateTesters(devices, parent=self) task1 = SetCurrent(devices, value=value, parent=self) task2 = CheckCurrent(devices, value=value, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Setting current...', 'Checking current value...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_check_opmode(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_ps() devices = [ dev for dev in devices if dev.sec != 'LI' and dev.dev not in ('FCH', 'FCV') ] if not devices: return state, ok = QInputDialog.getItem(self, "OpMode Input", "Select OpMode: ", _PSE.OPMODES, editable=False) if not ok: return state2set = getattr(_PSC.OpMode, state) state2check = getattr(_PSC.States, state) task0 = CreateTesters(devices, parent=self) task1 = SetOpMode(devices, state=state2set, parent=self) task2 = CheckOpMode(devices, state=state2check, parent=self) task2.itemDone.connect(self._log) labels = [ 'Connecting to devices...', 'Setting PS OpMode to ' + state + '...', 'Checking PS OpMode in ' + state + '...' ] tasks = [task0, task1, task2] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_test_pu(self): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_pu() if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetVoltage(devices, is_test=True, parent=self) task2 = CheckVoltage(devices, is_test=True, parent=self) task2.itemDone.connect(self._log) tasks = [task0, task1, task2] labels = [ 'Connecting to devices...', 'Testing PU... Setting voltage...', 'Testing PU... Checking voltage value...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _set_zero_pu(self, check=True): self.ok_ps.clear() self.nok_ps.clear() devices = self._get_selected_pu() if not devices: return task0 = CreateTesters(devices, parent=self) task1 = SetVoltage(devices, parent=self) tasks = [task0, task1] labels = ['Connecting to devices...', 'Setting voltage to zero...'] if check: task2 = CheckVoltage(devices, parent=self) task2.itemDone.connect(self._log) tasks.append(task2) labels.append('Checking voltage value...') else: task1.itemDone.connect(self._log) dlg = ProgressDialog(labels, tasks, self) dlg.exec_() def _restore_triggers_state(self, dev_type): self.ok_ps.clear() self.nok_ps.clear() if dev_type == 'PS': devices = self._get_selected_ps() elif dev_type == 'PU': devices = self._get_selected_pu() if not devices: return task1 = SetTriggerState(parent=self, restore_initial_value=True, dis=dev_type, devices=devices) task2 = CheckTriggerState(parent=self, restore_initial_value=True, dis=dev_type, devices=devices) task2.itemDone.connect(self._log) tasks = [task1, task2] labels = [ 'Restoring ' + dev_type + ' trigger states...', 'Checking ' + dev_type + ' trigger states...' ] dlg = ProgressDialog(labels, tasks, self) dlg.exec_() # ---------- log updates ----------- def _set_lastcomm(self, dev_type): sender_text = self.sender().text() self.label_lastcomm.setText('Last Command: ' + dev_type + ' - ' + sender_text) def _clear_lastcomm(self): self.ok_ps.clear() self.nok_ps.clear() self.label_lastcomm.setText('Last Command: ') def _log(self, name, status, show=True): if show: if status: self.ok_ps.addItem(name) else: self.nok_ps.addItem(name) else: if status: self.ok_ps_aux_list.append(name) else: self.nok_ps_aux_list.append(name) # ---------- devices control ---------- def _get_ps_tree_names(self): # add LI, TB, BO, TS psnames = PSSearch.get_psnames({'sec': '(LI|TB|BO|TS)', 'dis': 'PS'}) # add SI Fams psnames.extend( PSSearch.get_psnames({ 'sec': 'SI', 'sub': 'Fam', 'dis': 'PS', 'dev': '(B.*|Q.*|S.*)' })) # add SI Corrs psnames.extend( PSSearch.get_psnames({ 'sec': 'SI', 'sub': '[0-2][0-9].*', 'dis': 'PS', 'dev': '(CH|CV)' })) # add SI QTrims psnames.extend( PSSearch.get_psnames({ 'sec': 'SI', 'sub': '[0-2][0-9].*', 'dis': 'PS', 'dev': '(QD.*|QF.*|Q[1-4])' })) # add SI QSkews psnames.extend( PSSearch.get_psnames({ 'sec': 'SI', 'dis': 'PS', 'dev': 'QS' })) # add SI Fast Corrs psnames.extend( PSSearch.get_psnames({ 'sec': 'SI', 'dis': 'PS', 'dev': 'FC.*' })) return psnames def _get_pu_tree_names(self): punames = PSSearch.get_psnames({ 'sec': '(TB|BO|TS|SI)', 'dis': 'PU', 'dev': '.*(Kckr|Sept).*' }) return punames def _get_selected_ps(self): devices = self.ps_tree.checked_items() if not devices: QMessageBox.critical(self, 'Message', 'No power supply selected!') return False devices = [PVName(dev) for dev in devices] return devices def _get_selected_pu(self): devices = self.pu_tree.checked_items() if not devices: QMessageBox.critical(self, 'Message', 'No pulsed power supply selected!') return False devices = [PVName(dev) for dev in devices] return devices def _get_related_dclinks(self, psnames, include_regatrons=False): if isinstance(psnames, str): psnames = [ psnames, ] alldclinks = set() for name in psnames: if 'LI' in name: continue dclinks = PSSearch.conv_psname_2_dclink(name) if dclinks: dclink_model = PSSearch.conv_psname_2_psmodel(dclinks[0]) if dclink_model != 'REGATRON_DCLink': alldclinks.update(dclinks) elif include_regatrons: for dcl in dclinks: dcl_typ = PSSearch.conv_psname_2_pstype(dcl) if dcl_typ == 'as-dclink-regatron-master': alldclinks.add(dcl) alldclinks = [PVName(dev) for dev in alldclinks] return alldclinks def _open_detail(self, index): name = PVName(index.data()) if name.dis == 'TI': app = QApplication.instance() wind = create_window_from_widget(HLTriggerDetailed, title=name, is_main=True) app.open_window(wind, parent=self, **{'device': name}) return elif not _re.match('.*-.*:.*-.*', name): if index.model().rowCount(index) == 1: name = PVName(index.child(0, 0).data()) else: return if name.dis == 'PS': run_newprocess(['sirius-hla-as-ps-detail.py', name]) elif name.dis == 'PU': run_newprocess(['sirius-hla-as-pu-detail.py', name]) # ---------- update setup ---------- def _handle_checked_items_changed(self, item): devname = PVName(item.data(0, Qt.DisplayRole)) if not _re.match('.*-.*:.*-.*', devname): return state2set = item.checkState(0) # ensure power supplies that share dclinks are checked together if devname in self._si_fam_psnames and not self._is_adv_mode: dclinks = PSSearch.conv_psname_2_dclink(devname) if dclinks: psname2check = set() for dcl in dclinks: relps = PSSearch.conv_dclink_2_psname(dcl) relps.remove(devname) psname2check.update(relps) self.ps_tree.tree.blockSignals(True) for psn in psname2check: item2check = self.ps_tree._item_map[psn] if item2check.checkState(0) != state2set: item2check.setData(0, Qt.CheckStateRole, state2set) self.ps_tree.tree.blockSignals(False) self._needs_update_setup = True def _update_setup(self): if not self._needs_update_setup: return self._needs_update_setup = False # show/hide buttons to initialize SI Fam PS has_sifam = False for psn in self._si_fam_psnames: item = self.ps_tree._item_map[psn] has_sifam |= item.checkState(0) != 0 self.prep_sidclink_bt.setVisible(has_sifam) self.init_sips_bt.setVisible(has_sifam) self.aux_label.setVisible(has_sifam) # set CtrlLoop button label has_fast = False for psn in self._si_fastcorrs: item = self.ps_tree._item_map[psn] has_fast |= item.checkState(0) != 0 text = 'Set(FC) and Check PS CtrlLoop' \ if has_fast else 'Check PS CtrlLoop' self.setcheckctrlloop_ps_bt.setText(text) # ---------- events ---------- def keyPressEvent(self, evt): """Implement keyPressEvent.""" if evt.matches(QKeySequence.Copy): if self.ok_ps.underMouse(): items = self.ok_ps.selectedItems() elif self.nok_ps.underMouse(): items = self.nok_ps.selectedItems() items = '\n'.join([i.text() for i in items]) QApplication.clipboard().setText(items) if evt.key() == Qt.Key_Escape: if self.ok_ps.underMouse(): items = self.ok_ps.clearSelection() elif self.nok_ps.underMouse(): items = self.nok_ps.clearSelection() super().keyPressEvent(evt)
class SelectedLinesWidget(QWidget): """ Widget to show and enable lines to be selected inp : LineList Input LineList init_select : str or list of indices str -- 'All' """ def __init__(self, inp, parent=None, init_select=None, plot_widget=None): """ """ super(SelectedLinesWidget, self).__init__(parent) self.parent = parent # Line list Table if isinstance(inp, LineList): self.lines = inp._data self.llst = inp elif isinstance(inp, Table): raise ValueError('SelectedLineWidget: DEPRECATED') else: raise ValueError('SelectedLineWidget: Wrong type of input') self.plot_widget = plot_widget # Create the line list line_label = QLabel('Lines:') self.lines_widget = QListWidget(self) self.lines_widget.setSelectionMode(QAbstractItemView.MultiSelection) # Initialize list self.item_flg = 0 self.init_list() # Initial selection if init_select is None: self.selected = [0] elif init_select == 'All': self.selected = [] for ii in range(self.lines_widget.count()): self.lines_widget.item(ii).setSelected(True) self.selected.append(ii) else: self.selected = init_select if len(self.selected) == 0: self.selected = [0] for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem(self.lines_widget.item( self.selected[0])) # Events self.lines_widget.itemSelectionChanged.connect(self.on_item_change) # Layout vbox = QVBoxLayout() vbox.addWidget(line_label) vbox.addWidget(self.lines_widget) self.setLayout(vbox) def init_list(self): nlin = len(self.lines['wrest']) for ii in range(nlin): self.lines_widget.addItem('{:s} :: {:.3f} :: {}'.format( self.lines['name'][ii], self.lines['wrest'][ii], self.lines['f'][ii])) def on_item_change(self): #,item): # For big changes if self.item_flg == 1: return all_items = [ self.lines_widget.item(ii) for ii in range(self.lines_widget.count()) ] sel_items = self.lines_widget.selectedItems() self.selected = [all_items.index(isel) for isel in sel_items] self.selected.sort() #QtCore.pyqtRemoveInputHook() #xdb.set_trace() #QtCore.pyqtRestoreInputHook() # Update llist try: self.plot_widget.llist['show_line'] = self.selected except AttributeError: if self.parent is not None: self.parent.updated_slines(self.selected) return else: self.plot_widget.on_draw() def on_list_change(self, llist): # Clear if not isinstance(llist, LineList): raise ValueError('Expecting LineList!!') self.item_flg = 1 self.lines = llist._data self.llst = llist self.lines_widget.clear() # Initialize self.init_list() # Set selected for iselect in self.selected: self.lines_widget.item(iselect).setSelected(True) self.lines_widget.scrollToItem(self.lines_widget.item( self.selected[0])) self.item_flg = 0