def __generateKeyWidget__(self): topWidget = QWidget() topLayout = QHBoxLayout() topLayout.setSpacing(0) topLayout.setContentsMargins(0, 0, 0, 0) topWidget.setLayout(topLayout) keyInput = QComboBox() keyInput.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Maximum) keyInput.setEditable(True) keyInput.lineEdit().setPlaceholderText("'highway', 'name'...") keyInput.addItems(self.keyList) topLayout.addWidget(keyInput) filterOptionsButton = IconButton( QIcon(os.path.join(picturesDir, "options.png")), topWidget.windowHandle(), keyInput.height()) filterOptionsButton.setStyleSheet( """QPushButton::menu-indicator{image: none;}""") filterOptionsMenu = QMenu() removeAct = QAction('Remove filter', self) removeAct.triggered.connect(self.deleteLater) filterOptionsMenu.addAction(removeAct) helpAct = QAction('Help', self) helpAct.triggered.connect(self.getInfo) filterOptionsMenu.addAction(helpAct) filterOptionsButton.setMenu(filterOptionsMenu) filterOptionsButton.setFlat(True) topLayout.addWidget(filterOptionsButton) return topWidget, keyInput
class ComboDelegate(QItemDelegate): def __init__(self, parent, items, model): super(ComboDelegate, self).__init__(parent) self.combo = None self.items = items self.tableModel = model self.oldData = "" def createEditor(self, parent, options, index): self.combo = QComboBox(parent) self.combo.addItems(self.items) self.combo.setEditable(True) self.combo.setCurrentIndex(0) self.combo.currentData(QtCore.Qt.DisplayRole) self.combo.lineEdit().setReadOnly(True) self.combo.currentIndexChanged.connect(self.currentIndexChanged) return self.combo def setModelData(self, editor, model, index): model.setData(index, editor.currentText(), QtCore.Qt.DisplayRole) @QtCore.pyqtSlot() def currentIndexChanged(self): self.commitData.emit(self.sender()) def setData(self, index, value, role=QtCore.Qt.DisplayRole): print("setData", index.row(), index.column(), value) self.tableModel.setData(index, value, role) print(self.tableModel.data)
class Printing(preferences.Group): def __init__(self, page): super(Printing, self).__init__(page) layout = QGridLayout() self.setLayout(layout) self.printArthurBackend = QCheckBox(toggled=self.changed) self.useCups = QCheckBox(toggled=self.changed) self.resolutionLabel = QLabel() self.resolution = QComboBox(editable=True, editTextChanged=page.changed) self.resolution.addItems("300 600 1200".split()) self.resolution.lineEdit().setInputMask("9000") layout.addWidget(self.printArthurBackend, 0, 0, 1, 2) layout.addWidget(self.useCups, 1, 0, 1, 2) layout.addWidget(self.resolutionLabel, 2, 0) layout.addWidget(self.resolution, 2, 1) app.translateUI(self) if not qpageview.cupsprinter.handle(): self.useCups.setEnabled(False) def translateUI(self): self.setTitle(_("Printing of Music")) self.printArthurBackend.setText(_("Use vector based backend (Arthur) for printing PDF documents")) self.printArthurBackend.setToolTip(_( "If checked, Frescobaldi will use the Arthur backend of the Poppler\n" "library for printing PDF documents. A big advantage of the Arthur backend\n" "is that it is vector-based, in contrast to the default Splash backend,\n" "which is raster-based. But Arthur is more experimental.")) self.useCups.setText(_("Print PDF documents directly to CUPS if available.")) self.useCups.setToolTip(_( "If checked, Frescobaldi tries to print a PDF document directly using\n" "the CUPS server, if available.")) self.resolutionLabel.setText(_("Resolution:")) self.resolution.setToolTip(_( "Set the resolution if Frescobaldi prints using raster images.")) def loadSettings(self): s = QSettings() useArthurPrint = s.value("printing/arthurbackend_print", True, bool) self.printArthurBackend.setChecked(useArthurPrint) # see comment in pagedview and warning messages in musicview/__init__ # and viewers/__init__ for the rationale for the default value self.useCups.setChecked(s.value("printing/directcups", False if sys.platform.startswith('darwin') else True, bool)) with qutil.signalsBlocked(self.resolution): self.resolution.setEditText(format(s.value("printing/dpi", 300, int))) def saveSettings(self): s = QSettings() s.setValue("printing/arthurbackend_print", self.printArthurBackend.isChecked()) s.setValue("printing/directcups", self.useCups.isChecked()) s.setValue("printing/dpi", int(self.resolution.currentText()))
def createWidget(self, parent): w = QComboBox(parent) w.setSizeAdjustPolicy(QComboBox.AdjustToContents) w.setEditable(True) w.lineEdit().setReadOnly(True) w.setFocusPolicy(Qt.NoFocus) self._setupComboBox(w) self._adjustComboBox(w) w.activated[int].connect(self.setCurrentIndex) return w
class Window(QMainWindow): def __init__(self, application): super().__init__() self.application = application self.screenSize = application.desktop().screenGeometry() self.title = 'Nexus visualisation' self.left = 0 self.top = 0 self.width = self.screenSize.width() self.height = self.screenSize.height() self.init_ui() self.show() def init_ui(self): # Set the title and the geometry for the window self.setWindowTitle(self.title) self.setGeometry((self.width - self.screenSize.width()) // 2, (self.height - self.screenSize.height()) // 2, self.width, self.height) # Create the widget that will contain the button selection and the tabs widget = QWidget() layout = QVBoxLayout() self.selectionButton = QComboBox(self) # Let the software edit the text in the button self.selectionButton.setEditable(True) # Prevent user from rewrite the names of the detector self.selectionButton.lineEdit().setReadOnly(True) # Align the text self.selectionButton.lineEdit().setAlignment(Qt.AlignCenter) # Populate the selection button with the detectors' name for detector in Detectors: self.selectionButton.addItem(detector.value) self.selectionButton.currentTextChanged.connect(self.change_tab) self.table_widget = self.init_detector_ui() layout.addWidget(self.selectionButton) layout.addWidget(self.table_widget) widget.setLayout(layout) # Set the global widget in the window self.setCentralWidget(widget) def init_detector_ui(self) -> QTabWidget or None: if self.selectionButton.currentText() == Detectors.XPAD.value: return xpad.Xpad(self.application) if self.selectionButton.currentText() == Detectors.CIRPAD.value: return cirpad.CirpadContext() def change_tab(self) -> None: clear_tab(self.centralWidget().layout()) self.table_widget = self.initDetectorUI() self.centralWidget().layout().addWidget(self.table_widget)
def setup_combo_completer(combo_widget: QtWidgets.QComboBox): combo_widget.setEditable(True) combo_widget.setInsertPolicy(QtWidgets.QComboBox.NoInsert) combo_widget.lineEdit().editingFinished.connect( lambda: combo_widget.setCurrentIndex( combo_widget.findText(combo_widget.currentText()))) completer = combo_widget.completer() completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion) completer.popup().setAlternatingRowColors(True) completer.setCaseSensitivity(QtCore.Qt.CaseInsensitive) completer.setFilterMode(QtCore.Qt.MatchContains)
class MainWindow(QWidget): def __init__(self): super().__init__() self.w = None # No external window yet. self.activity_list_menu = QComboBox() self.activity_list_menu.setEditable(True) self.activity_list_menu.addItems(self.get_activities()) self.line_menu = self.activity_list_menu.lineEdit() self.line_menu.setAlignment(Qt.AlignCenter) self.button = QPushButton("Start Activity") self.button.clicked.connect(self.start_activity) layout=QGridLayout() layout.addWidget(self.activity_list_menu) layout.addWidget(self.button) self.setLayout(layout) def start_activity(self): self.timer_window = Activity(self) self.hide() self.timer_window.show() def get_activities(self): with open("data/activity.csv") as f: reader = csv.reader(f, delimiter=',') activity_list = [] a_list = set([row[0] for row in reader]) for a in a_list: activity_list.append(a) return activity_list
class Printing(preferences.Group): def __init__(self, page): super(Printing, self).__init__(page) layout = QGridLayout() self.setLayout(layout) self.useCups = QCheckBox(toggled=self.changed) self.resolutionLabel = QLabel() self.resolution = QComboBox(editable=True, editTextChanged=page.changed) self.resolution.addItems("300 600 1200".split()) self.resolution.lineEdit().setInputMask("9000") layout.addWidget(self.useCups, 0, 0, 1, 2) layout.addWidget(self.resolutionLabel, 1, 0) layout.addWidget(self.resolution, 1, 1) app.translateUI(self) if not qpageview.cupsprinter.handle(): self.useCups.setEnabled(False) def translateUI(self): self.setTitle(_("Printing of Music")) self.useCups.setText( _("Print PDF documents directly to CUPS if available.")) self.useCups.setToolTip( _("If checked, Frescobaldi tries to print a PDF document direcly using\n" "the CUPS server, if available.")) self.resolutionLabel.setText(_("Resolution:")) self.resolution.setToolTip( _("Set the resolution if Frescobaldi prints using raster images.")) def loadSettings(self): s = QSettings() self.useCups.setChecked(s.value("printing/directcups", True, bool)) with qutil.signalsBlocked(self.resolution): self.resolution.setEditText( format(s.value("printing/dpi", 300, int))) def saveSettings(self): s = QSettings() s.setValue("printing/directcups", self.useCups.isChecked()) s.setValue("printing/dpi", int(self.resolution.currentText()))
def _showEditDialog(self, title, rSite='', rUserAgent=''): if not rSite or not rUserAgent: return False, '', '' dialog = QDialog(self) layout = QFormLayout(dialog) editSite = QLineEdit(dialog) editAgent = QComboBox(dialog) editAgent.setLayoutDirection(Qt.LeftToRight) editAgent.setEditable(True) editAgent.addItems(self._knownUserAgents) box = QDialogButtonBox(dialog) box.addButton(QDialogButtonBox.Ok) box.addButton(QDialogButtonBox.Cancel) box.rejected.connect(dialog.reject) box.accepted.connect(dialog.accept) layout.addRow(QLabel(_('Site domain:')), editSite) layout.addRow(QLabel(_('User Agent:')), editAgent) layout.addRow(box) editSite.setText(rSite) editAgent.lineEdit().setText(rUserAgent) editSite.setFocus() editAgent.lineEdit().setCursorPosition(0) dialog.setWindowTitle(title) dialog.setMinimumSize(550, 100) dialog.setMaximumWidth(550) if dialog.exec_(): rSite = editSite.text() rUserAgent = editAgent.currentText() return bool(rSite and rUserAgent) return False
def createEditor(self): # setup data model self._model = QSqlTableModel() self._model.setTable(self.table) self._col = self._model.fieldIndex(self.column) # setup filter model for sorting and filtering self._proxy = QSortFilterProxyModel() self._proxy.setFilterCaseSensitivity(Qt.CaseInsensitive) self._proxy.setSourceModel(self._model) self._proxy.setFilterKeyColumn(self._col) # setup completer self._completer = QCompleter() self._completer.setModel(self._proxy) self._completer.setCompletionColumn(self._col) self._completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) # setup combobox editor = QComboBox() editor.setModel(self._proxy) editor.setModelColumn(self._col) editor.setEditable(True) editor.setFocusPolicy(Qt.StrongFocus) editor.setInsertPolicy(QComboBox.NoInsert) editor.setCompleter(self._completer) # setup connections editor.currentTextChanged[str].connect(self.onActivated) # setup editor appearence style = self.style() editor.setStyleSheet(style) editor.lineEdit().setStyleSheet(style) font = editor.font() self._completer.popup().setFont(font) return editor
class FontToolBar(QToolBar): def __init__(self, string, pointSize, parent=None): super(FontToolBar, self).__init__(parent) auxiliaryWidth = self.fontMetrics().width('0') * 8 self.leftTextField = QLineEdit(self) self.leftTextField.setMaximumWidth(auxiliaryWidth) self.textField = QLineEdit(string, self) self.rightTextField = QLineEdit(self) self.rightTextField.setMaximumWidth(auxiliaryWidth) self.leftTextField.textEdited.connect(self.textField.textEdited) self.rightTextField.textEdited.connect(self.textField.textEdited) self.comboBox = QComboBox(self) self.comboBox.setEditable(True) self.comboBox.setValidator(QIntValidator(self)) for p in pointSizes: self.comboBox.addItem(str(p)) self.comboBox.lineEdit().setText(str(pointSize)) self.configBar = QPushButton(self) self.configBar.setFlat(True) self.configBar.setIcon(QIcon(":/resources/settings.svg")) self.configBar.setStyleSheet("padding: 2px 0px; padding-right: 10px") self.toolsMenu = QMenu(self) showKerning = self.toolsMenu.addAction( "Show Kerning", self.showKerning) showKerning.setCheckable(True) showMetrics = self.toolsMenu.addAction( "Show Metrics", self.showMetrics) showMetrics.setCheckable(True) self.toolsMenu.addSeparator() wrapLines = self.toolsMenu.addAction("Wrap lines", self.wrapLines) wrapLines.setCheckable(True) noWrapLines = self.toolsMenu.addAction("No wrap", self.noWrapLines) noWrapLines.setCheckable(True) self.toolsMenu.addSeparator() verticalFlip = self.toolsMenu.addAction( "Vertical flip", self.verticalFlip) verticalFlip.setCheckable(True) """ lineHeight = QWidgetAction(self.toolsMenu) lineHeight.setText("Line height:") lineHeightSlider = QSlider(Qt.Horizontal, self) # QSlider works with integers so we'll just divide by 100 what comes # out of it lineHeightSlider.setMinimum(80) lineHeightSlider.setMaximum(160) lineHeightSlider.setValue(100) #lineHeightSlider.setContentsMargins(30, 0, 30, 0) lineHeightSlider.valueChanged.connect(self.lineHeight) lineHeight.setDefaultWidget(lineHeightSlider) self.toolsMenu.addAction(lineHeight) """ wrapLinesGroup = QActionGroup(self) wrapLinesGroup.addAction(wrapLines) wrapLinesGroup.addAction(noWrapLines) wrapLines.setChecked(True) # self.toolsMenu.setActiveAction(wrapLines) self.configBar.setMenu(self.toolsMenu) self.addWidget(self.leftTextField) self.addWidget(self.textField) self.addWidget(self.rightTextField) self.addWidget(self.comboBox) self.addWidget(self.configBar) def showEvent(self, event): super(FontToolBar, self).showEvent(event) self.textField.setFocus(True) def setPointSize(self, pointSize): self.comboBox.blockSignals(True) self.comboBox.setEditText(str(pointSize)) self.comboBox.blockSignals(False) def showKerning(self): action = self.sender() self.parent().canvas.setShowKerning(action.isChecked()) def showMetrics(self): action = self.sender() self.parent().canvas.setShowMetrics(action.isChecked()) def verticalFlip(self): action = self.sender() self.parent().canvas.setVerticalFlip(action.isChecked()) def lineHeight(self, value): self.parent().canvas.setLineHeight(value / 100) def wrapLines(self): self.parent().canvas.setWrapLines(True) def noWrapLines(self): self.parent().canvas.setWrapLines(False)
class LocationDialog(QDialog): def __init__(self, parent=None): super(LocationDialog, self).__init__(parent) self.formatComboBox = QComboBox() self.formatComboBox.addItem("Native") self.formatComboBox.addItem("INI") self.scopeComboBox = QComboBox() self.scopeComboBox.addItem("User") self.scopeComboBox.addItem("System") self.organizationComboBox = QComboBox() self.organizationComboBox.addItem("Trolltech") self.organizationComboBox.setEditable(True) self.applicationComboBox = QComboBox() self.applicationComboBox.addItem("Any") self.applicationComboBox.addItem("Application Example") self.applicationComboBox.addItem("Assistant") self.applicationComboBox.addItem("Designer") self.applicationComboBox.addItem("Linguist") self.applicationComboBox.setEditable(True) self.applicationComboBox.setCurrentIndex(3) formatLabel = QLabel("&Format:") formatLabel.setBuddy(self.formatComboBox) scopeLabel = QLabel("&Scope:") scopeLabel.setBuddy(self.scopeComboBox) organizationLabel = QLabel("&Organization:") organizationLabel.setBuddy(self.organizationComboBox) applicationLabel = QLabel("&Application:") applicationLabel.setBuddy(self.applicationComboBox) self.locationsGroupBox = QGroupBox("Setting Locations") self.locationsTable = QTableWidget() self.locationsTable.setSelectionMode(QAbstractItemView.SingleSelection) self.locationsTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.locationsTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.locationsTable.setColumnCount(2) self.locationsTable.setHorizontalHeaderLabels(("Location", "Access")) self.locationsTable.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch ) self.locationsTable.horizontalHeader().resizeSection(1, 180) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.formatComboBox.activated.connect(self.updateLocationsTable) self.scopeComboBox.activated.connect(self.updateLocationsTable) self.organizationComboBox.lineEdit().editingFinished.connect( self.updateLocationsTable ) self.applicationComboBox.lineEdit().editingFinished.connect( self.updateLocationsTable ) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) locationsLayout = QVBoxLayout() locationsLayout.addWidget(self.locationsTable) self.locationsGroupBox.setLayout(locationsLayout) mainLayout = QGridLayout() mainLayout.addWidget(formatLabel, 0, 0) mainLayout.addWidget(self.formatComboBox, 0, 1) mainLayout.addWidget(scopeLabel, 1, 0) mainLayout.addWidget(self.scopeComboBox, 1, 1) mainLayout.addWidget(organizationLabel, 2, 0) mainLayout.addWidget(self.organizationComboBox, 2, 1) mainLayout.addWidget(applicationLabel, 3, 0) mainLayout.addWidget(self.applicationComboBox, 3, 1) mainLayout.addWidget(self.locationsGroupBox, 4, 0, 1, 2) mainLayout.addWidget(self.buttonBox, 5, 0, 1, 2) self.setLayout(mainLayout) self.updateLocationsTable() self.setWindowTitle("Open Application Settings") self.resize(650, 400) def format(self): if self.formatComboBox.currentIndex() == 0: return QSettings.NativeFormat else: return QSettings.IniFormat def scope(self): if self.scopeComboBox.currentIndex() == 0: return QSettings.UserScope else: return QSettings.SystemScope def organization(self): return self.organizationComboBox.currentText() def application(self): if self.applicationComboBox.currentText() == "Any": return "" return self.applicationComboBox.currentText() def updateLocationsTable(self): self.locationsTable.setUpdatesEnabled(False) self.locationsTable.setRowCount(0) for i in range(2): if i == 0: if self.scope() == QSettings.SystemScope: continue actualScope = QSettings.UserScope else: actualScope = QSettings.SystemScope for j in range(2): if j == 0: if not self.application(): continue actualApplication = self.application() else: actualApplication = "" settings = QSettings( self.format(), actualScope, self.organization(), actualApplication ) row = self.locationsTable.rowCount() self.locationsTable.setRowCount(row + 1) item0 = QTableWidgetItem() item0.setText(settings.fileName()) item1 = QTableWidgetItem() disable = not (settings.childKeys() or settings.childGroups()) if row == 0: if settings.isWritable(): item1.setText("Read-write") disable = False else: item1.setText("Read-only") self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(disable) else: item1.setText("Read-only fallback") if disable: item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) self.locationsTable.setItem(row, 0, item0) self.locationsTable.setItem(row, 1, item1) self.locationsTable.setUpdatesEnabled(True)
class Widget(QWidget): def __init__(self, dockwidget): super(Widget, self).__init__(dockwidget) self._document = None self._fileSelector = QComboBox(editable=True, insertPolicy=QComboBox.NoInsert) gadgets.drag.ComboDrag(self._fileSelector).role = Qt.UserRole self._fileSelector.lineEdit().setReadOnly(True) self._fileSelector.lineEdit().setFocusPolicy(Qt.NoFocus) self._stopButton = QToolButton() self._playButton = QToolButton() self._timeSlider = QSlider(Qt.Horizontal, tracking=False, singleStep=500, pageStep=5000, invertedControls=True) self._display = Display() self._tempoFactor = QSlider(Qt.Vertical, minimum=-50, maximum=50, singleStep=1, pageStep=5) grid = QGridLayout(spacing=0) self.setLayout(grid) grid.addWidget(self._fileSelector, 0, 0, 1, 3) grid.addWidget(self._stopButton, 1, 0) grid.addWidget(self._playButton, 1, 1) grid.addWidget(self._timeSlider, 1, 2) grid.addWidget(self._display, 2, 0, 1, 3) grid.addWidget(self._tempoFactor, 0, 3, 3, 1) # size policy of combo p = self._fileSelector.sizePolicy() p.setHorizontalPolicy(QSizePolicy.Ignored) self._fileSelector.setSizePolicy(p) # size policy of combo popup p = self._fileSelector.view().sizePolicy() p.setHorizontalPolicy(QSizePolicy.MinimumExpanding) self._fileSelector.view().setSizePolicy(p) self._player = player.Player() self._outputCloseTimer = QTimer(interval=60000, singleShot=True, timeout=self.closeOutput) self._timeSliderTicker = QTimer(interval=200, timeout=self.updateTimeSlider) self._fileSelector.activated[int].connect(self.slotFileSelected) self._tempoFactor.valueChanged.connect(self.slotTempoChanged) self._timeSlider.valueChanged.connect(self.slotTimeSliderChanged) self._timeSlider.sliderMoved.connect(self.slotTimeSliderMoved) self._player.beat.connect(self.updateDisplayBeat) self._player.time.connect(self.updateDisplayTime) self._player.stateChanged.connect(self.slotPlayerStateChanged) self.slotPlayerStateChanged(False) dockwidget.mainwindow().currentDocumentChanged.connect(self.loadResults) app.jobFinished.connect(self.slotUpdatedFiles) app.aboutToQuit.connect(self.stop) midihub.aboutToRestart.connect(self.slotAboutToRestart) midihub.settingsChanged.connect(self.clearMidiSettings, -100) midihub.settingsChanged.connect(self.readMidiSettings) app.documentClosed.connect(self.slotDocumentClosed) app.translateUI(self) self.readMidiSettings() d = dockwidget.mainwindow().currentDocument() if d: self.loadResults(d) def translateUI(self): self._tempoFactor.setToolTip(_("Tempo")) def slotAboutToRestart(self): self.stop() self._player.set_output(None) def clearMidiSettings(self): """Called first when settings are changed.""" self.stop() self._outputCloseTimer.stop() self._player.set_output(None) def readMidiSettings(self): """Called after clearMidiSettings(), and on first init.""" pass def openOutput(self): """Called when playing starts. Ensures an output port is opened.""" self._outputCloseTimer.stop() if not self._player.output(): p = QSettings().value("midi/player/output_port", midihub.default_output(), str) o = midihub.output_by_name(p) if o: self._player.set_output(output.Output(o)) def closeOutput(self): """Called when the output close timer fires. Closes the output.""" self._player.set_output(None) def slotPlayerStateChanged(self, playing): ac = self.parentWidget().actionCollection # setDefaultAction also adds the action for b in self._stopButton, self._playButton: while b.actions(): b.removeAction(b.actions()[0]) if playing: self._timeSliderTicker.start() self._stopButton.setDefaultAction(ac.midi_stop) self._playButton.setDefaultAction(ac.midi_pause) else: self._timeSliderTicker.stop() self.updateTimeSlider() self._stopButton.setDefaultAction(ac.midi_restart) self._playButton.setDefaultAction(ac.midi_play) # close the output if the preference is set if QSettings().value("midi/close_outputs", False, bool): self._outputCloseTimer.start() def play(self): """Starts the MIDI player, opening an output if necessary.""" if not self._player.is_playing() and not self._player.has_events(): self.restart() self.openOutput() if not self._player.output(): self._display.statusMessage(_("No output found!")) self._player.start() def stop(self): """Stops the MIDI player.""" self._player.stop() def restart(self): """Restarts the MIDI player. If another file is in the file selector, or the file was updated, the new file is loaded. """ self._player.seek(0) self.updateTimeSlider() self._display.reset() if self._document: files = midifiles.MidiFiles.instance(self._document) index = self._fileSelector.currentIndex() if files and (files.song(index) is not self._player.song()): self.loadSong(index) def slotTempoChanged(self, value): """Called when the user drags the tempo.""" # convert -50 to 50 to 0.5 to 2.0 factor = 2 ** (value / 50.0) self._player.set_tempo_factor(factor) self._display.setTempo("{0}%".format(int(factor * 100))) def slotTimeSliderChanged(self, value): self._player.seek(value) self._display.setTime(value) if self._player.song(): self._display.setBeat(*self._player.song().beat(value)[1:]) def slotTimeSliderMoved(self, value): self._display.setTime(value) if self._player.song(): self._display.setBeat(*self._player.song().beat(value)[1:]) def updateTimeSlider(self): if not self._timeSlider.isSliderDown(): with qutil.signalsBlocked(self._timeSlider): self._timeSlider.setMaximum(self._player.total_time()) self._timeSlider.setValue(self._player.current_time()) def updateDisplayBeat(self, measnum, beat, num, den): if not self._timeSlider.isSliderDown(): self._display.setBeat(measnum, beat, num, den) def updateDisplayTime(self, time): if not self._timeSlider.isSliderDown(): self._display.setTime(time) def slotUpdatedFiles(self, document, job=None): """Called when there are new MIDI files.""" mainwindow = self.parentWidget().mainwindow() import engrave if document not in (mainwindow.currentDocument(), engrave.engraver(mainwindow).document()): return import jobattributes if job and jobattributes.get(job).mainwindow != mainwindow: return self.loadResults(document) def loadResults(self, document): files = midifiles.MidiFiles.instance(document) if files.update(): self._document = document self._fileSelector.setModel(files.model()) self._fileSelector.setCurrentIndex(files.current) if not self._player.is_playing(): self.loadSong(files.current) def loadSong(self, index): files = midifiles.MidiFiles.instance(self._document) self._player.set_song(files.song(index)) m, s = divmod(self._player.total_time() // 1000, 60) name = self._fileSelector.currentText() self.updateTimeSlider() self._display.reset() self._display.statusMessage( _("midi lcd screen", "LOADED"), name, _("midi lcd screen", "TOTAL"), "{0}:{1:02}".format(m, s)) def slotFileSelected(self, index): if self._document: self._player.stop() files = midifiles.MidiFiles.instance(self._document) if files: files.current = index self.restart() def slotDocumentClosed(self, document): if document == self._document: self._document = None self._fileSelector.clear() self._player.stop() self._player.clear() self.updateTimeSlider() self._display.reset()
class LocationDialog(QDialog): def __init__(self, parent=None): super(LocationDialog, self).__init__(parent) self.formatComboBox = QComboBox() self.formatComboBox.addItem("Native") self.formatComboBox.addItem("INI") self.scopeComboBox = QComboBox() self.scopeComboBox.addItem("User") self.scopeComboBox.addItem("System") self.organizationComboBox = QComboBox() self.organizationComboBox.addItem("Trolltech") self.organizationComboBox.setEditable(True) self.applicationComboBox = QComboBox() self.applicationComboBox.addItem("Any") self.applicationComboBox.addItem("Application Example") self.applicationComboBox.addItem("Assistant") self.applicationComboBox.addItem("Designer") self.applicationComboBox.addItem("Linguist") self.applicationComboBox.setEditable(True) self.applicationComboBox.setCurrentIndex(3) formatLabel = QLabel("&Format:") formatLabel.setBuddy(self.formatComboBox) scopeLabel = QLabel("&Scope:") scopeLabel.setBuddy(self.scopeComboBox) organizationLabel = QLabel("&Organization:") organizationLabel.setBuddy(self.organizationComboBox) applicationLabel = QLabel("&Application:") applicationLabel.setBuddy(self.applicationComboBox) self.locationsGroupBox = QGroupBox("Setting Locations") self.locationsTable = QTableWidget() self.locationsTable.setSelectionMode(QAbstractItemView.SingleSelection) self.locationsTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.locationsTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.locationsTable.setColumnCount(2) self.locationsTable.setHorizontalHeaderLabels(("Location", "Access")) self.locationsTable.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch) self.locationsTable.horizontalHeader().resizeSection(1, 180) self.buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.formatComboBox.activated.connect(self.updateLocationsTable) self.scopeComboBox.activated.connect(self.updateLocationsTable) self.organizationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable) self.applicationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) locationsLayout = QVBoxLayout() locationsLayout.addWidget(self.locationsTable) self.locationsGroupBox.setLayout(locationsLayout) mainLayout = QGridLayout() mainLayout.addWidget(formatLabel, 0, 0) mainLayout.addWidget(self.formatComboBox, 0, 1) mainLayout.addWidget(scopeLabel, 1, 0) mainLayout.addWidget(self.scopeComboBox, 1, 1) mainLayout.addWidget(organizationLabel, 2, 0) mainLayout.addWidget(self.organizationComboBox, 2, 1) mainLayout.addWidget(applicationLabel, 3, 0) mainLayout.addWidget(self.applicationComboBox, 3, 1) mainLayout.addWidget(self.locationsGroupBox, 4, 0, 1, 2) mainLayout.addWidget(self.buttonBox, 5, 0, 1, 2) self.setLayout(mainLayout) self.updateLocationsTable() self.setWindowTitle("Open Application Settings") self.resize(650, 400) def format(self): if self.formatComboBox.currentIndex() == 0: return QSettings.NativeFormat else: return QSettings.IniFormat def scope(self): if self.scopeComboBox.currentIndex() == 0: return QSettings.UserScope else: return QSettings.SystemScope def organization(self): return self.organizationComboBox.currentText() def application(self): if self.applicationComboBox.currentText() == "Any": return '' return self.applicationComboBox.currentText() def updateLocationsTable(self): self.locationsTable.setUpdatesEnabled(False) self.locationsTable.setRowCount(0) for i in range(2): if i == 0: if self.scope() == QSettings.SystemScope: continue actualScope = QSettings.UserScope else: actualScope = QSettings.SystemScope for j in range(2): if j == 0: if not self.application(): continue actualApplication = self.application() else: actualApplication = '' settings = QSettings(self.format(), actualScope, self.organization(), actualApplication) row = self.locationsTable.rowCount() self.locationsTable.setRowCount(row + 1) item0 = QTableWidgetItem() item0.setText(settings.fileName()) item1 = QTableWidgetItem() disable = not (settings.childKeys() or settings.childGroups()) if row == 0: if settings.isWritable(): item1.setText("Read-write") disable = False else: item1.setText("Read-only") self.buttonBox.button(QDialogButtonBox.Ok).setDisabled(disable) else: item1.setText("Read-only fallback") if disable: item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) self.locationsTable.setItem(row, 0, item0) self.locationsTable.setItem(row, 1, item1) self.locationsTable.setUpdatesEnabled(True)
class DuelLinksGui(QFrame, QMainWindow): _shouldShowSystrayBox = mock_data dlRunTime = None def __init__(self, duelLinksRunTime=None, assets=None): super(DuelLinksGui, self).__init__() self.assets = assets assert (type(duelLinksRunTime) is DuelLinkRunTime) self.dlRunTime = duelLinksRunTime # type: DuelLinkRunTime self.createRunTimeFields() self.createBotControls() self.setObjectName("BotFrame") self.setStyleSheet("#BotFrame {border: 2px solid #9e3939;}") self.createActions() self.createBotActions() self.createTrayIcon() self.setShouldShowSystrayBox(mock_data) self.hideButton.clicked.connect(self.close) self.exitButton.clicked.connect(self.__quit__) self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) # bot actions connected self.pauseButton.clicked.connect(self.pause_bot) self.runButton.clicked.connect(self.start_bot) # log creation textViewLog = QtHandler() # You can format what is printed to text box textViewLog.setFormatter( logging.Formatter( '%(asctime)s | %(levelname)s | %(name)s | %(message)s', datefmt='%Y-%m-%d %H:%M:%S')) logging.getLogger('bot').addHandler(textViewLog) # self.textViewLog.signal.connect(self.add_to_log) self.tabs = QTabWidget(self) self.tab1 = QWidget(self) mainLayout = QVBoxLayout(self.tab1) mainLayout.addWidget(self.runTimeGroupBox) mainLayout.addWidget(self.botControls) self.tab1.setLayout(mainLayout) self.tabs.addTab(self.tab1, "General") self.clear_log = QPushButton("Clear log") self.tab2 = QWidget(self) logLayout = QVBoxLayout(self.tab2) self.textViewLog = QTextEdit(self.tab2) self.textViewLog.setReadOnly(True) self.clear_log.clicked.connect(self.textViewLog.clear) XStream.stdout().messageWritten.connect(self.add_to_log) XStream.stderr().messageWritten.connect(self.add_to_log) logLayout.addWidget(self.textViewLog) logLayout.addWidget(self.clear_log) self.tab2.setLayout(logLayout) self.tabs.addTab(self.tab2, "Log") viewlayout = QVBoxLayout(self) viewlayout.addWidget(self.tabs) self.setLayout(viewlayout) self.setIcon() self.trayIcon.show() self.setWindowTitle(app_name) self.setFixedSize(400, 300) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup) self.location_on_the_screen() self.update_values(True) def add_to_log(self, msg): try: cursor = self.textViewLog.textCursor() src = msg.split('|') if len(src) != 4: self.textViewLog.append(msg) else: text = "" text += "<span>" text += "<b>{}</b>".format(src[0]) text += "<span style=\"color:blue;\">{}</span>".format(src[1]) text += src[2] text += src[3] text += "</span>" cursor.insertHtml(text + "<br>") self.textViewLog.moveCursor(QtGui.QTextCursor.End) except Exception as e: print('Error on updating log: ', end='') print(e) def location_on_the_screen(self): ag = QDesktopWidget().availableGeometry() sg = QDesktopWidget().screenGeometry() widget = self.geometry() position = self.get_task_bar_position() if position == WINDOWS_TASKBAR_LOCATION.BOTTOM: x = ag.width() - widget.width() y = 2 * ag.height() - sg.height() - widget.height() elif position == WINDOWS_TASKBAR_LOCATION.LEFT: x = sg.width() - ag.width() + default_open_offset y = 2 * ag.height() - sg.height() - widget.height( ) - default_open_offset elif position == WINDOWS_TASKBAR_LOCATION.TOP: x = ag.width() - widget.width() - default_open_offset y = sg.height() - ag.height() + default_open_offset elif position == WINDOWS_TASKBAR_LOCATION.RIGHT: x = ag.width() - widget.width() - default_open_offset y = 2 * ag.height() - sg.height() - widget.height( ) - default_open_offset self.move(x, y) def get_task_bar_position(self): desktop = QDesktopWidget() displayRect = desktop.screenGeometry() desktopRect = desktop.availableGeometry() if desktopRect.height() < displayRect.height(): if desktopRect.y() > displayRect.y(): return WINDOWS_TASKBAR_LOCATION.TOP else: return WINDOWS_TASKBAR_LOCATION.BOTTOM else: if desktopRect.x() > displayRect.x(): return WINDOWS_TASKBAR_LOCATION.LEFT else: return WINDOWS_TASKBAR_LOCATION.RIGHT def setVisible(self, visible): self.minimizeAction.setEnabled(visible) self.maximizeAction.setEnabled(not self.isMaximized()) self.restoreAction.setEnabled(self.isMaximized() or not visible) super(DuelLinksGui, self).setVisible(visible) def closeEvent(self, event): if self.trayIcon.isVisible(): if self.shouldShowSystrayBox(): QMessageBox.information( self, app_name, "The program will keep running in the system tray. To " "terminate the program, choose <b>Quit</b> in the " "context menu of the system tray entry.") self.hide() event.ignore() def setShouldShowSystrayBox(self, callback): self._shouldShowSystrayBox = callback def shouldShowSystrayBox(self): self._shouldShowSystrayBox() def setIcon(self): icon = QIcon(QIcon(':/assets/yugioh.ico')) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip('Duel-Links Bot') def iconActivated(self, reason): if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): self.showNormal() elif reason == QSystemTrayIcon.MiddleClick: self.showNotifcation( "In Development", "You pressed the middle mouse button.\n Hidden Feature!!!!") def showMessage(self): icon = QSystemTrayIcon.MessageIcon( self.typeComboBox.itemData(self.typeComboBox.currentIndex())) self.trayIcon.showMessage(self.titleEdit.text(), self.bodyEdit.toPlainText(), icon, self.durationSpinBox.value() * 1000) def showNotifcation(self, title, message): icon = QSystemTrayIcon.MessageIcon( self.typeComboBox.itemData(self.typeComboBox.currentIndex())) self.trayIcon.showMessage(title, message, icon, self.durationSpinBox.value() * 1000) def messageClicked(self): QMessageBox.information( None, "Systray", "Sorry, I already gave what help I could.\nMaybe you should " "try asking a human?") def modeChange(self, index): self.dlRunTime.playmode = self.available_modes.currentData() def createBotControls(self): self.botControls = QGroupBox("Controls") controlLayout = QGridLayout() self.runLabel = QLabel("Run the bot:") self.modeLabel = QLabel("Current Mode:") self.available_modes = QComboBox() for index, mode in enumerate(self.dlRunTime._available_modes): self.available_modes.addItem(mode.title(), mode) self.available_modes.setStyleSheet("QComboBox {text-align: center;}") self.available_modes.setEditable(True) self.available_modes.lineEdit().setReadOnly(True) self.available_modes.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.available_modes.setCurrentIndex( self.dlRunTime._available_modes.index(self.dlRunTime.playmode)) self.available_modes.currentIndexChanged.connect(self.modeChange) # self.available_modes.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.runButton = QPushButton("Run") self.showLabel = QLabel("Pause the bot:") self.pauseButton = QPushButton("Pause") self.exitButton = QPushButton("Exit") self.hideButton = QPushButton("Hide") controlLayout.addWidget(self.modeLabel, 0, 0, 1, 2) controlLayout.addWidget(self.available_modes, 0, 2, 1, 2) controlLayout.addWidget(self.runLabel, 1, 0) controlLayout.addWidget(self.runButton, 1, 2, 1, 2) controlLayout.addWidget(self.showLabel, 2, 0) controlLayout.addWidget(self.pauseButton, 2, 2, 1, 2) controlLayout.addWidget(self.hideButton, 3, 0, 1, 2) controlLayout.addWidget(self.exitButton, 3, 2, 1, 2) self.botControls.setLayout(controlLayout) def createRunTimeFields(self): self.runTimeGroupBox = QGroupBox("RunTime Fields") self.current_time = QLabel("Current Time: ") self.current_time_value = QLabel("") self.nox_status_label = QLabel("{} status: ".format( self.dlRunTime.get_provider().__str__())) self.nox_status_value = QLabel("") self.next_run_at_label = QLabel("Next Run At:") self.next_run_at_value = QLabel("") self.in_timer = QtCore.QTimer(self) self.in_timer.setInterval(1000) self.in_timer.timeout.connect(self.update_values) self.in_timer.start() layout = QVBoxLayout() top = QHBoxLayout() top.addWidget(self.current_time) top.addWidget(self.current_time_value) top.addStretch() runTimeLayout = QHBoxLayout() runTimeLayout.addWidget(self.nox_status_label) runTimeLayout.addWidget(self.nox_status_value) runTimeLayout.addStretch() runTimeLayout.addWidget(self.next_run_at_label) runTimeLayout.addWidget(self.next_run_at_value) layout.addLayout(top) layout.addLayout(runTimeLayout) self.runTimeGroupBox.setLayout(layout) _counter = 0 def update_values(self, force=False): self._counter += 1 if self._counter % update_intervals.get('current_time', 1) == 0 or force: self.current_time_value.setText( QtCore.QDateTime.currentDateTime().toString()) if self._counter % update_intervals.get('nox_status', 1) == 0 or force: self.nox_status_value.setText((lambda: "Running" if self.dlRunTime.get_provider(). is_process_running() else "Off")()) if self._counter % update_intervals.get('next_run_at', 1) == 0 or force: self.next_run_at_value.setText( self.dlRunTime.next_run_at.strftime("%Y-%m-%dT%H:%M:%S")) if self.dlRunTime.get_provider().current_thread is not None: self.runButton.setDisabled(False) self.runButton.setEnabled(False) self.pauseButton.setDisabled(True) self.pauseButton.setEnabled(True) else: self.runButton.setDisabled(True) self.runButton.setEnabled(True) self.pauseButton.setDisabled(False) self.pauseButton.setEnabled(False) if self.dlRunTime._shutdown: self.__quit__() def createActions(self): self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.quitAction = QAction("&Quit", self, triggered=self.__quit__) def __quit__(self): QApplication.instance().closingDown() self.hide() if not self.dlRunTime._shutdown: self.dlRunTime.shutdown() self.in_timer.stop() self.in_timer.deleteLater() self.close() qApp.closeAllWindows() time.sleep(1) del self.dlRunTime QApplication.instance().quit() def createBotActions(self): self.startAction = QAction('Start', self, triggered=self.start_bot) self.pauseAction = QAction('Pause', self, triggered=self.pause_bot) def start_bot(self): self.dlRunTime.stop = False self.dlRunTime.run_now = True def pause_bot(self): self.dlRunTime.stop = True self.dlRunTime.run_now = False def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu)
class DuelLinksGui(QFrame): _shouldShowSystrayBox = mock_data dlRunTime = None def __init__(self, duelLinksRunTime=None, assets=None): super(DuelLinksGui, self).__init__() self.assets = assets assert (type(duelLinksRunTime) is DuelLinkRunTime) self.dlRunTime = duelLinksRunTime # type: DuelLinkRunTime # self.createIconGroupBox() self.createRunTimeFields() self.createMessageGroupBox() self.createBotControls() self.setObjectName("BotFrame") self.setStyleSheet("#BotFrame {border: 2px solid #9e3939;}") self.createActions() self.createBotActions() self.createTrayIcon() self.setShouldShowSystrayBox(mock_data) # self.showMessageButton.clicked.connect(self.showMessage) self.hideButton.clicked.connect(self.close) self.exitButton.clicked.connect(self.__quit__) # self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible) # self.iconComboBox.currentIndexChanged.connect(self.setIcon) self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) # bot actions connected self.pauseButton.clicked.connect(self.pause_bot) self.runButton.clicked.connect(self.start_bot) mainLayout = QVBoxLayout() mainLayout.addWidget(self.runTimeGroupBox) mainLayout.addWidget(self.botControls) # mainLayout.addWidget(self.iconGroupBox) # mainLayout.addWidget(self.messageGroupBox) self.setLayout(mainLayout) self.setIcon() self.trayIcon.show() self.setWindowTitle(app_name) self.setFixedSize(400, 300) self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Popup) self.location_on_the_screen() self.update_values(True) def location_on_the_screen(self): ag = QDesktopWidget().availableGeometry() sg = QDesktopWidget().screenGeometry() print(ag, sg) widget = self.geometry() position = self.get_task_bar_position() if position == WINDOWS_TASKBAR_LOCATION.BOTTOM: x = ag.width() - widget.width() y = 2 * ag.height() - sg.height() - widget.height() elif position == WINDOWS_TASKBAR_LOCATION.LEFT: x = sg.width() - ag.width() + default_open_offset y = 2 * ag.height() - sg.height() - widget.height( ) - default_open_offset elif position == WINDOWS_TASKBAR_LOCATION.TOP: x = ag.width() - widget.width() - default_open_offset y = sg.height() - ag.height() + default_open_offset elif position == WINDOWS_TASKBAR_LOCATION.RIGHT: x = ag.width() - widget.width() - default_open_offset y = 2 * ag.height() - sg.height() - widget.height( ) - default_open_offset self.move(x, y) def get_task_bar_position(self): desktop = QDesktopWidget() displayRect = desktop.screenGeometry() desktopRect = desktop.availableGeometry() if desktopRect.height() < displayRect.height(): if desktopRect.y() > displayRect.y(): return WINDOWS_TASKBAR_LOCATION.TOP else: return WINDOWS_TASKBAR_LOCATION.BOTTOM else: if desktopRect.x() > displayRect.x(): return WINDOWS_TASKBAR_LOCATION.LEFT else: return WINDOWS_TASKBAR_LOCATION.RIGHT def setVisible(self, visible): self.minimizeAction.setEnabled(visible) self.maximizeAction.setEnabled(not self.isMaximized()) self.restoreAction.setEnabled(self.isMaximized() or not visible) super(DuelLinksGui, self).setVisible(visible) def closeEvent(self, event): if self.trayIcon.isVisible(): if self.shouldShowSystrayBox(): QMessageBox.information( self, app_name, "The program will keep running in the system tray. To " "terminate the program, choose <b>Quit</b> in the " "context menu of the system tray entry.") self.hide() event.ignore() def setShouldShowSystrayBox(self, callback): self._shouldShowSystrayBox = callback def shouldShowSystrayBox(self): self._shouldShowSystrayBox() def setIcon(self): icon = QIcon(QIcon(':/assets/yugioh.ico')) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip('Duel-Links Bot') def iconActivated(self, reason): if reason in (QSystemTrayIcon.Trigger, QSystemTrayIcon.DoubleClick): self.showNormal() elif reason == QSystemTrayIcon.MiddleClick: self.showNotifcation( "In Development", "You pressed the middle mouse button.\n Hidden Feature!!!!") def showMessage(self): icon = QSystemTrayIcon.MessageIcon( self.typeComboBox.itemData(self.typeComboBox.currentIndex())) self.trayIcon.showMessage(self.titleEdit.text(), self.bodyEdit.toPlainText(), icon, self.durationSpinBox.value() * 1000) def showNotifcation(self, title, message): icon = QSystemTrayIcon.MessageIcon( self.typeComboBox.itemData(self.typeComboBox.currentIndex())) self.trayIcon.showMessage(title, message, icon, self.durationSpinBox.value() * 1000) def messageClicked(self): QMessageBox.information( None, "Systray", "Sorry, I already gave what help I could.\nMaybe you should " "try asking a human?") def modeChange(self, index): self.dlRunTime.playmode = self.available_modes.currentData() def createIconGroupBox(self): self.iconGroupBox = QGroupBox("Tray Icon") self.iconLabel = QLabel("Icon:") self.iconComboBox = QComboBox() self.iconComboBox.addItem(QIcon('assets/yugioh.ico'), "Duel-Card") self.showIconCheckBox = QCheckBox("Show icon") self.showIconCheckBox.setChecked(True) iconLayout = QHBoxLayout() iconLayout.addWidget(self.iconLabel) iconLayout.addWidget(self.iconComboBox) iconLayout.addStretch() iconLayout.addWidget(self.showIconCheckBox) self.iconGroupBox.setLayout(iconLayout) def createBotControls(self): self.botControls = QGroupBox("Controls") controlLayout = QGridLayout() self.runLabel = QLabel("Run the bot:") self.modeLabel = QLabel("Current Mode:") self.available_modes = QComboBox() for index, mode in enumerate(self.dlRunTime._available_modes): self.available_modes.addItem(mode.title(), mode) self.available_modes.setStyleSheet("QComboBox {text-align: center;}") self.available_modes.setEditable(True) self.available_modes.lineEdit().setReadOnly(True) self.available_modes.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.available_modes.setCurrentIndex( self.dlRunTime._available_modes.index(self.dlRunTime.playmode)) self.available_modes.currentIndexChanged.connect(self.modeChange) # self.available_modes.lineEdit().setAlignment(QtCore.Qt.AlignCenter) self.runButton = QPushButton("Run") self.showLabel = QLabel("Pause the bot:") self.pauseButton = QPushButton("Pause") self.exitButton = QPushButton("Exit") self.hideButton = QPushButton("Hide") controlLayout.addWidget(self.modeLabel, 0, 0, 1, 2) controlLayout.addWidget(self.available_modes, 0, 2, 1, 2) controlLayout.addWidget(self.runLabel, 1, 0) controlLayout.addWidget(self.runButton, 1, 2, 1, 2) controlLayout.addWidget(self.showLabel, 2, 0) controlLayout.addWidget(self.pauseButton, 2, 2, 1, 2) controlLayout.addWidget(self.hideButton, 3, 0, 1, 2) controlLayout.addWidget(self.exitButton, 3, 2, 1, 2) self.botControls.setLayout(controlLayout) def createMessageGroupBox(self): # self.messageGroupBox = QGroupBox("Balloon Message") typeLabel = QLabel("Type:") self.typeComboBox = QComboBox() self.typeComboBox.addItem("None", QSystemTrayIcon.NoIcon) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxInformation), "Information", QSystemTrayIcon.Information) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxWarning), "Warning", QSystemTrayIcon.Warning) self.typeComboBox.addItem( self.style().standardIcon(QStyle.SP_MessageBoxCritical), "Critical", QSystemTrayIcon.Critical) self.typeComboBox.setCurrentIndex(1) self.durationLabel = QLabel("Duration:") self.durationSpinBox = QSpinBox() self.durationSpinBox.setRange(5, 60) self.durationSpinBox.setSuffix(" s") self.durationSpinBox.setValue(15) durationWarningLabel = QLabel("(some systems might ignore this hint)") durationWarningLabel.setIndent(10) titleLabel = QLabel("Title:") self.titleEdit = QLineEdit("Cannot connect to network") bodyLabel = QLabel("Body:") self.bodyEdit = QTextEdit() self.bodyEdit.setPlainText("Don't believe me. Honestly, I don't have " "a clue.\nClick this balloon for details.") # self.showMessageButton = QPushButton("Show Message") # self.showMessageButton.setDefault(True) """ messageLayout = QGridLayout() messageLayout.addWidget(typeLabel, 0, 0) messageLayout.addWidget(self.typeComboBox, 0, 1, 1, 2) messageLayout.addWidget(self.durationLabel, 1, 0) messageLayout.addWidget(self.durationSpinBox, 1, 1) messageLayout.addWidget(durationWarningLabel, 1, 2, 1, 3) messageLayout.addWidget(titleLabel, 2, 0) messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4) messageLayout.addWidget(bodyLabel, 3, 0) messageLayout.addWidget(self.bodyEdit, 3, 1, 2, 4) messageLayout.addWidget(self.showMessageButton, 5, 4) messageLayout.setColumnStretch(3, 1) messageLayout.setRowStretch(4, 1) self.messageGroupBox.setLayout(messageLayout)""" def createRunTimeFields(self): self.runTimeGroupBox = QGroupBox("RunTime Fields") self.current_time = QLabel("Current Time: ") self.current_time_value = QLabel("") self.nox_status_label = QLabel("{} status: ".format( self.dlRunTime.get_provider().__str__())) self.nox_status_value = QLabel("") self.next_run_at_label = QLabel("Next Run At:") self.next_run_at_value = QLabel("") self.in_timer = QtCore.QTimer(self) self.in_timer.setInterval(1000) self.in_timer.timeout.connect(self.update_values) self.in_timer.start() layout = QVBoxLayout() top = QHBoxLayout() top.addWidget(self.current_time) top.addWidget(self.current_time_value) top.addStretch() runTimeLayout = QHBoxLayout() runTimeLayout.addWidget(self.nox_status_label) runTimeLayout.addWidget(self.nox_status_value) runTimeLayout.addStretch() runTimeLayout.addWidget(self.next_run_at_label) runTimeLayout.addWidget(self.next_run_at_value) layout.addLayout(top) layout.addLayout(runTimeLayout) self.runTimeGroupBox.setLayout(layout) _counter = 0 def update_values(self, force=False): self._counter += 1 if self._counter % update_intervals.get('current_time', 1) == 0 or force: self.current_time_value.setText( QtCore.QDateTime.currentDateTime().toString()) if self._counter % update_intervals.get('nox_status', 1) == 0 or force: self.nox_status_value.setText((lambda: "Running" if self.dlRunTime.get_provider(). is_process_running() else "Off")()) if self._counter % update_intervals.get('next_run_at', 1) == 0 or force: self.next_run_at_value.setText( self.dlRunTime.next_run_at.strftime("%Y-%m-%dT%H:%M:%S")) if self.dlRunTime.get_provider().current_thread is not None: self.runButton.setDisabled(False) self.runButton.setEnabled(False) self.pauseButton.setDisabled(True) self.pauseButton.setEnabled(True) else: self.runButton.setDisabled(True) self.runButton.setEnabled(True) self.pauseButton.setDisabled(False) self.pauseButton.setEnabled(False) if self.dlRunTime._shutdown: self.hide() QApplication.instance().quit() def createActions(self): self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.quitAction = QAction("&Quit", self, triggered=self.__quit__) def __quit__(self): self.hide() self.dlRunTime.shutdown() self.close() def createBotActions(self): self.startAction = QAction('Start', self, triggered=self.start_bot) self.pauseAction = QAction('Pause', self, triggered=self.pause_bot) def start_bot(self): self.dlRunTime.stop = False self.dlRunTime.run_now = True def pause_bot(self): self.dlRunTime.stop = True self.dlRunTime.run_now = False def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu)
def addParam(self, *, layout=None, label='some param name', box=None, paramName='n/a', rowIndex=1, decimals=2, minimum=0, maximum=10000, unit='', float=True, listport=False, listmonitor=False, boolean=False): self.settings += [paramName] layout.addWidget(QLabel(label, box), rowIndex, 1, 1, 1) if listmonitor: valc = QComboBox() i = 0 for monitor in QApplication.screens(): valc.insertItem(i, str(i) + ': ' + monitor.name(), i) i += 1 sindex = valc.findData(AppData.configParams[paramName][-1][0]) if sindex == -1: sindex = 0 valc.setCurrentIndex(sindex) elif listport: valc = QComboBox() i = 0 for port in self.comports: valc.insertItem(i, 'RTS on ' + port[0] + ': ' + port[1], (port[0], 'RTS')) valc.insertItem(i, 'BREAK on ' + port[0] + ': ' + port[1], (port[0], 'BREAK')) i += 1 sindex = valc.findData(AppData.configParams[paramName][-1][0][1]) if sindex == -1: valc.insertItem( i, AppData.configParams[paramName][-1][0][1] + ' on ' + AppData.configParams[paramName][-1][0][0] + ': no device detected', AppData.configParams[paramName][-1][0]) else: valc.setCurrentIndex(sindex) elif boolean: valc = QComboBox() valc.insertItem(0, 'True', True) valc.insertItem(1, 'False', False) sindex = valc.findData(AppData.configParams[paramName][-1][0]) valc.setCurrentIndex(sindex) else: if float: valc = QDoubleSpinBox() valc.setDecimals(decimals) else: valc = QSpinBox() valc.lineEdit().setCursor(AppData.cursors['text']) valc.setMinimum(minimum) valc.setMaximum(maximum) valc.setValue(AppData.configParams[paramName][-1][0]) setattr(self, paramName, valc) dowc = QLabel() checkc = QPushButton() filec = QLabel() setattr(self, paramName + 'Default', checkc) def resetParam(): setattr(self, paramName + 'Override', False) if AppData.configParams[paramName][-1][1] == self.filename: dowc.setText('') checkc.setText('') filec.setText(unit + ' from ' + AppData.configParams[paramName][-2][1]) dowc.setEnabled(False) checkc.setEnabled(False) filec.setEnabled(False) valc.blockSignals(True) if listmonitor or listport or boolean: sindex = valc.findData( AppData.configParams[paramName][-2][0]) valc.setCurrentIndex(sindex) else: valc.setValue(AppData.configParams[paramName][-2][0]) valc.blockSignals(False) else: dowc.setText('') checkc.setText('') filec.setText(unit + ' from ' + AppData.configParams[paramName][-1][1]) dowc.setEnabled(False) checkc.setEnabled(False) filec.setEnabled(False) valc.blockSignals(True) if listmonitor or listport or boolean: sindex = valc.findData( AppData.configParams[paramName][-1][0]) valc.setCurrentIndex(sindex) else: valc.setValue(AppData.configParams[paramName][-1][0]) valc.blockSignals(False) def trumpParam(): setattr(self, paramName + 'Override', True) if AppData.configParams[paramName][-1][1] == self.filename: dowc.setText(unit + ' overriding') checkc.setText( str(AppData.configParams[paramName][-2][0]) + ' ' + unit) filec.setText('specified in ' + AppData.configParams[paramName][-2][1]) dowc.setEnabled(True) checkc.setEnabled(True) filec.setEnabled(True) else: dowc.setText(unit + ' overriding') checkc.setText( str(AppData.configParams[paramName][-1][0]) + ' ' + unit) filec.setText('\tspecified in ' + AppData.configParams[paramName][-1][1]) dowc.setEnabled(True) checkc.setEnabled(True) filec.setEnabled(True) checkc.pressed.connect(resetParam) if listport or listmonitor or boolean: valc.currentIndexChanged.connect(trumpParam) else: valc.valueChanged.connect(trumpParam) overridesParam = AppData.configParams[paramName][-1][ 1] == self.filename setattr(self, paramName + 'Override', overridesParam) if overridesParam: trumpParam() else: resetParam() layout.addWidget(valc, rowIndex, 2, 1, 1) layout.addWidget(dowc, rowIndex, 3, 1, 1) layout.addWidget(checkc, rowIndex, 4, 1, 1) layout.addWidget(filec, rowIndex, 5, 1, 1)
class Dialog(QDialog): def __init__(self, parent=None): super(Dialog, self).__init__(parent) self._filename = None self._page = None self._rect = None self.imageViewer = widgets.imageviewer.ImageViewer() self.dpiLabel = QLabel() self.dpiCombo = QComboBox(insertPolicy=QComboBox.NoInsert, editable=True) self.dpiCombo.lineEdit().setCompleter(None) self.dpiCombo.setValidator(QDoubleValidator(10.0, 1200.0, 4, self.dpiCombo)) self.dpiCombo.addItems([format(i) for i in (72, 100, 200, 300, 600, 1200)]) self.colorButton = widgets.colorbutton.ColorButton() self.colorButton.setColor(QColor(Qt.white)) self.grayscale = QCheckBox(checked=False) self.crop = QCheckBox() self.antialias = QCheckBox(checked=True) self.scaleup = QCheckBox(checked=False) self.dragfile = QPushButton(icons.get("image-x-generic"), None, None) self.fileDragger = FileDragger(self.dragfile) self.buttons = QDialogButtonBox(QDialogButtonBox.Close) self.copyButton = self.buttons.addButton('', QDialogButtonBox.ApplyRole) self.copyButton.setIcon(icons.get('edit-copy')) self.saveButton = self.buttons.addButton('', QDialogButtonBox.ApplyRole) self.saveButton.setIcon(icons.get('document-save')) layout = QVBoxLayout() self.setLayout(layout) layout.addWidget(self.imageViewer) controls = QHBoxLayout() layout.addLayout(controls) controls.addWidget(self.dpiLabel) controls.addWidget(self.dpiCombo) controls.addWidget(self.colorButton) controls.addWidget(self.grayscale) controls.addWidget(self.crop) controls.addWidget(self.antialias) controls.addWidget(self.scaleup) controls.addStretch() controls.addWidget(self.dragfile) layout.addWidget(widgets.Separator()) layout.addWidget(self.buttons) app.translateUI(self) self.readSettings() self.finished.connect(self.writeSettings) self.dpiCombo.editTextChanged.connect(self.drawImage) self.colorButton.colorChanged.connect(self.drawImage) self.grayscale.toggled.connect(self.drawImage) self.antialias.toggled.connect(self.drawImage) self.scaleup.toggled.connect(self.drawImage) self.crop.toggled.connect(self.cropImage) self.buttons.rejected.connect(self.reject) self.copyButton.clicked.connect(self.copyToClipboard) self.saveButton.clicked.connect(self.saveAs) qutil.saveDialogSize(self, "copy_image/dialog/size", QSize(480, 320)) def translateUI(self): self.setCaption() self.dpiLabel.setText(_("DPI:")) self.colorButton.setToolTip(_("Paper Color")) self.grayscale.setText(_("Gray")) self.grayscale.setToolTip(_("Convert image to grayscale.")) self.crop.setText(_("Auto-crop")) self.antialias.setText(_("Antialias")) self.scaleup.setText(_("Scale 2x")) self.scaleup.setToolTip(_( "Render twice as large and scale back down\n" "(recommended for small DPI values).")) self.dragfile.setText(_("Drag")) self.dragfile.setToolTip(_("Drag the image as a PNG file.")) self.copyButton.setText(_("&Copy to Clipboard")) self.saveButton.setText(_("&Save As...")) self.imageViewer.setWhatsThis(_( #xgettext:no-python-format "<p>\n" "Clicking toggles the display between 100% size and window size. " "Drag to copy the image to another application. " "Drag with Ctrl (or {command}) to scroll a large image.\n" "</p>\n" "<p>\n" "You can also drag the small picture icon in the bottom right, " "which drags the actual file on disk, e.g. to an e-mail message.\n" "</p>").format(command="\u2318")) def readSettings(self): s = QSettings() s.beginGroup('copy_image') self.dpiCombo.setEditText(s.value("dpi", "100", str)) self.colorButton.setColor(s.value("papercolor", QColor(Qt.white), QColor)) self.grayscale.setChecked(s.value("grayscale", False, bool)) self.crop.setChecked(s.value("autocrop", False, bool)) self.antialias.setChecked(s.value("antialias", True, bool)) self.scaleup.setChecked(s.value("scaleup", False, bool)) def writeSettings(self): s = QSettings() s.beginGroup('copy_image') s.setValue("dpi", self.dpiCombo.currentText()) s.setValue("papercolor", self.colorButton.color()) s.setValue("grayscale", self.grayscale.isChecked()) s.setValue("autocrop", self.crop.isChecked()) s.setValue("antialias", self.antialias.isChecked()) s.setValue("scaleup", self.scaleup.isChecked()) def setCaption(self): if self._filename: filename = os.path.basename(self._filename) else: filename = _("<unknown>") title = _("Image from {filename}").format(filename = filename) self.setWindowTitle(app.caption(title)) def setPage(self, page, rect, filename): self._page = page self._rect = rect self._filename = filename self.fileDragger.basename = os.path.splitext(os.path.basename(self._filename))[0] self.setCaption() self.drawImage() def drawImage(self): dpi = float(self.dpiCombo.currentText() or '100') dpi = max(dpi, self.dpiCombo.validator().bottom()) dpi = min(dpi, self.dpiCombo.validator().top()) options = qpopplerview.RenderOptions() options.setPaperColor(self.colorButton.color()) if self.antialias.isChecked(): if popplerqt5: options.setRenderHint( popplerqt5.Poppler.Document.Antialiasing | popplerqt5.Poppler.Document.TextAntialiasing) else: options.setRenderHint(0) m = 2 if self.scaleup.isChecked() else 1 i = self._page.image(self._rect, dpi * m, dpi * m , options) if m == 2: i = i.scaled(i.size() / 2, transformMode=Qt.SmoothTransformation) if self.grayscale.isChecked(): i = i.convertToFormat(QImage.Format_Grayscale8) self._image = i self.cropImage() def cropImage(self): image = self._image if self.crop.isChecked(): image = image.copy(autoCropRect(image)) self.imageViewer.setImage(image) self.fileDragger.setImage(image) def copyToClipboard(self): QApplication.clipboard().setImage(self.imageViewer.image()) def saveAs(self): if self._filename and not self.imageViewer.image().isNull(): filename = os.path.splitext(self._filename)[0] + ".png" else: filename = 'image.png' filename = QFileDialog.getSaveFileName(self, _("Save Image As"), filename)[0] if filename: if not self.imageViewer.image().save(filename): QMessageBox.critical(self, _("Error"), _( "Could not save the image.")) else: self.fileDragger.currentFile = filename
class DeviceManagementWidget(WidgetBase): # noinspection PyArgumentList,PyUnresolvedReferences def __init__( self, parent: typing.Optional[QWidget], on_connection_request: ConnectionRequestCallback, on_disconnection_request: DisconnectionRequestCallback, ): super(DeviceManagementWidget, self).__init__(parent) self.setAttribute( Qt.WA_DeleteOnClose) # This is required to stop background timers! self._port_discoverer = PortDiscoverer() self._port_mapping: typing.Dict[str:str] = {} self._port_combo = QComboBox(self) self._port_combo.setEditable(True) self._port_combo.setInsertPolicy(QComboBox.NoInsert) self._port_combo.setSizeAdjustPolicy(QComboBox.AdjustToContents) self._port_combo.setFont(get_monospace_font()) self._port_combo.lineEdit().returnPressed.connect( self._on_confirmation) self._connect_button = make_button(self, "Connect", "disconnected", on_clicked=self._on_confirmation) self._connect_button.setEnabled( False) # Empty by default, therefore disabled self._port_combo.currentTextChanged.connect( lambda: self._connect_button.setEnabled( bool(self._port_combo.currentText().strip()))) self._status_text = QLabel(self) self._status_text.setText(_STATUS_WHEN_NOT_CONNECTED) self._status_text.setWordWrap(True) self._device_info_widget = LittleBobbyTablesWidget(self) combo_completer = QCompleter() combo_completer.setCaseSensitivity(Qt.CaseInsensitive) combo_completer.setModel(self._port_combo.model()) self._port_combo.setCompleter(combo_completer) self._update_timer = QTimer(self) self._update_timer.timeout.connect(self._update_ports) self._update_timer.start(2000) self._connection_progress_bar = QProgressBar(self) self._connection_progress_bar.setMinimum(0) self._connection_progress_bar.setMaximum(100) self._connection_established = False self._last_task: typing.Optional[asyncio.Task] = None self._connection_request_callback: ConnectionRequestCallback = ( on_connection_request) self._disconnection_request_callback: DisconnectionRequestCallback = ( on_disconnection_request) # Layout self._overlay = QStackedLayout(self) self._init_overlay_widgets() self.setLayout(self._overlay) # Initialization self._update_ports() def on_connection_loss(self, reason: str): """ This method should be invoked when the connection becomes lost. It will cause the widget to change its state accordingly. :param reason: Human-readable description of the reason in one line. """ if self._connection_established: self._switch_state_disconnected() self._status_text.setText( f'Connection lost: {reason.strip() or "Unknown reason"}') def on_connection_initialization_progress_report(self, stage_description: str, progress: float): """ This method should be periodically invoked while connection is being initialized. :param stage_description: Human-readable short string displaying what is currently being done. E.g. "Opening port" :param progress: A float in [0, 1] that displays how much of the work has been completed so far, where 0 - nothing, 1 - all done. """ if self._overlay.currentIndex() != 1: raise RuntimeError( "Invalid usage: this method can only be invoked when connection initialization is " "in progress. Currently it is not.") # noinspection PyTypeChecker if not (0.0 <= progress <= 1.0): _logger.error( f"Connection progress estimate falls outside of [0, 1]: {progress}" ) stage_description = stage_description.strip() if stage_description[-1] in string.ascii_letters: stage_description += "..." self._connection_progress_bar.setValue(int(progress * 100)) self._connection_progress_bar.setFormat(stage_description) self._connection_progress_bar.setAlignment(Qt.AlignCenter) # noinspection PyArgumentList,PyUnresolvedReferences def _init_overlay_widgets(self): # Main widget operational = WidgetBase(self) operational_layout_top = QHBoxLayout() operational_layout_top.addWidget(QLabel("Port:")) operational_layout_top.addWidget(self._port_combo, stretch=1) operational_layout_top.addWidget(self._connect_button) operational_layout_bottom = QHBoxLayout() operational_layout_bottom.addWidget(self._status_text) operational_layout = QVBoxLayout() operational_layout.addLayout(operational_layout_top) operational_layout.addLayout(operational_layout_bottom) operational_layout.addWidget(self._device_info_widget, 1) operational.setLayout(operational_layout) self._overlay.addWidget(operational) # Progress widget - shown while connecting/disconnecting progress = WidgetBase(self) progress_layout = QVBoxLayout() progress_layout.addStretch(1) progress_layout.addWidget(self._connection_progress_bar) progress_layout.addStretch(1) progress.setLayout(progress_layout) self._overlay.addWidget(progress) def _update_ports(self): if self._connection_established: return # noinspection PyBroadException try: ports = self._port_discoverer.get_ports() except Exception as ex: _logger.exception("Could not list ports") self.flash(f"Could not list ports: {ex}", duration=10) ports = [] self._port_mapping = self._port_discoverer.display_ports( ports, self._port_combo) def _switch_state_connected(self, device_info: BasicDeviceInfo): self._connection_established = True self._overlay.setCurrentIndex(0) self._port_combo.setEnabled(False) self._connect_button.setEnabled(True) self._connect_button.setText("Disconnect") self._connect_button.setIcon(get_icon("connected")) self._status_text.setText("Connected") self._device_info_widget.set(device_info) def _switch_state_disconnected(self): self._connection_established = False self._overlay.setCurrentIndex(0) self._port_combo.setEnabled(True) self._connect_button.setEnabled(True) self._connect_button.setText("Connect") self._connect_button.setIcon(get_icon("disconnected")) self._device_info_widget.clear() self._status_text.setText(_STATUS_WHEN_NOT_CONNECTED) self._update_ports() async def _do_connect(self): _logger.info("Connection initialization task spawned") try: selected_port = self._port_mapping[str( self._port_combo.currentText()).strip()] except KeyError: selected_port = str(self._port_combo.currentText()).strip() # Activate the progress view and initialize it self._overlay.setCurrentIndex(1) self._connection_progress_bar.setValue(0) self._connection_progress_bar.setFormat("Requesting connection...") # noinspection PyBroadException try: device_info: BasicDeviceInfo = await self._connection_request_callback( selected_port) except Exception as ex: show_error( "Could not connect", f"Connection via the port {selected_port} could not be established.", f"Reason: {str(ex)}", parent=self, ) self._switch_state_disconnected() else: assert device_info is not None self._switch_state_connected(device_info) async def _do_disconnect(self): _logger.info("Connection termination task spawned") # Activate the progress view and initialize it self._overlay.setCurrentIndex(1) self._connection_progress_bar.setValue(100) self._connection_progress_bar.setFormat( "Disconnecting, please wait...") # noinspection PyBroadException try: await self._disconnection_request_callback() except Exception as ex: _logger.exception("Disconnect request failed") self.flash(f"Disconnection problem: {ex}", duration=10) self._switch_state_disconnected() def _on_confirmation(self): # Deactivate the controls in order to prevent accidental double-entry self._port_combo.setEnabled(False) self._connect_button.setEnabled(False) if (self._last_task is not None) and not self._last_task.done(): show_error( "I'm sorry Dave, I'm afraid I can't do that", "Cannot connect/disconnect while another connection/disconnection operation is still running", f"Pending future: {self._last_task}", self, ) else: if not self._connection_established: self._last_task = asyncio.get_event_loop().create_task( self._do_connect()) else: self._last_task = asyncio.get_event_loop().create_task( self._do_disconnect())
class Printing(preferences.Group): def __init__(self, page): super(Printing, self).__init__(page) layout = QGridLayout(spacing=1) self.setLayout(layout) self.messageLabel = QLabel(wordWrap=True) self.printCommandLabel = QLabel() self.printCommand = widgets.urlrequester.UrlRequester() self.printCommand.setFileMode(QFileDialog.ExistingFile) self.printCommand.changed.connect(page.changed) self.printDialogCheck = QCheckBox(toggled=page.changed) self.resolutionLabel = QLabel() self.resolution = QComboBox(editable=True, editTextChanged=page.changed) self.resolution.addItems("300 600 1200".split()) self.resolution.lineEdit().setInputMask("9000") layout.addWidget(self.messageLabel, 0, 0, 1, 2) layout.addWidget(self.printCommandLabel, 1, 0) layout.addWidget(self.printCommand, 1, 1) layout.addWidget(self.printDialogCheck, 2, 0, 1, 2) layout.addWidget(self.resolutionLabel, 3, 0) layout.addWidget(self.resolution, 3, 1) app.translateUI(self) def translateUI(self): self.setTitle(_("Printing Music")) self.messageLabel.setText(_( "Here you can enter a command to print a PDF or PostScript file. " "See the Help page for more information about printing music.")) self.printCommandLabel.setText(_("Printing command:")) self.printCommand.setToolTip('<qt>' + _( "The printing command is used to print a PostScript or PDF file. " "On Linux you don't need this, but on Windows and Mac OS X you can " "provide a command to avoid that PDF documents are being printed " "using raster images, which is less optimal.\n" "<code>$pdf</code> gets replaced with the PDF filename, or alternatively, " "<code>$ps</code> is replaced with the PostScript filename. " "<code>$printer</code> is replaced with the printer's name to use.")) self.printDialogCheck.setText(_("Use Frescobaldi's print dialog")) self.printDialogCheck.setToolTip('<qt>' + _( "If enabled, Frescobaldi will show the print dialog and create a " "PDF or PostScript document containing only the selected pages " "to print. Otherwise, the command is called directly and is expected " "to show a print dialog itself.")) self.resolutionLabel.setText(_("Resolution:")) self.resolution.setToolTip(_( "Set the resolution if Frescobaldi prints using raster images.")) def loadSettings(self): s = QSettings() s.beginGroup("helper_applications") self.printCommand.setPath(s.value("printcommand", "", str)) self.printDialogCheck.setChecked(s.value("printcommand/dialog", False, bool)) with qutil.signalsBlocked(self.resolution): self.resolution.setEditText(format(s.value("printcommand/dpi", 300, int))) def saveSettings(self): s= QSettings() s.beginGroup("helper_applications") s.setValue("printcommand", self.printCommand.path()) s.setValue("printcommand/dialog", self.printDialogCheck.isChecked()) s.setValue("printcommand/dpi", int(self.resolution.currentText()))
class Dataqc(QMainWindow): def __init__(self): super().__init__() self.setWindowIcon(QIcon(':/assets/icon.svg')) self.central_widget = QWidget() self.setCentralWidget(self.central_widget) self.header_key = {'NOx': 'NOx (uM)', 'Phosphate': 'Phosphate (uM)', 'Silicate': 'Silicate (uM)', 'Ammonia': 'Ammonia (uM)', 'Nitrite': 'Nitrite (uM)', 'Salinity': 'Salinity (PSU)', 'Oxygen': 'Oxygen (uM)'} self.flag_key = {'NOx': 'NOx flag', 'Phosphate': 'Phosphate flag', 'Silicate': 'Silicate flag', 'Ammonia': 'Ammonia flag', 'Nitrite': 'Nitrite flag', 'Salinity': 'Salinity flag', 'Oxygen': 'Oxygen flag'} self.init_ui() self.csvdf = pd.DataFrame() self.csvtempdf = pd.DataFrame() self.ncdf = pd.DataFrame() self.setStyleSheet(''' QLabel { font: 14px; } QPushButton { font: 14px; } QComboBox { font: 14px; } QListWidget { font: 14px; } QTableWidget { font: 14px; } QCheckBox { font: 14px; } ''') def init_ui(self): self.setFont(QFont('Segoe UI')) grid_layout = QGridLayout() grid_layout.setSpacing(10) self.setGeometry(0, 0, 1350, 750) qtRectangle = self.frameGeometry() screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos()) centerPoint = QApplication.desktop().screenGeometry(screen).center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) self.setWindowTitle('Hydro Data QuickQCer') depscsv_label = QLabel('Hydro Deps CSV:') self.csv_path = QLineEdit('') self.csv_path.setReadOnly(True) csv_browse = QPushButton('Browse for CSV') csv_browse.clicked.connect(self.csv_browse_path) depnc_label = QLabel('Hydro Deps NC Folder:') self.nc_path = QLineEdit('') self.nc_path.setReadOnly(True) nc_browse = QPushButton('Browse for NC folder') nc_browse.clicked.connect(self.nc_browse_path) load_files = QPushButton('Load files') load_files.clicked.connect(self.load_filesf) self.csv_loaded = QCheckBox('CSV Loaded!') # self.csv_loaded.setCheckable(False) self.nc_loaded = QCheckBox('NC Loaded!') # self.nc_loaded.setCheckable(False) linesep1 = QFrame() linesep1.setFrameShape(QFrame.HLine) linesep1.setFrameShadow(QFrame.Sunken) param_label = QLabel('Parameter: ') self.params_combo = QComboBox() self.params_combo.addItems(['NOx', 'Phosphate', 'Silicate', 'Ammonia', 'Nitrite', 'Salinity', 'Oxygen']) self.params_combo.setEditable(True) self.params_combo.lineEdit().setAlignment(Qt.AlignHCenter) self.params_combo.lineEdit().setReadOnly(True) deps_label = QLabel('Deployments:') self.deps_list = QListWidget() self.deps_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.draw_csv = QCheckBox('Draw CSV') self.draw_nc = QCheckBox('Draw NC') self.draw_woa = QCheckBox('Draw WOA18') view = QPushButton('View') view.setFixedWidth(130) view.clicked.connect(self.view_data) self.figure = plt.figure() self.figure.set_tight_layout(tight=True) self.canvas = FigureCanvas(self.figure) self.canvas.setParent(self) self.flag_table = QTableWidget() self.flag_table.setColumnCount(7) self.flag_table.setHorizontalHeaderLabels(['Dep', 'RP', 'CSV Flag', 'NC Flag', 'Match', 'CSV Conc', 'NC Conc']) header = self.flag_table.horizontalHeader() header.setSectionResizeMode(0, QHeaderView.ResizeToContents) header.setSectionResizeMode(1, QHeaderView.ResizeToContents) header.setSectionResizeMode(2, QHeaderView.ResizeToContents) header.setSectionResizeMode(3, QHeaderView.ResizeToContents) header.setSectionResizeMode(4, QHeaderView.ResizeToContents) header.setSectionResizeMode(5, QHeaderView.ResizeToContents) header.setSectionResizeMode(6, QHeaderView.ResizeToContents) grid_layout.addWidget(depscsv_label, 0, 0) grid_layout.addWidget(self.csv_path, 1, 0, 1, 2) grid_layout.addWidget(csv_browse, 2, 1) grid_layout.addWidget(depnc_label, 3, 0) grid_layout.addWidget(self.nc_path, 4, 0, 1, 2) grid_layout.addWidget(nc_browse, 5, 1) grid_layout.addWidget(load_files, 6, 0, 1, 2) grid_layout.addWidget(self.csv_loaded, 7, 0, Qt.AlignHCenter) grid_layout.addWidget(self.nc_loaded, 7, 1, Qt.AlignHCenter) grid_layout.addWidget(linesep1, 8, 0, 1, 2) grid_layout.addWidget(param_label, 9, 0) grid_layout.addWidget(self.params_combo, 9, 1) grid_layout.addWidget(deps_label, 10, 0) grid_layout.addWidget(self.deps_list, 11, 0, 14, 2) grid_layout.addWidget(self.draw_csv, 25, 0) grid_layout.addWidget(self.draw_nc, 26, 0) grid_layout.addWidget(self.draw_woa, 27, 0) grid_layout.addWidget(view, 25, 1, 3, 1, Qt.AlignHCenter) grid_layout.addWidget(self.canvas, 0, 3, 27, 5) grid_layout.addWidget(self.flag_table, 0, 9, 27, 3) self.mainplot = self.figure.add_subplot(111) self.mainplot.set_facecolor('#f4f4f4') self.centralWidget().setLayout(grid_layout) self.show() def increment_deployment(self): # Used to increment the deployment number by 1, this then updates the plot and tables # uses keyboard button/letter N to increment the deployment number try: selected_index = self.deps_list.currentRow() next_index = selected_index + 1 self.deps_list.setCurrentRow(next_index) self.view_data() except Exception: print(traceback.print_exc()) def view_flags(self): # The function that populates the table with all the flags and concentrations for each point # Get current parameter of interest and convert it the relevant header name current_param = self.params_combo.currentText() param_header_key = self.header_key[current_param] param_flag_key = self.flag_key[current_param] deps = [] rp = [] match = [] csvflags = [] csvconc = [] ncflags = [] ncconc = [] # Need to check if either dataset is getting drawn, as the draw function initially creates # the subsetted data to use if self.draw_csv.isChecked() == True: deps = self.csvdf['Deployment'] rp = self.csvdf['RP'] csvflags = self.csvdf[param_flag_key] csvflags = [int(x) for x in csvflags] csvconc = self.csvdf[param_header_key] csvconc = [round(x, 3) for x in csvconc] if self.draw_nc.isChecked() == True: ncflags = self.ncdf[param_flag_key] ncconc = self.ncdf[param_header_key] ncconc = [round(x, 3) for i, x in enumerate(ncconc) if x != '--' and ncflags[i] != 133] ncflags = [x for x in ncflags if x != 141 and x != 133] # Checking if the flags match and adding the result to a list to get displayed if self.draw_csv.isChecked() == True: match = [] for i, x in enumerate(csvflags): if x == ncflags[i]: match.append('OK') elif int(ncflags[i]) == 141: match.append('OK') elif int(ncflags[i] == 133): match.append('OK') else: match.append('*DIF*') # If both are csv and nc are ticked to be drawn, package data from both and populate table if self.draw_csv.isChecked() == True and self.draw_nc.isChecked() == True: packed = list(zip(deps, rp, csvflags, ncflags, match, csvconc, ncconc)) self.flag_table.setRowCount(len(packed)) for row, x in enumerate(packed): for col, y in enumerate(x): self.flag_table.setItem(row, col, QTableWidgetItem(str(y))) def view_data(self): # This subsets the data from the initial datasets based on the deployments selected to be viewed # this will also plot the nc data with varying markers based on bad data, csv includes no bad data so it is # just a straight plot self.mainplot.clear() self.csvdf = pd.DataFrame() self.ncdf = pd.DataFrame() data_hold = pd.DataFrame() legend_labels = [] current_param = self.params_combo.currentText() param_header_key = self.header_key[current_param] param_flag_key = self.flag_key[current_param] selected_deps = self.deps_list.selectedItems() sel_deps = [item.text() for item in selected_deps] try: if self.draw_csv.isChecked() == True: # If draw csv checkbox is checked draw the csv data to the plot, this is basic due to # the csv not carrying any bad data, the load data function already made the csv a DF for us for x in sel_deps: data_hold = data_hold.append(self.csvtempdf.loc[self.csvtempdf['Deployment'] == int(x)]) data_hold = data_hold.dropna(0, subset=[param_header_key]) self.mainplot.plot(data_hold[param_header_key], data_hold['Pressure'], marker='o', ms=5, mfc='none', linewidth=0.75) legend_labels.append('Deployment ' + str(x)) self.csvdf = self.csvdf.append(data_hold) data_hold = pd.DataFrame() if self.draw_woa.isChecked() == True and self.params_combo.currentText() == 'NOx': #The WOA data is separated by month, so work out the closest month data that will match the CTD time_check = self.csvdf['Time'].iloc[0] month = time_check[5:7] # Open file that corresponds to that month file_path = 'C:/Users/she384/Documents/woa18nitratecsv/woa18_all_n' + str(month) + 'mn01.csv' print(file_path) woa_df = pd.read_csv(file_path, header=1) lat_check = round(self.csvdf['Latitude'].iloc[0], 1) string_lat_check = str(lat_check) if int(string_lat_check[-1]) > 5: tt = float(str(string_lat_check[:-2] + '.5')) pp = lat_check - tt lat_check = lat_check - pp else: tt = float(str(string_lat_check[:-2] + '.5')) pp = tt - lat_check lat_check = lat_check + pp lon_check = round(self.csvdf['Longitude'].iloc[0], 1) string_lon_check = str(lon_check) if int(string_lon_check[-1]) > 5: tt = float(str(string_lon_check[:-2] + '.5')) pp = lon_check - tt lon_check = lon_check - pp else: tt = float(str(string_lon_check[:-2] + '.5')) pp = tt - lon_check lon_check = lon_check + pp woa_df2 = woa_df.loc[woa_df['#COMMA SEPARATED LATITUDE'] == lat_check] woa_df3 = woa_df2.loc[woa_df2[' LONGITUDE'] == lon_check] print(str(lat_check) + ' ' + str(lon_check)) print(woa_df3) woa_depths = [] woa_concs = [] if not woa_df3.empty: for i, col in enumerate(woa_df3): if i > 1: if i == 2: woa_depths.append(0) else: woa_depths.append(int(col)) woa_concs.append(woa_df3[col].iloc[0]) print(woa_depths) self.mainplot.plot(woa_concs, woa_depths, marker='o', ms=7, mfc='none', linewidth=0.75) if self.draw_nc.isChecked() == True: # If the nc checkbox is checked draw the nc data to the plot, this is more complicated due to a number # of factors, 1: the nc includes bad data, 2: missing data is stored as '--' which we need to get # rid of, 3: for missing data it includes an extra flag which needs to be discarded files_in_direc = os.listdir(self.nc_path.text()) for file in files_in_direc: # Cycle through the files in the specified directory if file[-2:] == 'nc': ds = Dataset(self.nc_path.text() + '/' + file) deploy = ds.Deployment for x in sel_deps: # Loop through the selected deployments to find the right file if int(x) == int(deploy): deps_list = [] for y in range(ds.dimensions['pressure'].size): deps_list.append(deploy) # Make a list of the dep number the length of data # Zip up the data so it can be easily input into a dataframe, the 3rd dimension # contains the data we want, this is the dimension based along pressure dszipped = list(zip(deps_list, ds.variables['pressure'][:], ds.variables['rosettePosition'][0, 0, :, 0], ds.variables['oxygen'][0, 0, :, 0], ds.variables['oxygenFlag'][0, 0, :, 0], ds.variables['salinity'][0, 0, :, 0], ds.variables['salinityFlag'][0, 0, :, 0], ds.variables['nox'][0, 0, :, 0], ds.variables['noxFlag'][0, 0, :, 0], ds.variables['phosphate'][0, 0, :, 0], ds.variables['phosphateFlag'][0, 0, :, 0], ds.variables['silicate'][0, 0, :, 0], ds.variables['silicateFlag'][0, 0, :, 0], ds.variables['ammonia'][0, 0, :, 0], ds.variables['ammoniaFlag'][0, 0, :, 0], ds.variables['nitrite'][0, 0, :, 0], ds.variables['nitriteFlag'][0, 0, :, 0])) tempdf = pd.DataFrame(dszipped, columns=['Deployment', 'Pressure', 'RP', 'Oxygen (uM)', 'Oxygen flag', 'Salinity (PSU)', 'Salinity flag', 'NOx (uM)', 'NOx flag', 'Phosphate (uM)', 'Phosphate flag', 'Silicate (uM)', 'Silicate flag', 'Ammonia (uM)', 'Ammonia flag', 'Nitrite (uM)', 'Nitrite flag']) # Plot the NC data onto the figure self.mainplot.plot(tempdf[param_header_key], tempdf['Pressure'], marker='o', ms=0, mfc='none', linewidth=0.0) ncflags = tempdf[param_flag_key] pres = tempdf['Pressure'] for i, v in enumerate(tempdf[param_header_key]): #print(ncflags[i]) if ncflags[i] == 133 or ncflags[i] == 134 or ncflags[i] == 129: self.mainplot.plot(v, pres[i], marker='x', ms=7, linewidth=0, mec='#ac3232', mfc='none') if ncflags[i] == 69 or ncflags[i] == 65: self.mainplot.plot(v, pres[i], marker='x', ms=9, linewidth=0, mec='#5fcde4', mfc='none') else: self.mainplot.plot(v, pres[i], marker='s', ms=2, linewidth=0, mec='#39c88e', mfc='#39c88e') # For use in matching up flags an instance var is used to hold the selected deployments # worth of data, appending using ignore index so it will just tack each dep on # to the bottom of the dataframe for each loop self.ncdf = self.ncdf.append(tempdf, ignore_index=True) # At the end of the whole fiasco format the figure to how we like self.mainplot.invert_yaxis() self.mainplot.set_facecolor('#f4f4f4') self.mainplot.grid(alpha=0.2, linewidth=0.5) self.mainplot.legend(legend_labels) self.mainplot.set_xlabel(self.header_key[str(current_param)]) self.mainplot.set_ylabel('Pressure (db)') self.canvas.draw() self.view_flags() except Exception: print(traceback.print_exc()) def load_filesf(self): # This function is a bit of a facade, it only really loads in the CSV file as a dataframe, this is to # populate the deployments list so a user can select a dep, it checks to make sure a nc file is in the # directory then ticks the nc loaded checkbox as a visual indicator for the user try: if os.path.exists(self.csv_path.text()): self.csvtempdf = pd.read_csv(self.csv_path.text()) deps = list(set(self.csvtempdf['Deployment'])) self.populate_list(deps) self.csv_loaded.setChecked(True) if os.path.exists(self.nc_path.text()): files_in_direc = os.listdir(self.nc_path.text()) for file in files_in_direc: if file[-2:] == 'nc': self.nc_loaded.setChecked(True) except Exception: print(traceback.print_exc()) def populate_list(self, items): # Adds the dep numbers to the qlistwidget self.deps_list.clear() for x in items: self.deps_list.addItem(str(x)) def csv_browse_path(self): path = QFileDialog.Options() files = QFileDialog.getOpenFileName(self, "Select CSV", 'c://', "csv (*.csv)") if os.path.exists(files[0]): self.csv_path.setText(files[0]) def nc_browse_path(self): path = QFileDialog.Options() files = QFileDialog.getExistingDirectory(self, "Select Folder containing NC files") if os.path.exists(files): self.nc_path.setText(files) def keyPressEvent(self, event): #print(event.key()) # Used for incrementing the deployment number to quickly zoom through plots if event.key() == 78: self.increment_deployment()
class PanelPlot(QFrame): flag = 0 padre = None def __init__(self, pPadre): super(PanelPlot, self).__init__() super().setFrameShape(QFrame.StyledPanel) super().setStyleSheet('background-color: #444952; border-radius: 10px') super().setFixedWidth(1150) self.lay = QVBoxLayout() self.lay2 = QVBoxLayout() self.lay3 = QHBoxLayout() self.panelGrafica = QFrame() self.panelBotones = QFrame() self.panelGrafica.setStyleSheet('border-radius: 10px') self.panelBotones.setStyleSheet('border-radius: 10px') self.setLayout(self.lay) self.panelGrafica.setLayout(self.lay2) self.padre = pPadre self.combo = QComboBox(self) self.combo.addItem("VOLTAJE CARGA") self.combo.addItem("SOC BATERÍA") self.lay.addWidget(self.panelGrafica) self.panelBotones.setLayout(self.lay3) self.panelBotones.setFixedHeight(100) self.font = QFont() self.font.setFamily('Times font') self.font.setFixedPitch(False) self.font.setPointSize(10) self.font.setBold(True) self.combo.setStyleSheet('QComboBox' '{' 'color:white;' 'background-color: #23252a;' 'border-radius: 10px' '}' 'QComboBox::drop-down' '{' 'width: 20px;' 'border-color: #23252a;' 'color: white' 'background-color: #23252a;' '}') self.combo.setFont(self.font) self.combo.setEditable(True) self.ledit = self.combo.lineEdit() self.ledit.setAlignment(Qt.AlignCenter) self.ledit.setReadOnly(True) self.ledit.setStyleSheet('background-color:#23252a') self.lay3.addWidget(self.combo) self.ledit.setFont(self.font) self.combo.setFixedSize(200, 50) self.lay.addWidget(self.panelBotones) self.panelGrafica.show() self.combo.currentIndexChanged.connect(self.on_currentIndexChanged) self.y2 = None self.y1 = None self.sc = MplCanvas(self, 5, 4, 100) self.lay2.addWidget(self.sc) self.activar = False def on_currentIndexChanged(self): self.plot() def setActivar(self): self.activar = True def plot(self): if self.activar == True: self.data = self.padre.getData() self.tiempo = self.data[:, 0] self.y1 = self.data[:, 1] self.y2 = self.data[:, 2] if (self.combo.currentText() == "VOLTAJE CARGA"): y = self.y1 elif (self.combo.currentText() == "SOC BATERÍA"): y = self.y2 self.sc.axes.clear() self.sc.axes.plot(self.tiempo, y, color='#6a8922', linewidth=1) self.sc.draw()
class TFWindow(QWidget): # take project object as input def __init__(self, parent=None): QWidget.__init__(self) self._threadExe = None self._threadSS = None self._ptName = os.path.join( os.path.dirname(os.path.realpath(__file__)), "tf_proj.xml") self._p = project_io.Project() self._pTree = ET.ElementTree() # widgets for multithread self._qst_exe_button = QStackedWidget() self._qst_exe_param = QStackedWidget() self._qpr_exe_progress = QProgressBar() self._qpr_exe_progress.setRange(0, 100) # info widget for updating infomation # history statistics self._ql_hist_exe = QLabel(str(self.get_hist_item("exe"))) self._ql_hist_ss = QLabel(str(self.get_hist_item("ss"))) self._ql_hist_doc = QLabel(str(self.get_hist_item("doc"))) # text self._qle_proj_filter = QLineEdit() # filter to project list self._qle_proj_filter.setPlaceholderText("Search...") self._qle_proj_filter.textChanged.connect( lambda: ui_logic.slot_project_list_filter(self)) self._qle_conf_file = QLineEdit() self._qle_dir_in = QLineEdit() self._qle_dir_out = QLineEdit() self._qle_exe_pv = QLineEdit() self._qle_exe_demo = QLineEdit() self._qcb_cur_ver = QComboBox() self._qcb_cur_ver.setEditable(True) self._qcb_cur_ver.lineEdit().setPlaceholderText("Input or Select..") self._qle_doc_name = QLineEdit() self._qpt_exe_param = QPlainTextEdit() # listview self._qlv_all_proj = create_QListView(self) self._qlv_all_proj.clicked.disconnect() self._qlv_all_proj.clicked.connect( lambda: ui_logic.slot_switch_proj(self)) self._qlv_all_proj.doubleClicked.connect( lambda: ui_logic.slot_open_proj_path(self)) self._qlv_exe_case = create_QListView(self, self._qle_dir_in) self._qlv_ss_case = create_QListView(self, self._qle_dir_out) self._qlv_ss_alg = create_QListView(self) self._qlv_ss_ver = create_QListView(self) self._qlv_doc_case = create_QListView(self, self._qle_dir_out) self._qlv_doc_alg = create_QListView(self) self._qlv_doc_ver = create_QListView(self) self._qlv_ss_ver.doubleClicked.connect( lambda: ui_logic.slot_open_ss_ver(self)) self._qlv_ss_alg.doubleClicked.connect( lambda: ui_logic.slot_open_ss_alg(self)) self._qlv_doc_ver.doubleClicked.connect( lambda: ui_logic.slot_open_doc_ver(self)) self._qlv_doc_alg.doubleClicked.connect( lambda: ui_logic.slot_open_doc_alg(self)) # doc type selector self._qcb_doc_type = QComboBox() # type of document to be generated self._qcb_doc_type.setEditable(False) self._qcb_doc_type.addItems([ "Screenshots", "Time_statistics", "CPU_MEM_statistics", "Hausdorf_dist" ]) # other object self._cmdDialog = ui_cmd_history.CMDHistory(self._qpt_exe_param) self._filenameSelector = None # layout grid = QGridLayout() grid.addWidget(self.create_history(), 0, 0) grid.addWidget(self.create_project_manage(), 1, 0, 2, 1) grid.addWidget(self.create_project_info(), 0, 1) grid.addWidget(self.create_list_manage(), 1, 1) grid.addWidget(self.create_control_region(), 2, 1) grid.setColumnStretch(0, 1) grid.setColumnStretch(1, 3) self.setLayout(grid) self.setWindowTitle("Test Framework") self.resize(1200, 800) ui_logic.load_ptree_obj(self) self.fill_proj_list() def fill_proj_list(self, flt=""): m = QStandardItemModel() flag = Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled for item in self._pTree.getroot(): p_name = item.attrib["name"] flt_pass = True if len(flt) > 0: sub_pname = p_name for c in flt: pos = sub_pname.find(c) if pos == -1: flt_pass = False break else: sub_pname = sub_pname[pos + 1:] if not flt_pass: continue qsi = QStandardItem(p_name) qsi.setFlags(flag) qsi.setCheckable(False) m.appendRow(qsi) self._qlv_all_proj.setModel(m) def get_pv_path(self): tr = self._pTree.getroot() return tr.find(pv_default).attrib["path"] # load information from TFobject to ui def fill_ui_info(self, in_obj): self._p = in_obj cur_obj = self._p self._qle_conf_file.setText(cur_obj._configFile) self._qle_dir_in.setText(cur_obj._dirInput) self._qle_dir_out.setText(cur_obj._dirOutput) self._qle_exe_pv.setText(cur_obj._exePV) self._qle_exe_demo.setText(cur_obj._exeDemo) self._qle_doc_name.setText(cur_obj._docName) self._qcb_cur_ver.clear() self._qcb_doc_type.setCurrentText(in_obj._curDocType) for v in cur_obj._ver: self._qcb_cur_ver.addItem(v) self._qcb_cur_ver.setEditText(cur_obj._eVer) self._qpt_exe_param.setPlainText(cur_obj._exeParam) self.fill_check_list(self._qlv_exe_case, cur_obj._case, cur_obj._eCaseCheck) self.fill_check_list(self._qlv_ss_case, cur_obj._case, cur_obj._sCaseCheck) self.fill_check_list(self._qlv_ss_ver, cur_obj._ver, cur_obj._sVerCheck) self.fill_check_list(self._qlv_ss_alg, cur_obj._alg, cur_obj._sAlgCheck) self.fill_check_list(self._qlv_doc_case, cur_obj._case, cur_obj._dCaseCheck) self.fill_check_list(self._qlv_doc_ver, cur_obj._ver, cur_obj._dVerCheck) self.fill_check_list(self._qlv_doc_alg, cur_obj._alg, cur_obj._dAlgCheck) def collect_ui_info(self): #out_obj = project_io.Project() out_obj = self._p out_obj._configFile = self._qle_conf_file.text() out_obj._dirInput = self._qle_dir_in.text() out_obj._dirOutput = self._qle_dir_out.text() out_obj._exePV = self._qle_exe_pv.text() out_obj._exeDemo = self._qle_exe_demo.text() out_obj._docName = self._qle_doc_name.text() out_obj._eVer = self._qcb_cur_ver.currentText() out_obj._exeParam = self._qpt_exe_param.toPlainText() out_obj._curDocType = self._qcb_doc_type.currentText() self.read_check_list(self._qlv_exe_case, out_obj._case, out_obj._eCaseCheck) self.read_check_list(self._qlv_ss_case, out_obj._case, out_obj._sCaseCheck) self.read_check_list(self._qlv_ss_ver, out_obj._ver, out_obj._sVerCheck) self.read_check_list(self._qlv_ss_alg, out_obj._alg, out_obj._sAlgCheck) self.read_check_list(self._qlv_doc_case, out_obj._case, out_obj._dCaseCheck) self.read_check_list(self._qlv_doc_ver, out_obj._ver, out_obj._dVerCheck) self.read_check_list(self._qlv_doc_alg, out_obj._alg, out_obj._dAlgCheck) return out_obj def get_hist_item(self, hist_type): return int(float(utils.get_reg_item(hist_type)) + 0.1) def add_hist_item(self, hist_type, val): cur_num = self.get_hist_item(hist_type) new_num = str(cur_num + val) utils.set_reg_item(hist_type, new_num) if hist_type == "exe": self._ql_hist_exe.setText(new_num) elif hist_type == "ss": self._ql_hist_ss.setText(new_num) elif hist_type == "doc": self._ql_hist_doc.setText(new_num) def create_history(self): hist = QGroupBox("History Statistics") grid = QGridLayout() grid.addWidget(QLabel("Total Demo Run: "), 0, 0) grid.addWidget(self._ql_hist_exe, 0, 1) grid.addWidget(QLabel("Total ScreenShots: "), 1, 0) grid.addWidget(self._ql_hist_ss, 1, 1) grid.addWidget(QLabel("Total Docs: "), 2, 0) grid.addWidget(self._ql_hist_doc, 2, 1) hist.setLayout(grid) return hist def create_project_manage(self): manage = QGroupBox("Project Manage") qpb_new = QPushButton("New Project") qpb_new.setStyleSheet("background-color:#9a9a9a") qpb_delete = QPushButton("Delete Project") qpb_save = QPushButton("Save Project") qpb_load = QPushButton("Load Project") qpb_new.clicked.connect(lambda: ui_logic.slot_new_project(self)) qpb_delete.clicked.connect(lambda: ui_logic.slot_delete_project(self)) qpb_save.clicked.connect(lambda: ui_logic.slot_save_project(self)) qpb_load.clicked.connect(lambda: ui_logic.slot_load_project(self)) grid = QGridLayout() grid.addWidget(self._qle_proj_filter, 0, 0, 1, 2) grid.addWidget(self._qlv_all_proj, 1, 0, 1, 2) grid.addWidget(qpb_new, 2, 0) grid.addWidget(qpb_load, 2, 1) grid.addWidget(qpb_delete, 3, 0) grid.addWidget(qpb_save, 3, 1) manage.setLayout(grid) return manage def create_project_info(self): # fill information # create widget info = QGroupBox("Project Information") grid = QGridLayout() self.get_f_bsw(self._qle_conf_file, grid, 0, "Configuration File", "xml") qpb_set_dir_in = QPushButton("Browse..") qpb_set_dir_in.setStyleSheet("background-color:#9a9a9a") qpb_open_dir_in = QPushButton("Open") grid.addWidget(QLabel("Input Directory"), 1, 0) grid.addWidget(self._qle_dir_in, 1, 1) grid.addWidget(qpb_set_dir_in, 1, 2) grid.addWidget(qpb_open_dir_in, 1, 3) qpb_set_dir_in.clicked.connect( lambda: ui_logic.slot_open_input_path(self)) qpb_open_dir_in.clicked.connect( lambda: ui_logic.slot_open_path(self._qle_dir_in)) self.get_f_bsw(self._qle_dir_out, grid, 2, "Output Directory") self.get_f_bsw(self._qle_exe_pv, grid, 3, "PVPython Interpreter", "exe") self.get_f_bsw(self._qle_exe_demo, grid, 4, "Demo Executable", "exe") info.setLayout(grid) return info def create_list_manage(self): lm = QGroupBox("List Manage") l_hb = QGridLayout() qpb_scan_input = QPushButton("Scan Input Dir") qpb_build_output = QPushButton("Build Output Dir") qpb_add_case = QPushButton("Add Case Item") qpb_add_ver = QPushButton("Add Version Item") qpb_add_alg = QPushButton("Add FillName Item") qpb_del_case = QPushButton("Del Case Item") qpb_del_ver = QPushButton("Del Version Item") qpb_del_alg = QPushButton("Del FillName Item") qpb_del_case.setStyleSheet("background-color:#c82508") qpb_del_ver.setStyleSheet("background-color:#c82508") qpb_del_alg.setStyleSheet("background-color:#c82508") qpb_scan_input.clicked.connect(lambda: ui_logic.slot_scan_input(self)) qpb_build_output.clicked.connect( lambda: ui_logic.slot_build_output(self)) qpb_add_case.clicked.connect( lambda: ui_logic.slot_add_list(self, self._p._case, "Case")) qpb_add_ver.clicked.connect( lambda: ui_logic.slot_add_list(self, self._p._ver, "Version")) #qpb_add_alg.clicked.connect( # lambda: ui_logic.slot_add_list(self, self._p._alg, "Algorithm")) qpb_add_alg.clicked.connect(self.slot_add_alg_list) qpb_del_case.clicked.connect(lambda: ui_logic.slot_del_list( self, self._qlv_ss_case, self._p._case)) qpb_del_ver.clicked.connect(lambda: ui_logic.slot_del_list( self, self._qlv_ss_ver, self._p._ver)) qpb_del_alg.clicked.connect(lambda: ui_logic.slot_del_list( self, self._qlv_ss_alg, self._p._alg)) l_hb.addWidget(qpb_scan_input, 0, 0) l_hb.addWidget(qpb_add_case, 0, 1) l_hb.addWidget(qpb_add_ver, 0, 2) l_hb.addWidget(qpb_add_alg, 0, 3) l_hb.addWidget(qpb_build_output, 1, 0) l_hb.addWidget(qpb_del_case, 1, 1) l_hb.addWidget(qpb_del_ver, 1, 2) l_hb.addWidget(qpb_del_alg, 1, 3) lm.setLayout(l_hb) return lm # get listview from project_object def fill_check_list(self, lv, item_list, check_dict): model = QStandardItemModel() #pt_item = QStandardItem() flag = Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled for i in item_list: item = QStandardItem(i) item.setFlags(flag) check = Qt.Checked if i in check_dict else Qt.Unchecked item.setCheckState(check) item.setCheckable(True) model.appendRow(item) lv.setModel(model) # get project_object info from listview def read_check_list(self, lv, item_list, check_dict): item_list.clear() check_dict.clear() model = lv.model() if model is None: return for index in range(model.rowCount()): item = model.item(index) text = item.text() item_list.append(text) if item.checkState() == Qt.Checked: check_dict[text] = 1 def create_control_region(self): control_region = QWidget() box = QHBoxLayout() box.addWidget(self.create_exe_region()) box.addWidget(self.create_ss_region()) box.addWidget(self.create_doc_region()) control_region.setLayout(box) return control_region # create a file browser def get_f_bsw(self, qle, grid, grid_line, label, f_type=""): qpb = QPushButton("Browse..", self) qpb_open = QPushButton("Open", self) qpb_open.clicked.connect(lambda: ui_logic.slot_open_path(qle)) grid.addWidget(QLabel(label), grid_line, 0) grid.addWidget(qle, grid_line, 1) grid.addWidget(qpb, grid_line, 2) grid.addWidget(qpb_open, grid_line, 3) if f_type == "": qpb.clicked.connect(lambda: ui_logic.slot_get_path(qle)) else: qpb.clicked.connect(lambda: ui_logic.slot_get_file(qle, f_type)) def create_exe_region(self): exe_region = QGroupBox("Executable Configuration") qpb_cmd_his = QPushButton("历史命令", self) qpb_cmd_his.clicked.connect(self.slot_show_cmd_history) qpb_exe_run = QPushButton('Run Demo') qpb_exe_stop = QPushButton('中断') qpb_exe_param = QPushButton("命令预览", self) qpb_exe_stop.setStyleSheet("background-color: red") qpb_exe_run.clicked.connect(lambda: ui_logic.slot_exe_run(self)) qpb_exe_stop.clicked.connect(lambda: ui_logic.slot_exe_stop(self)) qpb_exe_param.clicked.connect(lambda: ui_logic.slot_exe_param(self)) # initial stack widget # 1st group self._qst_exe_param.addWidget(qpb_exe_param) self._qst_exe_button.addWidget(qpb_exe_run) # 2nd group self._qst_exe_param.addWidget(self._qpr_exe_progress) self._qst_exe_button.addWidget(qpb_exe_stop) # initi group self._qst_exe_param.setCurrentIndex(0) self._qst_exe_button.setCurrentIndex(0) grid = QGridLayout() grid.addWidget(QLabel('Input Case'), 0, 0) grid.addWidget(self._qlv_exe_case, 0, 1) grid.addWidget( QLabel( "Parameter Line\n{i} for input\n{o} for output\n{case} for case" ), 1, 0) grid.addWidget(qpb_cmd_his, 2, 0) grid.addWidget(self._qpt_exe_param, 1, 1, 2, 1) grid.addWidget(QLabel('Use Version Name'), 3, 0) grid.addWidget(self._qcb_cur_ver, 3, 1) grid.addWidget(self._qst_exe_param, 4, 0) grid.addWidget(self._qst_exe_button, 4, 1) exe_region.setLayout(grid) return exe_region def create_ss_region(self): ss_region = QGroupBox("ScreenShot Configuration") qpb_ss_shot = QPushButton('Take Screenshot', self) qpb_ss_shot.clicked.connect( lambda: ui_logic.slot_create_screenshots(self)) qpb_ss_manage = QPushButton('设置视角', self) qpb_ss_manage.clicked.connect(lambda: ui_logic.slot_ss_manage(self)) qpb_ss_preview = QPushButton('截图预览', self) qpb_ss_preview.clicked.connect(lambda: ui_logic.slot_ss_preview(self)) grid = QGridLayout() grid.addWidget(QLabel('Case'), 1, 0) grid.addWidget(self._qlv_ss_case, 1, 1) grid.addWidget(QLabel('Version'), 2, 0) grid.addWidget(self._qlv_ss_ver, 2, 1) grid.addWidget(QLabel('FileName'), 3, 0) grid.addWidget(self._qlv_ss_alg, 3, 1) grid.addWidget(qpb_ss_manage, 4, 0) grid.addWidget(qpb_ss_preview, 4, 1) grid.addWidget(qpb_ss_shot, 5, 0, 1, 2) ss_region.setLayout(grid) return ss_region def create_doc_region(self): doc_region = QGroupBox("Docx Configuration") qpb_g_doc = QPushButton('Generate Doc', self) qpb_g_doc.clicked.connect(lambda: ui_logic.slot_generate_docx(self)) # qpb_gt_doc = QPushButton('Time Docx', self) # qpb_gt_doc.clicked.connect(lambda: ui_logic.slot_generate_time_docx(self)) # qpb_gp_doc = QPushButton('Proc Docx', self) # qpb_gp_doc.clicked.connect(lambda: ui_logic.slot_generate_proc_docx(self)) qpb_o_doc = QPushButton('Open Document', self) qpb_o_doc.clicked.connect(lambda: ui_logic.slot_open_docx(self)) qpb_o_path = QPushButton('Open Path', self) qpb_o_path.clicked.connect(lambda: ui_logic.slot_open_docx_path(self)) qw_doc = QWidget() hbox = QHBoxLayout() hbox.setContentsMargins(0, 0, 0, 0) # hbox.addWidget(qpb_gt_doc) # hbox.addWidget(qpb_gp_doc) hbox.addWidget(self._qcb_doc_type) hbox.addWidget(qpb_g_doc) qw_doc.setLayout(hbox) grid = QGridLayout() grid.addWidget(QLabel('Case'), 1, 0) grid.addWidget(self._qlv_doc_case, 1, 1) grid.addWidget(QLabel('Version'), 2, 0) grid.addWidget(self._qlv_doc_ver, 2, 1) grid.addWidget(QLabel('FileName'), 3, 0) grid.addWidget(self._qlv_doc_alg, 3, 1) grid.addWidget(QLabel('FileName'), 3, 0) grid.addWidget(self._qlv_doc_alg, 3, 1) grid.addWidget(QLabel('Doc Name'), 4, 0) grid.addWidget(self._qle_doc_name, 4, 1) grid.addWidget(qpb_o_path, 5, 0) grid.addWidget(qpb_o_doc, 5, 1) grid.addWidget(qw_doc, 6, 0, 1, 2) doc_region.setLayout(grid) return doc_region def slot_show_cmd_history(self): self._cmdDialog.resize(800, 200) self._cmdDialog.fill_list() self._cmdDialog.show() def slot_add_alg_list(self): cand = ui_logic.get_all_filenames(self._p) self._filenameSelector = FileNameSelector(self, cand) self._filenameSelector.exec_() self.fill_ui_info(self._p) # button status switch def new_run_button(self): self._qst_exe_button.setCurrentIndex(0) self._qst_exe_param.setCurrentIndex(0) def new_stop_button(self): self._qst_exe_button.setCurrentIndex(1) self._qst_exe_param.setCurrentIndex(1) def exe_progress(self, p): self._qpr_exe_progress.setValue(p) def exe_finish(self): self.new_run_button() self._qlv_all_proj.setEnabled(True)
class MainWindow(QWidget): def __init__(self, app): super().__init__() self.app = app self.async_fetch = AsyncFetch(CachedHttpClient(HttpClient(), 'cache')) self.async_fetch.ready.connect(self.on_fetch_ready) self.comboxBox = QComboBox(self) self.comboxBox.setEditable(True) self.comboxBox.setCurrentText('') self.comboxBox.currentTextChanged.connect(self.on_text_changed) font = QFont() font.setPointSize(font.pointSize() + ADD_TO_FONT_SIZE) self.comboxBox.setFont(font) self.browser = QTextBrowser(self) self.browser.setText(STYLE + HTML) self.browser.show() mainLayout = QVBoxLayout(self) mainLayout.setSpacing(0) mainLayout.setContentsMargins(0, 0, 0, 0) mainLayout.addWidget(self.comboxBox) mainLayout.addWidget(self.browser) self.setLayout(mainLayout) self.setWindowTitle('OrdbokUibNo') self.resize(WINDOW_WIDTH, WINDOW_HEIGHT) self.setWindowIcon(QIcon(ICON_FILENAME)) QTimer.singleShot(1, self.center) self.center() self.show() def activate(self): self.center() self.show() self.raise_() self.activateWindow() self.comboxBox.lineEdit().selectAll() self.comboxBox.setFocus() def center(self): qr = self.frameGeometry() desktop = QApplication.desktop() screen = desktop.screenNumber(desktop.cursor().pos()) cp = desktop.screenGeometry(screen).center() qr.moveCenter(cp) self.move(qr.topLeft()) def suggest(self, words): completer = QCompleter(words, self) completer.setCaseSensitivity(Qt.CaseInsensitive) self.comboxBox.setCompleter(completer) completer.complete() def set_text(self, text): self.browser.setText(STYLE + text) def on_text_changed(self, text): if text == '': return QTimer.singleShot(UPDATE_DELAY, lambda: self.update(text)) def update(self, old_text): if self.same_text(old_text): self.fetch(old_text) @pyqtSlot(object) def on_fetch_ready(self, result: object): if isinstance(result, Article): if self.same_text(result.word) and result.parts: self.set_text(result.html) elif isinstance(result, Suggestions): if self.same_text(result.word) and result.top: print(result) self.suggest(result.top) else: logging.warn('unknown fetch result: %s', result) def fetch(self, word): self.async_fetch.add(lambda client: Article(client, word)) self.async_fetch.add(lambda client: Suggestions(client, word)) def onTrayActivated(self, reason): if reason == QSystemTrayIcon.Trigger: self.show() def same_text(self, word): return word == self.text() def text(self): return self.comboxBox.currentText() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.hide() elif (e.key() == Qt.Key_Q) and (e.modifiers() == Qt.ControlModifier): self.close() elif (e.key() == Qt.Key_L) and (e.modifiers() == Qt.ControlModifier): self.comboxBox.lineEdit().selectAll() self.comboxBox.setFocus() elif e.key() == Qt.Key_Return: self.fetch(self.text())
def edit_contact_dialog(wallet_api, contact_key=None): editing = contact_key is not None if editing: title = _("Edit Contact") else: title = _("New Contact") d = WindowModalDialog(wallet_api.wallet_window, title) vbox = QVBoxLayout(d) vbox.addWidget(QLabel(title + ':')) def _contact_insert_completion(text): if text: index = combo1.findText(text) combo1.setCurrentIndex(index) identity_line = QLineEdit() name_line = QLineEdit() combo1 = QComboBox() combo1.setFixedWidth(280) combo1.setEditable(True) # add a filter model to filter matching items contact_filter_model = QSortFilterProxyModel(combo1) contact_filter_model.setFilterCaseSensitivity(Qt.CaseInsensitive) contact_filter_model.setSourceModel(combo1.model()) contact_completer = QCompleter(contact_filter_model, combo1) contact_completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) combo1.setCompleter(contact_completer) ok_button = OkButton(d) ok_button.setEnabled(False) def _validate_form() -> None: def _set_validation_state(element, is_valid) -> None: if not is_valid: element.setStyleSheet("border: 1px solid red") else: element.setStyleSheet("") can_submit = True system_name = combo1.currentText().lower().strip() is_valid = True try: system_id = get_system_id(system_name) except ContactDataError: system_id = None is_valid = False _set_validation_state(combo1, is_valid) can_submit = can_submit and is_valid identity_text = identity_line.text().strip() if system_id is None: identity_result = IdentityCheckResult.Invalid else: identity_result = wallet_api.check_identity_valid(system_id, identity_text, skip_exists=editing) is_valid = identity_result == IdentityCheckResult.Ok _set_validation_state(identity_line, is_valid) if is_valid: identity_line.setToolTip("") elif identity_result == IdentityCheckResult.Invalid: if system_id == IdentitySystem.OnChain: identity_line.setToolTip(_("Not a valid Bitcoin address")) else: identity_line.setToolTip(_("Incorrect format")) elif identity_result == IdentityCheckResult.InUse: identity_line.setToolTip(_("Already in use")) can_submit = can_submit and is_valid name_text = name_line.text().strip() name_result = wallet_api.check_label(name_text) is_valid = (name_result == IdentityCheckResult.Ok or editing and name_result == IdentityCheckResult.InUse) _set_validation_state(name_line, is_valid) if is_valid: name_line.setToolTip("") elif name_result == IdentityCheckResult.Invalid: name_line.setToolTip(_("Name too short")) elif name_result == IdentityCheckResult.InUse: name_line.setToolTip(_("Name already in use")) can_submit = can_submit and is_valid ok_button.setEnabled(can_submit) def _contact_text_changed(text: str) -> None: _validate_form() combo1.lineEdit().textEdited.connect(contact_filter_model.setFilterFixedString) combo1.editTextChanged.connect(_contact_text_changed) identity_line.textChanged.connect(_contact_text_changed) name_line.textChanged.connect(_contact_text_changed) contact_completer.activated.connect(_contact_insert_completion) combo1.addItems(list(IDENTITY_SYSTEM_NAMES.values())) grid = QGridLayout() identity_line.setFixedWidth(280) name_line.setFixedWidth(280) grid.addWidget(QLabel(_("Identity Type")), 1, 0) grid.addWidget(combo1, 1, 1) grid.addWidget(QLabel(_("Identity")), 2, 0) grid.addWidget(identity_line, 2, 1) grid.addWidget(QLabel(_("Name")), 3, 0) grid.addWidget(name_line, 3, 1) vbox.addLayout(grid) vbox.addLayout(Buttons(CancelButton(d), ok_button)) if contact_key is None: combo1.lineEdit().setText(IDENTITY_SYSTEM_NAMES[IdentitySystem.OnChain]) identity_line.setFocus() else: entry = wallet_api.get_contact(contact_key[0]) identity = [ ci for ci in entry.identities if ci.identity_id == contact_key[1] ][0] combo1.lineEdit().setText(IDENTITY_SYSTEM_NAMES[identity.system_id]) identity_line.setText(identity.system_data) name_line.setText(entry.label) name_line.setFocus() if d.exec_(): name_text = name_line.text().strip() identity_text = identity_line.text().strip() system_id = get_system_id(combo1.currentText()) if contact_key is not None: contact = wallet_api.get_contact(contact_key[0]) identity = [ ci for ci in contact.identities if ci.identity_id == contact_key[1] ][0] if contact_key[1] != identity.identity_id: wallet_api.remove_identity(contact_key[0], contact_key[1]) wallet_api.add_identity(contact_key[0], system_id, identity_text) if contact.label != name_text: wallet_api.set_label(contact_key[0], name_text) else: wallet_api.add_contact(system_id, name_text, identity_text)
class MainWindow(QMainWindow): """ handle the bulk of the ugliness of window/widget geometry QMainWindow affords a built-in status bar, otherwise this could just be a QWidget written by 'hand' instead of by Qt Designer as an exercise in understanding PyQt5 in hindsight, this should've been broken down into more functions or even classes... """ def __init__(self): super(MainWindow, self).__init__() self.mission_folder = getcwd() # central widgets/layouts self.central_widget = QTabWidget() self.central_layout = QVBoxLayout(self.central_widget) self.tabs = QTabWidget() self.label_about = QLabelClickable('About Maximum Roverdrive', self.tabs) self.label_about.setObjectName('label_about') # monitor widgets (left tab) self.widget_mav_monitor = QWidget() self.layout_mav_monitor = QVBoxLayout(self.widget_mav_monitor) self.frame_connect = QFrame() self.layout_connect = QHBoxLayout(self.frame_connect) self.button_connect = QPushButton('Connect') self.combo_port = QComboBox() self.widget_add_remove_buttons = QWidget() self.layout_add_remove_buttons = QHBoxLayout( self.widget_add_remove_buttons) self.button_msg_add = QPushButton('Add Message') self.button_msg_remove = QPushButton('Remove Selected Message') self.table_messages = QTableView() self.frame_msg_request = QFrame() self.form_msg_request = QFormLayout() self.form_msg_request.setLabelAlignment(Qt.AlignRight) self.button_msg_refresh = QPushButton('Refresh Messages') self.combo_msg_select = QComboBox() self.combo_attr_select = QComboBox() self.text_multiplier = QLineEdit('1.0') self.text_low = QLineEdit('0.0') self.text_high = QLineEdit('1000.0') # utilities widgets (center tab) self.widget_utilities = QWidget() self.layout_utilities = QVBoxLayout(self.widget_utilities) self.frame_utilities_header = QFrame() self.grid_utilities_header = QGridLayout(self.frame_utilities_header) self.button_arm = QPushButton('ARM') self.button_disarm = QPushButton('DISARM') self.label_headlight_relay = QLabel('Light Relay') self.checkbox_auto_headlights = QCheckBox( ' Enable\n Automatic\nHeadlights') self.checkbox_relay_active_low = QCheckBox(' Relays\nActive Low') self.combo_headlight_relay = QComboBox() self.frame_mav_command_start = QFrame() self.frame_mav_command_start.setObjectName('mav_command_start') self.grid_mav_command_start = QGridLayout(self.frame_mav_command_start) self.label_mav_command_start = QLabel( 'Command at <span style="color: #94d6a3; font-weight: bold;">' 'Mission Start:</span>') self.combo_mav_command_start = QComboBox() self.labels_mav_command_start = [QLabel()] * 8 self.texts_mav_command_start = [QComboBox()] self.texts_mav_command_start.extend([QLineEdit()] * 7) self.button_mav_command_start_send = QPushButton('Send Now') self.checkbox_mav_command_start = QCheckBox('On Mission Start') self.checkbox_mav_command_start_all = QCheckBox('At Each WP') self.frame_mav_command_end = QFrame() self.frame_mav_command_end.setObjectName('mav_command_end') self.grid_mav_command_end = QGridLayout(self.frame_mav_command_end) self.label_mav_command_end = QLabel( 'Command at <span style="color: #ffe383; font-weight: bold;">' 'Mission End:</span>') self.combo_mav_command_end = QComboBox() self.labels_mav_command_end = [QLabel()] * 8 self.texts_mav_command_end = [QComboBox()] self.texts_mav_command_end.extend([QLineEdit()] * 7) self.button_mav_command_end_send = QPushButton('Send Now') self.checkbox_mav_command_end = QCheckBox('On Mission End') self.checkbox_mav_command_end_all = QCheckBox('At Each WP') # status messages (right tab) self.widget_status = QWidget() self.layout_status = QVBoxLayout(self.widget_status) self.text_status = QTextStatus() # file widgets at bottom of central vertical layout self.frame_mission_file = QFrame() self.grid_mission_file = QGridLayout(self.frame_mission_file) self.button_mission_file = QPushButton('Select\nMission File') self.text_mission_file = QTextEdit() self.button_convert_file = QPushButton('Convert File') self.button_modify_file = QPushButton('Modify File') def __init_ui__(self): """ called by 'public' method self.initialize() """ # set up the monitor widget layout self.layout_connect.setContentsMargins(0, 0, 0, 0) self.frame_connect.setStyleSheet('QFrame { border: 0px; }') self.combo_port.setEditable(True) self.button_connect.setMaximumWidth(85) self.layout_connect.addWidget(self.combo_port) self.layout_connect.addWidget(self.button_connect) self.layout_mav_monitor.addWidget(self.frame_connect) self.table_messages.horizontalHeader().setStretchLastSection(True) self.table_messages.horizontalHeader().hide() self.table_messages.verticalHeader().hide() self.layout_mav_monitor.addWidget(self.table_messages) self.layout_add_remove_buttons.addWidget(self.button_msg_add) self.layout_add_remove_buttons.addWidget(self.button_msg_remove) self.layout_add_remove_buttons.setContentsMargins(5, 5, 5, 5) self.layout_mav_monitor.addWidget(self.widget_add_remove_buttons) self.combo_msg_select.addItem('Click refresh') self.form_msg_request.addRow(self.button_msg_refresh, self.combo_msg_select) self.form_msg_request.addRow('Message Content:', self.combo_attr_select) self.form_msg_request.addRow('Multiplier:', self.text_multiplier) self.form_msg_request.addRow('Low Threshold:', self.text_low) self.form_msg_request.addRow('High Threshold:', self.text_high) self.frame_msg_request.setLayout(self.form_msg_request) self.layout_mav_monitor.addWidget(self.frame_msg_request) # set up the utilities widget layout self.grid_utilities_header.setColumnStretch(0, 4) self.button_arm.setMinimumWidth(76) self.button_disarm.setMinimumWidth(76) self.grid_utilities_header.setRowStretch(1, 2) self.button_arm.setEnabled(False) self.button_disarm.setEnabled(False) self.button_mav_command_start_send.setEnabled(False) self.button_mav_command_end_send.setEnabled(False) self.grid_utilities_header.addWidget(self.button_arm, 0, 0) self.grid_utilities_header.addWidget(self.button_disarm, 1, 0) self.grid_utilities_header.addWidget(self.checkbox_relay_active_low, 0, 2, 2, 1) self.grid_utilities_header.addWidget(self.checkbox_auto_headlights, 0, 3, 2, 1) self.label_headlight_relay.setStyleSheet( 'QLabel { qproperty-alignment: AlignCenter; }') self.grid_utilities_header.addWidget(self.label_headlight_relay, 0, 4) self.combo_headlight_relay.addItems([str(x) for x in range(7)]) self.combo_headlight_relay.setEditable(True) self.combo_headlight_relay.lineEdit().setReadOnly(True) self.combo_headlight_relay.lineEdit().setAlignment(Qt.AlignCenter) self.grid_utilities_header.addWidget(self.combo_headlight_relay, 1, 4) self.grid_utilities_header.setContentsMargins(5, 5, 5, 5) self.grid_mav_command_start.addWidget(self.label_mav_command_start, 0, 0, 1, 2) self.grid_mav_command_start.addWidget(self.combo_mav_command_start, 0, 2, 1, 2) for x in range(8): self.grid_mav_command_start.setColumnStretch(x, 2) y = 1 if x < 4 else 3 self.labels_mav_command_start[x] = QLabel(f'Arg{x + 1}') self.labels_mav_command_start[x].setObjectName('label_arg') if x > 1: self.texts_mav_command_start[x] = QLineEdit() else: self.texts_mav_command_start[x] = QWideComboBox() self.texts_mav_command_start[x].setEditable(True) self.texts_mav_command_start[x].lineEdit().setAlignment( Qt.AlignCenter) self.texts_mav_command_start[x].SizeAdjustPolicy( QComboBox.AdjustToContentsOnFirstShow) self.texts_mav_command_start[x].setObjectName('text_arg') self.texts_mav_command_start[x].setMinimumWidth(82) self.grid_mav_command_start.addWidget( self.labels_mav_command_start[x], y, x % 4) self.grid_mav_command_start.addWidget( self.texts_mav_command_start[x], y + 1, x % 4) self.grid_mav_command_start.addWidget( self.button_mav_command_start_send, 5, 3) self.grid_mav_command_start.addWidget(self.checkbox_mav_command_start, 5, 0, 1, 2) self.grid_mav_command_start.addWidget( self.checkbox_mav_command_start_all, 5, 2) self.grid_mav_command_end.addWidget(self.label_mav_command_end, 0, 0, 1, 2) self.grid_mav_command_end.addWidget(self.combo_mav_command_end, 0, 2, 1, 2) for x in range(8): self.grid_mav_command_end.setColumnStretch(x, 2) y = 1 if x < 4 else 3 self.labels_mav_command_end[x] = QLabel(f'Arg{x + 1}') self.labels_mav_command_end[x].setObjectName('label_arg') if x > 1: self.texts_mav_command_end[x] = QLineEdit() else: self.texts_mav_command_end[x] = QWideComboBox() self.texts_mav_command_end[x].setEditable(True) self.texts_mav_command_end[x].lineEdit().setAlignment( Qt.AlignCenter) self.texts_mav_command_end[x].SizeAdjustPolicy( QComboBox.AdjustToContentsOnFirstShow) self.texts_mav_command_end[x].setMinimumWidth(82) self.grid_mav_command_end.addWidget(self.labels_mav_command_end[x], y, x % 4) self.grid_mav_command_end.addWidget(self.texts_mav_command_end[x], y + 1, x % 4) self.grid_mav_command_end.addWidget(self.button_mav_command_end_send, 5, 3) self.grid_mav_command_end.addWidget(self.checkbox_mav_command_end, 5, 0, 1, 2) self.grid_mav_command_end.addWidget(self.checkbox_mav_command_end_all, 5, 2) self.layout_utilities.addWidget(self.frame_utilities_header) self.layout_utilities.addWidget(self.frame_mav_command_start) self.layout_utilities.addWidget(self.frame_mav_command_end) # set up the filename container at the bottom of the central vertical layout self.frame_mission_file.setStyleSheet( 'QFrame { border: 0px; }' 'QTextEdit { border: 1px solid #515253; }' 'QTextEdit:focus { border: 1px solid #53a0ed; }') self.button_mission_file.setContentsMargins(10, 10, 10, 10) self.text_mission_file.setFixedHeight(40) self.text_mission_file.setToolTip( 'Auto-filled when files change in the Mission file folder\n' 'Override by clicking the button to choose a file') for x in range(4): self.grid_mission_file.setColumnStretch(x, 2) self.grid_mission_file.addWidget(self.button_mission_file, 0, 0) self.grid_mission_file.addWidget(self.text_mission_file, 0, 1, 1, 3) self.grid_mission_file.addWidget(self.button_convert_file, 1, 0, 1, 2) self.grid_mission_file.addWidget(self.button_modify_file, 1, 2, 1, 2) self.grid_mission_file.setContentsMargins(5, 0, 5, 5) # set up the status messages tab self.text_status.setReadOnly(True) self.layout_status.addWidget(self.text_status) self.layout_status.setContentsMargins(0, 0, 0, 0) def initialize( self ): # allows for instantiating method to populate list/text items before painting """ must be called after class instantiation for window to be displayed """ self.__init_ui__() # populate the layouts # set up the main window self.setWindowFlag(Qt.WindowStaysOnTopHint) self.setWindowTitle('Maximum Roverdrive') # put the central widget together self.central_layout.setContentsMargins(5, 5, 5, 5) self.layout_mav_monitor.setContentsMargins(5, 5, 5, 5) self.layout_utilities.setContentsMargins(5, 5, 5, 5) self.central_widget.setMinimumWidth(400) self.central_widget.setMinimumHeight(502) self.tabs.addTab(self.widget_mav_monitor, 'Monitor') self.tabs.addTab(self.widget_utilities, 'Utilities') self.tabs.addTab(self.widget_status, 'Messages') self.central_layout.addWidget(self.tabs) self.central_layout.addWidget(self.frame_mission_file) self.label_about.setCursor(QCursor(Qt.PointingHandCursor)) self.label_about.move(240, 2) self.setCentralWidget(self.central_widget) self.__init_connections__() # connect the ui signals def __init_connections__(self): """ called by 'public' method self.initialize() instances of this class must define slots for the following connections: """ self.combo_port.lineEdit().returnPressed.connect(self.mav_connect) self.button_connect.clicked.connect(self.mav_connect) self.button_msg_add.clicked.connect(self.add_msg) self.button_msg_remove.clicked.connect(self.remove_selected_msg) self.button_msg_refresh.clicked.connect(self.refresh_msg_select) self.combo_msg_select.currentIndexChanged.connect( self.refresh_attr_select) self.combo_attr_select.currentIndexChanged.connect( self.update_button_msg_add) self.button_arm.clicked.connect(self.arm) self.button_disarm.clicked.connect(self.disarm) self.checkbox_relay_active_low.clicked.connect(self.save_relay_state) self.checkbox_auto_headlights.clicked.connect( self.save_auto_headlights_state) self.combo_headlight_relay.currentTextChanged.connect( self.save_headlight_relay) self.button_mav_command_start_send.clicked.connect( self.mav_command_send) self.button_mav_command_end_send.clicked.connect(self.mav_command_send) self.combo_mav_command_start.currentIndexChanged.connect( self.update_combo_mav_command) self.combo_mav_command_end.currentIndexChanged.connect( self.update_combo_mav_command) self.button_convert_file.clicked.connect(self.convert_mission_file) self.button_modify_file.clicked.connect(self.modify_mission_file) # these are not abstract self.label_about.clicked.connect(self.about) self.button_mission_file.clicked.connect(self.mission_file_dialog) @pyqtSlot() def mission_file_dialog(self): filename = QFileDialog.getOpenFileName( self, caption='Select Waypoint/Poly File', directory=self.mission_folder, filter='Mission Files (*.waypoints *.poly);;All files (*.*)', options=QFileDialog.DontUseNativeDialog) if filename != ('', ''): filename = QDir.toNativeSeparators(filename[0]) self.text_mission_file.setText(filename) return filename return None @pyqtSlot() def about(self): msg = QMessageBox(icon=QMessageBox.NoIcon) msg.setWindowFlag(Qt.WindowStaysOnTopHint) msg.setWindowTitle('About Maximum Roverdrive') msg.setStandardButtons(QMessageBox.Ok) msg.setTextFormat(Qt.RichText) msg.setStyleSheet( 'QMessageBox { border-image: url(:/images/logo.png); }' 'QLabel { width: 443px; min-width: 443px; max-width: 443px;' 'height: 251px; min-height: 251px; max-height: 251px; ' 'font-family: "Copperplate Gothic Bold", sans-serif; ' 'font-size: 20px; font-weight: 500; }') msg.setText( '© 2020, Yuri<br>' '''<a style="font-family: 'Calibri, sans-serif'; font-size: 12px; font-weight: normal;"''' 'href="https://github.com/yuri-rage/MaximumRoverdrive">View on GitHub</a>' '<br><br><br><br><br><br><br><br><br><br>') msg.exec()
class SnapshotCompareWidget(QWidget): pvs_filtered = QtCore.pyqtSignal(set) restore_requested = QtCore.pyqtSignal(list) rgx_icon = None def __init__(self, snapshot, common_settings, parent=None, **kw): super().__init__(parent, **kw) self.snapshot = snapshot self.common_settings = common_settings # ----------- PV Table ------------- # PV table consist of: # self.model: holding the data, values, being updated by PV callbacks, etc # self._proxy: a proxy model implementing the filter functionality # self.view: visual representation of the PV table self.view = SnapshotPvTableView(self) self.view.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.view.restore_requested.connect(self._handle_restore_request) self.model = SnapshotPvTableModel(snapshot, self) self.model.file_parse_errors.connect(self._show_snapshot_parse_errors) self._proxy = SnapshotPvFilterProxyModel(self) self._proxy.setSourceModel(self.model) self._proxy.filtered.connect(self.pvs_filtered) # Build model and set default visualization on view (column widths, etc) self.model.set_pvs(snapshot.pvs.values()) self.view.setModel(self._proxy) # ---------- Filter control elements --------------- # - text input to filter by name # - drop down to filter by compare status # - check box to select if showing pvs with incomplete data if SnapshotCompareWidget.rgx_icon is None: SnapshotCompareWidget.rgx_icon = QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), "images/rgx.png")) # #### PV name filter pv_filter_label = QLabel("Filter:", self) pv_filter_label.setAlignment(Qt.AlignCenter | Qt.AlignRight) self.pv_filter_sel = QComboBox(self) self.pv_filter_sel.setEditable(True) self.pv_filter_sel.setIconSize(QtCore.QSize(35, 15)) self.pv_filter_inp = self.pv_filter_sel.lineEdit() self.pv_filter_inp.setPlaceholderText("Filter by PV name") policy = self.pv_filter_sel.sizePolicy() policy.setHorizontalPolicy(policy.Expanding) self.pv_filter_sel.setSizePolicy(policy) self.pv_filter_sel.currentIndexChanged.connect( self._predefined_filter_selected) self.pv_filter_inp.textChanged.connect(self._create_name_filter) self._populate_filter_list() # Prepare pallets to color the pv name filter input if rgx not valid self._inp_palette_ok = self.pv_filter_inp.palette() self._inp_palette_err = QPalette() self._inp_palette_err.setColor(QPalette.Base, QColor("#F39292")) # Create a PV name filter layout and add items pv_filter_layout = QHBoxLayout() pv_filter_layout.setSpacing(10) pv_filter_layout.addWidget(pv_filter_label) pv_filter_layout.addWidget(self.pv_filter_sel) # #### Regex selector self.regex = QCheckBox("Regex", self) self.regex.stateChanged.connect(self._handle_regex_change) # #### Selector for comparison filter self.compare_filter_inp = QComboBox(self) self.compare_filter_inp.addItems( ["Show all", "Different only", "Equal only"]) self.compare_filter_inp.currentIndexChanged.connect( self._proxy.set_eq_filter) self.compare_filter_inp.setMaximumWidth(200) # ### Show disconnected selector self.show_disconn_inp = QCheckBox("Show disconnected PVs.", self) self.show_disconn_inp.setChecked(True) self.show_disconn_inp.stateChanged.connect( self._proxy.set_disconn_filter) self.show_disconn_inp.setMaximumWidth(500) # Tolerance setting tol_label = QLabel("Tolerance:") tol = QSpinBox() tol.setRange(1, 1000000) tol.setValue(1) tol.valueChanged[int].connect(self.model.change_tolerance) self.model.change_tolerance(tol.value()) # ### Put all tolerance and filter selectors in one layout filter_layout = QHBoxLayout() filter_layout.addWidget(tol_label) filter_layout.addWidget(tol) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addLayout(pv_filter_layout) filter_layout.addWidget(self.regex) filter_layout.addWidget(make_separator(self, 'vertical')) filter_layout.addWidget(self.compare_filter_inp) filter_layout.addWidget(self.show_disconn_inp) filter_layout.setAlignment(Qt.AlignLeft) filter_layout.setSpacing(10) # ------- Build main layout --------- layout = QVBoxLayout(self) layout.setContentsMargins(10, 10, 10, 10) layout.addLayout(filter_layout) layout.addWidget(self.view) self.setLayout(layout) def _populate_filter_list(self): predefined_filters = self.common_settings['predefined_filters'] self.pv_filter_sel.blockSignals(True) self.pv_filter_sel.clear() self.pv_filter_sel.addItem(None) for rgx in predefined_filters.get('rgx-filters', list()): self.pv_filter_sel.addItem(SnapshotCompareWidget.rgx_icon, rgx) self.pv_filter_sel.addItems(predefined_filters.get('filters', list())) self.pv_filter_sel.blockSignals(False) def _handle_regex_change(self, state): txt = self.pv_filter_inp.text() if state and txt.strip() == '': self.pv_filter_inp.setText('.*') elif not state and txt.strip() == '.*': self.pv_filter_inp.setText('') else: self._create_name_filter(txt) def _create_name_filter(self, txt): if self.regex.isChecked(): try: srch_filter = re.compile(txt) self.pv_filter_inp.setPalette(self._inp_palette_ok) except: # Syntax error (happens a lot during typing an expression). In such cases make compiler which will # not match any pv name srch_filter = re.compile("") self.pv_filter_inp.setPalette(self._inp_palette_err) else: srch_filter = txt self.pv_filter_inp.setPalette(self._inp_palette_ok) self._proxy.set_name_filter(srch_filter) def _show_snapshot_parse_errors(self, errors): show_snapshot_parse_errors(self, errors) def new_selected_files(self, selected_files): self.model.clear_snap_files() self.model.add_snap_files(selected_files) self._proxy.apply_filter() def clear_snap_files(self): self.model.clear_snap_files() def handle_new_snapshot_instance(self, snapshot): self.snapshot = snapshot self.model.snapshot = snapshot self.model.set_pvs(snapshot.pvs.values()) self.view.sortByColumn(0, Qt.AscendingOrder) # default sorting self._populate_filter_list() def _handle_restore_request(self, pvs_list): self.restore_requested.emit(pvs_list) def _predefined_filter_selected(self, idx): txt = self.pv_filter_inp.text() if idx == 0: # First empty option; the menu is always reset to this. return if not self.pv_filter_sel.itemIcon(idx).isNull(): # Set back to first index, to get rid of the icon. Set to regex and # pass text of filter to the input self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(True) self.pv_filter_inp.setText(txt) else: # Imitate same behaviour self.pv_filter_sel.setCurrentIndex(0) self.regex.setChecked(False) self.pv_filter_inp.setText(txt) def filter_update(self): self._proxy.apply_filter()
class Widget(QWidget): def __init__(self, dockwidget): super(Widget, self).__init__(dockwidget) self._document = None self._fileSelector = QComboBox(editable=True, insertPolicy=QComboBox.NoInsert) gadgets.drag.ComboDrag(self._fileSelector).role = Qt.UserRole self._fileSelector.lineEdit().setReadOnly(True) self._fileSelector.lineEdit().setFocusPolicy(Qt.NoFocus) self._stopButton = QToolButton() self._playButton = QToolButton() self._timeSlider = QSlider(Qt.Horizontal, tracking=False, singleStep=500, pageStep=5000, invertedControls=True) self._display = Display() self._tempoFactor = QSlider(Qt.Vertical, minimum=-50, maximum=50, singleStep=1, pageStep=5) grid = QGridLayout(spacing=0) self.setLayout(grid) grid.addWidget(self._fileSelector, 0, 0, 1, 3) grid.addWidget(self._stopButton, 1, 0) grid.addWidget(self._playButton, 1, 1) grid.addWidget(self._timeSlider, 1, 2) grid.addWidget(self._display, 2, 0, 1, 3) grid.addWidget(self._tempoFactor, 0, 3, 3, 1) # size policy of combo p = self._fileSelector.sizePolicy() p.setHorizontalPolicy(QSizePolicy.Ignored) self._fileSelector.setSizePolicy(p) # size policy of combo popup p = self._fileSelector.view().sizePolicy() p.setHorizontalPolicy(QSizePolicy.MinimumExpanding) self._fileSelector.view().setSizePolicy(p) self._player = player.Player() self._outputCloseTimer = QTimer(interval=60000, singleShot=True, timeout=self.closeOutput) self._timeSliderTicker = QTimer(interval=200, timeout=self.updateTimeSlider) self._fileSelector.activated[int].connect(self.slotFileSelected) self._tempoFactor.valueChanged.connect(self.slotTempoChanged) self._timeSlider.valueChanged.connect(self.slotTimeSliderChanged) self._timeSlider.sliderMoved.connect(self.slotTimeSliderMoved) self._player.beat.connect(self.updateDisplayBeat) self._player.time.connect(self.updateDisplayTime) self._player.stateChanged.connect(self.slotPlayerStateChanged) self.slotPlayerStateChanged(False) dockwidget.mainwindow().currentDocumentChanged.connect(self.loadResults) app.documentLoaded.connect(self.slotDocumentLoaded) app.jobFinished.connect(self.slotUpdatedFiles) app.aboutToQuit.connect(self.stop) midihub.aboutToRestart.connect(self.slotAboutToRestart) midihub.settingsChanged.connect(self.clearMidiSettings, -100) midihub.settingsChanged.connect(self.readMidiSettings) app.documentClosed.connect(self.slotDocumentClosed) app.translateUI(self) self.readMidiSettings() d = dockwidget.mainwindow().currentDocument() if d: self.loadResults(d) def translateUI(self): self._tempoFactor.setToolTip(_("Tempo")) def slotAboutToRestart(self): self.stop() self._player.set_output(None) def clearMidiSettings(self): """Called first when settings are changed.""" self.stop() self._outputCloseTimer.stop() self._player.set_output(None) def readMidiSettings(self): """Called after clearMidiSettings(), and on first init.""" pass def openOutput(self): """Called when playing starts. Ensures an output port is opened.""" self._outputCloseTimer.stop() if not self._player.output(): p = QSettings().value("midi/player/output_port", midihub.default_output(), str) o = midihub.output_by_name(p) if o: self._player.set_output(output.Output(o)) def closeOutput(self): """Called when the output close timer fires. Closes the output.""" self._player.set_output(None) def slotPlayerStateChanged(self, playing): ac = self.parentWidget().actionCollection # setDefaultAction also adds the action for b in self._stopButton, self._playButton: while b.actions(): b.removeAction(b.actions()[0]) if playing: self._timeSliderTicker.start() self._stopButton.setDefaultAction(ac.midi_stop) self._playButton.setDefaultAction(ac.midi_pause) else: self._timeSliderTicker.stop() self.updateTimeSlider() self._stopButton.setDefaultAction(ac.midi_restart) self._playButton.setDefaultAction(ac.midi_play) # close the output if the preference is set if QSettings().value("midi/close_outputs", False, bool): self._outputCloseTimer.start() def play(self): """Starts the MIDI player, opening an output if necessary.""" if not self._player.is_playing() and not self._player.has_events(): self.restart() self.openOutput() if not self._player.output(): self._display.statusMessage(_("No output found!")) self._player.start() def stop(self): """Stops the MIDI player.""" self._player.stop() def restart(self): """Restarts the MIDI player. If another file is in the file selector, or the file was updated, the new file is loaded. """ self._player.seek(0) self.updateTimeSlider() self._display.reset() if self._document: files = midifiles.MidiFiles.instance(self._document) index = self._fileSelector.currentIndex() if files and (files.song(index) is not self._player.song()): self.loadSong(index) def slotTempoChanged(self, value): """Called when the user drags the tempo.""" # convert -50 to 50 to 0.5 to 2.0 factor = 2 ** (value / 50.0) self._player.set_tempo_factor(factor) self._display.setTempo("{0}%".format(int(factor * 100))) def slotTimeSliderChanged(self, value): self._player.seek(value) self._display.setTime(value) if self._player.song(): self._display.setBeat(*self._player.song().beat(value)[1:]) def slotTimeSliderMoved(self, value): self._display.setTime(value) if self._player.song(): self._display.setBeat(*self._player.song().beat(value)[1:]) def updateTimeSlider(self): if not self._timeSlider.isSliderDown(): with qutil.signalsBlocked(self._timeSlider): self._timeSlider.setMaximum(self._player.total_time()) self._timeSlider.setValue(self._player.current_time()) def updateDisplayBeat(self, measnum, beat, num, den): if not self._timeSlider.isSliderDown(): self._display.setBeat(measnum, beat, num, den) def updateDisplayTime(self, time): if not self._timeSlider.isSliderDown(): self._display.setTime(time) def slotDocumentLoaded(self, document): """Called when a new document is loaded. Only calls slotUpdatedFiles when this is the first document, as that slot will be called anyway when the current document is switched. When the first document is loaded, it is loaded into the existing empty document, so mainwindow.currentDocumentChanged() will never be emitted. """ if len(app.documents) == 1: self.slotUpdatedFiles(document) def slotUpdatedFiles(self, document, j=None): """Called when there are new MIDI files.""" mainwindow = self.parentWidget().mainwindow() import engrave if document not in (mainwindow.currentDocument(), engrave.engraver(mainwindow).document()): return import job if j and job.attributes.get(j).mainwindow != mainwindow: return self.loadResults(document) def loadResults(self, document): files = midifiles.MidiFiles.instance(document) if files.update(): self._document = document self._fileSelector.setModel(files.model()) self._fileSelector.setCurrentIndex(files.current) if not self._player.is_playing(): self.loadSong(files.current) def loadSong(self, index): files = midifiles.MidiFiles.instance(self._document) self._player.set_song(files.song(index)) m, s = divmod(self._player.total_time() // 1000, 60) name = self._fileSelector.currentText() self.updateTimeSlider() self._display.reset() self._display.statusMessage( _("midi lcd screen", "LOADED"), name, _("midi lcd screen", "TOTAL"), "{0}:{1:02}".format(m, s)) def slotFileSelected(self, index): if self._document: self._player.stop() files = midifiles.MidiFiles.instance(self._document) if files: files.current = index self.restart() def slotDocumentClosed(self, document): if document == self._document: self._document = None self._fileSelector.setModel(listmodel.ListModel([])) self._player.stop() self._player.clear() self.updateTimeSlider() self._display.reset()
class SettingInput(Widget): key = "SettingInput" def __init__(self, settings, parent=None): super(SettingInput, self).__init__(parent) self.settings = settings self.formatComboBox = QComboBox(self) self.formatComboBox.addItem('INI') self.formatComboBox.addItem('Native') self.scopeComboBox = QComboBox(self) self.scopeComboBox.addItem('User') self.scopeComboBox.addItem('System') self.organizationComboBox = QComboBox(self) self.organizationComboBox.addItem('DAMGteam') self.organizationComboBox.setEditable(True) self.applicationComboBox = QComboBox(self) self.applicationComboBox.addItem('PLM') self.applicationComboBox.setEditable(True) self.applicationComboBox.setCurrentIndex(0) for cb in [ self.formatComboBox, self.scopeComboBox, self.organizationComboBox, self.applicationComboBox ]: cb.currentIndexChanged.connect(self.updateLocationsTable) formatLabel = Label({ 'txt': "&Format: ", 'setBuddy': self.formatComboBox }) scopeLabel = Label({'txt': "&Scope:", 'setBuddy': self.scopeComboBox}) organizationLabel = Label({ 'txt': "&Organization:", 'setBuddy': self.organizationComboBox }) applicationLabel = Label({ 'txt': "&Application:", 'setBuddy': self.applicationComboBox }) grpBox, grid = GroupGrid("Setting Locations") self.locationsTable = QTableWidget() self.locationsTable.setSelectionMode(QAbstractItemView.SingleSelection) self.locationsTable.setSelectionBehavior(QAbstractItemView.SelectRows) self.locationsTable.setEditTriggers(QAbstractItemView.NoEditTriggers) self.locationsTable.setColumnCount(2) self.locationsTable.setHorizontalHeaderLabels(("Location", "Access")) self.locationsTable.horizontalHeader().setSectionResizeMode( 0, QHeaderView.Stretch) self.locationsTable.horizontalHeader().resizeSection(1, 180) self.locationsTable.setMinimumHeight(180) self.formatComboBox.activated.connect(self.updateLocationsTable) self.scopeComboBox.activated.connect(self.updateLocationsTable) self.organizationComboBox.lineEdit().editingFinished.connect( self.updateLocationsTable) self.applicationComboBox.lineEdit().editingFinished.connect( self.updateLocationsTable) self.layout = QGridLayout() grid.addWidget(formatLabel, 1, 0) grid.addWidget(self.formatComboBox, 1, 1) grid.addWidget(scopeLabel, 2, 0) grid.addWidget(self.scopeComboBox, 2, 1) grid.addWidget(organizationLabel, 3, 0) grid.addWidget(self.organizationComboBox, 3, 1) grid.addWidget(applicationLabel, 4, 0) grid.addWidget(self.applicationComboBox, 4, 1) grid.addWidget(self.locationsTable, 0, 2, 6, 4) self.updateLocationsTable() self.layout.addWidget(grpBox) self.setLayout(self.layout) def format(self): if self.formatComboBox.currentIndex() == 0: return QSettings.NativeFormat else: return INI def scope(self): if self.scopeComboBox.currentIndex() == 0: return QSettings.UserScope else: return QSettings.SystemScope def organization(self): return self.organizationComboBox.currentText() def application(self): if self.applicationComboBox.currentText() == "Any": return '' return self.applicationComboBox.currentText() def updateLocationsTable(self): self.locationsTable.setUpdatesEnabled(False) self.locationsTable.setRowCount(0) for i in range(2): if i == 0: if self.scope() == QSettings.SystemScope: continue actualScope = QSettings.UserScope else: actualScope = QSettings.SystemScope for j in range(2): if j == 0: if not self.application(): continue actualApplication = self.application() else: actualApplication = '' settings = QSettings(self.format(), actualScope, self.organization(), actualApplication) row = self.locationsTable.rowCount() self.locationsTable.setRowCount(row + 1) item0 = QTableWidgetItem() item0.setText(settings.fileName()) item1 = QTableWidgetItem() disable = not (settings.childKeys() or settings.childGroups()) if row == 0: if settings.isWritable(): item1.setText("Read-write") disable = False else: item1.setText("Read-only") # self.buttonBox.okButton(QDialogButtonBox.Ok).setDisabled(disable) else: item1.setText("Read-only fallback") if disable: item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled) item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled) self.locationsTable.setItem(row, 0, item0) self.locationsTable.setItem(row, 1, item1) self.locationsTable.setUpdatesEnabled(True)
def __init__(self, dictionary: dict): super().__init__() self.setFocus() self.setMinimumWidth(300) self.setWindowTitle('Any Dictionary Tester') self.setFont(QFont('Arial', 14)) self.setLayout(self.grid) self.dictionary = dict((value, key) for key, value in dictionary.items()) if self.get_translation_swap_flag() \ else dictionary words = list(self.dictionary.keys()) random.shuffle(words) self.is_hard_mode = self.get_hard_mode_flag() if self.is_hard_mode: self.is_hard_mode_with_autocomplete = self.get_hard_mode_with_autocomplete_flag( ) for word in words: dialog = QDialog() dialog_grid = QGridLayout() dialog.setLayout(dialog_grid) values = list( np.random.choice(list(self.dictionary.values()), 4, replace=False)) if self.dictionary[word] not in values: values.remove(values[0]) values.append(self.dictionary[word]) random.shuffle(values) original_word_label = QLabel(word) dialog_grid.addWidget(original_word_label, 0, 0, Qt.AlignCenter) if self.is_hard_mode and not self.is_hard_mode_with_autocomplete: answer_line_edit = QLineEdit() answer_line_edit.returnPressed.connect( lambda associated_word=word, associated_line_edit= answer_line_edit: self.set_answer( associated_word, associated_line_edit.text())) dialog_grid.addWidget(answer_line_edit, 1, 0) answer_button = QPushButton("Answer") answer_button.clicked.connect( lambda state, associated_word=word, associated_line_edit= answer_line_edit: self.set_answer( associated_word, associated_line_edit.text())) dialog_grid.addWidget(answer_button, 2, 0) elif self.is_hard_mode and self.is_hard_mode_with_autocomplete: answer_combo_box = QComboBox() answer_combo_box.setEditable(True) answer_combo_box.addItems( sample(list(self.dictionary.values()), len(self.dictionary.values()))) answer_combo_box.setCurrentText('') answer_combo_box.lineEdit().returnPressed.connect( lambda associated_word=word, associated_combo_box= answer_combo_box: self.set_answer( associated_word, associated_combo_box.currentText())) dialog_grid.addWidget(answer_combo_box, 1, 0) answer_button = QPushButton("Answer") answer_button.clicked.connect( lambda state, associated_word=word, associated_combo_box= answer_combo_box: self.set_answer( associated_word, associated_combo_box.currentText())) dialog_grid.addWidget(answer_button, 2, 0) else: for i in range(4): answer_button = QPushButton(values[i]) answer_button.clicked.connect( partial(self.set_answer, word, values[i])) dialog_grid.addWidget(answer_button, i + 1, 0) self.dialogs.append(dialog) self.current_dialog = next(self.dialogs_iterator) self.grid.addWidget(self.current_dialog) self.show()
class ConvGUI(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): self.camera_chosen = 0 self.log_chosen = 0 self.log_type_chosen = 0 self.output_dir_chosen = 0 centralWidget = QWidget() # ______Labels______ # Label for camera drop down list cameraLabel = QLabel('Choose Camera') cameraLabel.setAlignment(Qt.AlignCenter) # Label for log type drown down list logTypeLabel = QLabel('Choose Type of Log') logTypeLabel.setAlignment(Qt.AlignCenter) # Label for log conversion buttons self.logLabel = QLabel('Log Conversion') self.logLabel.setAlignment(Qt.AlignCenter) # Labels for User Input values inputLabel = QLabel('User Values') inputLabel.setAlignment(Qt.AlignCenter) amslLabel = QLabel('Average AMSL (m)') amslLabel.setAlignment(Qt.AlignCenter) # Unnecessary labels # # missionIdLabel = QLabel('Name of Mission') # missionIdLabel.setAlignment(Qt.AlignCenter) # tailNumLabel = QLabel('Aircraft N or registration number') # tailNumLabel.setAlignment(Qt.AlignCenter) # platDesigLabel = QLabel('Aircraft Designation') # platDesigLabel.setAlignment(Qt.AlignCenter) # imageSensorLabel = QLabel('Image sensor name') # imageSensorLabel.setAlignment(Qt.AlignCenter) # coordSysLabel = QLabel('Coordinate system (e.g. WGS84)') # coordSysLabel.setAlignment(Qt.AlignCenter) # QLineEdits for getting user values self.amslInput = QLineEdit() # self.missionIdInput = QLineEdit() # self.tailNumInput = QLineEdit() # self.platDesigInput = QLineEdit() # self.imageSensorInput = QLineEdit() # self.coordSysInput = QLineEdit() # ______Buttons______ # Create camera drop down list of supported cameras # from cameras.xml profile document # Use parser to ensure pretty print works, see: # http://lxml.de/FAQ.html#why-doesn-t-the- # pretty-print-option-reformat-my-xml-output parser = etree.XMLParser(remove_blank_text=True) self.xml_tree = etree.parse('cameras.xml', parser) self.xml_root = self.xml_tree.getroot() # Initialize QComboBox object with default text cameraType = QComboBox(self) cameraType.addItem('Select a Camera') # Loop through the children of the xml root # get the camera name and add to the drop down list index = 0 for child in self.xml_root: self.camera = child[NAME].text cameraType.addItem(self.camera) # Populate the xml map list with the camera name and # it's index in the root list so we can refer to it's values # later xml_map[self.camera] = index index += 1 cameraType.setEditable(True) cameraType.lineEdit().setReadOnly(True) cameraType.lineEdit().setAlignment(Qt.AlignCenter) # Align the dropdown options to be centered for i in range(0, cameraType.count()): cameraType.setItemData(i, Qt.AlignCenter, Qt.TextAlignmentRole) cameraType.activated[str].connect(self.cameraChosen) # Create Log type drop down menu self.logType = QComboBox(self) self.logType.addItem('Select Log Type') # self.logType.addItem('DJI GO') self.logType.addItem('Litchi') for i in range(0, self.logType.count()): self.logType.setItemData(i, Qt.AlignCenter, Qt.TextAlignmentRole) self.logType.setEditable(True) self.logType.lineEdit().setReadOnly(True) self.logType.lineEdit().setAlignment(Qt.AlignCenter) self.logType.activated[str].connect(self.logTypeChosen) # Create push button for adding a new camera profile newCameraBtn = QPushButton('Add New Camera') newCameraBtn.clicked.connect(self.newCamera) # Create push button object that opens the log file # to be converted chooseLogBtn = QPushButton('Open log file...') chooseLogBtn.clicked.connect(self.openFile) # Push button to start log conversion convertBtn = QPushButton('Convert!') convertBtn.clicked.connect(self.convertLog) # Button for setting output file location saveLocBtn = QPushButton('Set output save location...') saveLocBtn.clicked.connect(self.saveLocation) # Quit button quitBtn = QPushButton('Exit Program') quitBtn.clicked.connect(self.close) # ______Layout______ # Create vertical box layout vbox = QVBoxLayout() vbox.addStretch() vbox.addWidget(cameraLabel) vbox.addWidget(cameraType) vbox.addWidget(newCameraBtn) vbox.addStretch() vbox.addWidget(logTypeLabel) vbox.addWidget(self.logType) vbox.addStretch() vbox.addWidget(inputLabel) vbox.addWidget(amslLabel) vbox.addWidget(self.amslInput) # vbox.addWidget(missionIdLabel) # vbox.addWidget(self.missionIdInput) # vbox.addWidget(tailNumLabel) # vbox.addWidget(self.tailNumInput) # vbox.addWidget(platDesigLabel) # vbox.addWidget(self.platDesigInput) # vbox.addWidget(imageSensorLabel) # vbox.addWidget(self.imageSensorInput) # vbox.addWidget(coordSysLabel) # vbox.addWidget(self.coordSysInput) vbox.addStretch() vbox.addWidget(self.logLabel) vbox.addWidget(chooseLogBtn) vbox.addWidget(saveLocBtn) vbox.addWidget(convertBtn) vbox.addStretch() vbox.addWidget(quitBtn) centralWidget.setLayout(vbox) # Create quit action quit = QAction('Quit', self) quit.setShortcut('Ctrl+Q') quit.setStatusTip('Exit program') quit.triggered.connect(self.close) # Create about action about = QAction('About LogConverter', self) about.setShortcut('Ctrl+A') about.setStatusTip('About program') about.triggered.connect(self.aboutProgram) # Create menubar menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(quit) aboutMenu = menubar.addMenu('&About') aboutMenu.addAction(about) self.setCentralWidget(centralWidget) self.resize(WINDOW_WIDTH, WINDOW_HEIGHT) self.center() self.setWindowTitle('Log Converter') self.show() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def openFile(self): self.log_file = QFileDialog.getOpenFileName(self, 'Open file', None)[0] self.log_chosen = 1 # QFileDialog:getSaveFileName will pass name even if it doesn't exist # unlike QFileDialog:getOpenFileName; Linux doesn't seem to care but # Windows definitely does def saveLocation(self): self.save_location = QFileDialog.getSaveFileName( self, 'Choose save location', None)[0] def cameraChosen(self, text): # Make sure an actual camera was chosen if text != 'Select a Camera': # When a camera is chosen change the value in our camera variable self.camera = text # We need the index for the particular camera chosen. # We obtain this from our xml_map dictionary: # xml_map[camera] maps to index number of that # particular camera's element in root # the constant e.g. FLEN then maps to the index of that subelement # So we get: xml_root[camera_index][parameter_index] # and .text gives us the actual text value of that element self.flen = self.xml_root[xml_map[self.camera]][FLEN].text self.imagew = self.xml_root[xml_map[self.camera]][IMGW].text self.imageh = self.xml_root[xml_map[self.camera]][IMGH].text self.sensorw = self.xml_root[xml_map[self.camera]][SENW].text self.sensorh = self.xml_root[xml_map[self.camera]][SENH].text self.camera_chosen = 1 def logTypeChosen(self, text): if text != 'Select Log Type': self.log = text self.log_type_chosen = 1 def newCamera(self): self.newCamera = QWidget() # Labels for popup window nameLbl = QLabel('Enter Descriptive Camera Name:') flenLbl = QLabel('Enter Camera Focal Length [mm]:') imghLbl = QLabel('Enter Image Height [pixels]:') imgwLbl = QLabel('Enter Image Width [pixels]:') senwLbl = QLabel('Enter Sensor Width [mm]:') senhLbl = QLabel('Enter Sensor Height [mm]:') notice = QLabel('New camera will be available upon restart of the GUI') notice.setAlignment(Qt.AlignCenter) # Set font size and color for notice notice_font = QFont() notice_font.setPointSize(8) notice_font.setBold(True) notice.setFont(notice_font) notice.setStyleSheet("QLabel { color : red;}") # QLineEdit objects for user values self.nameEdit = QLineEdit() self.flenEdit = QLineEdit() self.imghEdit = QLineEdit() self.imgwEdit = QLineEdit() self.senwEdit = QLineEdit() self.senhEdit = QLineEdit() # Button for submitting values submitBtn = QPushButton('Create Camera Profile') submitBtn.clicked.connect(self.writeXML) # Build a vertical layout vLayout = QVBoxLayout() vLayout.addStretch() vLayout.addWidget(nameLbl) vLayout.addWidget(self.nameEdit) vLayout.addWidget(flenLbl) vLayout.addWidget(self.flenEdit) vLayout.addWidget(imgwLbl) vLayout.addWidget(self.imgwEdit) vLayout.addWidget(imghLbl) vLayout.addWidget(self.imghEdit) vLayout.addWidget(senwLbl) vLayout.addWidget(self.senwEdit) vLayout.addWidget(senhLbl) vLayout.addWidget(self.senhEdit) vLayout.addStretch() vLayout.addWidget(submitBtn) vLayout.addStretch() vLayout.addWidget(notice) vLayout.addStretch() # Set up and show window self.newCamera.setLayout(vLayout) self.newCamera.resize(WINDOW_WIDTH, WINDOW_HEIGHT) # Center widget qr = self.newCamera.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.newCamera.move(qr.topLeft()) # Set title, show and call exec function self.newCamera.setWindowTitle('Add New Camera') self.newCamera.show() # self.newCamera.exec_() def writeXML(self): # Get values from user name = self.nameEdit.text() flen = self.flenEdit.text() imgw = self.imgwEdit.text() imgh = self.imghEdit.text() senw = self.senwEdit.text() senh = self.senhEdit.text() # Create subelement "camera" and then sub elements to that: # name, flen, imgw, imagh, senw, senh Camera = etree.SubElement(self.xml_root, 'Camera') xml_name = etree.SubElement(Camera, 'name') xml_name.text = name xml_flen = etree.SubElement(Camera, 'flen') xml_flen.text = flen xml_imgw = etree.SubElement(Camera, 'imgw') xml_imgw.text = imgw xml_imgh = etree.SubElement(Camera, 'imgh') xml_imgh.text = imgh xml_senw = etree.SubElement(Camera, 'senw') xml_senw.text = senw xml_senh = etree.SubElement(Camera, 'senh') xml_senh.text = senh # Overwrite camera profile file self.xml_tree.write('cameras.xml', pretty_print=True) # Close widget to return user to main GUI window self.newCamera.close() def aboutProgram(self): QMessageBox.about(self, "About LogConverter", ABOUT_TEXT) def convertLog(self): if self.camera_chosen == 0: QMessageBox.information(self, 'Warning', 'Select a Camera!') elif self.log_type_chosen == 0: QMessageBox.information(self, 'Warning', 'Select Log Type!') elif self.log_chosen == 0: QMessageBox.information(self, 'Warning', 'Select Log File!') else: fov_values = fov.fov(self.flen, self.sensorw, self.sensorh) fov_horizontal = fov_values[0] fov_vertical = fov_values[1] amsl = self.amslInput.text() # if (self.log == 'DJI GO'): # converter.converter( # self.log_file, self.save_location, # fov_h, fov_w, amsl) # QMessageBox.information(self, 'Message', 'Log converted!') if (self.log == 'Litchi'): self.converted = litchiconverter.converter( self.log_file, self.save_location, fov_horizontal, fov_vertical, amsl) if (self.converted): QMessageBox.information(self, 'Message', 'Log converted!') else: QMessageBox.information(self, 'Message', 'No video in log file.')
class AuthWindowUI(object): def __init__(self, MainWin): # Main Window MainWin.setObjectName("AuthWindow") MainWin.setWindowIcon(MainWin.window_icon) MainWin.setWindowTitle(MainWin.tr(MainWin.window_title)) MainWin.resize(1024, 860) self.config = MainWin.config self.centralWidget = QWidget(MainWin) self.centralWidget.setObjectName("centralWidget") MainWin.setCentralWidget(self.centralWidget) self.verticalLayout = QVBoxLayout(self.centralWidget) self.verticalLayout.setContentsMargins(11, 11, 11, 11) self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") self.tabWidget = QTabWidget(MainWin) self.tabWidget.currentChanged.connect(MainWin.onTabChanged) self.tabWidget.tabCloseRequested.connect(MainWin.onTabClosed) self.tabWidget.setTabsClosable(True) # workaround for https://bugreports.qt.io/browse/QTBUG-58267 if "darwin" in sys.platform: self.tabWidget.setDocumentMode(True) # Splitter for log self.splitter = QSplitter(Qt.Vertical) self.splitter.addWidget(self.tabWidget) # Log Widget self.logTextBrowser = QPlainTextEditLogger(self.centralWidget) self.logTextBrowser.widget.setObjectName("logTextBrowser") self.logTextBrowser.widget.setStyleSheet(""" QPlainTextEdit { border: 2px solid grey; border-radius: 5px; background-color: lightgray; } """) self.splitter.addWidget(self.logTextBrowser.widget) # add splitter self.splitter.setSizes([800, 100]) self.verticalLayout.addWidget(self.splitter) # Tool Bar self.mainToolBar = QToolBar(MainWin) self.mainToolBar.setObjectName("mainToolBar") self.mainToolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.mainToolBar.setContextMenuPolicy(Qt.PreventContextMenu) MainWin.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # Servers self.serverWidget = QWidget(MainWin) self.serverLayout = QHBoxLayout() self.serverLabel = QLabel("Server:") self.serverLayout.addWidget(self.serverLabel) self.serverComboBox = QComboBox() self.serverComboBox.setEditable(True) self.serverComboBox.setDuplicatesEnabled(False) self.serverComboBox.setMinimumContentsLength(50) self.serverComboBox.currentIndexChanged.connect( MainWin.onServerListChanged) lineEdit = self.serverComboBox.lineEdit() lineEdit.returnPressed.connect(MainWin.on_actionAdd_triggered) self.serverLayout.addWidget(self.serverComboBox) self.serverWidget.setLayout(self.serverLayout) self.mainToolBar.addWidget(self.serverWidget) # Add self.actionAdd = QAction(MainWin) self.actionAdd.setObjectName("actionAdd") self.actionAdd.setText(MainWin.tr("Add")) self.actionAdd.setToolTip(MainWin.tr("Add to server list")) self.actionAdd.setShortcut(MainWin.tr("Ctrl+A")) # Remove self.actionRemove = QAction(MainWin) self.actionRemove.setObjectName("actionRemove") self.actionRemove.setText(MainWin.tr("Remove")) self.actionRemove.setToolTip(MainWin.tr("Remove from server list")) self.actionRemove.setShortcut(MainWin.tr("Ctrl+X")) # Show Token self.actionShowToken = QAction(MainWin) self.actionShowToken.setEnabled(False) self.actionShowToken.setObjectName("actionShowToken") self.actionShowToken.setText(MainWin.tr("Show Token")) self.actionShowToken.setToolTip( MainWin.tr("Display the current authentication token")) self.actionShowToken.setShortcut(MainWin.tr("Ctrl+S")) # Login self.actionLogin = QAction(MainWin) self.actionLogin.setObjectName("actionLogin") self.actionLogin.setText(MainWin.tr("Login")) self.actionLogin.setToolTip( MainWin.tr("Login to the currently selected server")) self.actionLogin.setShortcut(MainWin.tr("Ctrl+L")) # Logout self.actionLogout = QAction(MainWin) self.actionLogout.setObjectName("actionLogout") self.actionLogout.setText(MainWin.tr("Logout")) self.actionLogout.setToolTip( MainWin.tr("Logout of the currently selected server")) self.actionLogout.setShortcut(MainWin.tr("Ctrl+O")) # Add self.mainToolBar.addAction(self.actionAdd) self.actionAdd.setIcon(qApp.style().standardIcon( QStyle.SP_FileDialogNewFolder)) # Remove self.mainToolBar.addAction(self.actionRemove) self.actionRemove.setIcon(qApp.style().standardIcon( QStyle.SP_DialogDiscardButton)) # Show Token self.mainToolBar.addAction(self.actionShowToken) self.actionShowToken.setIcon(qApp.style().standardIcon( QStyle.SP_FileDialogInfoView)) self.mainToolBar.addSeparator() # this spacer right justifies everything that comes after it spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mainToolBar.addWidget(spacer) # Login self.mainToolBar.addSeparator() self.mainToolBar.addAction(self.actionLogin) self.actionLogin.setIcon(qApp.style().standardIcon( QStyle.SP_DialogApplyButton)) # Logout self.mainToolBar.addSeparator() self.mainToolBar.addAction(self.actionLogout) self.actionLogout.setIcon(qApp.style().standardIcon( QStyle.SP_DialogOkButton)) # Status Bar self.statusBar = QStatusBar(MainWin) self.statusBar.setToolTip("") self.statusBar.setStatusTip("") self.statusBar.setObjectName("statusBar") MainWin.setStatusBar(self.statusBar) # configure logging self.logTextBrowser.widget.log_update_signal.connect(MainWin.updateLog) self.logTextBrowser.setFormatter( logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logging.getLogger().addHandler(self.logTextBrowser) logging.getLogger().setLevel(logging.INFO) # finalize UI setup QMetaObject.connectSlotsByName(MainWin)
def initUI(self): self.camera_chosen = 0 self.log_chosen = 0 self.log_type_chosen = 0 self.output_dir_chosen = 0 centralWidget = QWidget() # ______Labels______ # Label for camera drop down list cameraLabel = QLabel('Choose Camera') cameraLabel.setAlignment(Qt.AlignCenter) # Label for log type drown down list logTypeLabel = QLabel('Choose Type of Log') logTypeLabel.setAlignment(Qt.AlignCenter) # Label for log conversion buttons self.logLabel = QLabel('Log Conversion') self.logLabel.setAlignment(Qt.AlignCenter) # Labels for User Input values inputLabel = QLabel('User Values') inputLabel.setAlignment(Qt.AlignCenter) amslLabel = QLabel('Average AMSL (m)') amslLabel.setAlignment(Qt.AlignCenter) # Unnecessary labels # # missionIdLabel = QLabel('Name of Mission') # missionIdLabel.setAlignment(Qt.AlignCenter) # tailNumLabel = QLabel('Aircraft N or registration number') # tailNumLabel.setAlignment(Qt.AlignCenter) # platDesigLabel = QLabel('Aircraft Designation') # platDesigLabel.setAlignment(Qt.AlignCenter) # imageSensorLabel = QLabel('Image sensor name') # imageSensorLabel.setAlignment(Qt.AlignCenter) # coordSysLabel = QLabel('Coordinate system (e.g. WGS84)') # coordSysLabel.setAlignment(Qt.AlignCenter) # QLineEdits for getting user values self.amslInput = QLineEdit() # self.missionIdInput = QLineEdit() # self.tailNumInput = QLineEdit() # self.platDesigInput = QLineEdit() # self.imageSensorInput = QLineEdit() # self.coordSysInput = QLineEdit() # ______Buttons______ # Create camera drop down list of supported cameras # from cameras.xml profile document # Use parser to ensure pretty print works, see: # http://lxml.de/FAQ.html#why-doesn-t-the- # pretty-print-option-reformat-my-xml-output parser = etree.XMLParser(remove_blank_text=True) self.xml_tree = etree.parse('cameras.xml', parser) self.xml_root = self.xml_tree.getroot() # Initialize QComboBox object with default text cameraType = QComboBox(self) cameraType.addItem('Select a Camera') # Loop through the children of the xml root # get the camera name and add to the drop down list index = 0 for child in self.xml_root: self.camera = child[NAME].text cameraType.addItem(self.camera) # Populate the xml map list with the camera name and # it's index in the root list so we can refer to it's values # later xml_map[self.camera] = index index += 1 cameraType.setEditable(True) cameraType.lineEdit().setReadOnly(True) cameraType.lineEdit().setAlignment(Qt.AlignCenter) # Align the dropdown options to be centered for i in range(0, cameraType.count()): cameraType.setItemData(i, Qt.AlignCenter, Qt.TextAlignmentRole) cameraType.activated[str].connect(self.cameraChosen) # Create Log type drop down menu self.logType = QComboBox(self) self.logType.addItem('Select Log Type') # self.logType.addItem('DJI GO') self.logType.addItem('Litchi') for i in range(0, self.logType.count()): self.logType.setItemData(i, Qt.AlignCenter, Qt.TextAlignmentRole) self.logType.setEditable(True) self.logType.lineEdit().setReadOnly(True) self.logType.lineEdit().setAlignment(Qt.AlignCenter) self.logType.activated[str].connect(self.logTypeChosen) # Create push button for adding a new camera profile newCameraBtn = QPushButton('Add New Camera') newCameraBtn.clicked.connect(self.newCamera) # Create push button object that opens the log file # to be converted chooseLogBtn = QPushButton('Open log file...') chooseLogBtn.clicked.connect(self.openFile) # Push button to start log conversion convertBtn = QPushButton('Convert!') convertBtn.clicked.connect(self.convertLog) # Button for setting output file location saveLocBtn = QPushButton('Set output save location...') saveLocBtn.clicked.connect(self.saveLocation) # Quit button quitBtn = QPushButton('Exit Program') quitBtn.clicked.connect(self.close) # ______Layout______ # Create vertical box layout vbox = QVBoxLayout() vbox.addStretch() vbox.addWidget(cameraLabel) vbox.addWidget(cameraType) vbox.addWidget(newCameraBtn) vbox.addStretch() vbox.addWidget(logTypeLabel) vbox.addWidget(self.logType) vbox.addStretch() vbox.addWidget(inputLabel) vbox.addWidget(amslLabel) vbox.addWidget(self.amslInput) # vbox.addWidget(missionIdLabel) # vbox.addWidget(self.missionIdInput) # vbox.addWidget(tailNumLabel) # vbox.addWidget(self.tailNumInput) # vbox.addWidget(platDesigLabel) # vbox.addWidget(self.platDesigInput) # vbox.addWidget(imageSensorLabel) # vbox.addWidget(self.imageSensorInput) # vbox.addWidget(coordSysLabel) # vbox.addWidget(self.coordSysInput) vbox.addStretch() vbox.addWidget(self.logLabel) vbox.addWidget(chooseLogBtn) vbox.addWidget(saveLocBtn) vbox.addWidget(convertBtn) vbox.addStretch() vbox.addWidget(quitBtn) centralWidget.setLayout(vbox) # Create quit action quit = QAction('Quit', self) quit.setShortcut('Ctrl+Q') quit.setStatusTip('Exit program') quit.triggered.connect(self.close) # Create about action about = QAction('About LogConverter', self) about.setShortcut('Ctrl+A') about.setStatusTip('About program') about.triggered.connect(self.aboutProgram) # Create menubar menubar = self.menuBar() fileMenu = menubar.addMenu('&File') fileMenu.addAction(quit) aboutMenu = menubar.addMenu('&About') aboutMenu.addAction(about) self.setCentralWidget(centralWidget) self.resize(WINDOW_WIDTH, WINDOW_HEIGHT) self.center() self.setWindowTitle('Log Converter') self.show()