class MainWindow(QMainWindow, main_ui.Ui_MainWindow): serial_failed = Signal() data_received = Signal() def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.setupUi(self) self.collections = [] try: saved = open(PQCOM_DATA_FILE, 'rb') self.collections = pickle.load(saved) saved.close() except IOError: pass self.input_history = '' self.output_history = [] self.repeater = Repeater() self.setWindowIcon(QIcon(resource_path('img/pqcom-logo.png'))) self.aboutDialog = AboutDialog(self) self.setupDialog = SetupDialog(self) port, baud, bytebits, stopbits, parity = self.setupDialog.get() self.setWindowTitle('pqcom - ' + port + ' ' + str(baud)) self.actionNew.setIcon(QIcon(resource_path('img/new.svg'))) self.actionSetup.setIcon(QIcon(resource_path('img/settings.svg'))) self.actionRun.setIcon(QIcon(resource_path('img/run.svg'))) self.actionHex.setIcon(QIcon(resource_path('img/hex.svg'))) self.actionClear.setIcon(QIcon(resource_path('img/clear.svg'))) self.actionPin.setIcon(QIcon(resource_path('img/pin.svg'))) self.actionAbout.setIcon(QIcon(resource_path('img/about.svg'))) self.actionUseCR = QAction('EOL - \\r', self) self.actionUseCR.setCheckable(True) self.actionUseLF = QAction('EOL - \\n', self) self.actionUseLF.setCheckable(True) self.actionUseCRLF = QAction('EOL - \\r\\n', self) self.actionUseCRLF.setCheckable(True) self.actionUseCRLF.setChecked(True) eolGroup = QActionGroup(self) eolGroup.addAction(self.actionUseCR) eolGroup.addAction(self.actionUseLF) eolGroup.addAction(self.actionUseCRLF) eolGroup.setExclusive(True) self.actionAppendEol = QAction('Append extra EOL', self) self.actionAppendEol.setCheckable(True) popupMenu = QMenu(self) popupMenu.addAction(self.actionUseCR) popupMenu.addAction(self.actionUseLF) popupMenu.addAction(self.actionUseCRLF) popupMenu.addSeparator() popupMenu.addAction(self.actionAppendEol) self.sendButton.setMenu(popupMenu) self.outputHistoryActions = [] self.outputHistoryMenu = QMenu(self) self.outputHistoryMenu.addAction('None') self.historyButton.setMenu(self.outputHistoryMenu) self.collectActions = [] self.collectMenu = QMenu(self) self.collectMenu.setTearOffEnabled(True) if not self.collections: self.collectMenu.addAction('None') else: for item in self.collections: icon = QIcon(resource_path(ICON_LIB[item[0]])) action = self.collectMenu.addAction(icon, item[1]) self.collectActions.append(action) self.collectButton.setMenu(self.collectMenu) self.collectButton.setIcon(QIcon(resource_path('img/star.svg'))) self.collectMenu.setContextMenuPolicy(Qt.CustomContextMenu) # self.connect(self.collectMenu, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), # self.on_collect_context_menu) self.collectContextMenu = QMenu(self) self.removeCollectionAction = QAction('Remove', self) self.removeAllCollectionsAction = QAction('Remove all', self) self.collectContextMenu.addAction(self.removeCollectionAction) self.collectContextMenu.addAction(self.removeAllCollectionsAction) self.activeCollectAction = None self.removeCollectionAction.triggered.connect(self.remove_collection) self.removeAllCollectionsAction.triggered.connect(self.remove_all_collections) self.sendButton.clicked.connect(self.send) self.repeatCheckBox.toggled.connect(self.repeat) self.actionSetup.triggered.connect(self.setup) self.actionNew.triggered.connect(self.new) self.actionRun.toggled.connect(self.run) self.actionHex.toggled.connect(self.convert) self.actionClear.triggered.connect(self.clear) self.actionPin.toggled.connect(self.pin) self.actionAbout.triggered.connect(self.aboutDialog.show) self.outputHistoryMenu.triggered.connect(self.on_history_item_clicked) self.collectButton.clicked.connect(self.collect) self.collectMenu.triggered.connect(self.on_collect_item_clicked) self.serial_failed.connect(self.handle_serial_error) self.data_received.connect(self.display) QShortcut(QKeySequence('Ctrl+Return'), self.sendPlainTextEdit, self.send) def gen_shortcut_callback(n): def on_shortcut(): self.send_collection(n - 1) return on_shortcut for i in range(1, 10): QShortcut(QKeySequence('Ctrl+' + str(i)), self, gen_shortcut_callback(i)) # self.extendRadioButton.setVisible(False) self.periodSpinBox.setVisible(False) def new(self): save = open(PQCOM_DATA_FILE, 'wb') pickle.dump(self.collections, save) save.close() args = sys.argv if args != [sys.executable]: args = [sys.executable] + args subprocess.Popen(args) def send(self): if self.repeatCheckBox.isChecked(): if self.sendButton.text().find('Stop') >= 0: self.repeater.stop() self.sendButton.setText('Start') return raw = str(self.sendPlainTextEdit.toPlainText()) data = raw form = 'N' if self.normalRadioButton.isChecked(): if self.actionAppendEol.isChecked(): data += '\n' if self.actionUseCRLF.isChecked(): data = data.replace('\n', '\r\n') elif self.actionUseCR.isChecked(): data = data.replace('\n', '\r') elif self.hexRadioButton.isChecked(): form = 'H' data = translator.from_hex_string(data) else: form = 'E' data = translator.from_extended_string(data) if self.repeatCheckBox.isChecked(): self.repeater.start(data, self.periodSpinBox.value()) self.sendButton.setText('Stop') else: serial.write(data) # record history record = [form, raw, data] if record in self.output_history: self.output_history.remove(record) self.output_history.insert(0, record) self.outputHistoryActions = [] self.outputHistoryMenu.clear() for item in self.output_history: icon = QIcon(resource_path(ICON_LIB[item[0]])) action = self.outputHistoryMenu.addAction(icon, item[1]) self.outputHistoryActions.append(action) def repeat(self, is_true): if is_true: self.periodSpinBox.setVisible(True) self.sendButton.setText('Start') else: self.periodSpinBox.setVisible(False) self.sendButton.setText('Send') self.repeater.stop() def on_history_item_clicked(self, action): try: index = self.outputHistoryActions.index(action) except ValueError: return form, raw, data = self.output_history[index] if form == 'H': self.hexRadioButton.setChecked(True) elif form == 'E': self.extendRadioButton.setChecked(True) else: self.normalRadioButton.setChecked(True) self.sendPlainTextEdit.clear() self.sendPlainTextEdit.insertPlainText(raw) self.send() def collect(self): if not self.collections: self.collectMenu.clear() raw = str(self.sendPlainTextEdit.toPlainText()) form = 'N' if self.hexRadioButton.isChecked(): form = 'H' elif self.extendRadioButton.isChecked(): form = 'E' item = [form, raw] if item in self.collections: return self.collections.append(item) icon = QIcon(resource_path(ICON_LIB[form])) action = self.collectMenu.addAction(icon, raw) self.collectActions.append(action) def on_collect_context_menu(self, point): self.activeCollectAction = self.collectMenu.activeAction() self.collectContextMenu.exec_(self.collectMenu.mapToGlobal(point)) def on_collect_item_clicked(self, action): try: index = self.collectActions.index(action) except ValueError: return self.send_collection(index) def send_collection(self, index): if len(self.collections) > index: form, raw = self.collections[index] if form == 'H': self.hexRadioButton.setChecked(True) elif form == 'E': self.extendRadioButton.setChecked(True) else: self.normalRadioButton.setChecked(True) self.sendPlainTextEdit.clear() self.sendPlainTextEdit.insertPlainText(raw) self.send() def remove_collection(self): try: index = self.collectActions.index(self.activeCollectAction) except ValueError: return del self.collectActions[index] del self.collections[index] self.collectMenu.clear() for item in self.collections: icon = QIcon(resource_path(ICON_LIB[item[0]])) action = self.collectMenu.addAction(icon, item[1]) self.collectActions.append(action) save = open(PQCOM_DATA_FILE, 'wb') pickle.dump(self.collections, save) save.close() def remove_all_collections(self): self.collectMenu.clear() self.collections = [] self.collectActions = [] self.collectMenu.addAction('None') save = open(PQCOM_DATA_FILE, 'wb') save.close() pickle.dump(self.collections, save) def on_serial_failed(self): if self.sendButton.text().find('Stop') >= 0: self.repeater.stop() self.sendButton.setText('Start') self.serial_failed.emit() def handle_serial_error(self): self.actionRun.setChecked(False) self.setup(True) def on_data_received(self): self.data_received.emit() def setup(self, warning=False): choice = self.setupDialog.show(warning) if choice == QDialog.Accepted: if self.actionRun.isChecked(): self.actionRun.setChecked(False) self.actionRun.setChecked(True) def run(self, is_true): port, baud, bytebits, stopbits, parity = self.setupDialog.get() if is_true: serial.start(port, baud, bytebits, stopbits, parity) self.setWindowTitle('pqcom - ' + port + ' ' + str(baud) + ' opened') else: if self.sendButton.text().find('Stop') >= 0: self.repeater.stop() self.sendButton.setText('Start') serial.join() self.setWindowTitle('pqcom - ' + port + ' ' + str(baud) + ' closed') def display(self): data = serial.read() self.input_history += data # store history data if self.actionHex.isChecked(): data = translator.to_hex_prefix_string(data) self.recvTextEdit.moveCursor(QTextCursor.End) self.recvTextEdit.insertPlainText(data) self.recvTextEdit.moveCursor(QTextCursor.End) def convert(self, is_true): if is_true: text = translator.to_hex_prefix_string(self.input_history) else: text = self.input_history self.recvTextEdit.clear() self.recvTextEdit.insertPlainText(text) self.recvTextEdit.moveCursor(QTextCursor.End) def pin(self, is_true): if is_true: self.setWindowFlags(self.windowFlags() | Qt.WindowStaysOnTopHint) else: self.setWindowFlags(self.windowFlags() & ~Qt.WindowStaysOnTopHint) self.show() def clear(self): self.recvTextEdit.clear() self.input_history = '' def closeEvent(self, event): save = open(PQCOM_DATA_FILE, 'wb') pickle.dump(self.collections, save) save.close() event.accept()
class Slideshow(QMainWindow): def __init__(self, parent, gallery=list(), index=0): super(Slideshow, self).__init__() self.parent = parent self.configure_gui() self.create_widgets(gallery) self.create_menu() self.index = index self.move() self.showMaximized() self.activateWindow() def configure_gui(self): self.stack = QStackedWidget(self) self.setCentralWidget(self.stack) def create_widgets(self, gallery): self.model = Model(self, gallery) self.image = imageViewer(self) self.video = videoPlayer(self) self.stack.addWidget(self.image) self.stack.addWidget(self.video) self.setMouseTracking(True) self.timer = QTimer() self.timer.timeout.connect( lambda: self.setCursor(Qt.BlankCursor) ) def create_menu(self): self.menubar = self.menuBar() self.menubar.triggered.connect(self.menuPressEvent) # File file = self.menubar.addMenu('File') file.addAction('Copy Image to', lambda: copy_to(self, [self.model.index(self.index)]), shortcut='CTRL+SHIFT+C') file.addSeparator() file.addAction('Exit', self.close, shortcut='Ctrl+W') # View view = self.menubar.addMenu('View') view.addAction('Fullscreen', self.fullscreen, shortcut='F11') # Help help = self.menubar.addMenu('Help') menu = QMenu(self) # menu = create_menu( # self, # ) self.full = QAction( 'Fullscreen', menu, triggered=self.fullscreen ) menu.addAction(self.full) self.play_menu = QMenu('Slideshow') group1 = QActionGroup(self.play_menu) group1.setExclusive(True) self.play_menu.addActions([ QAction( 'Pause', self.play_menu, triggered=self.playEvent, checked=True ), QAction( 'Play', self.play_menu, triggered=self.playEvent ) ]) self.play_menu.addSeparator() group2 = QActionGroup(self.play_menu) group2.setExclusive(True) self.play_menu.addActions([ QAction( 'Speed - Slow', self.play_menu, triggered=self.playEvent ), QAction( 'Speed - Medium', self.play_menu, triggered=self.playEvent, checked=True ), QAction( 'Speed - Fast', self.play_menu, triggered=self.playEvent ) ]) # menu.addMenu(self.play_menu) menu.addSeparator() menu.addAction(QAction( 'Rotate right', menu, triggered=lambda: self.rotate(+1) )) menu.addAction(QAction( 'Rotate left', menu, triggered=lambda: self.rotate(-1) )) menu.addSeparator() menu.addAction(QAction( 'Copy', menu, triggered=self.copy )) menu.addAction(QAction( 'Delete', menu, triggered=self.delete )) menu.addSeparator() menu.addAction(QAction( 'Properties', menu, triggered=self.openEditor )) self.menu = menu def move(self, delta=0): if not self.model.gallery: self.image.no_image() return 0 self.index = (self.index + delta) % len(self.model.gallery) path = self.model.index(self.index).data(Qt.UserRole)[0] self.setWindowTitle(f'{Path(path).name} - Slideshow') if path is None: pixmap = QPixmap() else: if path.endswith(('.jpg', '.png')): image = QImage(path) path = None elif path.endswith(('.gif', '.mp4', '.webm')): image = get_frame(path) else: print(path) pixmap = QPixmap(image).scaled( self.size(), Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation ) self.image.update(pixmap) self.stack.setCurrentIndex(0) self.video.update(path) def fullscreen(self): if self.isFullScreen(): self.timer.stop() self.image.setStyleSheet('background: ') self.full.setText('Fullscreen') self.setCursor(Qt.ArrowCursor) self.showMaximized() self.menubar.show() else: self.image.setStyleSheet('background: black') self.full.setText('Exit fullscreen') self.setCursor(Qt.BlankCursor) self.menubar.hide() self.showFullScreen() def rotate(self, sign): path = self.model.index(self.index).data(Qt.UserRole)[0] if path.endswith(('jpg', 'png')): self.image.rotate(path, sign) else: self.video.rotate(path, sign) self.move() def copy(self): path = self.model.index(self.index).data(Qt.UserRole)[0] if path.endswith(('gif', '.mp4', '.webm')): return path = mktemp(suffix='.png') image = ImageGrab.grab( (0, 0, self.width(),self.height()) ) image.save(path) cb = QApplication.clipboard() cb.clear(mode=cb.Clipboard) cb.setText(path, mode=cb.Clipboard) def delete(self): if self.stack.currentIndex(): self.video.update(None) else: self.image.update(None) index = [self.model.index(self.index)] if self.parent.delete_records(index): del self.model.gallery[self.index] self.model.layoutChanged.emit() def openEditor(self): index = self.model.index(self.index) Properties(self.parent, [index.data(Qt.EditRole)]) def playEvent(self, event): match self.play_menu.activeAction().text: case 'Speed - Slow': pass case 'Speed - Medium': pass case 'Speed - Fast': pass def contextMenuEvent(self, event): self.menu.popup(event.globalPos()) def menuPressEvent(self, event=None): action = event.text() # if action == 'Copy Image to': # copy_to(self, [self.model.index(self.index)]) if action == 'Exit': self.close() def keyPressEvent(self, event): key_press = event.key() video = self.stack.currentIndex() alt = event.modifiers() == Qt.AltModifier ctrl = event.modifiers() == Qt.ControlModifier shift = event.modifiers() == Qt.ShiftModifier if key_press in (Qt.Key_Right, Qt.Key_Left): self.move(1 if key_press == Qt.Key_Right else -1) elif video and key_press in (Qt.Key_Home, Qt.Key_End): if key_press == Qt.Key_Home: self.video.position(0) else: self.video.position(0) elif video and key_press in (Qt.Key_Period, Qt.Key_Comma): sign = 1 if key_press == Qt.Key_Period else -1 if ctrl: self.video.position(sign * 50) else: self.video.position(sign * 5000) elif video and key_press in (Qt.Key_Up, Qt.Key_Down): sign = 1 if key_press == Qt.Key_Up else -1 if ctrl: self.video.volume(sign * 1) else: self.video.volume(sign * 10) elif video and key_press == Qt.Key_Space: self.video.pause() elif video and key_press == Qt.Key_M: self.video.mute() elif key_press == Qt.Key_Delete: self.delete() elif key_press == Qt.Key_F11: self.fullscreen() elif key_press == Qt.Key_Escape: if self.isFullScreen(): self.fullscreen() else: self.close() elif alt: if key_press in (Qt.Key_Return, Qt.Key_Enter): self.openEditor() elif ctrl: if shift and key_press == Qt.Key_C: opy_to(self, [self.model.index(self.index)]) elif key_press == Qt.Key_C: self.copy() elif key_press == Qt.Key_W: self.close() def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton: if event.x() > (self.width() * .5): self.move(+1) elif event.x() < (self.width() * .5): self.move(-1) def mouseMoveEvent(self, event): if self.isFullScreen(): self.setCursor(Qt.ArrowCursor) self.timer.start(1500) def closeEvent(self, event): self.video.update(None)