class MainWindow_Ui(QMainWindow): def __init__(self, persepolis_setting): super().__init__() # MainWindow self.persepolis_setting = persepolis_setting # add support for other languages locale = str(self.persepolis_setting.value('settings/locale')) QLocale.setDefault(QLocale(locale)) self.translator = QTranslator() if self.translator.load(':/translations/locales/ui_' + locale, 'ts'): QCoreApplication.installTranslator(self.translator) # set ui direction ui_direction = self.persepolis_setting.value('ui_direction') if ui_direction == 'rtl': self.setLayoutDirection(Qt.RightToLeft) elif ui_direction in 'ltr': self.setLayoutDirection(Qt.LeftToRight) icons = ':/' + \ str(self.persepolis_setting.value('settings/icons')) + '/' self.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) self.setWindowIcon( QIcon.fromTheme('persepolis', QIcon(':/persepolis.svg'))) self.centralwidget = QWidget(self) self.verticalLayout = QVBoxLayout(self.centralwidget) # enable drag and drop self.setAcceptDrops(True) # frame self.frame = QFrame(self.centralwidget) # download_table_horizontalLayout download_table_horizontalLayout = QHBoxLayout() horizontal_splitter = QSplitter(Qt.Horizontal) vertical_splitter = QSplitter(Qt.Vertical) # category_tree self.category_tree_qwidget = QWidget(self) category_tree_verticalLayout = QVBoxLayout() self.category_tree = CategoryTreeView(self) category_tree_verticalLayout.addWidget(self.category_tree) self.category_tree_model = QStandardItemModel() self.category_tree.setModel(self.category_tree_model) category_table_header = [ QCoreApplication.translate("mainwindow_ui_tr", 'Category') ] self.category_tree_model.setHorizontalHeaderLabels( category_table_header) self.category_tree.header().setStretchLastSection(True) self.category_tree.header().setDefaultAlignment(Qt.AlignCenter) # queue_panel self.queue_panel_widget = QWidget(self) queue_panel_verticalLayout_main = QVBoxLayout(self.queue_panel_widget) # queue_panel_show_button self.queue_panel_show_button = QPushButton(self) queue_panel_verticalLayout_main.addWidget(self.queue_panel_show_button) # queue_panel_widget_frame self.queue_panel_widget_frame = QFrame(self) self.queue_panel_widget_frame.setFrameShape(QFrame.StyledPanel) self.queue_panel_widget_frame.setFrameShadow(QFrame.Raised) queue_panel_verticalLayout_main.addWidget( self.queue_panel_widget_frame) queue_panel_verticalLayout = QVBoxLayout(self.queue_panel_widget_frame) queue_panel_verticalLayout_main.setContentsMargins(50, -1, 50, -1) # start_end_frame self.start_end_frame = QFrame(self) # start time start_verticalLayout = QVBoxLayout(self.start_end_frame) self.start_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.start_checkBox) self.start_frame = QFrame(self) self.start_frame.setFrameShape(QFrame.StyledPanel) self.start_frame.setFrameShadow(QFrame.Raised) start_frame_verticalLayout = QVBoxLayout(self.start_frame) self.start_time_qDataTimeEdit = MyQDateTimeEdit(self.start_frame) self.start_time_qDataTimeEdit.setDisplayFormat('H:mm') start_frame_verticalLayout.addWidget(self.start_time_qDataTimeEdit) start_verticalLayout.addWidget(self.start_frame) # end time self.end_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.end_checkBox) self.end_frame = QFrame(self) self.end_frame.setFrameShape(QFrame.StyledPanel) self.end_frame.setFrameShadow(QFrame.Raised) end_frame_verticalLayout = QVBoxLayout(self.end_frame) self.end_time_qDateTimeEdit = MyQDateTimeEdit(self.end_frame) self.end_time_qDateTimeEdit.setDisplayFormat('H:mm') end_frame_verticalLayout.addWidget(self.end_time_qDateTimeEdit) start_verticalLayout.addWidget(self.end_frame) self.reverse_checkBox = QCheckBox(self) start_verticalLayout.addWidget(self.reverse_checkBox) queue_panel_verticalLayout.addWidget(self.start_end_frame) # limit_after_frame self.limit_after_frame = QFrame(self) # limit_checkBox limit_verticalLayout = QVBoxLayout(self.limit_after_frame) self.limit_checkBox = QCheckBox(self) limit_verticalLayout.addWidget(self.limit_checkBox) # limit_frame self.limit_frame = QFrame(self) self.limit_frame.setFrameShape(QFrame.StyledPanel) self.limit_frame.setFrameShadow(QFrame.Raised) limit_verticalLayout.addWidget(self.limit_frame) limit_frame_verticalLayout = QVBoxLayout(self.limit_frame) # limit_spinBox limit_frame_horizontalLayout = QHBoxLayout() self.limit_spinBox = QDoubleSpinBox(self) self.limit_spinBox.setMinimum(1) self.limit_spinBox.setMaximum(1023) limit_frame_horizontalLayout.addWidget(self.limit_spinBox) # limit_comboBox self.limit_comboBox = QComboBox(self) self.limit_comboBox.addItem("") self.limit_comboBox.addItem("") limit_frame_horizontalLayout.addWidget(self.limit_comboBox) limit_frame_verticalLayout.addLayout(limit_frame_horizontalLayout) # limit_pushButton self.limit_pushButton = QPushButton(self) limit_frame_verticalLayout.addWidget(self.limit_pushButton) # after_checkBox self.after_checkBox = QCheckBox(self) limit_verticalLayout.addWidget(self.after_checkBox) # after_frame self.after_frame = QFrame(self) self.after_frame.setFrameShape(QFrame.StyledPanel) self.after_frame.setFrameShadow(QFrame.Raised) limit_verticalLayout.addWidget(self.after_frame) after_frame_verticalLayout = QVBoxLayout(self.after_frame) # after_comboBox self.after_comboBox = QComboBox(self) self.after_comboBox.addItem("") after_frame_verticalLayout.addWidget(self.after_comboBox) # after_pushButton self.after_pushButton = QPushButton(self) after_frame_verticalLayout.addWidget(self.after_pushButton) queue_panel_verticalLayout.addWidget(self.limit_after_frame) category_tree_verticalLayout.addWidget(self.queue_panel_widget) # keep_awake_checkBox self.keep_awake_checkBox = QCheckBox(self) queue_panel_verticalLayout.addWidget(self.keep_awake_checkBox) self.category_tree_qwidget.setLayout(category_tree_verticalLayout) horizontal_splitter.addWidget(self.category_tree_qwidget) # download table widget self.download_table_content_widget = QWidget(self) download_table_content_widget_verticalLayout = QVBoxLayout( self.download_table_content_widget) # download_table self.download_table = DownloadTableWidget(self) vertical_splitter.addWidget(self.download_table) horizontal_splitter.addWidget(self.download_table_content_widget) self.download_table.setColumnCount(13) self.download_table.setSelectionBehavior(QAbstractItemView.SelectRows) self.download_table.setEditTriggers(QAbstractItemView.NoEditTriggers) self.download_table.verticalHeader().hide() # hide column of GID and column of link. self.download_table.setColumnHidden(8, True) self.download_table.setColumnHidden(9, True) download_table_header = [ QCoreApplication.translate("mainwindow_ui_tr", 'File Name'), QCoreApplication.translate("mainwindow_ui_tr", 'Status'), QCoreApplication.translate("mainwindow_ui_tr", 'Size'), QCoreApplication.translate("mainwindow_ui_tr", 'Downloaded'), QCoreApplication.translate("mainwindow_ui_tr", 'Percentage'), QCoreApplication.translate("mainwindow_ui_tr", 'Connections'), QCoreApplication.translate("mainwindow_ui_tr", 'Transfer Rate'), QCoreApplication.translate("mainwindow_ui_tr", 'Estimated Time Left'), 'Gid', QCoreApplication.translate("mainwindow_ui_tr", 'Link'), QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'), QCoreApplication.translate("mainwindow_ui_tr", 'Last Try Date'), QCoreApplication.translate("mainwindow_ui_tr", 'Category') ] self.download_table.setHorizontalHeaderLabels(download_table_header) # fixing the size of download_table when window is Maximized! self.download_table.horizontalHeader().setSectionResizeMode( QHeaderView.ResizeMode.Interactive) self.download_table.horizontalHeader().setStretchLastSection(True) horizontal_splitter.setStretchFactor(0, 3) # category_tree width horizontal_splitter.setStretchFactor(1, 10) # ratio of tables's width # video_finder_widget self.video_finder_widget = QWidget(self) video_finder_horizontalLayout = QHBoxLayout(self.video_finder_widget) self.muxing_pushButton = QPushButton(self) self.muxing_pushButton.setIcon(QIcon(icons + 'video_finder')) video_finder_horizontalLayout.addWidget(self.muxing_pushButton) video_finder_horizontalLayout.addSpacing(20) video_audio_verticalLayout = QVBoxLayout() self.video_label = QLabel(self) video_audio_verticalLayout.addWidget(self.video_label) self.audio_label = QLabel(self) video_audio_verticalLayout.addWidget(self.audio_label) video_finder_horizontalLayout.addLayout(video_audio_verticalLayout) status_muxing_verticalLayout = QVBoxLayout() self.video_finder_status_label = QLabel(self) status_muxing_verticalLayout.addWidget(self.video_finder_status_label) self.muxing_status_label = QLabel(self) status_muxing_verticalLayout.addWidget(self.muxing_status_label) video_finder_horizontalLayout.addLayout(status_muxing_verticalLayout) vertical_splitter.addWidget(self.video_finder_widget) download_table_content_widget_verticalLayout.addWidget( vertical_splitter) download_table_horizontalLayout.addWidget(horizontal_splitter) self.frame.setLayout(download_table_horizontalLayout) self.verticalLayout.addWidget(self.frame) self.setCentralWidget(self.centralwidget) # menubar self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 600, 24)) self.setMenuBar(self.menubar) fileMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&File')) editMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Edit')) viewMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&View')) downloadMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Download')) queueMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Queue')) videoFinderMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", 'V&ideo Finder')) helpMenu = self.menubar.addMenu( QCoreApplication.translate("mainwindow_ui_tr", '&Help')) # viewMenu submenus sortMenu = viewMenu.addMenu( QCoreApplication.translate("mainwindow_ui_tr", 'Sort by')) # statusbar self.statusbar = QStatusBar(self) self.setStatusBar(self.statusbar) self.statusbar.showMessage( QCoreApplication.translate("mainwindow_ui_tr", "Persepolis Download Manager")) # toolBar self.toolBar2 = QToolBar(self) self.addToolBar(Qt.TopToolBarArea, self.toolBar2) self.toolBar2.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", 'Menu')) self.toolBar2.setFloatable(False) self.toolBar2.setMovable(False) self.toolBar = QToolBar(self) self.addToolBar(Qt.TopToolBarArea, self.toolBar) self.toolBar.setWindowTitle( QCoreApplication.translate("mainwindow_ui_tr", 'Toolbar')) self.toolBar.setFloatable(False) self.toolBar.setMovable(False) #toolBar and menubar and actions self.persepolis_setting.beginGroup('settings/shortcuts') # videoFinderAddLinkAction self.videoFinderAddLinkAction = QAction( QIcon(icons + 'video_finder'), QCoreApplication.translate("mainwindow_ui_tr", 'Find Video Links...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Download video or audio from Youtube, Vimeo, etc.'), triggered=self.showVideoFinderAddLinkWindow) self.videoFinderAddLinkAction_shortcut = QShortcut( self.persepolis_setting.value('video_finder_shortcut'), self, self.showVideoFinderAddLinkWindow) videoFinderMenu.addAction(self.videoFinderAddLinkAction) # stopAllAction self.stopAllAction = QAction(QIcon(icons + 'stop_all'), QCoreApplication.translate( "mainwindow_ui_tr", 'Stop All Active Downloads'), self, statusTip='Stop All Active Downloads', triggered=self.stopAllDownloads) downloadMenu.addAction(self.stopAllAction) # sort_file_name_Action self.sort_file_name_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'File Name'), self, triggered=self.sortByName) sortMenu.addAction(self.sort_file_name_Action) # sort_file_size_Action self.sort_file_size_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'File Size'), self, triggered=self.sortBySize) sortMenu.addAction(self.sort_file_size_Action) # sort_first_try_date_Action self.sort_first_try_date_Action = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'First Try Date'), self, triggered=self.sortByFirstTry) sortMenu.addAction(self.sort_first_try_date_Action) # sort_last_try_date_Action self.sort_last_try_date_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'Last Try Date'), self, triggered=self.sortByLastTry) sortMenu.addAction(self.sort_last_try_date_Action) # sort_download_status_Action self.sort_download_status_Action = QAction(QCoreApplication.translate( "mainwindow_ui_tr", 'Download Status'), self, triggered=self.sortByStatus) sortMenu.addAction(self.sort_download_status_Action) # trayAction self.trayAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show System Tray Icon'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Show/Hide system tray icon"), triggered=self.showTray) self.trayAction.setCheckable(True) viewMenu.addAction(self.trayAction) # showMenuBarAction self.showMenuBarAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Menubar'), triggered=self.showMenuBar) self.showMenuBarAction.setCheckable(True) viewMenu.addAction(self.showMenuBarAction) # showSidePanelAction self.showSidePanelAction = QAction( QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Show Side Panel'), triggered=self.showSidePanel) self.showSidePanelAction.setCheckable(True) viewMenu.addAction(self.showSidePanelAction) # minimizeAction self.minimizeAction = QAction( QIcon(icons + 'minimize'), QCoreApplication.translate("mainwindow_ui_tr", 'Minimize to System Tray'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Minimize to System Tray"), triggered=self.minMaxTray) self.minimizeAction_shortcut = QShortcut( self.persepolis_setting.value('hide_window_shortcut'), self, self.minMaxTray) viewMenu.addAction(self.minimizeAction) # addlinkAction self.addlinkAction = QAction( QIcon(icons + 'add'), QCoreApplication.translate("mainwindow_ui_tr", 'Add New Download Link...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Add New Download Link"), triggered=self.addLinkButtonPressed) self.addlinkAction_shortcut = QShortcut( self.persepolis_setting.value('add_new_download_shortcut'), self, self.addLinkButtonPressed) fileMenu.addAction(self.addlinkAction) # importText self.addtextfileAction = QAction( QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Import Links from Text File...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Create a text file and put links in it, line by line!'), triggered=self.importText) self.addtextfileAction_shortcut = QShortcut( self.persepolis_setting.value('import_text_shortcut'), self, self.importText) fileMenu.addAction(self.addtextfileAction) # resumeAction self.resumeAction = QAction( QIcon(icons + 'play'), QCoreApplication.translate("mainwindow_ui_tr", 'Resume Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Resume Download"), triggered=self.resumeButtonPressed) downloadMenu.addAction(self.resumeAction) # pauseAction self.pauseAction = QAction( QIcon(icons + 'pause'), QCoreApplication.translate("mainwindow_ui_tr", 'Pause Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Pause Download"), triggered=self.pauseButtonPressed) downloadMenu.addAction(self.pauseAction) # stopAction self.stopAction = QAction( QIcon(icons + 'stop'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop Download'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Stop/Cancel Download"), triggered=self.stopButtonPressed) downloadMenu.addAction(self.stopAction) # propertiesAction self.propertiesAction = QAction( QIcon(icons + 'setting'), QCoreApplication.translate("mainwindow_ui_tr", 'Properties'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Properties"), triggered=self.propertiesButtonPressed) downloadMenu.addAction(self.propertiesAction) # progressAction self.progressAction = QAction( QIcon(icons + 'window'), QCoreApplication.translate("mainwindow_ui_tr", 'Progress'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Progress"), triggered=self.progressButtonPressed) downloadMenu.addAction(self.progressAction) # openFileAction self.openFileAction = QAction( QIcon(icons + 'file'), QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open File...'), triggered=self.openFile) fileMenu.addAction(self.openFileAction) # openDownloadFolderAction self.openDownloadFolderAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Open Download Folder'), triggered=self.openDownloadFolder) fileMenu.addAction(self.openDownloadFolderAction) # openDefaultDownloadFolderAction self.openDefaultDownloadFolderAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate("mainwindow_ui_tr", 'Open Default Download Folder'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Open Default Download Folder'), triggered=self.openDefaultDownloadFolder) fileMenu.addAction(self.openDefaultDownloadFolderAction) # exitAction self.exitAction = QAction( QIcon(icons + 'exit'), QCoreApplication.translate("mainwindow_ui_tr", 'Exit'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", "Exit"), triggered=self.closeAction) self.exitAction_shortcut = QShortcut( self.persepolis_setting.value('quit_shortcut'), self, self.closeAction) fileMenu.addAction(self.exitAction) # clearAction self.clearAction = QAction(QIcon(icons + 'multi_remove'), QCoreApplication.translate( "mainwindow_ui_tr", 'Clear Download List'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Clear all items in download list'), triggered=self.clearDownloadList) editMenu.addAction(self.clearAction) # removeSelectedAction self.removeSelectedAction = QAction( QIcon(icons + 'remove'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove Selected Downloads from List'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Remove Selected Downloads from List'), triggered=self.removeSelected) self.removeSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('remove_shortcut'), self, self.removeSelected) editMenu.addAction(self.removeSelectedAction) self.removeSelectedAction.setEnabled(False) # deleteSelectedAction self.deleteSelectedAction = QAction( QIcon(icons + 'trash'), QCoreApplication.translate("mainwindow_ui_tr", 'Delete Selected Download Files'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Delete Selected Download Files'), triggered=self.deleteSelected) self.deleteSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('delete_shortcut'), self, self.deleteSelected) editMenu.addAction(self.deleteSelectedAction) self.deleteSelectedAction.setEnabled(False) # moveSelectedDownloadsAction self.moveSelectedDownloadsAction = QAction( QIcon(icons + 'folder'), QCoreApplication.translate( "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder...'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move Selected Download Files to Another Folder'), triggered=self.moveSelectedDownloads) editMenu.addAction(self.moveSelectedDownloadsAction) self.moveSelectedDownloadsAction.setEnabled(False) # createQueueAction self.createQueueAction = QAction( QIcon(icons + 'add_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Create New Queue...'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Create new download queue'), triggered=self.createQueue) queueMenu.addAction(self.createQueueAction) # removeQueueAction self.removeQueueAction = QAction( QIcon(icons + 'remove_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Remove Queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Remove this queue'), triggered=self.removeQueue) queueMenu.addAction(self.removeQueueAction) # startQueueAction self.startQueueAction = QAction( QIcon(icons + 'start_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Start this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Start Queue'), triggered=self.startQueue) queueMenu.addAction(self.startQueueAction) # stopQueueAction self.stopQueueAction = QAction( QIcon(icons + 'stop_queue'), QCoreApplication.translate("mainwindow_ui_tr", 'Stop this queue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Stop Queue'), triggered=self.stopQueue) queueMenu.addAction(self.stopQueueAction) # moveUpSelectedAction self.moveUpSelectedAction = QAction( QIcon(icons + 'multi_up'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Up'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move currently selected items up by one row'), triggered=self.moveUpSelected) self.moveUpSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('move_up_selection_shortcut'), self, self.moveUpSelected) queueMenu.addAction(self.moveUpSelectedAction) # moveDownSelectedAction self.moveDownSelectedAction = QAction( QIcon(icons + 'multi_down'), QCoreApplication.translate("mainwindow_ui_tr", 'Move Selected Items Down'), self, statusTip=QCoreApplication.translate( "mainwindow_ui_tr", 'Move currently selected items down by one row'), triggered=self.moveDownSelected) self.moveDownSelectedAction_shortcut = QShortcut( self.persepolis_setting.value('move_down_selection_shortcut'), self, self.moveDownSelected) queueMenu.addAction(self.moveDownSelectedAction) # preferencesAction self.preferencesAction = QAction( QIcon(icons + 'preferences'), QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Preferences'), triggered=self.openPreferences, menuRole=QAction.MenuRole.PreferencesRole) editMenu.addAction(self.preferencesAction) # aboutAction self.aboutAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'About'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'About'), triggered=self.openAbout, menuRole=QAction.MenuRole.AboutRole) helpMenu.addAction(self.aboutAction) # issueAction self.issueAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Report an Issue'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Report an issue'), triggered=self.reportIssue) helpMenu.addAction(self.issueAction) # updateAction self.updateAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Check for Newer Version'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Check for newer release'), triggered=self.newUpdate) helpMenu.addAction(self.updateAction) # logAction self.logAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Show Log File'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.showLog) helpMenu.addAction(self.logAction) # helpAction self.helpAction = QAction( QIcon(icons + 'about'), QCoreApplication.translate("mainwindow_ui_tr", 'Help'), self, statusTip=QCoreApplication.translate("mainwindow_ui_tr", 'Help'), triggered=self.persepolisHelp) helpMenu.addAction(self.helpAction) self.persepolis_setting.endGroup() self.qmenu = MenuWidget(self) self.toolBar2.addWidget(self.qmenu) # labels self.queue_panel_show_button.setText( QCoreApplication.translate("mainwindow_ui_tr", "Hide Options")) self.start_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Start Time")) self.end_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "End Time")) self.reverse_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Download bottom of\n the list first")) self.limit_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Limit Speed")) self.limit_comboBox.setItemText(0, "KiB/s") self.limit_comboBox.setItemText(1, "MiB/s") self.limit_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Apply")) self.after_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "After download")) self.after_comboBox.setItemText( 0, QCoreApplication.translate("mainwindow_ui_tr", "Shut Down")) self.keep_awake_checkBox.setText( QCoreApplication.translate("mainwindow_ui_tr", "Keep System Awake!")) self.keep_awake_checkBox.setToolTip( QCoreApplication.translate( "mainwindow_ui_tr", "<html><head/><body><p>This option will prevent the system from going to sleep.\ It is necessary if your power manager is suspending the system automatically. </p></body></html>" )) self.after_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Apply")) self.muxing_pushButton.setText( QCoreApplication.translate("mainwindow_ui_tr", "Start Mixing")) self.video_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Video File Status: </b>")) self.audio_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Audio File Status: </b>")) self.video_finder_status_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Status: </b>")) self.muxing_status_label.setText( QCoreApplication.translate("mainwindow_ui_tr", "<b>Mixing status: </b>"))
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.cameraInfo = QCameraInfo.defaultCamera() self.camera = QCamera(self.cameraInfo) self.camera.setCaptureMode(QCamera.CaptureStillImage) self.imageCapture = QCameraImageCapture(self.camera) self.imageCapture.imageCaptured.connect(self.imageCaptured) self.imageCapture.imageSaved.connect(self.imageSaved) self.currentPreview = QImage() toolBar = QToolBar() self.addToolBar(toolBar) fileMenu = self.menuBar().addMenu("&File") shutterIcon = QIcon( os.path.join(os.path.dirname(__file__), "shutter.svg")) self.takePictureAction = QAction(shutterIcon, "&Take Picture", self, shortcut="Ctrl+T", triggered=self.takePicture) self.takePictureAction.setToolTip("Take Picture") fileMenu.addAction(self.takePictureAction) toolBar.addAction(self.takePictureAction) exitAction = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=self.close) fileMenu.addAction(exitAction) aboutMenu = self.menuBar().addMenu("&About") aboutQtAction = QAction("About &Qt", self, triggered=qApp.aboutQt) aboutMenu.addAction(aboutQtAction) self.tabWidget = QTabWidget() self.setCentralWidget(self.tabWidget) self.cameraViewfinder = QCameraViewfinder() self.camera.setViewfinder(self.cameraViewfinder) self.tabWidget.addTab(self.cameraViewfinder, "Viewfinder") if self.camera.status() != QCamera.UnavailableStatus: name = self.cameraInfo.description() self.setWindowTitle("PySide6 Camera Example (" + name + ")") self.statusBar().showMessage("Starting: '" + name + "'", 5000) self.camera.start() else: self.setWindowTitle("PySide6 Camera Example") self.takePictureAction.setEnabled(False) self.statusBar().showMessage("Camera unavailable", 5000) def nextImageFileName(self): picturesLocation = QStandardPaths.writableLocation( QStandardPaths.PicturesLocation) dateString = QDate.currentDate().toString("yyyyMMdd") pattern = picturesLocation + "/pyside6_camera_" + dateString + "_{:03d}.jpg" n = 1 while True: result = pattern.format(n) if not os.path.exists(result): return result n = n + 1 return None def takePicture(self): self.currentPreview = QImage() self.camera.searchAndLock() self.imageCapture.capture(self.nextImageFileName()) self.camera.unlock() def imageCaptured(self, id, previewImage): self.currentPreview = previewImage def imageSaved(self, id, fileName): index = self.tabWidget.count() imageView = ImageView(self.currentPreview, fileName) self.tabWidget.addTab(imageView, "Capture #{}".format(index)) self.tabWidget.setCurrentIndex(index)
class App(QMainWindow): def __init__(self): super(App, self).__init__() self.treeView = None self.model = None self.pattern_delegate = None self.callee_delegate = None self.sig_trie = None self.searchResults = None self.searchIndex = -1 self.findNextAction = None self.findPrevAction = None # these two maps are used to make the hyperlinks work # mapping from href to FunctionNode self.hrefs_to_funcs = {} # mapping from FunctionNode to tree view element (QStandardItem) self.func_node_items = {} self.init_ui() def init_ui(self): self.setWindowTitle('Signature Explorer') self.resize(1000, 640) app_icon = QIcon() app_icon.addFile('icon.ico', QSize(48, 48)) self.setWindowIcon(app_icon) self.pattern_delegate = PatternDelegate() self.callee_delegate = CalleesDelegate() self.treeView = TrieView() # self.treeView.setAlternatingRowColors(True) self.model = QStandardItemModel(0, 7, self.treeView) self.model.setHeaderData(0, Qt.Horizontal, 'Signature') self.model.setHeaderData(1, Qt.Horizontal, 'Function') self.model.setHeaderData(2, Qt.Horizontal, 'Callees') self.model.setHeaderData(3, Qt.Horizontal, 'Offset Extra Pattern') self.model.setHeaderData(4, Qt.Horizontal, 'Extra Pattern') self.model.setHeaderData(5, Qt.Horizontal, 'Source Binary') self.model.setHeaderData(6, Qt.Horizontal, 'ID') self.treeView.setModel(self.model) self.treeView.setSelectionBehavior(QAbstractItemView.SelectRows) self.treeView.setColumnWidth(0, 400) self.treeView.setColumnWidth(1, 200) self.treeView.setColumnWidth(2, 250) self.treeView.setColumnWidth(3, 25) self.treeView.setColumnWidth(4, 100) self.treeView.setColumnWidth(5, 200) self.treeView.setColumnWidth(6, 75) self.treeView.setItemDelegateForColumn(0, self.pattern_delegate) self.treeView.setItemDelegateForColumn(2, self.callee_delegate) self.treeView.setItemDelegateForColumn(4, self.pattern_delegate) self.treeView.horizontalScrollBar().setEnabled(True) self.treeView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.treeView.setEditTriggers(QAbstractItemView.NoEditTriggers) self.treeView.linkActivated.connect(self.on_func_link_clicked) # self.treeView.expanded.connect(lambda x: self.treeView.resizeColumnToContents(1)) # self.treeView.collapsed.connect(lambda x: self.treeView.resizeColumnToContents(1)) main_layout = QVBoxLayout() main_layout.addWidget(self.treeView) panel = QWidget() panel.setLayout(main_layout) self.setCentralWidget(panel) menuBar = self.menuBar() fileMenu = QMenu("File") openAction = QAction("&Open", self) openAction.setShortcuts(QKeySequence.Open) openAction.triggered.connect(self.open_file) fileMenu.addAction(openAction) closeAction = QAction("&Close", self) closeAction.setShortcuts(QKeySequence.Close) closeAction.triggered.connect(self.close_file) fileMenu.addAction(closeAction) saveAsAction = QAction("Save As...", self) saveAsAction.setShortcuts(QKeySequence.Save) saveAsAction.triggered.connect(self.save_as) fileMenu.addAction(saveAsAction) menuBar.addMenu(fileMenu) editMenu = QMenu("Edit") findAction = QAction("&Find", self) findAction.setShortcuts(QKeySequence.Find) findAction.triggered.connect(self.search) editMenu.addAction(findAction) self.findNextAction = QAction("&Find Next", self) self.findNextAction.setShortcuts(QKeySequence.FindNext) self.findNextAction.triggered.connect(self.select_next) self.findNextAction.setEnabled(False) editMenu.addAction(self.findNextAction) self.findPrevAction = QAction("&Find Prev", self) self.findPrevAction.setShortcuts(QKeySequence.FindPrevious) self.findPrevAction.triggered.connect(self.select_prev) self.findPrevAction.setEnabled(False) editMenu.addAction(self.findPrevAction) menuBar.addMenu(editMenu) viewMenu = QMenu("View") expandAction = QAction("&Expand All", self) expandAction.triggered.connect(self.treeView.expandAll) viewMenu.addAction(expandAction) collapseAction = QAction("&Collapse All", self) collapseAction.triggered.connect(self.treeView.collapseAll) viewMenu.addAction(collapseAction) menuBar.addMenu(viewMenu) def search(self): query_string, ok = QInputDialog.getText(self, 'Find in Trie', 'Function name') if not ok or not query_string: return self.searchResults = self.model.findItems( query_string, Qt.MatchContains | Qt.MatchRecursive, 1) if self.searchResults: self.findNextAction.setEnabled(True) self.findPrevAction.setEnabled(True) self.searchIndex = 0 self.select_next() else: self.findNextAction.setEnabled(False) self.findPrevAction.setEnabled(False) self.searchIndex = -1 QMessageBox.warning(self, 'Find in Trie', 'No results found') def select_next(self): next_item = self.searchResults[self.searchIndex] self.searchIndex = (self.searchIndex + 1) % len(self.searchResults) self.select_tree_item(next_item) def select_prev(self): prev_item = self.searchResults[self.searchIndex] self.searchIndex = (self.searchIndex - 1) % len(self.searchResults) self.select_tree_item(prev_item) def select_tree_item(self, item): path = [] while item: path.insert(0, self.model.indexFromItem(item)) item = item.parent() # print(path) for index in path: self.treeView.setExpanded(index, True) self.treeView.selectionModel().select( path[-1], QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) self.treeView.scrollTo(path[-1]) def close_file(self): self.model.removeRows(0, self.model.rowCount()) self.sig_trie = None self.hrefs_to_funcs = {} self.func_node_items = {} def open_file(self): sig_filter = 'Signature library (*.sig)' json_zlib_filter = 'Compressed JSON signature library (*.json.zlib)' json_filter = 'JSON signature library (*.json)' pkl_filter = 'Pickled signature library (*.pkl)' fname, filter = QFileDialog.getOpenFileName(self, 'Open file', filter=';;'.join([ sig_filter, json_zlib_filter, json_filter, pkl_filter ])) if filter and fname: print('Opening signature library %s' % (fname, )) if filter == json_zlib_filter: with open(fname, 'rb') as f: json_trie = zlib.decompress(f.read()).decode('utf-8') sig_trie = sig_serialize_json.deserialize(json.loads(json_trie)) elif filter == json_filter: with open(fname, 'r') as f: json_trie = f.read() sig_trie = sig_serialize_json.deserialize(json.loads(json_trie)) elif filter == sig_filter: with open(fname, 'rb') as f: fb_trie = f.read() sig_trie = sig_serialize_fb.SignatureLibraryReader().deserialize( fb_trie) elif filter == pkl_filter: with open(fname, 'rb') as f: sig_trie = pickle.load(f) else: return self.open_trie(sig_trie, os.path.basename(fname)) def save_as(self): sig_filter = 'Signature library (*.sig)' json_zlib_filter = 'Compressed JSON signature library (*.json.zlib)' json_filter = 'JSON signature library (*.json)' pkl_filter = 'Pickled signature library (*.pkl)' fname, filter = QFileDialog.getSaveFileName(self, 'Open file', filter=';;'.join([ sig_filter, json_zlib_filter, json_filter, pkl_filter ])) if filter == json_zlib_filter: with open(fname, 'wb') as f: f.write( zlib.compress( sig_serialize_json.serialize( self.sig_trie).encode('utf-8'))) elif filter == json_filter: with open(fname, 'w') as f: json.dump(sig_serialize_json.serialize(self.sig_trie), f, indent=4) elif filter == sig_filter: with open(fname, 'wb') as f: f.write(sig_serialize_fb.SignatureLibraryWriter().serialize( self.sig_trie)) elif filter == pkl_filter: with open(fname, 'wb') as f: pickle.dump(self.sig_trie, f) else: return print('Saved as ' + fname) @staticmethod def generate_href(func): return str(id(func)) def get_func_name(self, func_node): if func_node is None: return '<missing>' else: return '<a href="' + self.generate_href( func_node) + '">' + func_node.name + '</a>' # handles when the user clicks on a hyperlink to a function node def on_func_link_clicked(self, link): print('Hyperlink clicked: ' + link) self.select_tree_item(self.func_node_items[self.hrefs_to_funcs[link]]) # Generate treeview row for function (leaf) node in the trie def add_func_node(self, parent, pattern_col_item, func): self.hrefs_to_funcs[self.generate_href(func)] = func self.func_node_items[func] = pattern_col_item if not func.callees: func.callees = {} callees_text = '<br />'.join([ str(k) + ': ' + self.get_func_name(v) for k, v in func.callees.items() ]) callees_item = QStandardItem(callees_text) cols = [ pattern_col_item, QStandardItem(func.name), callees_item, QStandardItem(str(func.pattern_offset) if func.pattern else ''), QStandardItem(str(func.pattern) if func.pattern else ''), QStandardItem(func.source_binary), QStandardItem(self.generate_href(func)) ] boldface = cols[1].font() boldface.setBold(True) cols[1].setFont(boldface) parent.appendRow(cols) # Recursively add rows for this trie node and its children def add_trie_node(self, parent, pattern_text, node): left_item = QStandardItem(pattern_text) if not node.value: # Stem node parent.appendRow([left_item, QStandardItem('')]) else: # Leaf node self.add_func_node(parent, left_item, node.value[0]) for func in node.value[1:]: self.add_func_node(parent, QStandardItem(''), func) pairs = map(lambda node: (str(node.pattern), node), node.children.values()) pairs = sorted(pairs, key=lambda kv: kv[0].replace('?', '\xff')) for text, child in pairs: self.add_trie_node(left_item, text, child) return left_item # Add bridge nodes to a special node at the root def add_bridge_nodes(self, parent, sig_trie): bridge_item = QStandardItem('(bridge)') parent.appendRow([bridge_item, QStandardItem('')]) def visit(func, visited): if func is None or func in visited: return visited.add(func) if func.is_bridge: self.add_func_node(bridge_item, QStandardItem(''), func) for callee in func.callees.values(): visit(callee, visited) visited = set() for func in sig_trie.all_values(): visit(func, visited) def open_trie(self, sig_trie, filename): self.close_file() self.sig_trie = sig_trie root_node = self.add_trie_node(self.model, filename, sig_trie) self.add_bridge_nodes(root_node, sig_trie)
class MainWindow(QMainWindow, Ui_JAL_MainWindow): def __init__(self, language): QMainWindow.__init__(self, None) self.setupUi(self) self.currentLanguage = language self.current_index = None # this is used in onOperationContextMenu() to track item for menu self.ledger = Ledger() self.downloader = QuoteDownloader() self.taxes = TaxesRus() self.statements = StatementLoader() self.backup = JalBackup(self, get_dbfilename(get_app_path())) self.estimator = None self.price_chart = None self.actionImportSlipRU.setEnabled( dependency_present(['pyzbar', 'PIL'])) self.actionAbout = QAction(text=self.tr("About"), parent=self) self.MainMenu.addAction(self.actionAbout) self.langGroup = QActionGroup(self.menuLanguage) self.createLanguageMenu() self.statementGroup = QActionGroup(self.menuStatement) self.createStatementsImportMenu() # Set icons self.setWindowIcon(load_icon("jal.png")) self.NewOperationBtn.setIcon(load_icon("new.png")) self.CopyOperationBtn.setIcon(load_icon("copy.png")) self.DeleteOperationBtn.setIcon(load_icon("delete.png")) # Operations view context menu self.contextMenu = QMenu(self.OperationsTableView) self.actionReconcile = QAction(load_icon("reconcile.png"), self.tr("Reconcile"), self) self.actionCopy = QAction(load_icon("copy.png"), self.tr("Copy"), self) self.actionDelete = QAction(load_icon("delete.png"), self.tr("Delete"), self) self.contextMenu.addAction(self.actionReconcile) self.contextMenu.addSeparator() self.contextMenu.addAction(self.actionCopy) self.contextMenu.addAction(self.actionDelete) # Customize Status bar and logs self.ProgressBar = QProgressBar(self) self.StatusBar.addWidget(self.ProgressBar) self.ProgressBar.setVisible(False) self.ledger.setProgressBar(self, self.ProgressBar) self.NewLogEventLbl = QLabel(self) self.StatusBar.addWidget(self.NewLogEventLbl) self.Logs.setNotificationLabel(self.NewLogEventLbl) self.Logs.setFormatter( logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) self.logger = logging.getLogger() self.logger.addHandler(self.Logs) log_level = os.environ.get('LOGLEVEL', 'INFO').upper() self.logger.setLevel(log_level) # Setup reports tab self.reports = Reports(self.ReportTableView, self.ReportTreeView) # Customize UI configuration self.balances_model = BalancesModel(self.BalancesTableView) self.BalancesTableView.setModel(self.balances_model) self.balances_model.configureView() self.holdings_model = HoldingsModel(self.HoldingsTableView) self.HoldingsTableView.setModel(self.holdings_model) self.holdings_model.configureView() self.HoldingsTableView.setContextMenuPolicy(Qt.CustomContextMenu) self.operations_model = OperationsModel(self.OperationsTableView) self.OperationsTableView.setModel(self.operations_model) self.operations_model.configureView() self.OperationsTableView.setContextMenuPolicy(Qt.CustomContextMenu) self.connect_signals_and_slots() self.NewOperationMenu = QMenu() for i in range(self.OperationsTabs.count()): if hasattr(self.OperationsTabs.widget(i), "isCustom"): self.OperationsTabs.widget(i).dbUpdated.connect( self.ledger.rebuild) self.OperationsTabs.widget(i).dbUpdated.connect( self.operations_model.refresh) self.NewOperationMenu.addAction( self.OperationsTabs.widget(i).name, partial(self.createOperation, i)) self.NewOperationBtn.setMenu(self.NewOperationMenu) # Setup balance and holdings parameters current_time = QDateTime.currentDateTime() current_time.setTimeSpec( Qt.UTC) # We use UTC everywhere so need to force TZ info self.BalanceDate.setDateTime(current_time) self.BalancesCurrencyCombo.setIndex( JalSettings().getValue('BaseCurrency')) self.HoldingsDate.setDateTime(current_time) self.HoldingsCurrencyCombo.setIndex( JalSettings().getValue('BaseCurrency')) self.OperationsTabs.setCurrentIndex(TransactionType.NA) self.OperationsTableView.selectRow(0) self.OnOperationsRangeChange(0) def connect_signals_and_slots(self): self.actionExit.triggered.connect(QApplication.instance().quit) self.actionAbout.triggered.connect(self.showAboutWindow) self.langGroup.triggered.connect(self.onLanguageChanged) self.statementGroup.triggered.connect(self.statements.load) self.actionReconcile.triggered.connect( self.reconcileAtCurrentOperation) self.action_Load_quotes.triggered.connect( partial(self.downloader.showQuoteDownloadDialog, self)) self.actionImportSlipRU.triggered.connect(self.importSlip) self.actionBackup.triggered.connect(self.backup.create) self.actionRestore.triggered.connect(self.backup.restore) self.action_Re_build_Ledger.triggered.connect( partial(self.ledger.showRebuildDialog, self)) self.actionAccountTypes.triggered.connect( partial(self.onDataDialog, "account_types")) self.actionAccounts.triggered.connect( partial(self.onDataDialog, "accounts")) self.actionAssets.triggered.connect( partial(self.onDataDialog, "assets")) self.actionPeers.triggered.connect(partial(self.onDataDialog, "agents")) self.actionCategories.triggered.connect( partial(self.onDataDialog, "categories")) self.actionTags.triggered.connect(partial(self.onDataDialog, "tags")) self.actionCountries.triggered.connect( partial(self.onDataDialog, "countries")) self.actionQuotes.triggered.connect( partial(self.onDataDialog, "quotes")) self.PrepareTaxForms.triggered.connect( partial(self.taxes.showTaxesDialog, self)) self.BalanceDate.dateChanged.connect( self.BalancesTableView.model().setDate) self.HoldingsDate.dateChanged.connect( self.HoldingsTableView.model().setDate) self.BalancesCurrencyCombo.changed.connect( self.BalancesTableView.model().setCurrency) self.BalancesTableView.doubleClicked.connect(self.OnBalanceDoubleClick) self.HoldingsCurrencyCombo.changed.connect( self.HoldingsTableView.model().setCurrency) self.ReportRangeCombo.currentIndexChanged.connect( self.onReportRangeChange) self.RunReportBtn.clicked.connect(self.onRunReport) self.SaveReportBtn.clicked.connect(self.reports.saveReport) self.ShowInactiveCheckBox.stateChanged.connect( self.BalancesTableView.model().toggleActive) self.DateRangeCombo.currentIndexChanged.connect( self.OnOperationsRangeChange) self.ChooseAccountBtn.changed.connect( self.OperationsTableView.model().setAccount) self.SearchString.editingFinished.connect(self.updateOperationsFilter) self.HoldingsTableView.customContextMenuRequested.connect( self.onHoldingsContextMenu) self.OperationsTableView.selectionModel().selectionChanged.connect( self.OnOperationChange) self.OperationsTableView.customContextMenuRequested.connect( self.onOperationContextMenu) self.DeleteOperationBtn.clicked.connect(self.deleteOperation) self.actionDelete.triggered.connect(self.deleteOperation) self.CopyOperationBtn.clicked.connect(self.copyOperation) self.actionCopy.triggered.connect(self.copyOperation) self.downloader.download_completed.connect(self.balances_model.update) self.downloader.download_completed.connect(self.holdings_model.update) self.statements.load_completed.connect(self.onStatementImport) self.ledger.updated.connect(self.balances_model.update) self.ledger.updated.connect(self.holdings_model.update) @Slot() def closeEvent(self, event): self.logger.removeHandler( self.Logs ) # Removing handler (but it doesn't prevent exception at exit) logging.raiseExceptions = False # Silencing logging module exceptions def createLanguageMenu(self): langPath = get_app_path() + Setup.LANG_PATH + os.sep langDirectory = QDir(langPath) for language_file in langDirectory.entryList(['*.qm']): language_code = language_file.split('.')[0] language = QLocale.languageToString( QLocale(language_code).language()) language_icon = QIcon(langPath + language_code + '.png') action = QAction(language_icon, language, self) action.setCheckable(True) action.setData(language_code) self.menuLanguage.addAction(action) self.langGroup.addAction(action) @Slot() def onLanguageChanged(self, action): language_code = action.data() if language_code != self.currentLanguage: JalSettings().setValue('Language', JalDB().get_language_id(language_code)) QMessageBox().information( self, self.tr("Restart required"), self.tr("Language was changed to ") + QLocale.languageToString(QLocale(language_code).language()) + "\n" + self.tr("You should restart application to apply changes\n" "Application will be terminated now"), QMessageBox.Ok) self.close() # Create import menu for all known statements based on self.statements.sources values def createStatementsImportMenu(self): for i, source in enumerate(self.statements.sources): if 'icon' in source: source_icon = load_icon(source['icon']) action = QAction(source_icon, source['name'], self) else: action = QAction(source['name'], self) action.setData(i) self.menuStatement.addAction(action) self.statementGroup.addAction(action) @Slot() def showAboutWindow(self): about_box = QMessageBox(self) about_box.setAttribute(Qt.WA_DeleteOnClose) about_box.setWindowTitle(self.tr("About")) title = self.tr( "<h3>JAL</h3><p>Just Another Ledger, version {version}</p>".format( version=__version__)) about_box.setText(title) about = self.tr( "<p>More information, manuals and problem reports are at " "<a href=https://github.com/titov-vv/jal>github home page</a></p>" "<p>Questions, comments, help or donations:</p>" "<p><a href=mailto:[email protected]>[email protected]</a></p>" "<p><a href=https://t.me/jal_support>Telegram</a></p>") about_box.setInformativeText(about) about_box.show() def showProgressBar(self, visible=False): self.ProgressBar.setVisible(visible) self.centralwidget.setEnabled(not visible) self.MainMenu.setEnabled(not visible) @Slot() def OnBalanceDoubleClick(self, index): self.ChooseAccountBtn.account_id = index.model().getAccountId( index.row()) @Slot() def onReportRangeChange(self, range_index): report_ranges = { 0: lambda: (0, 0), 1: ManipulateDate.Last3Months, 2: ManipulateDate.RangeYTD, 3: ManipulateDate.RangeThisYear, 4: ManipulateDate.RangePreviousYear } begin, end = report_ranges[range_index]() self.ReportFromDate.setDateTime( QDateTime.fromSecsSinceEpoch(begin, spec=Qt.UTC)) self.ReportToDate.setDateTime( QDateTime.fromSecsSinceEpoch(end, spec=Qt.UTC)) @Slot() def onRunReport(self): types = { 0: ReportType.IncomeSpending, 1: ReportType.ProfitLoss, 2: ReportType.Deals, 3: ReportType.ByCategory } report_type = types[self.ReportTypeCombo.currentIndex()] begin = self.ReportFromDate.dateTime().toSecsSinceEpoch() end = self.ReportToDate.dateTime().toSecsSinceEpoch() group_dates = 1 if self.ReportGroupCheck.isChecked() else 0 if report_type == ReportType.ByCategory: self.reports.runReport(report_type, begin, end, self.ReportCategoryEdit.selected_id, group_dates) else: self.reports.runReport(report_type, begin, end, self.ReportAccountBtn.account_id, group_dates) @Slot() def OnOperationsRangeChange(self, range_index): view_ranges = { 0: ManipulateDate.startOfPreviousWeek, 1: ManipulateDate.startOfPreviousMonth, 2: ManipulateDate.startOfPreviousQuarter, 3: ManipulateDate.startOfPreviousYear, 4: lambda: 0 } self.OperationsTableView.model().setDateRange( view_ranges[range_index]()) @Slot() def importSlip(self): dialog = ImportSlipDialog(self) dialog.finished.connect(self.onSlipImportFinished) dialog.open() @Slot() def onSlipImportFinished(self): self.ledger.rebuild() @Slot() def onHoldingsContextMenu(self, pos): index = self.HoldingsTableView.indexAt(pos) contextMenu = QMenu(self.HoldingsTableView) actionShowChart = QAction(text=self.tr("Show Price Chart"), parent=self.HoldingsTableView) actionShowChart.triggered.connect( partial(self.showPriceChart, self.HoldingsTableView.viewport().mapToGlobal(pos), index)) contextMenu.addAction(actionShowChart) actionEstimateTax = QAction(text=self.tr("Estimate Russian Tax"), parent=self.HoldingsTableView) actionEstimateTax.triggered.connect( partial(self.estimateRussianTax, self.HoldingsTableView.viewport().mapToGlobal(pos), index)) contextMenu.addAction(actionEstimateTax) contextMenu.popup(self.HoldingsTableView.viewport().mapToGlobal(pos)) @Slot() def showPriceChart(self, position, index): model = index.model() account, asset, asset_qty = model.get_data_for_tax(index) self.price_chart = ChartWindow(account, asset, asset_qty, position) if self.price_chart.ready: self.price_chart.open() @Slot() def estimateRussianTax(self, position, index): model = index.model() account, asset, asset_qty = model.get_data_for_tax(index) self.estimator = TaxEstimator(account, asset, asset_qty, position) if self.estimator.ready: self.estimator.open() @Slot() def OnOperationChange(self, selected, _deselected): self.checkForUncommittedChanges() if len(self.OperationsTableView.selectionModel().selectedRows()) != 1: self.OperationsTabs.setCurrentIndex(TransactionType.NA) else: idx = selected.indexes() if idx: selected_row = idx[0].row() operation_type, operation_id = self.OperationsTableView.model( ).get_operation(selected_row) self.OperationsTabs.setCurrentIndex(operation_type) self.OperationsTabs.widget(operation_type).setId(operation_id) @Slot() def checkForUncommittedChanges(self): for i in range(self.OperationsTabs.count()): if hasattr(self.OperationsTabs.widget(i), "isCustom") and self.OperationsTabs.widget(i).modified: reply = QMessageBox().warning( None, self.tr("You have unsaved changes"), self.OperationsTabs.widget(i).name + self.tr( " has uncommitted changes,\ndo you want to save it?"), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.OperationsTabs.widget(i).saveChanges() else: self.OperationsTabs.widget(i).revertChanges() @Slot() def onOperationContextMenu(self, pos): self.current_index = self.OperationsTableView.indexAt(pos) if len(self.OperationsTableView.selectionModel().selectedRows()) != 1: self.actionReconcile.setEnabled(False) self.actionCopy.setEnabled(False) else: self.actionReconcile.setEnabled(True) self.actionCopy.setEnabled(True) self.contextMenu.popup( self.OperationsTableView.viewport().mapToGlobal(pos)) @Slot() def reconcileAtCurrentOperation(self): idx = self.operations_model.index( self.current_index.row(), 0) # we need only row to address fields by name timestamp = self.operations_model.data(idx, Qt.UserRole, field="timestamp") account_id = self.operations_model.data(idx, Qt.UserRole, field="account_id") JalDB().reconcile_account(account_id, timestamp) self.operations_model.refresh() @Slot() def deleteOperation(self): if QMessageBox().warning( None, self.tr("Confirmation"), self.tr("Are you sure to delete selected transacion(s)?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return rows = [] for index in self.OperationsTableView.selectionModel().selectedRows(): rows.append(index.row()) self.operations_model.deleteRows(rows) self.ledger.rebuild() @Slot() def createOperation(self, operation_type): self.checkForUncommittedChanges() self.OperationsTabs.widget(operation_type).createNew( account_id=self.operations_model.getAccount()) self.OperationsTabs.setCurrentIndex(operation_type) @Slot() def copyOperation(self): operation_type = self.OperationsTabs.currentIndex() if operation_type == TransactionType.NA: return self.checkForUncommittedChanges() self.OperationsTabs.widget(operation_type).copyNew() @Slot() def updateOperationsFilter(self): self.OperationsTableView.model().filterText(self.SearchString.text()) @Slot() def onDataDialog(self, dlg_type): if dlg_type == "account_types": AccountTypeListDialog().exec() elif dlg_type == "accounts": AccountListDialog().exec() elif dlg_type == "assets": AssetListDialog().exec() elif dlg_type == "agents": PeerListDialog().exec() elif dlg_type == "categories": CategoryListDialog().exec() elif dlg_type == "tags": TagsListDialog().exec() elif dlg_type == "countries": CountryListDialog().exec() elif dlg_type == "quotes": QuotesListDialog().exec() else: assert False @Slot() def onStatementImport(self, timestamp, totals): self.ledger.rebuild() for account_id in totals: for asset_id in totals[account_id]: amount = JalDB().get_asset_amount(timestamp, account_id, asset_id) if amount is not None: if abs(totals[account_id][asset_id] - amount) <= Setup.DISP_TOLERANCE: JalDB().reconcile_account(account_id, timestamp) self.balances_model.update( ) # Update required to display reconciled else: account = JalDB().get_account_name(account_id) asset = JalDB().get_asset_name(asset_id) logging.warning( self.tr( "Statement ending balance doesn't match: ") + f"{account} / {asset} / {amount} <> {totals[account_id][asset_id]}" )
class MainWindow(QMainWindow): def __init__(self) -> None: super().__init__() self.ui = MainWindowUI() # action self.action_run = QAction(parent=self, text="Run") self.action_stop = QAction(parent=self, text="Stop") self.action_run.triggered.connect(self.start_plot) # type:ignore self.action_stop.triggered.connect(self.stop_plot) # type:ignore self.action_run.setEnabled(False) self.action_stop.setEnabled(False) # setup ui self.ui.setup_ui(self) self.ui.port_combobox.currentTextChanged.connect( self.change_port_combobox) # setup toolbar self.addToolBar(self.ui.toolbar) self.ui.toolbar.addAction(self.action_run) # type:ignore self.ui.toolbar.addSeparator() self.ui.toolbar.addAction(self.action_stop) # type:ignore self.ui.toolbar.addSeparator() self.ui.toolbar.addWidget(self.ui.port_combobox) def start_plot(self): self.action_run.setEnabled(False) self.action_stop.setEnabled(True) try: port = self.ui.port_combobox.get_current_port_info().device with serial.Serial(port, 9600, parity=serial.PARITY_ODD) as ser: ser.close() ser.parity = serial.PARITY_NONE ser.open() time = 0 while self.action_stop.isEnabled(): time = time + 0.1 analog_value = ser.readline().decode().rstrip() voltage = float(analog_value) * 5 / 1024 self.ui.graph_voltage.curve.append_data(time, voltage) except Exception as e: QMessageBox.critical(self, "ERROR", str(e)) self.action_run.setEnabled(False) self.action_stop.setEnabled(False) def stop_plot(self): self.action_run.setEnabled(True) self.action_stop.setEnabled(False) def change_port_combobox(self) -> None: if self.ui.port_combobox.get_current_port_info() is not None: self.action_run.setEnabled(True)
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.settings_tree = SettingsTree() self.setCentralWidget(self.settings_tree) self.location_dialog = None self.create_actions() self.create_menus() self.auto_refresh_action.setChecked(True) self.fallbacks_action.setChecked(True) self.setWindowTitle("Settings Editor") self.resize(500, 600) def open_settings(self): if self.location_dialog is None: self.location_dialog = LocationDialog(self) if self.location_dialog.exec_(): settings = QSettings(self.location_dialog.format(), self.location_dialog.scope(), self.location_dialog.organization(), self.location_dialog.application()) self.set_settings_object(settings) self.fallbacks_action.setEnabled(True) def open_inifile(self): file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File", '', "INI Files (*.ini *.conf)") if file_name: self.load_ini_file(file_name) def load_ini_file(self, file_name): settings = QSettings(file_name, QSettings.IniFormat) if settings.status() != QSettings.NoError: return self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) def open_property_list(self): file_name, _ = QFileDialog.getOpenFileName(self, "Open Property List", '', "Property List Files (*.plist)") if file_name: settings = QSettings(file_name, QSettings.NativeFormat) self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) def open_registry_path(self): path, ok = QInputDialog.getText(self, "Open Registry Path", "Enter the path in the Windows registry:", QLineEdit.Normal, 'HKEY_CURRENT_USER\\') if ok and path != '': settings = QSettings(path, QSettings.NativeFormat) self.set_settings_object(settings) self.fallbacks_action.setEnabled(False) def about(self): QMessageBox.about(self, "About Settings Editor", "The <b>Settings Editor</b> example shows how to access " "application settings using Qt.") def createActions(self): self.openSettingsAct = QtGui.QAction("&Open Application Settings...", self, shortcut="Ctrl+O", triggered=self.openSettings) self.openIniFileAct = QtGui.QAction("Open I&NI File...", self, shortcut="Ctrl+N", triggered=self.openIniFile) self.openPropertyListAct = QtGui.QAction("Open macOS &Property List...", self, shortcut="Ctrl+P", triggered=self.openPropertyList) def create_actions(self): self.open_settings_action = QAction("&Open Application Settings...", self, shortcut="Ctrl+O", triggered=self.open_settings) self.open_ini_file_action = QAction("Open I&NI File...", self, shortcut="Ctrl+N", triggered=self.open_inifile) self.open_property_list_action = QAction("Open macOS &Property List...", self, shortcut="Ctrl+P", triggered=self.open_property_list) if sys.platform != 'darwin': self.open_property_list_action.setEnabled(False) self.open_registry_path_action = QAction( "Open Windows &Registry Path...", self, shortcut="Ctrl+G", triggered=self.open_registry_path) if sys.platform != 'win32': self.open_registry_path_action.setEnabled(False) self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R", enabled=False, triggered=self.settings_tree.refresh) self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q", triggered=self.close) self.auto_refresh_action = QAction("&Auto-Refresh", self, shortcut="Ctrl+A", checkable=True, enabled=False) self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh) self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled) self.fallbacks_action = QAction("&Fallbacks", self, shortcut="Ctrl+F", checkable=True, enabled=False) self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled) self.about_action = QAction("&About", self, triggered=self.about) self.about_Qt_action = QAction("About &Qt", self, triggered=qApp.aboutQt) def create_menus(self): self.file_menu = self.menuBar().addMenu("&File") self.file_menu.addAction(self.open_settings_action) self.file_menu.addAction(self.open_ini_file_action) self.file_menu.addAction(self.open_property_list_action) self.file_menu.addAction(self.open_registry_path_action) self.file_menu.addSeparator() self.file_menu.addAction(self.refresh_action) self.file_menu.addSeparator() self.file_menu.addAction(self.exit_action) self.options_menu = self.menuBar().addMenu("&Options") self.options_menu.addAction(self.auto_refresh_action) self.options_menu.addAction(self.fallbacks_action) self.menuBar().addSeparator() self.help_menu = self.menuBar().addMenu("&Help") self.help_menu.addAction(self.about_action) self.help_menu.addAction(self.about_Qt_action) def set_settings_object(self, settings): settings.setFallbacksEnabled(self.fallbacks_action.isChecked()) self.settings_tree.set_settings_object(settings) self.refresh_action.setEnabled(True) self.auto_refresh_action.setEnabled(True) nice_name = QDir.fromNativeSeparators(settings.fileName()) nice_name = nice_name.split('/')[-1] if not settings.isWritable(): nice_name += " (read only)" self.setWindowTitle("{} - Settings Editor".format(nice_name))
class Window(QMainWindow): def __init__(self): QMainWindow.__init__(self) self.images = [] self.index = -1 self.ratio = 1 # ratio for QLabel image self.mouse_position = None self.settings = None # Extensions self.extensions = [] for format in QImageReader.supportedImageFormats(): self.extensions.append(format.data().decode('utf-8')) # Filters self.filters = [] for extension in self.extensions: self.filters.append('*.{0}'.format(str(extension))) # UI self.set_up_ui() # settings self.load_settings() def on_message_received(self, msg): """ on message received from single application Args: msg (string): file path """ self.create_images(msg) self.display_image() def set_up_ui(self): # Status Bar self.status_bar = self.statusBar() self.label_name = QLabel() self.label_numero = QLabel() self.status_bar.addPermanentWidget(self.label_name, 1) self.status_bar.addPermanentWidget(self.label_numero, 0) # Main Window self.setWindowTitle('BaloViewer') self.setWindowIcon(QIcon('baloviewer.ico')) # Label image self.image = QLabel() self.image.setScaledContents(True) # Scroll area self.scroll_area = QScrollArea() self.scroll_area.setWidget(self.image) self.scroll_area.showMaximized() self.scroll_area.setFocusPolicy(Qt.FocusPolicy.NoFocus) self.scroll_area.setAlignment(Qt.AlignmentFlag.AlignCenter) self.scroll_area.viewport().installEventFilter(self) # image list self.image_gallery = ImageGallery() self.image_gallery.itemClicked.connect(self.image_gallery_clicked) self.image_gallery.viewport().installEventFilter(self) self.dock_widget = QDockWidget('Image Gallery', self) self.dock_widget.setWidget(self.image_gallery) self.dock_widget.setFloating(False) self.addDockWidget(Qt.LeftDockWidgetArea, self.dock_widget) # central widget self.setCentralWidget(self.scroll_area) # Action bar self.create_actions() self.create_menubar() self.create_toolbar() # option parser parser = OptionParser() parser.add_option("-f", "--file", dest="filename", help="open a file") (options, args) = parser.parse_args() parser_file = options.filename if parser_file is not None and os.path.isfile(parser_file): self.create_images(parser_file) self.display_image() def create_actions(self): # Action Open self.action_open = QAction(QIcon.fromTheme('document-open'), 'Open', self) self.action_open.setShortcut('Ctrl+O') self.action_open.setStatusTip('Open file') self.action_open.triggered.connect(self.open) # Action Save self.action_save = QAction(QIcon.fromTheme('document-save'), 'Save', self) self.action_save.setShortcut('Ctrl+S') self.action_save.setStatusTip('Save file') self.action_save.triggered.connect(self.save) # Action Copy self.action_copy = QAction(QIcon.fromTheme('edit-copy'), 'Copy', self) self.action_copy.setStatusTip('Copy') self.action_copy.triggered.connect(self.copy) # Action move self.action_move = QAction(QIcon.fromTheme('edit-cut'), 'Move', self) self.action_move.setStatusTip('Move') self.action_move.triggered.connect(self.move) # Action Delete self.action_delete = QAction(QIcon.fromTheme('edit-delete'), 'Delete', self) self.action_delete.setStatusTip('Delete') self.action_delete.triggered.connect(self.delete) # Action Quit self.action_quit = QAction(QIcon.fromTheme('application-exit'), 'Quit', self) self.action_quit.setShortcut('Ctrl+Q') self.action_quit.setStatusTip('Quit') self.action_quit.triggered.connect(self.close) # Action Rotate left self.action_rotate_left = QAction( QIcon.fromTheme('object-rotate-left'), 'Rotate left', self) self.action_rotate_left.setStatusTip('Rotate left') self.action_rotate_left.triggered.connect(self.rotate_left) # Action Rotate right self.action_rotate_right = QAction( QIcon.fromTheme('object-rotate-right'), 'Rotate right', self) self.action_rotate_right.setStatusTip('Rotate right') self.action_rotate_right.triggered.connect(self.rotate_right) # Action Mirror self.action_flip_horizontal = QAction( QIcon.fromTheme('object-flip-horizontal'), 'Flip horizontally', self) self.action_flip_horizontal.setStatusTip('Flip horizontally') self.action_flip_horizontal.triggered.connect(self.flip_horizontal) # Action Flip vertical self.action_flip_vertical = QAction( QIcon.fromTheme('object-flip-vertical'), 'Flip vertically', self) self.action_flip_vertical.setStatusTip('Flip vertically') self.action_flip_vertical.triggered.connect(self.flip_vertical) # Action Previous image self.action_previous_image = QAction(QIcon.fromTheme('go-previous'), 'Previous image', self) self.action_previous_image.setStatusTip('Previous image') self.action_previous_image.triggered.connect(self.previous_image) # Action Full screen self.action_fullscreen = QAction(QIcon.fromTheme('view-fullscreen'), 'Full screen', self) self.action_fullscreen.setStatusTip('Full screen') self.action_fullscreen.triggered.connect(self.fullscreen) # Action Normal size self.action_normal_size = QAction(QIcon.fromTheme('zoom-original'), 'Normal size', self) self.action_normal_size.setStatusTip('Normal Size') self.action_normal_size.triggered.connect(self.normal_size) # Action Fit Screen self.action_fit_screen = QAction(QIcon.fromTheme('zoom-fit-best'), 'Fit to screen', self) self.action_fit_screen.setStatusTip('Fit to screen') self.action_fit_screen.setCheckable(True) self.action_fit_screen.triggered.connect(self.fit_screen) # Action Zoom in self.action_zoom_in = QAction(QIcon.fromTheme('zoom-in'), 'Zoom in', self) self.action_zoom_in.setStatusTip('Zoom in') self.action_zoom_in.triggered.connect(self.zoom_in) # Action Zoom out self.action_zoom_out = QAction(QIcon.fromTheme('zoom-out'), 'Zoom out', self) self.action_zoom_out.setStatusTip('Zoom out') self.action_zoom_out.triggered.connect(self.zoom_out) # Action Fit height self.action_fit_vertical = QAction('Fit vertically', self) self.action_fit_vertical.setStatusTip('Fit vertically') self.action_fit_vertical.setCheckable(True) self.action_fit_vertical.triggered.connect(self.fit_height) # Action Fit width self.action_fit_horizontal = QAction('Fit horizontally', self) self.action_fit_horizontal.setStatusTip('Fit horizontally') self.action_fit_horizontal.setCheckable(True) self.action_fit_horizontal.triggered.connect(self.fit_width) # Action Fit width self.action_fit_horizontal = QAction('Fit horizontally', self) self.action_fit_horizontal.setStatusTip('Fit horizontally') self.action_fit_horizontal.setCheckable(True) self.action_fit_horizontal.triggered.connect(self.fit_width) # Action Image list self.action_image_gallery = QAction('Image gallery', self) self.action_image_gallery.setStatusTip('Image gallery') self.action_image_gallery.setCheckable(True) self.action_image_gallery.triggered.connect( self.image_gallery_triggered) # Action Next_image self.action_next_image = QAction(QIcon.fromTheme('go-next'), 'Next image', self) self.action_next_image.setStatusTip('Next image') self.action_next_image.triggered.connect(self.next_image) # Action First image self.action_first_image = QAction(QIcon.fromTheme('go-first'), 'First image', self) self.action_first_image.setStatusTip('First image') self.action_first_image.triggered.connect(self.first_image) # Action Last image self.action_last_image = QAction(QIcon.fromTheme('go-last'), 'Last image', self) self.action_last_image.setStatusTip('Last image') self.action_last_image.triggered.connect(self.last_image) # Action About self.action_about = QAction(QIcon.fromTheme('help-about'), 'About', self) self.action_about.setStatusTip('About') self.action_about.triggered.connect(self.about) def create_menubar(self): self.menubar = self.menuBar() # File self.menu_file = self.menubar.addMenu('File') self.menu_file.addAction(self.action_open) self.menu_file.addAction(self.action_save) self.menu_file.addSeparator() self.menu_file.addAction(self.action_copy) self.menu_file.addAction(self.action_move) self.menu_file.addAction(self.action_delete) self.menu_file.addSeparator() self.menu_file.addAction(self.action_quit) # Edit self.menu_edit = self.menubar.addMenu('Edit') self.menu_edit.addAction(self.action_rotate_left) self.menu_edit.addAction(self.action_rotate_right) self.menu_edit.addSeparator() self.menu_edit.addAction(self.action_flip_horizontal) self.menu_edit.addAction(self.action_flip_vertical) # View self.menu_view = self.menubar.addMenu('View') self.menu_view.addAction(self.action_fullscreen) self.menu_view.addAction(self.action_normal_size) self.menu_view.addAction(self.action_fit_screen) self.menu_view.addSeparator() self.menu_view.addAction(self.action_zoom_in) self.menu_view.addAction(self.action_zoom_out) self.menu_view.addSeparator() self.menu_view.addAction(self.action_fit_vertical) self.menu_view.addAction(self.action_fit_horizontal) self.menu_view.addSeparator() self.menu_view.addAction(self.action_image_gallery) # Go self.menu_go = self.menubar.addMenu('Go') self.menu_go.addAction(self.action_previous_image) self.menu_go.addAction(self.action_next_image) self.menu_go.addSeparator() self.menu_go.addAction(self.action_first_image) self.menu_go.addAction(self.action_last_image) # About self.menu_about = self.menubar.addMenu('About') self.menu_about.addAction(self.action_about) def create_toolbar(self): self.toolbar = self.addToolBar('Tool bar') self.toolbar.addAction(self.action_open) self.toolbar.addAction(self.action_save) self.toolbar.addSeparator() self.toolbar.addAction(self.action_fullscreen) self.toolbar.addAction(self.action_normal_size) self.toolbar.addAction(self.action_fit_screen) self.toolbar.addSeparator() self.toolbar.addAction(self.action_zoom_in) self.toolbar.addAction(self.action_zoom_out) self.toolbar.addSeparator() self.toolbar.addAction(self.action_rotate_left) self.toolbar.addAction(self.action_rotate_right) self.toolbar.addSeparator() self.toolbar.addAction(self.action_first_image) self.toolbar.addAction(self.action_previous_image) self.toolbar.addAction(self.action_next_image) self.toolbar.addAction(self.action_last_image) self.toolbar.addSeparator() self.toolbar.addAction(self.action_copy) self.toolbar.addAction(self.action_move) def load_settings(self): self.settings = QSettings() check_state = self.settings.value('view/image_gallery', True, type=bool) self.action_image_gallery.setChecked(check_state) self.image_gallery_triggered() def contextMenuEvent(self, QContextMenuEvent): menu = QMenu() menu.addAction(self.action_fullscreen) menu.addSeparator() menu.addAction(self.action_image_gallery) menu.addSeparator() menu.addAction(self.action_previous_image) menu.addAction(self.action_next_image) menu.addSeparator() menu.addAction(self.action_normal_size) menu.addAction(self.action_fit_screen) menu.addAction(self.action_fit_vertical) menu.addAction(self.action_fit_horizontal) menu.addSeparator() menu.addAction(self.action_zoom_in) menu.addAction(self.action_zoom_out) menu.addSeparator() menu.addAction(self.action_copy) menu.addAction(self.action_move) menu.addSeparator() menu.addAction(self.action_delete) menu.exec_(QContextMenuEvent.globalPos()) def eventFilter(self, obj, event): """ filter events for wheel events Args: obj (QWidget): scroll_area event (QEvent): event """ # try: if event.type() == QEvent.Wheel: if event.angleDelta().y() < 0: self.next_image() else: self.previous_image() return True elif event.type() == QEvent.MouseButtonPress and event.button( ) == Qt.RightButton: index = self.image_gallery.select_row_pos() if index > -1: self.index = index self.display_image() return True # pass the event on to the parent class return super(QMainWindow, self).eventFilter(obj, event) def keyPressEvent(self, event): key = event.key() if key == Qt.Key_Delete: self.delete() elif key == Qt.Key_Left: self.previous_image() elif key == Qt.Key_Right: self.next_image() elif key == Qt.Key_PageUp: self.first_image() elif key == Qt.Key_PageDown: self.last_image() elif key == Qt.Key_Escape and self.isFullScreen(): self.fullscreen() else: QWidget.keyPressEvent(self, event) def mouseDoubleClickEvent(self, QMouseEvent): self.fullscreen() def mousePressEvent(self, QMouseEvent): self.mouse_position = QMouseEvent.pos() def mouseMoveEvent(self, QMouseEvent): diff = QPoint(QMouseEvent.pos() - self.mouse_position) self.mouse_position = QMouseEvent.pos() self.scroll_area.verticalScrollBar().setValue( self.scroll_area.verticalScrollBar().value() - diff.y()) self.scroll_area.horizontalScrollBar().setValue( self.scroll_area.horizontalScrollBar().value() - diff.x()) def resizeEvent(self, event): if not self.index == -1: self.display_image() def create_images(self, filename): """Create image list Args: filename (string): file from which to retrieve the list of images in the folder """ self.images.clear() # get images only with an allowed extension for ext in self.extensions: self.images += glob.glob( os.path.join( glob.escape(os.path.dirname(filename)), '*.' + ''.join('[%s%s]' % (e.lower(), e.upper()) for e in ext))) self.images.sort() if filename in self.images: self.index = self.images.index(filename) else: self.index = -1 # iamge list self.image_gallery.add_images(self.images) def remove_index(self): """ remove file from list images and display next or previous image """ del self.images[self.index] self.image_gallery.remove_row(self.index) if len(self.images) == 0: self.images.clear() self.index = -1 self.image.clear() self.image.resize(self.image.minimumSizeHint()) elif self.index < len(self.images) - 1: self.display_image() else: self.index = len(self.images) - 1 self.display_image() def display_image(self): if not self.index == -1: self.image.clear() self.image.resize(self.image.minimumSizeHint()) file = self.images[self.index] if os.path.isfile(file): self.label_name.setText(file) self.label_numero.setText( str(self.index + 1) + ' / ' + str(len(self.images))) # image list self.image_gallery.select_row(self.index) image_reader = QImageReader(file) if image_reader.imageCount() > 1: # Animated image movie = QMovie(file) movie.setCacheMode(QMovie.CacheAll) movie.jumpToFrame(0) movie_size = movie.currentPixmap().size() self.image.setMovie(movie) self.image.resize(movie_size) movie.start() else: self.image.setPixmap(QPixmap(file)) self.image.resize(self.image.pixmap().size()) # fit image if self.action_fit_screen.isChecked(): self.fit_screen() elif self.action_fit_horizontal.isChecked(): self.fit_width() elif self.action_fit_vertical.isChecked(): self.fit_height() else: self.ratio = 1.0 self.action_zoom_in.setEnabled(True) self.action_zoom_out.setEnabled(True) # scrollbar position self.scroll_area.verticalScrollBar().setSliderPosition(0) self.scroll_area.horizontalScrollBar().setSliderPosition(0) def resize_image(self): if self.action_fit_screen.isChecked(): self.fit_screen() elif self.action_fit_horizontal.isChecked(): self.fit_width() elif self.action_fit_vertical.isChecked(): self.fit_height() elif self.image.pixmap(): self.image.resize(self.ratio * self.image.pixmap().size()) elif movie := self.image.movie(): movie.jumpToFrame(0) movie_size = movie.currentPixmap().size() self.image.resize(self.ratio * movie_size)
class Window(QDialog): def __init__(self, parent=None): super(Window, self).__init__(parent) self.iconGroupBox = QGroupBox() self.iconLabel = QLabel() self.iconComboBox = QComboBox() self.showIconCheckBox = QCheckBox() self.messageGroupBox = QGroupBox() self.typeLabel = QLabel() self.durationLabel = QLabel() self.durationWarningLabel = QLabel() self.titleLabel = QLabel() self.bodyLabel = QLabel() self.typeComboBox = QComboBox() self.durationSpinBox = QSpinBox() self.titleEdit = QLineEdit() self.bodyEdit = QTextEdit() self.showMessageButton = QPushButton() self.minimizeAction = QAction() self.maximizeAction = QAction() self.restoreAction = QAction() self.quitAction = QAction() self.trayIcon = QSystemTrayIcon() self.trayIconMenu = QMenu() self.createIconGroupBox() self.createMessageGroupBox() self.iconLabel.setMinimumWidth(self.durationLabel.sizeHint().width()) self.createActions() self.createTrayIcon() self.showMessageButton.clicked.connect(self.showMessage) self.showIconCheckBox.toggled.connect(self.trayIcon.setVisible) self.iconComboBox.currentIndexChanged.connect(self.setIcon) self.trayIcon.messageClicked.connect(self.messageClicked) self.trayIcon.activated.connect(self.iconActivated) self.mainLayout = QVBoxLayout() self.mainLayout.addWidget(self.iconGroupBox) self.mainLayout.addWidget(self.messageGroupBox) self.setLayout(self.mainLayout) self.iconComboBox.setCurrentIndex(1) self.trayIcon.show() self.setWindowTitle("Systray") self.resize(400, 300) def setVisible(self, visible): self.minimizeAction.setEnabled(visible) self.maximizeAction.setEnabled(not self.isMaximized()) self.restoreAction.setEnabled(self.isMaximized() or not visible) super().setVisible(visible) def closeEvent(self, event): if not event.spontaneous() or not self.isVisible(): return if self.trayIcon.isVisible(): QMessageBox.information( self, "Systray", "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() @Slot(int) def setIcon(self, index): icon = self.iconComboBox.itemIcon(index) self.trayIcon.setIcon(icon) self.setWindowIcon(icon) self.trayIcon.setToolTip(self.iconComboBox.itemText(index)) @Slot(str) def iconActivated(self, reason): if reason == QSystemTrayIcon.Trigger: pass if reason == QSystemTrayIcon.DoubleClick: self.iconComboBox.setCurrentIndex( (self.iconComboBox.currentIndex() + 1) % self.iconComboBox.count()) if reason == QSystemTrayIcon.MiddleClick: self.showMessage() @Slot() def showMessage(self): self.showIconCheckBox.setChecked(True) selectedIcon = self.typeComboBox.itemData( self.typeComboBox.currentIndex()) msgIcon = QSystemTrayIcon.MessageIcon(selectedIcon) if selectedIcon == -1: # custom icon icon = QIcon( self.iconComboBox.itemIcon(self.iconComboBox.currentIndex())) self.trayIcon.showMessage( self.titleEdit.text(), self.bodyEdit.toPlainText(), icon, self.durationSpinBox.value() * 1000, ) else: self.trayIcon.showMessage( self.titleEdit.text(), self.bodyEdit.toPlainText(), msgIcon, self.durationSpinBox.value() * 1000, ) @Slot() def messageClicked(self): QMessageBox.information( None, "Systray", "Sorry, I already gave what help I could.\n" "Maybe you should try asking a human?") def createIconGroupBox(self): self.iconGroupBox = QGroupBox("Tray Icon") self.iconLabel = QLabel("Icon:") self.iconComboBox = QComboBox() self.iconComboBox.addItem(QIcon(":/images/bad.png"), "Bad") self.iconComboBox.addItem(QIcon(":/images/heart.png"), "Heart") self.iconComboBox.addItem(QIcon(":/images/trash.png"), "Trash") self.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 createMessageGroupBox(self): self.messageGroupBox = QGroupBox("Balloon Message") self.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.addItem(QIcon(), "Custom icon", -1) self.typeComboBox.setCurrentIndex(1) self.durationLabel = QLabel("Duration:") self.durationSpinBox = QSpinBox() self.durationSpinBox.setRange(5, 60) self.durationSpinBox.setSuffix(" s") self.durationSpinBox.setValue(15) self.durationWarningLabel = QLabel( "(some systems might ignore this hint)") self.durationWarningLabel.setIndent(10) self.titleLabel = QLabel("Title:") self.titleEdit = QLineEdit("Cannot connect to network") self.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(self.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(self.durationWarningLabel, 1, 2, 1, 3) messageLayout.addWidget(self.titleLabel, 2, 0) messageLayout.addWidget(self.titleEdit, 2, 1, 1, 4) messageLayout.addWidget(self.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 createActions(self): self.minimizeAction = QAction("Minimize", self) self.minimizeAction.triggered.connect(self.hide) self.maximizeAction = QAction("Maximize", self) self.maximizeAction.triggered.connect(self.showMaximized) self.restoreAction = QAction("Restore", self) self.restoreAction.triggered.connect(self.showNormal) self.quitAction = QAction("Quit", self) self.quitAction.triggered.connect(qApp.quit) def createTrayIcon(self): self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.quitAction) self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu)
class OperationsWidget(MdiWidget, Ui_OperationsWidget): dbUpdated = Signal() def __init__(self, parent=None): MdiWidget.__init__(self, parent) self.setupUi(self) self.current_index = None # this is used in onOperationContextMenu() to track item for menu # Set icons self.NewOperationBtn.setIcon(load_icon("new.png")) self.CopyOperationBtn.setIcon(load_icon("copy.png")) self.DeleteOperationBtn.setIcon(load_icon("delete.png")) # Operations view context menu self.contextMenu = QMenu(self.OperationsTableView) self.actionReconcile = QAction(load_icon("reconcile.png"), self.tr("Reconcile"), self) self.actionCopy = QAction(load_icon("copy.png"), self.tr("Copy"), self) self.actionDelete = QAction(load_icon("delete.png"), self.tr("Delete"), self) self.contextMenu.addAction(self.actionReconcile) self.contextMenu.addSeparator() self.contextMenu.addAction(self.actionCopy) self.contextMenu.addAction(self.actionDelete) # Customize UI configuration self.balances_model = BalancesModel(self.BalancesTableView) self.BalancesTableView.setModel(self.balances_model) self.balances_model.configureView() self.operations_model = OperationsModel(self.OperationsTableView) self.OperationsTableView.setModel(self.operations_model) self.operations_model.configureView() self.OperationsTableView.setContextMenuPolicy(Qt.CustomContextMenu) self.connect_signals_and_slots() self.NewOperationMenu = QMenu() for i in range(self.OperationsTabs.count()): if hasattr(self.OperationsTabs.widget(i), "isCustom"): self.OperationsTabs.widget(i).dbUpdated.connect(self.dbUpdated) self.OperationsTabs.widget(i).dbUpdated.connect( self.operations_model.refresh) self.NewOperationMenu.addAction( self.OperationsTabs.widget(i).name, partial(self.createOperation, i)) self.NewOperationBtn.setMenu(self.NewOperationMenu) # Setup balance and holdings parameters current_time = QDateTime.currentDateTime() current_time.setTimeSpec( Qt.UTC) # We use UTC everywhere so need to force TZ info self.BalanceDate.setDateTime(current_time) self.BalancesCurrencyCombo.setIndex( JalSettings().getValue('BaseCurrency')) self.OperationsTabs.setCurrentIndex(LedgerTransaction.NA) self.OperationsTableView.selectRow(0) self.DateRange.setCurrentIndex(0) def connect_signals_and_slots(self): self.actionReconcile.triggered.connect( self.reconcileAtCurrentOperation) self.BalanceDate.dateChanged.connect( self.BalancesTableView.model().setDate) self.BalancesCurrencyCombo.changed.connect( self.BalancesTableView.model().setCurrency) self.BalancesTableView.doubleClicked.connect(self.OnBalanceDoubleClick) self.ShowInactiveCheckBox.stateChanged.connect( self.BalancesTableView.model().toggleActive) self.DateRange.changed.connect(self.operations_model.setDateRange) self.ChooseAccountBtn.changed.connect( self.OperationsTableView.model().setAccount) self.SearchString.editingFinished.connect(self.updateOperationsFilter) self.OperationsTableView.selectionModel().selectionChanged.connect( self.OnOperationChange) self.OperationsTableView.customContextMenuRequested.connect( self.onOperationContextMenu) self.DeleteOperationBtn.clicked.connect(self.deleteOperation) self.actionDelete.triggered.connect(self.deleteOperation) self.CopyOperationBtn.clicked.connect(self.copyOperation) self.actionCopy.triggered.connect(self.copyOperation) @Slot() def deleteOperation(self): if QMessageBox().warning( None, self.tr("Confirmation"), self.tr("Are you sure to delete selected transacion(s)?"), QMessageBox.Yes, QMessageBox.No) == QMessageBox.No: return rows = [] for index in self.OperationsTableView.selectionModel().selectedRows(): rows.append(index.row()) self.operations_model.deleteRows(rows) self.dbUpdated.emit() @Slot() def createOperation(self, operation_type): self.checkForUncommittedChanges() self.OperationsTabs.widget(operation_type).createNew( account_id=self.operations_model.getAccount()) self.OperationsTabs.setCurrentIndex(operation_type) @Slot() def copyOperation(self): operation_type = self.OperationsTabs.currentIndex() if operation_type == LedgerTransaction.NA: return self.checkForUncommittedChanges() self.OperationsTabs.widget(operation_type).copyNew() @Slot() def updateOperationsFilter(self): self.OperationsTableView.model().filterText(self.SearchString.text()) @Slot() def OnBalanceDoubleClick(self, index): self.ChooseAccountBtn.account_id = index.model().getAccountId( index.row()) @Slot() def OnOperationChange(self, selected, _deselected): self.checkForUncommittedChanges() if len(self.OperationsTableView.selectionModel().selectedRows()) != 1: self.OperationsTabs.setCurrentIndex(LedgerTransaction.NA) else: idx = selected.indexes() if idx: selected_row = idx[0].row() operation_type, operation_id = self.OperationsTableView.model( ).get_operation(selected_row) self.OperationsTabs.setCurrentIndex(operation_type) self.OperationsTabs.widget(operation_type).setId(operation_id) @Slot() def checkForUncommittedChanges(self): for i in range(self.OperationsTabs.count()): if hasattr(self.OperationsTabs.widget(i), "isCustom") and self.OperationsTabs.widget(i).modified: reply = QMessageBox().warning( None, self.tr("You have unsaved changes"), self.OperationsTabs.widget(i).name + self.tr( " has uncommitted changes,\ndo you want to save it?"), QMessageBox.Yes, QMessageBox.No) if reply == QMessageBox.Yes: self.OperationsTabs.widget(i).saveChanges() else: self.OperationsTabs.widget(i).revertChanges() @Slot() def onOperationContextMenu(self, pos): self.current_index = self.OperationsTableView.indexAt(pos) if len(self.OperationsTableView.selectionModel().selectedRows()) != 1: self.actionReconcile.setEnabled(False) self.actionCopy.setEnabled(False) else: self.actionReconcile.setEnabled(True) self.actionCopy.setEnabled(True) self.contextMenu.popup( self.OperationsTableView.viewport().mapToGlobal(pos)) @Slot() def reconcileAtCurrentOperation(self): idx = self.operations_model.index( self.current_index.row(), 0) # we need only row to address fields by name timestamp = self.operations_model.data(idx, Qt.UserRole, field="timestamp") account_id = self.operations_model.data(idx, Qt.UserRole, field="account_id") JalDB().reconcile_account(account_id, timestamp) self.operations_model.refresh() def refresh(self): self.balances_model.update()
def _create_menu(self): file_menu = self.menuBar().addMenu("&File") exit_action = QAction(QIcon.fromTheme("application-exit"), "E&xit", self, shortcut="Ctrl+Q", triggered=qApp.quit) file_menu.addAction(exit_action) navigation_menu = self.menuBar().addMenu("&Navigation") style_icons = ':/qt-project.org/styles/commonstyle/images/' back_action = QAction(QIcon.fromTheme( "go-previous", QIcon(style_icons + 'left-32.png')), "Back", self, shortcut=QKeySequence(QKeySequence.Back), triggered=self._tab_widget.back) self._actions[QWebEnginePage.Back] = back_action back_action.setEnabled(False) navigation_menu.addAction(back_action) forward_action = QAction(QIcon.fromTheme( "go-next", QIcon(style_icons + 'right-32.png')), "Forward", self, shortcut=QKeySequence(QKeySequence.Forward), triggered=self._tab_widget.forward) forward_action.setEnabled(False) self._actions[QWebEnginePage.Forward] = forward_action navigation_menu.addAction(forward_action) reload_action = QAction(QIcon(style_icons + 'refresh-32.png'), "Reload", self, shortcut=QKeySequence(QKeySequence.Refresh), triggered=self._tab_widget.reload) self._actions[QWebEnginePage.Reload] = reload_action reload_action.setEnabled(False) navigation_menu.addAction(reload_action) navigation_menu.addSeparator() new_tab_action = QAction("New Tab", self, shortcut='Ctrl+T', triggered=self.add_browser_tab) navigation_menu.addAction(new_tab_action) close_tab_action = QAction("Close Current Tab", self, shortcut="Ctrl+W", triggered=self._close_current_tab) navigation_menu.addAction(close_tab_action) navigation_menu.addSeparator() history_action = QAction("History...", self, triggered=self._tab_widget.show_history) navigation_menu.addAction(history_action) edit_menu = self.menuBar().addMenu("&Edit") find_action = QAction("Find", self, shortcut=QKeySequence(QKeySequence.Find), triggered=self._show_find) edit_menu.addAction(find_action) edit_menu.addSeparator() undo_action = QAction("Undo", self, shortcut=QKeySequence(QKeySequence.Undo), triggered=self._tab_widget.undo) self._actions[QWebEnginePage.Undo] = undo_action undo_action.setEnabled(False) edit_menu.addAction(undo_action) redo_action = QAction("Redo", self, shortcut=QKeySequence(QKeySequence.Redo), triggered=self._tab_widget.redo) self._actions[QWebEnginePage.Redo] = redo_action redo_action.setEnabled(False) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction("Cut", self, shortcut=QKeySequence(QKeySequence.Cut), triggered=self._tab_widget.cut) self._actions[QWebEnginePage.Cut] = cut_action cut_action.setEnabled(False) edit_menu.addAction(cut_action) copy_action = QAction("Copy", self, shortcut=QKeySequence(QKeySequence.Copy), triggered=self._tab_widget.copy) self._actions[QWebEnginePage.Copy] = copy_action copy_action.setEnabled(False) edit_menu.addAction(copy_action) paste_action = QAction("Paste", self, shortcut=QKeySequence(QKeySequence.Paste), triggered=self._tab_widget.paste) self._actions[QWebEnginePage.Paste] = paste_action paste_action.setEnabled(False) edit_menu.addAction(paste_action) edit_menu.addSeparator() select_all_action = QAction("Select All", self, shortcut=QKeySequence( QKeySequence.SelectAll), triggered=self._tab_widget.select_all) self._actions[QWebEnginePage.SelectAll] = select_all_action select_all_action.setEnabled(False) edit_menu.addAction(select_all_action) self._bookmark_menu = self.menuBar().addMenu("&Bookmarks") add_bookmark_action = QAction("&Add Bookmark", self, triggered=self._add_bookmark) self._bookmark_menu.addAction(add_bookmark_action) add_tool_bar_bookmark_action = QAction( "&Add Bookmark to Tool Bar", self, triggered=self._add_tool_bar_bookmark) self._bookmark_menu.addAction(add_tool_bar_bookmark_action) self._bookmark_menu.addSeparator() tools_menu = self.menuBar().addMenu("&Tools") download_action = QAction( "Open Downloads", self, triggered=DownloadWidget.open_download_directory) tools_menu.addAction(download_action) window_menu = self.menuBar().addMenu("&Window") window_menu.addAction(self._bookmark_dock.toggleViewAction()) window_menu.addSeparator() zoom_in_action = QAction(QIcon.fromTheme("zoom-in"), "Zoom In", self, shortcut=QKeySequence(QKeySequence.ZoomIn), triggered=self._zoom_in) window_menu.addAction(zoom_in_action) zoom_out_action = QAction(QIcon.fromTheme("zoom-out"), "Zoom Out", self, shortcut=QKeySequence(QKeySequence.ZoomOut), triggered=self._zoom_out) window_menu.addAction(zoom_out_action) reset_zoom_action = QAction(QIcon.fromTheme("zoom-original"), "Reset Zoom", self, shortcut="Ctrl+0", triggered=self._reset_zoom) window_menu.addAction(reset_zoom_action) about_menu = self.menuBar().addMenu("&About") about_action = QAction("About Qt", self, shortcut=QKeySequence( QKeySequence.HelpContents), triggered=qApp.aboutQt) about_menu.addAction(about_action)
class MainWindow(QMainWindow): def __init__(self) -> None: super().__init__() self.ui = MainWindowUI() # action self.action_run = QAction(parent=self, text="Run") self.action_stop = QAction(parent=self, text="Stop") self.action_run.triggered.connect(self.start_plot) # type:ignore self.action_stop.triggered.connect(self.stop_plot) # type:ignore self.action_run.setEnabled(False) self.action_stop.setEnabled(False) # setup ui self.ui.setup_ui(self) self.resize(1100, 600) self.move(50, 50) self.ui.port_combobox1.currentTextChanged.connect( self.change_port_combobox1) self.ui.port_combobox2.currentTextChanged.connect( self.change_port_combobox2) self.ui.btn_clear_monitor1.clicked.connect( self.clear_monitor1) # type:ignore self.ui.btn_clear_monitor2.clicked.connect( self.clear_monitor2) # type:ignore # setup toolbar self.ui.toolbar.addAction(self.action_run) # type:ignore self.ui.toolbar.addSeparator() self.ui.toolbar.addAction(self.action_stop) # type:ignore self.ui.toolbar.addSeparator() self.ui.toolbar.addWidget(self.ui.baudrate_combobox) self.ui.toolbar.addSeparator() self.ui.toolbar.addWidget(self.ui.port_combobox1) self.ui.toolbar.addSeparator() self.ui.toolbar.addWidget(self.ui.port_combobox2) def start_plot(self): self.action_run.setEnabled(False) self.action_stop.setEnabled(True) try: port1_info = self.ui.port_combobox1.get_current_port_info() port1 = port1_info.device if port1_info is not None else port1_info port2_info = self.ui.port_combobox2.get_current_port_info() port2 = port2_info.device if port2_info is not None else port2_info baudrate = self.ui.baudrate_combobox.currentText() with Serial(port1, baudrate) as ser1, Serial(port2, baudrate) as ser2: while self.action_stop.isEnabled(): if port1 is not None: data = ser1.readline().decode().rstrip() self.ui.monitor1.appendPlainText(data) if port2 is not None: data = ser2.readline().decode().rstrip() self.ui.monitor2.appendPlainText(data) QApplication.processEvents() except Exception as e: QMessageBox.critical(self, "ERROR", str(e)) self.action_run.setEnabled(False) self.action_stop.setEnabled(False) def stop_plot(self): self.action_run.setEnabled(True) self.action_stop.setEnabled(False) def change_port_combobox1(self) -> None: port_info = self.ui.port_combobox1.get_current_port_info() if port_info is not None: self.action_run.setEnabled(True) self.ui.group_monitor1.setTitle(port_info.device) def change_port_combobox2(self) -> None: port_info = self.ui.port_combobox2.get_current_port_info() if port_info is not None: self.action_run.setEnabled(True) self.ui.group_monitor2.setTitle(port_info.device) def clear_monitor1(self) -> None: self.ui.monitor1.clear() def clear_monitor2(self) -> None: self.ui.monitor2.clear()