class MainWnd(QMainWindow): def __init__(self): super().__init__() self.exporter = Exporter() self.initUI() self.controlTower = None def setControlTower(self, ct): self.controlTower = ct def initUI(self): self.menuBar = QMenuBar(self) self.menuBar.setNativeMenuBar(False) file_menu = self.menuBar.addMenu('File') file_menu.addAction('New', self.save) file_menu.addAction('Open', self.save) file_menu.addAction('Close', self.save) file_menu.addAction('Save', self.save) file_menu.addAction('Save as', self.save) file_menu.addAction('Quit', self.quit) repo_menu = self.menuBar.addMenu('Repository') repo_menu.addAction('private') repo_menu.addAction('global') config_menu = self.menuBar.addMenu('Tools') config_menu.addAction('Preferences') config_menu.addAction('Export', self.export) box_menu = config_menu.addMenu('Box') box_menu.addAction('New combo box', self.createCompositeBox) box_menu.addAction('New Code box', self.createCodeBox) window_menu = self.menuBar.addMenu('Window') window_menu.addAction('Description') help_menu = self.menuBar.addMenu('Help') help_menu.addAction('online document', self.quit) self.setMenuBar(self.menuBar) self.setWindowTitle('Athena') self.statusBar().show() def createCodeBox(self): self.controlTower.createNewBox('CODE') def createCompositeBox(self): self.controlTower.createNewBox('COMPOSITION') def save(self): pass def export(self): exporter = Exporter() exporter.processExport(self.controlTower.getOpenedBox()) def quit(self): sys.exit(0)
def createMenus(self): menus = QMenuBar() fileMenu = menus.addMenu("&Fichier") file_newMenu = fileMenu.addMenu( self.style().standardIcon(QStyle.SP_FileIcon), "Nouveau") file_newMenu.addAction("Date", self.createDateDialog, 'CTRL+D') file_newMenu.addAction("Durée", self.createDurationDialog, 'CTRL+N') fileMenu.addSeparator() fileMenu.addAction(self.style().standardIcon(QStyle.SP_BrowserStop), "Quitter", sys.exit, 'CTRL+Q') optionMenu = menus.addMenu("&Options") optionMenu.addAction( self.style().standardIcon(QStyle.SP_MessageBoxInformation), "Évènements", self.createNotificationPopup, 'CTRL+O') optionMenu.addAction( QAction("Rester au premier plan", optionMenu, triggered=self.stayOnTop, checkable=True)) aideMenu = menus.addMenu("&Aide") aideMenu.addAction( self.style().standardIcon(QStyle.SP_DialogHelpButton), "À propos", lambda: QMessageBox.information( self, "À propos", TITLE + " " + str(VERSION)), 'CTRL+H') aideMenu.addSeparator() aideMenu.addAction( self.style().standardIcon(QStyle.SP_TitleBarMenuButton), "À propos de Qt", QApplication.aboutQt, 'CTRL+A') self.setMenuBar(menus)
def __init__(self, parent, title, person, unsorted_objects, autoshow=True, progress_indicator=None): super().__init__(None) act = QAction("close", self) act.triggered.connect(self._do_close) act.setShortcut(QKeySequence("escape")) self.addAction(act) layout = QGridLayout() bar = QMenuBar(self) self._object_actions = bar.addMenu(_("Object actions")) property_actions = bar.addMenu(_("Property actions")) self._create_item(property_actions, _("Copy property value"), "ctrl+c", self.on_copypropvalue_selected) self._create_item(property_actions, _("Copy property name"), "alt+c", self.on_copypropname_selected) self._create_item(property_actions, _("Copy property name and value"), "ctrl+alt+c", self.on_copypropline_selected) objects_label = QLabel(_("Objects"), self) layout.addWidget(objects_label, 0, 0) self._objects_list = QListWidget(self) self._objects_list.setAccessibleName(objects_label.text()) self._objects_list.setContextMenuPolicy(Qt.CustomContextMenu) self._objects_list.currentRowChanged.connect(self.on_objects_listbox) self._objects_list.customContextMenuRequested.connect( self._on_objects_list_menu) objects_label.setBuddy(self._objects_list) layout.addWidget(self._objects_list, 1, 0) props_label = QLabel(_("Object properties"), self) layout.addWidget(props_label, 0, 1) self._props = QTreeWidget(self) self._props.setAccessibleName(props_label.text()) props_label.setBuddy(self._props) layout.addWidget(self._props, 1, 1) goto_button = QPushButton(_("Go to"), self) goto_button.setDefault(True) goto_button.clicked.connect(self.on_goto_clicked) self._objects_list.itemActivated.connect(goto_button.click) layout.addWidget(goto_button, 2, 0) close_button = QPushButton(_("Close"), self) close_button.clicked.connect(self.close) layout.addWidget(close_button, 2, 1) self.setLayout(layout) self.setWindowTitle(title + _(" ({num_objects} objects shown)").format( num_objects=len(unsorted_objects))) self._person = person self._autoshow = autoshow self._progress_indicator = progress_indicator self._all_actions = [] for member in object_actions.__dict__.values(): if inspect.isclass(member) and issubclass(member, ObjectAction): self._all_actions.append(member) self._objects_list.setCurrentRow(0) self._sorter = ObjectsSorter(unsorted_objects, person) self._sorter.objects_sorted.connect(self._objects_sorted) self._sorter.start()
def _create_menu_bar(self): menuBar = QMenuBar(self) home_menu = menuBar.addMenu('Homepage') home_menu.addAction(self.home_action) workspace_menu = menuBar.addMenu('Workspace') workspace_menu.addAction(self.search_action) workspace_menu.addAction(self.list_documents_action) workspace_menu.addAction(self.list_labels_action) workspace_menu.addAction(self.write_notebook_action) workspace_menu.addAction(self.write_remark_action) workspace_menu.addAction(self.upload_pdfs_action) workspace_menu.addAction(self.download_action) network_menu = menuBar.addMenu('Network') network_menu.addAction(self.find_path_action) network_menu.addAction(self.print_label_action) network_menu.addAction(self.loc_label_action) network_menu.addAction(self.show_labels_action) network_menu.addAction(self.show_network_action) help_menu = menuBar.addMenu('Help') help_menu.addAction(self.about_action) help_menu.addAction(self.source_code_action) help_menu.addAction(self.online_docs_action) self.setMenuBar(menuBar)
def createMenuBar(self): menuBar = QMenuBar(parent=None) # File Menu fileMenu = menuBar.addMenu('&File') dumpListsAction = QAction('Dump Lists', fileMenu) dumpListsAction.setShortcut('Ctrl+S') dumpListsAction.triggered.connect(self.writeLists) fileMenu.addAction(dumpListsAction) fileMenu.addSeparator() exitAction = QAction('E&xit', fileMenu) exitAction.setShortcut('Ctrl+Q') exitAction.triggered.connect(QApplication.closeAllWindows) fileMenu.addAction(exitAction) # View Menu viewMenu = menuBar.addMenu('V&iew') t = ' pair' if self.mode == MAIN_WINDOW_MODE.COMPARE else '' nextImageAction = QAction('next image' + t, viewMenu) nextImageAction.setShortcut('Ctrl+]') nextImageAction.triggered.connect(self.nextImage) viewMenu.addAction(nextImageAction) prevImageAction = QAction('prev image' + t, viewMenu) prevImageAction.setShortcut('Ctrl+[') prevImageAction.triggered.connect(self.prevImage) viewMenu.addAction(prevImageAction) viewMenu.addSeparator() dropImageAction = QAction('Drop image' + t, viewMenu) dropImageAction.setShortcut('Ctrl+D') dropImageAction.triggered.connect(self.dropImage) viewMenu.addAction(dropImageAction) if self.mode == MAIN_WINDOW_MODE.COMPARE: viewMenu.addSeparator() nextDiffPixelAction = QAction('next diff pixel', viewMenu) nextDiffPixelAction.setShortcut('Ctrl+.') nextDiffPixelAction.triggered.connect( self.centralWidgetDict[MAIN_WINDOW_MODE.COMPARE].nextDiffPixel) viewMenu.addAction(nextDiffPixelAction) prevDiffPixelAction = QAction('prev diff pixel', viewMenu) prevDiffPixelAction.setShortcut('Ctrl+,') prevDiffPixelAction.triggered.connect( self.centralWidgetDict[MAIN_WINDOW_MODE.COMPARE].prevDiffPixel) viewMenu.addAction(prevDiffPixelAction) return menuBar
def _menubar(self) -> None: menubar = QMenuBar() self.layout.addWidget(menubar) fileMenu = menubar.addMenu('File') fileMenu.addAction('Open', self.browse_file) fileMenu.addSeparator() fileMenu.addAction('Exit', self.close) helpMenu = menubar.addMenu('Help') helpMenu.addSeparator() helpMenu.addAction('About Us', self.about_us)
class MainWindow(QMainWindow): def __init__(self,application,parent=None): super(MainWindow,self).__init__(parent) #private attributes self.menuBar_ = QMenuBar() self.fileMenu_ = FileMenu(self) self.editMenu_ = EditMenu(self) self.toolbarDoc_ = QDockWidget(self) self.toolbarWidget_ = Toolbar(self) self.setGeometry(0,0,1280,720) #public attributes self.application = application self.contentTab = ContentTab() self.statusBar = QStatusBar() #menuBar self.menuBar_.addMenu(self.fileMenu_) self.menuBar_.addMenu(self.editMenu_) self.menuBar_.addAction("Help",self.helpFunc) self.setMenuBar(self.menuBar_) #statusBar self.statusBar.showMessage("status bar") self.setStatusBar(self.statusBar) #toolBar self.toolbarDoc_.setFeatures(QDockWidget.NoDockWidgetFeatures ) self.addDockWidget(Qt.LeftDockWidgetArea,self.toolbarDoc_) self.toolbarDoc_.setWidget(self.toolbarWidget_) #contentab self.setCentralWidget(self.contentTab) def helpFunc(self): """ Call the help tab To be added """ print("called helpFunc") def quit(self): """ Quit the application """ self.application.quit()
def add_menu_bar(self, menus): """Adds the menu bar to the main window.""" menu_bar = QMenuBar(self) for menu, actions in menus.items(): menu = menu_bar.addMenu(menu) for action in actions: menu.addAction(action) return menu_bar
class AppWindow(QMainWindow): def __init__(self): super().__init__() #initialize app menu bar self.menu_bar = QMenuBar(parent=None) file_menu = self.menu_bar.addMenu('File') file_menu.addAction('To Excel') edit_menu = self.menu_bar.addMenu('Edit') edit_menu.addAction('&Undo') view_menu = self.menu_bar.addMenu('View') view_menu.addAction('Show All') # set manager widget manager = AppManager() self.setCentralWidget(manager) self.show()
def __init__(self, parent=None): super().__init__(parent) self.view: QtCharts.QChartView = None menuBar = QMenuBar() fileMenu = menuBar.addMenu('File') self._saveAc = QAction('Save as image', fileMenu) self._saveAc.setShortcut(QKeySequence(self.tr('Ctrl+S'))) self._closeAc = QAction('Close', fileMenu) viewMenu = menuBar.addMenu('View') self._resetAc = QAction('Reset zoom', viewMenu) self._resetAc.setShortcut(QKeySequence(self.tr('Ctrl+R'))) self._zoomInAc = QAction('Zoom in', viewMenu) self._zoomInAc.setShortcut(QKeySequence(self.tr('Ctrl++'))) self._zoomOutAc = QAction('Zoom out', viewMenu) self._zoomOutAc.setShortcut(QKeySequence(self.tr('Ctrl+-'))) fileMenu.addActions([self._saveAc, self._closeAc]) viewMenu.addActions([self._zoomInAc, self._zoomOutAc, self._resetAc]) self.setMenuBar(menuBar) self.setWindowTitle('dataMole - Plot')
def initActions(self): self.newEntryAction = QAction("New entry", self) self.newEntryAction.setShortcut(QKeySequence.New) self.newEntryAction.triggered.connect(self.addNewEntry) self.removeEntryAction = QAction("Remove entry", self) self.removeEntryAction.setShortcut(QKeySequence.Delete) self.removeEntryAction.triggered.connect(self.removeEntry) self.demoAction = QAction("Demo entry", self) self.demoAction.triggered.connect(self.addDemoEntry) self.printAction = QAction("Print Preview...", self) self.printAction.triggered.connect(self.printPreview) self.zoomInAction = QAction("Zoom In", self) self.zoomInAction.setShortcut(QKeySequence.ZoomIn) self.zoomInAction.triggered.connect(self.zoomIn) self.zoomOutAction = QAction("Zoom Out", self) self.zoomOutAction.setShortcut(QKeySequence.ZoomOut) self.zoomOutAction.triggered.connect(self.zoomOut) self.ui.ganttView.leftView().setContextMenuPolicy(Qt.CustomContextMenu) self.ui.ganttView.leftView().addAction(self.newEntryAction) self.ui.ganttView.leftView().addAction(self.removeEntryAction) menuBar = QMenuBar(self) self.setMenuBar(menuBar) entryMenu = menuBar.addMenu("Entry") entryMenu.addAction(self.newEntryAction) entryMenu.addAction(self.removeEntryAction) entryMenu.addSeparator() entryMenu.addAction(self.demoAction) entryMenu.addSeparator() entryMenu.addAction(self.printAction) zoomMenu = menuBar.addMenu("Zoom") zoomMenu.addAction(self.zoomInAction) zoomMenu.addAction(self.zoomOutAction)
def buildMenuBar(self): menuBar = QMenuBar(self) fileMenu = QMenu("File") loadImageAction = QAction("Load Image", self) loadDataAction = QAction("Load Lot Data", self) saveDataAction = QAction("Save Lot Data", self) exitAction = QAction("Exit", self) # Set Trigger for each Action loadImageAction.triggered.connect(self.loadImage) loadDataAction.triggered.connect(self.loadData) saveDataAction.triggered.connect(self.saveData) exitAction.triggered.connect(QtGui.qApp.quit) # Add actions to menu fileMenu.addAction(loadImageAction) fileMenu.addAction(loadDataAction) fileMenu.addAction(saveDataAction) fileMenu.addAction(exitAction) menuBar.addMenu(fileMenu) return menuBar
def __init__(self, parent=None): super().__init__(parent) self.setWindowTitle('Stat Tracker') exit_action = QAction('&exit', self) exit_action.triggered.connect(self.close) menu_bar = QMenuBar(self) file_menu = menu_bar.addMenu('&File') file_menu.addAction('Save') file_menu.addAction(exit_action) self.widget = MainWidget() self.setCentralWidget(self.widget)
def setup_menu(self) -> QMenuBar: """Create the menu of the main window""" menubar = QMenuBar(self) setting_menu = menubar.addMenu("Einstellungen") # bell sound selection bell_select_action = QAction("Klingelton", self) bell_select_action.setStatusTip("Wähle einen Klingelton") bell_select_action.triggered.connect(self.on_bell_select) setting_menu.addAction(bell_select_action) # volume settings volume_setting_action = QAction("Lautstärke", self) volume_setting_action.setStatusTip("Lautstärke ändern") volume_setting_action.triggered.connect(self.on_volume_setting) setting_menu.addAction(volume_setting_action) return menubar
class MainWindow(QMainWindow): def __init__(self, *, setup_input_panel=None, plot_panel=None): super().__init__() self.setup_input_panel = setup_input_panel self.plot_panel = plot_panel self.main_widget = None self.menu_bar = None self.new_menu_action = None self.setup_ui() self.setWindowTitle(TITLE) def setup_ui(self): self.main_widget = QSplitter(Qt.Horizontal) self.setCentralWidget(self.main_widget) self.main_widget.addWidget(self.setup_input_panel) self.main_widget.addWidget(self.plot_panel) self.main_widget.setStretchFactor(0, 2) self.main_widget.setStretchFactor(1, 1) self.create_menu_bar() def create_menu_bar(self): self.menu_bar = QMenuBar(self) self.setMenuBar(self.menu_bar) self.create_file_menu() def create_file_menu(self): file_menu = self.menu_bar.addMenu('&File') self.new_menu_action = QAction('New', self) self.new_menu_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_N)) file_menu.addAction(self.new_menu_action)
class MainGui(QMainWindow): def __init__(self, parent=None): QLocale.setDefault(QLocale.English) QMainWindow.__init__(self, parent) self.setWindowTitle("AMLab Software v1.0") self.supported_printers = ['DLP', 'Metal'] self.selected_printer = self.supported_printers[0] self.__init_setup__() self.dlp_gui = None self.metal_gui = None # self.dlp_gui.hide() # self.metal_gui.hide() self.setup_widget.hide() self.__init_menu_bar__() if not self.__load_settings__(): self.__select_setup_widget__() def __load_settings__(self): base_path = Path(__file__).parent settings_path = str( (base_path / './resources/PRINTER_SETTINGS.json').resolve()) settings_file = QFile(settings_path) if settings_file.open(QIODevice.ReadOnly | QIODevice.Text): file_data = QJsonDocument.fromJson( settings_file.readAll()).object() if "printer_type" in file_data: printer_type = str(file_data["printer_type"]) if printer_type == self.supported_printers[0]: self.__select_dlp__() elif printer_type == self.supported_printers[1]: self.__select_metal__() else: return False return True settings_file.close() return False def __init_setup__(self): self.setup_widget = QWidget(self) dlp_button = QPushButton("DLP \nPrinter", self.setup_widget) dlp_button.setFixedSize(200, 200) dlp_button.clicked.connect(self.__select_dlp__) metal_button = QPushButton("Metal \nPrinter", self.setup_widget) metal_button.setFixedSize(200, 200) metal_button.clicked.connect(self.__select_metal__) self.setup_layout = QHBoxLayout() self.setup_layout.addWidget(dlp_button) self.setup_layout.addWidget(metal_button) self.setup_widget.setLayout(self.setup_layout) def __select_setup_widget__(self): self.setup_widget.show() self.setCentralWidget(self.setup_widget) def __select_dlp__(self): self.dlp_gui = DLPGUI(self) self.selected_printer = self.supported_printers[0] self.setCentralWidget(self.dlp_gui) self.adjustSize() def __select_metal__(self): self.metal_gui = MetalGUI(self) self.selected_printer = self.supported_printers[1] self.setCentralWidget(self.metal_gui) self.adjustSize() @Slot() def closeEvent(self, event: QCloseEvent): try: self.dlp_gui.close() except: print("already deleted") try: self.metal_gui.close() except: print("already deleted") event.accept() def __init_menu_bar__(self): self.__menu_bar = QMenuBar(self) self.__init_file_menu() self.setMenuBar(self.__menu_bar) def __init_file_menu(self): self.file_menu = self.__menu_bar.addMenu("File") settings_action = QAction("Settings", self) settings_action.setStatusTip("Open Settings") settings_action.triggered.connect(self.__open_settings_window) self.file_menu.addAction(settings_action) @Slot() def __open_settings_window(self): settings_window = QDialog(self) settings_window.setWindowTitle("Settings") printer_settings = QGroupBox("Printer Settings:", settings_window) printer_type_label = QLabel("Printer Type:", printer_settings) self.printer_type_combo = QComboBox(printer_settings) self.printer_type_combo.addItems(self.supported_printers) self.printer_type_combo.setCurrentIndex( self.supported_printers.index(self.selected_printer)) apply_button = QPushButton("Apply Changes", printer_settings) apply_button.clicked.connect(self.__apply_settings) apply_button.clicked.connect(settings_window.close) apply_button.setAutoDefault(False) printer_settings_layout = QGridLayout(printer_settings) printer_settings_layout.addWidget(printer_type_label, 0, 0) printer_settings_layout.addWidget(self.printer_type_combo, 0, 1) printer_settings_layout.addWidget(apply_button, 4, 1) if self.selected_printer is self.supported_printers[0]: extra_settings = self.dlp_gui.get_settings_window(settings_window) elif self.selected_printer is self.supported_printers[1]: extra_settings = self.metal_gui.get_settings_window( settings_window) settings_layout = QVBoxLayout(settings_window) settings_layout.addWidget(printer_settings) settings_layout.addWidget(extra_settings) settings_window.open() @Slot() def __apply_settings(self): self.selected_printer = self.supported_printers[ self.printer_type_combo.currentIndex()] try: self.dlp_gui.close() except: print("already deleted") try: self.metal_gui.close() except: print("already deleted") if self.selected_printer is self.supported_printers[0]: self.__select_dlp__() elif self.selected_printer is self.supported_printers[1]: self.__select_metal__()
class ExtensionMenu: """MDIArea menu for extensions.""" # ---------------------------------------------------------------------- def build_menu_visualization(self, visualization: bool, debugger: Optional[bool] = False) -> None: """Menu for visualizations.""" self.menubar = QMenuBar(self) self.menubar.clear() self.menubar.setMinimumWidth(1e4) self.accent_menubar = QMenuBar(self) # Title if debugger: menu_stimuli = QMenu(f"Debugging: {visualization}") else: if visualization: menu_stimuli = QMenu(f'{visualization } 🞃') else: menu_stimuli = QMenu('Data analysis 🞃') # Add visualizations for viz, path in self.extensions_list: if viz != visualization: menu_stimuli.addAction(QAction(viz, menu_stimuli, triggered=self.set_extension(path))) self.accent_menubar.addMenu(menu_stimuli) # Menu with accent color self.accent_menubar.setStyleSheet(f""" QMenuBar::item {{ background-color: {os.getenv('QTMATERIAL_PRIMARYCOLOR', '#ffffff')}; color: {os.getenv('QTMATERIAL_PRIMARYTEXTCOLOR', '#ffffff')}; }}""") # Set the menu in first position self.menubar.setCornerWidget( self.accent_menubar, corner=Qt.TopLeftCorner) # View menu_view = QMenu("View") if visualization: menu_view.addAction( QAction('Reload', menu_view, triggered=self.reload)) menu_view.addAction( QAction('Save capture', menu_view, triggered=self.save_img)) if not debugger: menu_view.addSeparator() menu_view.addAction( QAction('Close', menu_view, triggered=self.remove)) else: menu_view.setEnabled(False) self.menubar.addMenu(menu_view) # DPI menu_dpi = QMenu("DPI (60)") if visualization: for dpi in [60, 70, 80, 90, 100, 110, 120, 130]: menu_dpi.addAction(QAction( f'{dpi}', menu_dpi, checkable=True, triggered=self.set_dpi(menu_dpi, f'{dpi}', dpi))) if dpi == 60: self.set_dpi(menu_dpi, f'{dpi}', dpi)() else: menu_dpi.setEnabled(False) self.menubar.addMenu(menu_dpi) # ---------------------------------------------------------------------- def build_menu_stimuli(self, visualization: bool, debugger: Optional[bool] = False) -> None: """Menu for stimuli delivery.""" self.menubar = QMenuBar(self) self.menubar.clear() self.accent_menubar = QMenuBar(self) self.accent_menubar.clear() self.menubar.setMinimumWidth(1e4) # Title if debugger: menu_stimuli = QMenu(f"Debugging: {visualization}") else: if visualization: menu_stimuli = QMenu(visualization + ' 🞃') else: menu_stimuli = QMenu('Stimuli' + ' 🞃') for viz, path in self.extensions_list: if viz != visualization: menu_stimuli.addAction(QAction(viz, menu_stimuli, triggered=self.set_extension(path))) # self.menubar.addMenu(menu_stimuli) self.accent_menubar.addMenu(menu_stimuli) self.accent_menubar.setStyleSheet(f""" QMenuBar::item {{ background-color: {os.getenv('QTMATERIAL_PRIMARYCOLOR', '#ffffff')}; color: {os.getenv('QTMATERIAL_PRIMARYTEXTCOLOR', '#ffffff')}; }} """) # self.menubar.addMenu(menu_stimuli) self.menubar.setCornerWidget( self.accent_menubar, corner=Qt.TopLeftCorner) # View menu_view = QMenu("View") if visualization: menu_view.addAction( QAction('Reload', menu_view, triggered=self.reload)) if debugger: menu_view.addAction( QAction('Open subwindow delivery', menu_view, triggered=debugger.open_subwindow)) if not debugger: menu_view.addSeparator() menu_view.addAction( QAction('Close', menu_view, triggered=self.remove)) else: menu_view.setEnabled(False) self.menubar.addMenu(menu_view) # ---------------------------------------------------------------------- def set_dpi(self, menu_dpi, text: str, dpi: int) -> Callable: """Set the DPI value for matplotlib figures.""" def wrap(): [action.setChecked(False) for action in menu_dpi.actions()] [action.setChecked( True) for action in menu_dpi.actions() if action.text() == text] self.main.DPI = dpi menu_dpi.setTitle(f'DPI ({dpi})') return wrap # ---------------------------------------------------------------------- def set_extension(self, visualization: str) -> Callable: """Load extension from menu.""" def wrap(): self.load_extension(visualization) return wrap
class Window(QMainWindow): """Class to create main widnow Creates main window for displaying frame read from a connected camera. The main window contains memu bar, tool bar, status bar, sliders and the boxes showing the camera's information. These widget are created and added to main window in the instance method of this class. """ def __init__( self, device: int = 0, suffix: str = "png", camtype: str = "usb_cam", color: str = "RGB", dst: str = ".", param: str = "full", rule: str = "Sequential", parent=None): super(Window, self).__init__(parent) self.device = device self.camtype = camtype self.colorspace = color self.image_suffix = suffix self.video_codec = "AVC1" self.video_suffix = "avi" self.dst = Path(dst) self.parent_dir = Path(__file__).parent.resolve() self.filename_rule_lst = FileIO.file_save self.filename_rule = FileIO.file_save_lst[-1] self.is_display = True self.param_separate = False self.slot = Slot(self) cam = self.get_cam() self.camera = cam(self.device, self.colorspace, parent=self) self.support_params = self.camera.get_supported_params() self.current_params = self.camera.get_current_params(param) # List of camera properties with temporal initial values self.prop_table = [ ["Fourcc", "aa"], ["Width", 640], ["Height", 480], ["FPS", 30.0], ["Bit depth", 8], ["File naming style", self.filename_rule] ] self.setup() self.set_timer() def get_cam(self) -> str: """Return camera object according to current OS. Detects what OS you are using, return camera objects in order to function properly. - Linux: LinuxCamera - RaspberryPi OS: RaspiCamera - Windows: WindowsCamera Returns: Camera class """ if self.camtype == "raspi": return RaspiCamera self.system = platform.system() if re.search("linux", self.system, re.IGNORECASE): return LinuxCamera elif re.search("windows", self.system, re.IGNORECASE): return WindowsCamera else: return "Unknown type" def setup(self): """Setup the main window for displaying frame and widget. Creates a QMainWindow object, then add menubar, toolbar, statusbar, widgets and layout into the window. """ self.setFocusPolicy(Qt.ClickFocus) self.setContentsMargins(20, 0, 20, 0) self.information_window_setup() self.view_setup() self.layout_setup() self.image_setup() self.toolbar_setup() self.setWindowTitle("usbcamGUI") self.update_prop_table() self.adjust_windowsize() self.set_theme() def adjust_windowsize(self): """Adjusts the main window size """ system = Utility.get_os() if system == "linux": w, h, _ = self.get_screensize() wscale = 0.5 hscale = 0.7 self.resize(wscale * w, hscale * h) else: self.resize(800, 600) def set_theme(self): """Set color theme of the main window. """ self.style_theme = "light" self.style_theme_sheet = ":/{}.qss".format(self.style_theme) self.slot.switch_theme() self.set_font(self.camera.font_family, self.camera.font_size) def set_font(self, family: str = "Yu Gothic", size: int = 14): """Sets font-family and size of UI. Args: family (str, optional): Font-family. Defaults to "Yu Gothic". size (int, optional): Font-size. Defaults to 20. """ self.setStyleSheet('font-family: "{}"; font-size: {}px;'.format(family, size)) def set_timer(self): """Set QTimer Creates a QTimer object to update frame on view area. The interval is set to the inverse of camera FPS. """ self.qtime_factor = 0.8 self.fps = 30.0 if self.fps: self.msec = 1 / self.fps * 1000 * self.qtime_factor else: self.msec = 1 / 30.0 * 1000 * self.qtime_factor self.timer = QTimer() self.timer.setInterval(self.msec) self.timer.timeout.connect(self.next_frame) self.timer.start() def stop_timer(self): """Deactivate the Qtimer object. """ self.timer.stop() def start_timer(self): """Activate the Qtimer object. """ self.fps = 30.0 if self.fps: self.msec = 1 / self.fps * 1000 * self.qtime_factor else: self.msec = 1 / 30.0 * 1000 * self.qtime_factor self.timer.setInterval(self.msec) self.timer.start() def toolbar_setup(self): """Create toolbar """ self.toolbar = QToolBar("test", self) self.addToolBar(self.toolbar) current_size = str(self.font().pointSize()) lst = [str(i) for i in range(6, 14)] lst.extend([str(i) for i in range(14, 40, 2)]) index = lst.index(current_size) self.fontsize_combo = QComboBox() self.fontsize_combo.addItems(lst) self.fontsize_combo.setCurrentIndex(index) self.fontsize_combo.currentTextChanged.connect(self.slot.set_fontsize) self.fontsize_label = QLabel("Font size") self.fontsize_label.setFrameShape(QFrame.Box) self.comb = QFontComboBox() self.toolbar.addWidget(self.save_button) self.toolbar.addWidget(self.stop_button) self.toolbar.addWidget(self.rec_button) self.toolbar.addWidget(self.close_button) self.toolbar.addWidget(self.theme_button) self.toolbar.addWidget(self.help_button) self.toolbar.addWidget(self.fontsize_label) self.toolbar.addWidget(self.fontsize_combo) self.toolbar.setStyleSheet( """ QToolBar {spacing:5px;} """ ) def view_setup(self): """Set view area to diplay read frame in part of the main window """ self.view = QGraphicsView() self.scene = QGraphicsScene() self.view.setScene(self.scene) self.width = 640 self.height = 480 self.scene.setSceneRect(0, 0, self.width, self.height) self.view.setMouseTracking(True) self.view.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContents) self.view.setCacheMode(QGraphicsView.CacheBackground) self.view.setViewportUpdateMode(QGraphicsView.SmartViewportUpdate) def layout_setup(self): """Set layout of objects on the window. """ self.window = QWidget() self.setCentralWidget(self.window) #self.view.mouseMoveEvent = self.get_coordinates self.main_layout = QHBoxLayout() self.window.setLayout(self.main_layout) self.add_actions() self.add_menubar() self.add_statusbar() self.button_block = self.add_buttons() self.slider_group = self.add_params() self.prop_block = self.add_prop_window() self.create_mainlayout() def image_setup(self): """Creates a Qimage to assign frame, then initialize with an image which has zero in all pixels. """ self.frame = np.zeros((640, 480, 3), dtype=np.uint8) #cinit = np.ctypeslib.as_ctypes(self.frame) #self.frame.buffer = sharedctypes.RawArray(cinit._type_, cinit) self.qimage = QImage( self.frame.data, 640, 480, 640 * 3, QImage.Format_RGB888 ) self.pixmap = QPixmap.fromImage(self.qimage) def add_actions(self): """Add actions executed when press each item in the memu window. """ self.save_act = self.create_action("&Save", self.save_frame, "Ctrl+s") self.stop_act = self.create_action("&Pause", self.stop_frame, "Ctrl+p", checkable=True) self.rec_act = self.create_action("&Record", self.record, "Ctrl+r", True) self.quit_act = self.create_action("&Quit", self.slot.quit, "Ctrl+q") self.theme_act = self.create_action("Switch &Theme", self.slot.switch_theme, "Ctrl+t") self.param_act = self.create_action("Choose parameter slider", self.slot.switch_paramlist, "Ctrl+g") self.show_paramlist_act = self.create_action("Parameters &List", self.slot.show_paramlist, "Ctrl+l") self.show_shortcut_act = self.create_action("&Keybord shortcut", self.slot.show_shortcut, "Ctrl+k") self.font_act = self.create_action("&Font", self.slot.set_font, "Ctrl+f") self.usage_act = self.create_action("&Usage", self.slot.usage, "Ctrl+h") self.about_act = self.create_action("&About", self.slot.about, "Ctrl+a") def create_action(self, text: str, slot: Callable, key: str = None, checkable: bool = False, check_defalut: bool = False) -> QAction: """Create a QAction object. Args: text (str): Text shown on menu. slot (Callable): A method called when click the menu. key (str, optional): Shortcut key. Defaults to None. checkable (bool, optional): Add a checkbox into the menu. Defaults to False. check_defalut (bool, optional): Check default status. Defaults to False. Returns: QAction: PySide2 QAction """ act = QAction(text) act.setShortcut(key) if checkable: act.setCheckable(True) act.setChecked(check_defalut) act.toggled.connect(slot) else: act.triggered.connect(slot) return act def add_menubar(self): """Create menu bar, then add to the main window. """ self.menubar = QMenuBar() self.setMenuBar(self.menubar) self.file_tab = QMenu("&File") self.file_tab.addAction(self.save_act) self.file_tab.addAction(self.stop_act) self.file_tab.addAction(self.rec_act) self.file_tab.addSeparator() self.file_tab.addAction(self.quit_act) #self.file_tab.setSizePolicy(policy) self.view_tab = QMenu("&View") self.view_tab.addAction(self.theme_act) self.view_tab.addAction(self.font_act) self.view_tab.addAction(self.param_act) self.view_tab.addAction(self.show_shortcut_act) self.view_tab.addAction(self.show_paramlist_act) self.help_tab = QMenu("&Help") self.help_tab.addAction(self.usage_act) self.help_tab.addAction(self.about_act) self.menubar.addMenu(self.file_tab) self.menubar.addMenu(self.view_tab) self.menubar.addMenu(self.help_tab) self.menubar.setStyleSheet( """ QMenuBar { font-size: 16px; spacing:10px; padding-top: 5px; padding-bottom: 10px; } """ ) def add_statusbar(self): """Create status bar, then add to the main window. The status bar shows the coordinates on the frame where the cursor is located and its pixel value. The pixel value has RGB if the format of is color (RGB), does grayscale value if grayscale. """ self.statbar_list = [] if self.colorspace == "rgb": self.stat_css = { "postion": "color: white", "R": "color: white;", "G": "color: white;", "B": "color: white;", "alpha": "color: white;", } else: self.stat_css = { "postion": "color: black;", "gray": "color: black" } for s in self.stat_css.values(): stat = QStatusBar(self) stat.setStyleSheet(s) self.statbar_list.append(stat) first = True for stat in self.statbar_list: if first: self.setStatusBar(stat) self.statbar_list[0].reformat() first = False else: self.statbar_list[0].addPermanentWidget(stat) def add_buttons(self): """Add push buttons on the window. Add quit, save stop and usage buttons on the windows. When press each button, the set method (called "slot" in Qt framework) are execeuted. """ self.save_button = self.create_button("&Save", self.save_frame, None, None, "Save the frame") self.stop_button = self.create_button("&Pause", self.stop_frame, None, None, "Stop reading frame", True) self.rec_button = self.create_button("&Rec", self.record, None, None, "Start recording", True) self.close_button = self.create_button("&Quit", self.slot.quit, None, None, "Quit the program") self.theme_button = self.create_button("Light", self.slot.switch_theme, None, None, "Switche color theme") self.help_button = self.create_button("&Usage", self.slot.usage, None, None, "Show usage") self.frame_button = self.create_button( "Properties", self.slot.change_frame_prop, None, tip="Change properties", minsize=(150, 30) ) self.default_button = self.create_button( "&Default params", self.set_param_default, "Ctrl+d", tip="Set default parameters", minsize=(150, 30) ) self.filerule_button = self.create_button( "&Naming style", self.slot.set_file_rule, "Ctrl+n", tip="Change naming style", minsize=(150, 30) ) hbox = QHBoxLayout() hbox.addWidget(self.save_button) hbox.addWidget(self.stop_button) hbox.addWidget(self.rec_button) hbox.addWidget(self.close_button) hbox.addWidget(self.theme_button) hbox.addWidget(self.help_button) return hbox def create_button(self, text: str, slot: Callable, key: str = None, icon: Icon = None, tip: str = None, checkable: bool = False, minsize: tuple = None) -> QPushButton: """Create a QPushButton object. Args: text (str): Text shown on the button. slot (Callable): A method called when click the button. key (str, optional): Shortcut key. Defaults to None. icon (Icon, optional): An icon shown on the button. Defaults to None. tip (str, optional): A tips shown when position the pointer on the button. Defaults to None. checkable (bool, optional): Add button to checkbox. Defaults to False. msize (tuple, optional): Minimum size of the button box, (width, height). Returns: QPushButton: PySide2 QPushButton """ button = QPushButton(text) if checkable: button.setCheckable(True) button.toggled.connect(slot) else: button.clicked.connect(slot) if key: button.setShortcut(key) if icon: button.setIcon(QIcon(icon)) if tip: button.setToolTip(tip) if minsize: button.setMinimumSize(minsize[0], minsize[1]) else: button.setMinimumSize(80, 30) return button def add_params(self) -> QGridLayout: """Set the properties of camera parameter. Set the properties of camera parameter, then add sliders to change each parameter. When change value on the slider, the value of paramter also changes by the caller function. """ lst = self.current_params for key, value in lst.items(): self.add_slider(key) # add sliders self.slider_table = QGridLayout() self.slider_table.setSpacing(15) self.slider_table.setContentsMargins(20, 20, 20, 20) for row, param in enumerate(self.current_params): self.slider_table.addWidget(self.current_params[param]["slider_label"], row, 0) self.slider_table.addWidget(self.current_params[param]["slider"], row, 1) self.slider_table.addWidget(self.current_params[param]["slider_value"], row, 2) if len(self.current_params) > 15: self.param_separate = True else: self.param_separate = False return self.slider_table def update_params(self, plist: list) -> QGridLayout: """Update camera's paramters and sliders shown on the windows. """ #self.current_params.clear() self.current_params = self.camera.get_current_params("selected", plist) for key, value in self.current_params.items(): self.add_slider(key) # add sliders grid = QGridLayout() grid.setSpacing(15) grid.setContentsMargins(20, 20, 20, 20) for row, param in enumerate(self.current_params): grid.addWidget(self.current_params[param]["slider_label"], row, 0) grid.addWidget(self.current_params[param]["slider"], row, 1) grid.addWidget(self.current_params[param]["slider_value"], row, 2) if len(self.current_params) > 15: self.param_separate = True else: self.param_separate = False self.slider_group = grid self.update_mainlayout() self.update_prop_table() self.write_text("update sliders") return grid def add_slider(self, param: str): """Creates slider, labels to show pamarater's name and its value. Args: param (str): A parameter to create slider. """ min_ = self.current_params[param]["min"] max_ = self.current_params[param]["max"] step = self.current_params[param]["step"] value = self.current_params[param]["value"] slider = QSlider(Qt.Horizontal) if max_: slider.setRange(min_, max_) else: slider.setRange(0, 1) slider.setValue(int(value)) slider.setTickPosition(QSlider.TicksBelow) slider.valueChanged.connect(lambda val, p=param: self.set_sliderval(p, val)) if step: if max_ < 5: slider.setTickInterval(step) else: slider.setTickInterval(10) slider_label = QLabel(param) slider_value = QLabel(str(value)) slider_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) slider_value.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) self.current_params[param]["slider"] = slider self.current_params[param]["slider_label"] = slider_label self.current_params[param]["slider_value"] = slider_value def add_prop_window(self) -> QGridLayout: """Create a table to show the current properties of camera. Returns: QGridLayout: PySide2 QGridLayout """ header = ["property", "value"] self.prop_table_widget = QTableWidget(self) self.prop_table_widget.setColumnCount(len(header)) self.prop_table_widget.setRowCount(len(self.prop_table)) self.prop_table_widget.setHorizontalHeaderLabels(header) self.prop_table_widget.verticalHeader().setVisible(False) self.prop_table_widget.setAlternatingRowColors(True) self.prop_table_widget.horizontalHeader().setStretchLastSection(True) self.prop_table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.prop_table_widget.setFocusPolicy(Qt.NoFocus) for row, content in enumerate(self.prop_table): for col, elem in enumerate(content): self.item = QTableWidgetItem(elem) self.prop_table_widget.setItem(row, col, self.item) self.prop_table_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) #self.prop_table_widget.resizeColumnsToContents() #self.prop_table_widget.resizeRowsToContents() self.prop_table_widget.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.prop_table_widget.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.prop_table_widget.setSizeAdjustPolicy(QAbstractScrollArea.AdjustToContentsOnFirstShow) self.prop_table_widget.setColumnWidth(0, 150) self.prop_table_widget.setColumnWidth(1, 150) vbox = QVBoxLayout() vbox.addWidget(self.prop_table_widget) vbox.setContentsMargins(20, 20, 20, 20) self.prop_group = QGroupBox("Frame Properties") self.prop_group.setLayout(vbox) return self.prop_group def information_window_setup(self): """Creates information window. Creates the information window where the event related to camera or window. """ self.text_edit = QTextEdit() self.text_edit.setReadOnly(True) self.text_edit.show() vbox = QVBoxLayout() vbox.addWidget(self.text_edit) self.text_edit_box = QGroupBox("Information", self) self.text_edit_box.setLayout(vbox) self.text_edit_box.setAlignment(Qt.AlignLeft) def create_mainlayout(self): """Create the main layout which consists of view area and information window. """ self.main_layout.addLayout(self.create_view_area_layout()) self.main_layout.addLayout(self.create_information_layout()) def update_mainlayout(self): """Recreate the main layout. """ self.delete_layout(self.information_layout) self.delete_layout(self.upper_right) self.add_prop_window() self.main_layout.addLayout(self.create_information_layout()) def delete_layout(self, layout): """Delete layout Args: layout (QBoxLayout): QBoxLayout class object to delete """ while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() try: child.spacerIitem().deleteLater() except: pass def create_view_area_layout(self) -> QVBoxLayout: """Creates view area layout """ self.view_area_layout = QVBoxLayout() self.view_area_layout.addWidget(self.view, 2) self.view_area_layout.addWidget(self.text_edit_box) return self.view_area_layout def create_information_layout(self): """Creates information part layout upper-left: current properties upper-right: buttons lower: sliders """ if self.param_separate: self.entry_box = QVBoxLayout() self.entry_box.addWidget(self.frame_button) self.entry_box.addWidget(self.filerule_button) self.entry_box.addWidget(self.default_button) self.entry_box.addStretch(1) self.entry_box.setSpacing(20) self.entry_box.setContentsMargins(20, 20, 20, 20) self.button_group_box = QGroupBox("Buttons", self) self.button_group_box.setLayout(self.entry_box) self.button_group_box.setAlignment(Qt.AlignLeft) self.upper_right = QVBoxLayout() self.upper_right.addWidget(self.prop_group, 1) self.upper_right.addWidget(self.button_group_box, 1) self.slider_group_box = QGroupBox("Parameters") self.slider_group_box.setLayout(self.slider_group) self.slider_group_box.setContentsMargins(20, 20, 20, 20) self.information_layout = QHBoxLayout() self.information_layout.addLayout(self.upper_right, 1) self.information_layout.addWidget(self.slider_group_box, 2) #self.information_layout.addStretch(1) return self.information_layout else: self.entry_box = QVBoxLayout() self.entry_box.addWidget(self.frame_button) self.entry_box.addWidget(self.filerule_button) self.entry_box.addWidget(self.default_button) self.entry_box.addStretch(1) self.entry_box.setSpacing(20) self.entry_box.setContentsMargins(20, 20, 20, 20) self.button_group_box = QGroupBox("Buttons", self) self.button_group_box.setLayout(self.entry_box) self.button_group_box.setAlignment(Qt.AlignLeft) self.upper_right = QHBoxLayout() self.upper_right.addWidget(self.prop_group) self.upper_right.addWidget(self.button_group_box) self.slider_group_box = QGroupBox("Parameters") self.slider_group_box.setLayout(self.slider_group) self.slider_group_box.setContentsMargins(20, 20, 20, 20) self.information_layout = QVBoxLayout() self.information_layout.addLayout(self.upper_right) self.information_layout.addWidget(self.slider_group_box) self.information_layout.setSpacing(30) return self.information_layout # decorator def display(func): def wrapper(self, *args, **kwargs): try: self.is_display = False self.stop_timer() func(self, *args, **kwargs) finally: self.is_display = True self.start_timer() return wrapper def stop_frame(self, checked: bool): """Stop reading next frame. Args: checked (bool): True when presse the Stop button (toggle on). False when press again (toggel off). """ if checked: self.write_text("Stop !!") self.is_display = False self.stop_button.setText('Start') self.stop_button.setChecked(True) self.stop_act.setText('Start') self.stop_act.setChecked(True) else: self.write_text("Start !!") self.is_display = True self.stop_button.setText('&Pause') self.stop_button.setChecked(False) self.stop_act.setText('&Pause') self.stop_act.setChecked(False) def keyPressEvent(self, event): """Exit the program This method will be called when press the Escape key on the window. """ if event.key() == Qt.Key_Escape: QApplication.quit() def get_coordinates(self, event): """Show the current coordinates and value in the pixel where the cursor is located. The status bar is updates by the obtained values. """ if self.item is self.view.itemAt(event.pos()): sp = self.view.mapToScene(event.pos()) lp = self.item.mapFromScene(sp).toPoint() (x, y) = lp.x(), lp.y() #color = self.frame.image.pixel(x, y) color = self.qimage.pixelColor(x, y) if self.colorspace == "rgb": value = color.getRgb() elif self.colorspace == "gray": value = color.value() # Return none if the coordinates are out of range if x < 0 and self.frame.width < x: return elif y < 0 and self.frame.height < y: return if self.frame.img_is_rgb: status_list = [ "( x : {}, y :{} )".format(x, y), "R : {}".format(value[0]), "G : {}".format(value[1]), "B : {}".format(value[2]), "alpha : {}".format(value[3]) ] else: status_list = [ "( x : {}, y :{} )".format(x, y), "gray value : {}".format(value), ] for statbar, stat in zip(self.statbar_list, status_list): statbar.showMessage(stat) def next_frame(self): """Get next frame from the connected camera. Get next frame, set it to the view area and update. """ #print("display :", self.is_display) if self.is_display: self.camera.read_frame() self.convert_frame() self.scene.clear() self.scene.addPixmap(self.pixmap) self.update() #print("update") def convert_frame(self): """Convert the class of frame Create qimage, qpixmap objects from ndarray frame for displaying on the window. """ if self.colorspace == "rgb": self.qimage = QImage( self.camera.frame.data, self.camera.frame.shape[1], self.camera.frame.shape[0], self.camera.frame.shape[1] * 3, QImage.Format_RGB888 ) elif self.colorspace == "gray": self.qimage = QImage( self.camera.frame.data, self.camera.frame.shape[1], self.camera.frame.shape[0], self.camera.frame.shape[1] * 1, QImage.Format_Grayscale8) self.pixmap.convertFromImage(self.qimage) def save_frame(self): """Save the frame on the window as an image. """ if self.filename_rule == "Manual": self.save_frame_manual() if not self.filename: return None prm = re.sub(r"\.(.*)", ".csv", str(self.filename)) else: self.filename = FileIO.get_filename(self.filename_rule, self.image_suffix, self.parent_dir) prm = str(self.filename).replace(self.image_suffix, "csv") if not self.dst.exists(): self.dst.mkdir(parents=True) im = Image.fromarray(self.camera.frame) im.save(self.filename) # make a parameter file with open(prm, "w") as f: for name, key in self.current_params.items(): f.write("{},{}\n".format(name, self.current_params[name]["value"])) self.write_text("{:<10}: {}".format("save image", self.filename)) self.write_text("{:<10}: {}".format("save param", prm)) def update_prop_table(self): """Updates the table that shows the camera properties. """ w, h, cc, f = self.camera.get_properties() self.prop_table = [ ["Fourcc", cc], ["Width", int(w)], ["Height", int(h)], ["FPS", "{:.1f}".format(f)], ["Bit depth", 8], ["Naming Style", self.filename_rule] ] col = 1 for row in range(len(self.prop_table)): text = str(self.prop_table[row][col]) self.prop_table_widget.item(row, col).setText(text) def record(self): """Start or end recording """ if self.camera.is_recording: self.camera.stop_recording() self.rec_button.setText('&Rec') self.rec_act.setText('&Record') self.write_text("save : {}".format(self.video_filename)) else: self.video_filename = FileIO.get_filename(self.filename_rule, self.video_suffix, self.parent_dir) self.camera.start_recording(self.video_filename, self.video_codec) self.rec_button.setText('Stop rec') self.rec_act.setText('Stop record') @display def save_frame_manual(self) -> bool: """Determine file name of image to save with QFileDialog """ self.dialog = QFileDialog() self.dialog.setWindowTitle("Save File") self.dialog.setNameFilters([ "image (*.jpg *.png *.tiff *.pgm)", "All Files (*)" ]) self.dialog.setAcceptMode(QFileDialog.AcceptSave) self.dialog.setOption(QFileDialog.DontUseNativeDialog) if self.dialog.exec_(): r = self.dialog.selectedFiles() # If the file name doesn't include supproted suffixes, add to the end. if re.search(".pgm$|.png$|.jpg$|.tiff$", r[0]): self.filename = r[0] else: self.filename = "{}.{}".format(r[0], self.image_suffix) return True else: return False def get_screensize(self): """Get current screen size from the output of linux cmd `xrandr`. """ cmd = ["xrandr"] ret = subprocess.check_output(cmd) output = ret.decode() pattern = r"current(\s+\d+\s+x\s+\d+)" m = re.search(pattern, output) if m: size = re.sub(" ", "", m.group(1)) w, h = map(int, size.split("x")) return w, h, size else: return None def set_sliderval(self, param: str, value: int): """Changes a camera parameter. Updates the label on the right of the slider if input value is valid. Args: param (str): A camera parameter value (int): its value """ val = self.camera.set_parameter(param, value) self.current_params[param]["value"] = str(val) self.current_params[param]["slider_value"].setText(str(value)) def set_param_default(self): """Sets all paramters to default. """ for param, values in self.current_params.items(): default = values["default"] self.camera.set_parameter(param, default) self.current_params[param]["slider"].setValue(int(default)) self.current_params[param]["slider_value"].setText(str(default)) def get_properties(self) -> list: """Get the current camera properties. Returns: list: parameters. fourcc, width, height, fps. """ tmp = [] for row in range(4): tmp.append(self.prop_table[row][1]) return tmp def write_text(self, text: str, level: str = "info", color: str = None): """Writes the message into information window. Args: text (str): A text to write. level (str, optional): Log lebel of the message. Defaults to "info". color (str, optional): Font color. Defaults to None. """ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f") now = now[:-3] if color == "red": form = f"<font color='red'>[{level.upper():<4} {now}] {text}</font>" elif color == "yellow": form = f"<font color='yellow'>[{level.upper():<4} {now}] {text}</font>" else: form = f"[{level.upper():<4} {now}] {text}" self.text_edit.append(form)
class App(QWidget): def __init__(self): super().__init__() self.setWindowIcon(QIcon('icon.ico')) QApplication.setFont( QFont("Helvetica", 9, QFont.Normal, italic=False)) self.title = 'DanielPaskalev' self.left, self.top = 0, 0 self.width, self.height = 900, 920 self.current_tests = process(get_filename()) self.original_tests = process(get_filename()) self.search = "" self.initUI() def initUI(self): # self.is_button_clicked = False self.setWindowTitle(self.title) self.setWindowState(Qt.WindowMaximized) self.createLabel() self.createLe() self.createTable() self.createButton() self.createCopyButton() self.createRefreshButton() self.createMergeButton() self.createMenuBox() # Create vertical box layout and horizontal box layout, # add label, button, to hbox, # add hbox to vbox, # and add table to vbox. self.layout = QVBoxLayout() self.layout.setContentsMargins(0, 5, 0, 0) # self.layout.setSpacing(0) self.hbox = QHBoxLayout() self.hbox.addWidget(self.menuBar) self.hbox.addWidget(self.label) self.hbox.addWidget(self.le) self.hbox.addWidget(self.button) self.hbox.addWidget(self.copyButton) self.hbox.addWidget(self.refreshButton) self.hbox.addWidget(self.mergeButton) self.layout.addLayout(self.hbox) self.layout.addWidget(self.tableWidget) self.setLayout(self.layout) # Show widget self.show() def createLe(self): # Create user input box for filter. self.le = QLineEdit() self.le.setObjectName("Filter") self.le.setPlaceholderText("Filter") self.le.setToolTip('Enter text to filter the pending list') self.le.setMaximumWidth(200) self.le.returnPressed.connect( self.filter_accessions) # Enter pressed def createButton(self): # Create "Filter Accessions" button. self.button = QPushButton('Filter', self) self.button.setToolTip('Filters the accessions by text matches.') self.button.setMaximumWidth(60) self.button.clicked.connect(self.filter_accessions) def createCopyButton(self): # Create Copy accessions button. self.copyButton = QPushButton('Copy', self) self.copyButton.setToolTip( 'Copy all unique accessions to clipboard.') self.copyButton.setMaximumWidth(60) self.copyButton.clicked.connect(self.on_copyButton_click) def createRefreshButton(self): # Re-acquire the ref pending list. self.refreshButton = QPushButton('Refresh', self) self.refreshButton.setToolTip( 'Refresh table with new pending list.') self.refreshButton.setMaximumWidth(60) self.refreshButton.clicked.connect(self.on_refresh) def createMergeButton(self): # Merge the currently showing accessions. self.mergeButton = QPushButton('Merge', self) self.mergeButton.setToolTip( 'Merges tests for duplicate accessions') self.mergeButton.setMaximumWidth(60) self.mergeButton.clicked.connect(self.on_merge) def createLabel(self): # label with general information. self.label = QLabel() self.label.setTextFormat(Qt.PlainText) text = ("Double click entry to copy | Order count: " + str(len(self.current_tests))) self.label.setText(text) self.label.setAlignment(Qt.AlignCenter) def createTable(self): # Initialize table with proper attributes. Then populateTable. self.tableWidget = QTableWidget() self.tableWidget.clicked.connect( self.on_click) # If cell is clicked, copy. self.tableWidget.setEditTriggers( QAbstractItemView.NoEditTriggers) # no edit self.tableWidget.setWordWrap(True) self.tableWidget.setColumnCount(5) self.tableWidget.setHorizontalHeaderLabels( ["Accession", "Name", "DOC", "Worklist(s)", "Pending Tests"]) self.tableWidget.horizontalHeader().setStretchLastSection(True) self.tableWidget.horizontalHeaderItem(4).setTextAlignment( Qt.AlignLeft) self.populateTable(self.current_tests) # Populate table with data self.tableWidget.resizeColumnsToContents( ) # Resize columns only once. self.tableWidget.resizeRowsToContents( ) # Resize height to fit tests. def populateTable(self, orderList): # Re-populate table with argument 'orderList' self.tableWidget.setSortingEnabled(False) self.tableWidget.clearContents() self.tableWidget.setRowCount(len(orderList)) for i in range(len(orderList)): self.tableWidget.setItem(i, 0, QTableWidgetItem(orderList[i][1])) self.tableWidget.setItem(i, 1, QTableWidgetItem(orderList[i][4])) self.tableWidget.setItem(i, 2, QTableWidgetItem(orderList[i][3])) self.tableWidget.setItem(i, 3, QTableWidgetItem(orderList[i][0])) self.tableWidget.setItem(i, 4, QTableWidgetItem(orderList[i][2])) self.tableWidget.resizeRowsToContents( ) # Resize height to fit tests # self.tableWidget.resizeColumnsToContents() self.label.setText("Double click entry to copy | Order count: " + str(len(orderList))) self.tableWidget.setSortingEnabled(True) def createMenuBox(self): # Menu bar at the top of the window. self.menuBar = QMenuBar() self.fileMenu = self.menuBar.addMenu('File') self.open_action = QAction('Open', self) self.exit_action = QAction('Exit', self) self.open_action.triggered.connect(self.open) self.exit_action.triggered.connect(app.quit()) # just quit self.fileMenu.addAction(self.open_action) self.fileMenu.addAction(self.exit_action) @Slot() def open(self): fname = QFileDialog.getOpenFileName( self, 'Open File', os.path.expanduser('~/Documents'), 'Text Files (*.txt)') if fname[0]: self.original_tests = process(fname[0]) self.filter_accessions() @Slot() def on_click(self): # Double click to put selected item into clipboard qApp.clipboard().setText( self.tableWidget.selectedItems()[0].text()) @Slot() def on_refresh(self): # Refresh button clicked to re-create table with new pending list. self.original_tests = process(get_filename()) self.filter_accessions() @Slot() def on_copyButton_click(self): # Click to copy all accessions into clipboard self.cp = [i[1] for i in self.current_tests] qApp.clipboard().setText( self.search + ''.join(['\n' + a for a in filter_dups(self.cp)])) @Slot() def on_merge(self): # Merge current accessions to remove situation where multiple tests for # the same patient are on multiple worklists. self.tableWidget.setSortingEnabled(False) # Aglorithm to merge duplicate accessions. temp_tests = deepcopy(self.current_tests) # DEEEEPcopy temp_tests.sort(key=lambda x: x[1]) # Sort by accession number result = [] i = 0 # Compare the current accession to the next to see if duplicate. length = len(temp_tests) - 1 while i < length: if temp_tests[i][1] == temp_tests[i + 1][1]: # If duplicates, add worklist and tests to first accesion. temp_tests[i][0] += ', ' + temp_tests[i + 1][0] temp_tests[i][2] += ' ||| ' + temp_tests[i + 1][2] del temp_tests[i + 1] # Delete second accession. length -= 1 else: # If current and next accession numbers don't match result.append(temp_tests[i]) i += 1 result.append( temp_tests[-1]) # add the last accession that we missed. # Re-create table with 'result' self.populateTable(result) self.mergeButton.setDown(True) # Click down merge button @Slot() def filter_accessions(self): # Button / Return pressed to filter the orders. self.tableWidget.setSortingEnabled(False) self.mergeButton.setDown(False) # Unclick merge button self.search = self.le.text().upper() self.current_tests = [ a for a in self.original_tests if self.search in a[0] or self.search in a[1] or self.search in a[2] or self.search in a[3] or self.search in a[4] ] self.populateTable(self.current_tests)
class ListenerView(QMainWindow): def __init__(self, *args): super(ListenerView, self).__init__(*args) self.layout = QVBoxLayout() self.setLayout(self.layout) self.window_branding() self.create_menu() self.create_main() self.create_status() self.create_container_view() self.create_audio_view() app = property(appref.app) def window_branding(self): """Setup our general branding for the window""" self.setWindowTitle(defaults.APP_NAME_HUMAN) self.setWindowIcon(icons.get_icon("microphone")) def create_menu(self): """Create the overall application menu""" self.menu = QMenuBar() app = self.app self.dictation_menu = self.menu.addMenu('&Dictation') self.dictation_menu.addAction(app.start_listening) self.dictation_menu.addAction(app.stop_listening) self.dictation_menu.addAction(app.reposition_overlay) self.setMenuBar(self.menu) def create_main(self): """Create our main central widgets""" self.mdi = QMdiArea(self) self.setCentralWidget(self.mdi) def create_status(self): """Create our listening status-bar""" self.status_bar = QStatusBar(self) self.setStatusBar(self.status_bar) self.status_bar.showMessage('%s is loading...' % (defaults.APP_NAME_HUMAN)) def create_audio_view(self): """Create the audio control view""" self.audio_dock = QDockWidget("Audio", self) self.audio_dock.setFloating(False) self.audio_view = audioview.ListenerAudio(self) self.audio_dock.setWidget(self.audio_view) self.addDockWidget( Qt.LeftDockWidgetArea, self.audio_dock, ) def create_container_view(self): """Create the container view""" self.container_dock = QDockWidget("Container", self) self.container_dock.setFloating(False) self.container_view = containersettings.ContainerSettings(self) self.container_dock.setWidget(self.container_view) self.addDockWidget( Qt.LeftDockWidgetArea, self.container_dock, )
def setupUi(self, Main_Window): def about_fn(): about_msgbox = QMessageBox() about_msgbox.setWindowTitle("About") about_msgbox.setIcon(QMessageBox.Information) about_msgbox.setText(f"CV Collector {settings.version}") about_msgbox.setInformativeText(f''' CV Collector is an app that lets you store your CVs in a secure and lightweight sqlite3 database. {settings.special_text}. ''') about_msgbox.setStandardButtons(QMessageBox.Close) about_msgbox.exec_() font1 = QFont("Roboto", 20) font2 = QFont("Roboto", 16) Main_Window.setObjectName("Main_Window") Main_Window.resize(700, 535) Main_Window.setFixedSize(Main_Window.size()) self.centralwidget = QWidget(Main_Window) self.centralwidget.setObjectName("centralwidget") self.search = QLineEdit(self.centralwidget) self.search.setGeometry(QRect(60, 30, 460, 50)) self.search.setFont(font1) self.search.setPlaceholderText("Search..") menuBar = QMenuBar(self.centralwidget) Help = menuBar.addMenu("Help") About = Help.addAction("About") About.triggered.connect(about_fn) self.addBtn = QPushButton(self.centralwidget) self.addBtn.setGeometry(QRect(20, 90, 191, 41)) self.addBtn.setObjectName("addBtn") self.deleteBtn = QPushButton(self.centralwidget) self.deleteBtn.setGeometry(QRect(245, 90, 191, 41)) self.deleteBtn.setObjectName("deleteBtn") self.showBtn = QPushButton(self.centralwidget) self.showBtn.setGeometry(QRect(470, 90, 191, 41)) self.showBtn.setObjectName("showBtn") self.listWidget = QTableWidget(self.centralwidget) self.listWidget.setGeometry(QRect(20, 150, 661, 350)) self.listWidget.setRowCount(20) self.listWidget.setColumnCount(4) self.listWidget.setObjectName("listWidget") self.listWidget.hideColumn(0) horizontal_names = ('hidden', 'CV Name', 'CV Filename', 'Date') self.listWidget.setHorizontalHeaderLabels(horizontal_names) self.listWidget.setColumnWidth( 1, 200) self.listWidget.setColumnWidth( 2, 200) self.listWidget.setColumnWidth( 3, 200) self.listWidget.setFont(font2) def initialize(): self.Show_Data() initialize() def search_fn(): columnOfInterest = 1 valueOfInterest = self.search.text() for rowIndex in range(self.listWidget.rowCount()): item = self.listWidget.item(rowIndex, columnOfInterest) if self.search.text() in item.text(): self.listWidget.setRowHidden(rowIndex, False) else: self.listWidget.setRowHidden(rowIndex, True) self.searchBtn = QPushButton(self.centralwidget) self.searchBtn.setGeometry(QRect(530, 30, 100, 50)) self.searchBtn.setObjectName("addBtn") self.searchBtn.clicked.connect(search_fn) Main_Window.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(Main_Window) self.menubar.setGeometry(QRect(0, 0, 723, 22)) self.menubar.setObjectName("menubar") Main_Window.setMenuBar(self.menubar) self.statusbar = QStatusBar(Main_Window) self.statusbar.setObjectName("statusbar") Main_Window.setStatusBar(self.statusbar) self.retranslateUi(Main_Window) QMetaObject.connectSlotsByName(Main_Window) self.showBtn.clicked.connect(self.Show_Data) self.addBtn.clicked.connect(self.Add) self.deleteBtn.clicked.connect(self.Delete)
class MainWidget(QWidget): data_directory_group_box: DataDirectoryBox error_message: QErrorMessage grid: QVBoxLayout network_grid: Tabs network_widget: NetworkWidget settings_tab: SettingsTabDialog def __init__(self): super().__init__() self.setWindowTitle('Node Launcher') self.error_message = QErrorMessage(self) try: self.network_widget = NetworkWidget() except ZmqPortsNotOpenError as e: self.error_message.showMessage(str(e)) self.error_message.exec_() sys.exit(0) self.network_grid = Tabs(self, self.network_widget) self.network_grid.currentChanged.connect(self.on_tab_change) self.grid = QVBoxLayout() self.settings_tab = SettingsTabDialog( node_set=self.network_widget.node_set) settings_action = QAction('&Settings', self) settings_action.setShortcut(QKeySequence.Preferences) settings_action.triggered.connect(self.settings_tab.show) self.menubar = QMenuBar() self.menubar.setMinimumWidth(self.menubar.sizeHint().width()) file_menu = self.menubar.addMenu('&File') file_menu.addAction(settings_action) self.grid.addWidget(self.menubar) self.grid.addStretch() self.grid.addWidget(self.network_grid) self.setLayout(self.grid) self.settings_tab.bitcoin_tab.change_network.connect( self.change_network) timer = QTimer() timer.singleShot(1000, self.check_version) def on_tab_change(self, index: int): log.info('on_tab_change', index=index) if index == 1: self.network_grid.lncli_widget.input.setFocus() elif index == 2: self.network_grid.bitcoin_cli_widget.input.setFocus() def check_version(self): latest_version = LauncherSoftware().get_latest_release_version() if latest_version is None: return latest_major, latest_minor, latest_bugfix = latest_version.split('.') major, minor, bugfix = NODE_LAUNCHER_RELEASE.split('.') major_upgrade = latest_major > major minor_upgrade = (latest_major == major and latest_minor > minor) bugfix_upgrade = (latest_major == major and latest_minor == minor and latest_bugfix > bugfix) if major_upgrade or minor_upgrade or bugfix_upgrade: message_box = QMessageBox(self) message_box.setTextFormat(Qt.RichText) message_box.setText(UPGRADE) message_box.setInformativeText( f'Your version: {NODE_LAUNCHER_RELEASE}\n' f'New version: {latest_version}') message_box.exec_() def change_network(self, network: Network): self.network_widget.nodes_layout.image_label.set_image( f'bitcoin-{network}.png') def mousePressEvent(self, event): focused_widget = QApplication.focusWidget() if isinstance(focused_widget, QLineEdit): focused_widget.clearFocus() QMainWindow.mousePressEvent(self, event)
class MainWindow(QMainWindow): """ Main Window service for the nexxT frameworks. Other services usually create dock windows, filters use the subplot functionality to create grid-layouted views. """ mdiSubWindowCreated = Signal( QMdiSubWindow ) # TODO: remove, is not necessary anymore with subplot feature aboutToClose = Signal(object) def __init__(self, config): super().__init__() self.config = config self.config.appActivated.connect(self._appActivated) self.mdi = QMdiArea(self) self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setCentralWidget(self.mdi) self.menu = self.menuBar().addMenu("&Windows") self.aboutMenu = QMenuBar(self) self.menuBar().setCornerWidget(self.aboutMenu) m = self.aboutMenu.addMenu("&About") self.aboutNexxT = QAction("About nexxT ...") self.aboutQt = QAction("About Qt ...") self.aboutPython = QAction("About Python ...") m.addActions([self.aboutNexxT, self.aboutQt, self.aboutPython]) self.aboutNexxT.triggered.connect(lambda: QMessageBox.about( self, "About nexxT", """\ This program uses <b>nexxT</b> %(version)s, a generic hybrid python/c++ framework for developing computer vision algorithms.<br><br> nexxT is available under the <a href='https://github.com/ifm/nexxT/blob/master/LICENSE'>Apache 2.0 License</a> together with the <a href='https://github.com/ifm/nexxT/blob/master/NOTICE'>notice</a>. """ % dict(version=nexxT.__version__))) self.aboutQt.triggered.connect(lambda: QMessageBox.aboutQt(self)) self.aboutPython.triggered.connect(self._aboutPython) self.toolbar = None self.managedMdiWindows = [] self.managedSubplots = {} self.windows = {} self.activeApp = None self._ignoreCloseEvent = False def closeEvent(self, closeEvent): """ Override from QMainWindow, saves the state. :param closeEvent: a QCloseEvent instance :return: """ self._ignoreCloseEvent = False self.aboutToClose.emit(self) if self._ignoreCloseEvent: logger.info("Ignoring event") closeEvent.ignore() return closeEvent.accept() self.saveState() self.saveMdiState() super().closeEvent(closeEvent) def ignoreCloseEvent(self): """ Can be called in slots connected to aboutToClose for requesting to ignore the event. Use case is the "There are unsaved changes" dialog. :return: """ self._ignoreCloseEvent = True def restoreState(self): """ restores the state of the main window including the dock windows of Services :return: """ logger.info("restoring main window's state") settings = QSettings() v = settings.value("MainWindowState") if v is not None: super().restoreState(v) v = settings.value("MainWindowGeometry") if v is not None: self.restoreGeometry(v) if self.toolbar is not None: # TODO: add toolbar to windows menu, so we don't need this self.toolbar.show() def saveState(self): """ saves the state of the main window including the dock windows of Services :return: """ logger.info("saving main window's state") settings = QSettings() settings.setValue("MainWindowState", super().saveState()) settings.setValue("MainWindowGeometry", self.saveGeometry()) def saveMdiState(self): """ saves the state of the individual MDI windows :return: """ for i in self.managedMdiWindows: window = i["window"] propColl = i["propColl"] prefix = i["prefix"] logger.debug("save window geometry %s: %s", prefix, window.geometry()) geom = str(window.saveGeometry().toBase64(), "ascii") visible = self.windows[shiboken2.getCppPointer(window) [0]].isChecked() # pylint: disable=no-member propColl.setProperty(prefix + "_geom", geom) logger.debug("%s is visible: %d", prefix, int(visible)) propColl.setProperty(prefix + "_visible", int(visible)) self.managedMdiWindows = [] def __del__(self): logging.getLogger(__name__).debug("deleting MainWindow") @Slot() def getToolBar(self): """ Get the main toolbar (adds seperators as appropriate). :return: """ if self.toolbar is None: self.toolbar = self.addToolBar("NexxT") self.toolbar.setObjectName("NexxT_main_toolbar") else: self.toolbar.addSeparator() return self.toolbar @Slot(str, QObject, int, int) def newDockWidget(self, name, parent, defaultArea, allowedArea=Qt.LeftDockWidgetArea | Qt.BottomDockWidgetArea, defaultLoc=None): """ This function is supposed to be called by services :param name: the name of the dock window :param parent: the parent (usually None) :param defaultArea: the default dock area :param allowedArea: the allowed dock areas :return: a new QDockWindow instance """ res = NexxTDockWidget(name, parent if parent is not None else self) res.setAllowedAreas(allowedArea) res.setAttribute(Qt.WA_DeleteOnClose, False) self.addDockWidget(defaultArea, res) self._registerWindow(res, res.objectNameChanged) res.setObjectName(name) if defaultLoc is not None: dl = self.findChild(QDockWidget, defaultLoc) if dl is not None: self.tabifyDockWidget(dl, res) return res @staticmethod def parseWindowId(windowId): """ convers a subplot window id into windowTitle, row and column :param windowId: the window id :return: title, row, column """ regexp = re.compile(r"([^\[]+)\[(\d+),\s*(\d+)\]") match = regexp.match(windowId) if not match is None: return match.group(1), int(match.group(2)), int(match.group(3)) return windowId, 0, 0 @Slot(str, QObject, QWidget) def subplot(self, windowId, theFilter, widget): """ Adds widget to the GridLayout specified by windowId. :param windowId: a string with the format "<windowTitle>[<row>,<col>]" where <windowTitle> is the caption of the MDI window (and it is used as identifier for saving/restoring window state) and <row>, <col> are the coordinates of the addressed subplots (starting at 0) :param theFilter: a Filter instance which is requesting the subplot :param widget: a QWidget which shall be placed into the grid layout. Note that this widget is reparented as a result of this operation and the parents can be used to get access to the MDI sub window. Use releaseSubplot to remove the window :return: None """ logger.internal("subplot '%s'", windowId) title, row, col = self.parseWindowId(windowId) if title == "": title = "(view)" if title in self.managedSubplots and ( row, col) in self.managedSubplots[title]["plots"]: logger.warning( "subplot %s[%d,%d] is already registered. Creating a new window for the plot.", title, row, col) i = 2 while "%s(%d)" % (title, i) in self.managedSubplots: i += 1 title = "%s(%d)" % (title, i) row = 0 col = 0 if title not in self.managedSubplots: subWindow = self._newMdiSubWindow(theFilter, title) swwidget = QWidget() subWindow.setWidget(swwidget) layout = QGridLayout(swwidget) swwidget.setLayout(layout) self.managedSubplots[title] = dict(mdiSubWindow=subWindow, layout=layout, swwidget=swwidget, plots={}) self.managedSubplots[title]["layout"].addWidget(widget, row, col) self.managedSubplots[title]["mdiSubWindow"].updateGeometry() widget.setParent(self.managedSubplots[title]["swwidget"]) QTimer.singleShot( 0, lambda: (self.managedSubplots[title]["mdiSubWindow"].adjustSize() if widget.parent().size().height() < widget.minimumSizeHint(). height() or widget.parent().size().height() < widget.minimumSize( ).height() else None)) self.managedSubplots[title]["plots"][row, col] = widget @Slot(QWidget) @Slot(str) def releaseSubplot(self, arg): """ This needs to be called to release the previously allocated subplot called windowId. The managed widget is deleted as a consequence of this function. :param arg: the widget as passed to subplot. Passing the windowId is also supported, but deprecated. :return: """ if isinstance(arg, str): windowId = arg logger.warning( "Using deprecated API to release a subplot. Please pass the widget instead of the windowId." ) logger.internal("releaseSubplot '%s'", windowId) title, row, col = self.parseWindowId(windowId) if title not in self.managedSubplots or ( row, col) not in self.managedSubplots[title]["plots"]: logger.warning("releasSubplot: cannot find %s", windowId) return widget = self.managedSubplots[title]["plots"][row, col] elif isinstance(arg, QWidget): widget = arg found = False for title in self.managedSubplots: for row, col in self.managedSubplots[title]["plots"]: if self.managedSubplots[title]["plots"][row, col] is widget: found = True break if found: break if not found: raise RuntimeError( "cannot find widget given for releaseSubplot.") else: raise RuntimeError( "arg of releaseSubplot must be either a string or a QWidget instance." ) self.managedSubplots[title]["layout"].removeWidget(widget) self.managedSubplots[title]["plots"][row, col].deleteLater() del self.managedSubplots[title]["plots"][row, col] if len(self.managedSubplots[title]["plots"]) == 0: self.managedSubplots[title]["layout"].deleteLater() self.managedSubplots[title]["swwidget"].deleteLater() self.managedSubplots[title]["mdiSubWindow"].deleteLater() del self.managedSubplots[title] @Slot(QObject) @Slot(QObject, str) def newMdiSubWindow(self, filterOrService, windowTitle=None): """ Deprectated (use subplot(...) instead): This function is supposed to be called by filters. :param filterOrService: a Filter instance :param windowTitle: the title of the window (might be None) :return: a new QMdiSubWindow instance """ logger.warning( "This function is deprecated. Use subplot function instead.") return self._newMdiSubWindow(filterOrService, windowTitle) def _newMdiSubWindow(self, filterOrService, windowTitle): res = NexxTMdiSubWindow(None) res.setAttribute(Qt.WA_DeleteOnClose, False) self.mdi.addSubWindow(res) self._registerWindow(res, res.windowTitleChanged) if isinstance(filterOrService, Filter): propColl = filterOrService.guiState() res.setWindowTitle( propColl.objectName() if windowTitle is None else windowTitle) else: app = Application.activeApplication.getApplication() propColl = app.guiState("services/MainWindow") res.setWindowTitle( "<unnamed>" if windowTitle is None else windowTitle) prefix = re.sub(r'[^A-Za-z_0-9]', '_', "MainWindow_MDI_" + res.windowTitle()) i = dict(window=res, propColl=propColl, prefix=prefix) self.managedMdiWindows.append(i) window = i["window"] propColl = i["propColl"] prefix = i["prefix"] propColl.defineProperty(prefix + "_geom", "", "Geometry of MDI window") b = QByteArray.fromBase64( bytes(propColl.getProperty(prefix + "_geom"), "ascii")) window.restoreGeometry(b) logger.debug("restored geometry %s:%s (%s)", prefix, window.geometry(), b) propColl.defineProperty(prefix + "_visible", 1, "Visibility of MDI window") if propColl.getProperty(prefix + "_visible"): window.show() else: window.hide() self.mdiSubWindowCreated.emit(res) return res def _registerWindow(self, window, nameChangedSignal): act = QAction("<unnamed>", self) act.setCheckable(True) act.toggled.connect(window.setVisible) window.visibleChanged.connect(act.setChecked) nameChangedSignal.connect(act.setText) self.windows[shiboken2.getCppPointer(window)[0]] = act # pylint: disable=no-member self.menu.addAction(act) logger.debug("Registering window %s, new len=%d", shiboken2.getCppPointer(window), len(self.windows)) # pylint: disable=no-member window.destroyed.connect(self._windowDestroyed) def _windowDestroyed(self, obj): logger.internal("_windowDestroyed") ptr = shiboken2.getCppPointer(obj) # pylint: disable=no-member try: ptr = ptr[0] except TypeError: pass logger.debug("Deregistering window %s, old len=%d", ptr, len(self.windows)) self.windows[ptr].deleteLater() del self.windows[ptr] logger.debug("Deregistering window %s done", ptr) def _appActivated(self, name, app): if app is not None: self.activeApp = name app.aboutToClose.connect(self.saveMdiState, Qt.UniqueConnection) else: self.activeApp = None def _aboutPython(self): piplic = subprocess.check_output( [sys.executable, "-m", "piplicenses", "--format=plain"], encoding="utf-8").replace("\n", "<br>").replace(" ", " ") QMessageBox.about( self, "About python", """\ This program uses <b>python</b> %(version)s and the following installed python packages.<br><br> <pre> %(table)s </pre> """ % dict(version=sys.version, table=piplic))
class IccMainWindow(QMainWindow): def __init__(self, environment): QMainWindow.__init__(self) self.setWindowTitle("ImmoControlCenter " + environment) self._menubar: QMenuBar = None self._viewsMenu: QMenu = None self._submenuShowTableContent: QMenu = None self._toolbar: QToolBar = None self._sdLetzteBuchung: SmartDateEdit = SmartDateEdit(self) self._leLetzteBuchung: QLineEdit = QLineEdit(self) # Summen self._idSumMiete = self._createSumDisplay("Summe aller Bruttomieten") self._idSummeSonstAus = self._createSumDisplay( "Summe aller sonstigen Ausgaben") self._idSummeHGV = self._createSumDisplay( "Summe aller Hausgeld-Vorauszahlungen") self._idSaldo = self._createSumDisplay( "Bruttomieten minus Ausgaben minus HG-Vorauszahlungen") self._summenfont = QFont("Times New Roman", 16, weight=QFont.Bold) self._summenartfont = QFont("Times New Roman", 9) # give others access to sum fields via Singleton SumFieldsAccess: self._sumfieldsAccess = SumFieldsProvider( self._idSumMiete, self._idSummeSonstAus, self._idSummeHGV, self._idSaldo, self.onSumFieldsProvidingFailed) self._actionCallbackFnc = None #callback function for all action callbacks self._shutdownCallback = None # callback function for shutdown action self._createUI() def onSumFieldsProvidingFailed(self, msg: str): self.showException(msg) def _createUI(self): self._menubar = QMenuBar(self) self._toolBar = QToolBar(self) self._createImmoCenterMenu() self._createMietobjektMenu() self._createMietverhaeltnisMenu() self._createZahlungenMenu() self._createAbrechnungenMenu() self._createAnlageVMenu() self._createExtrasMenu() self._createSqlMenu() self._createShowViewsMenu() self._toolBar.addSeparator() lbl = QLabel(self, text="Letzte verarbeitete Buchung: ") self._toolBar.addWidget(lbl) self._sdLetzteBuchung.setToolTip( "Freier Eintrag der Kenndaten der letzten Buchung,\n " "um beim nächsten Anwendungsstart gezielt weiterarbeiten zu können." ) self._sdLetzteBuchung.setMaximumWidth(90) self._toolBar.addWidget(self._sdLetzteBuchung) dummy = QWidget() dummy.setFixedWidth(5) self._toolBar.addWidget(dummy) self._leLetzteBuchung.setToolTip( "Freier Eintrag der Kenndaten der letzten Buchung,\n " "um beim nächsten Anwendungsstart gezielt weiterarbeiten zu können." ) # self._leLetzteBuchung.setMaximumWidth( 300 ) self._toolBar.addWidget(self._leLetzteBuchung) dummy = QWidget() dummy.setFixedWidth(30) #dummy.setSizePolicy( QSizePolicy.Expanding, QSizePolicy.Preferred ) self._toolBar.addWidget(dummy) self._addSumFields() self.setMenuBar(self._menubar) self.addToolBar(QtCore.Qt.TopToolBarArea, self._toolBar) def _createImmoCenterMenu(self): # Menü "ImmoCenter" menu = QtWidgets.QMenu(self._menubar, title="ImmoCenter") self._menubar.addMenu(menu) # Menüpunkt "Alle Änderungen speichern" action = QAction(self, text="Alle Änderungen speichern") action.setShortcut(QKeySequence("Ctrl+Shift+s")) action.triggered.connect(self.onSaveAll) menu.addAction(action) menu.addSeparator() # Menü "Datenbank zum Server exportieren" action = QAction(self, text="Datenbank zum Server exportieren") action.triggered.connect(self.onExportDatabaseToServer) menu.addAction(action) # Menü "Datenbank vom Server importieren" action = QAction(self, text="Datenbank vom Server importieren") action.triggered.connect(self.onImportDatabaseFromServer) menu.addAction(action) menu.addSeparator() # Menüpunkt "Folgejahr einrichten" action = QAction(self, text="Folgejahr einrichten") action.triggered.connect(self.onFolgejahrEinrichten) menu.addAction(action) menu.addSeparator() # Menüpunkt "Ende" action = QAction(self, text="Ende") action.triggered.connect(self.onExit) action.setShortcut("Alt+F4") menu.addAction(action) def _createMietobjektMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Mietobjekt") self._menubar.addMenu(menu) # Menüpunkt "Wohnungsstammdaten..." action = QAction(self, text="Objektstammdaten...") action.triggered.connect(self.onViewObjektStammdaten) menu.addAction(action) # # Menüpunkt "Mietverhältnis..." # action = QAction( self, text="Mietverhältnis..." ) # action.triggered.connect( self.onViewMietverhaeltnis ) # menu.addAction( action ) menu.addSeparator() # action = QAction( self, text="Mieterwechsel..." ) # action.triggered.connect( self.onMieterwechsel ) # menu.addAction( action ) action = QAction(self, text="Verwalterwechsel...") action.triggered.connect(self.onVerwalterwechsel) menu.addAction(action) def _createMietverhaeltnisMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Mietverhältnis") self._menubar.addMenu(menu) # Menüpunkt "Mietverhältnis..." action = QAction(self, text="Mietverhältnis anschauen und ändern...") action.triggered.connect(self.onViewMietverhaeltnis) menu.addAction(action) menu.addSeparator() action = QAction(self, text="Mieterwechsel...") action.triggered.connect(self.onMieterwechsel) menu.addAction(action) def _createZahlungenMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Ist- und Sollzahlungen") self._menubar.addMenu(menu) # Menüpunkt "Mietzahlungen..." action = QAction(self, text="Mietzahlungen...") action.triggered.connect(self.onViewMietzahlungen) menu.addAction(action) # Menüpunkt "HG-Vorauszahlungen..." action = QAction(self, text="HG-Vorauszahlungen...") action.triggered.connect(self.onViewHGVorauszahlungen) menu.addAction(action) # Menüpunkt "Rechnungen, Abgaben, Gebühren..." action = QAction(self, text="Rechnungen, Abgaben, Gebühren...") action.triggered.connect(self.onViewRechnungen) menu.addAction(action) menu.addSeparator() # Menüpunkt "Offene Posten" action = QAction(self, text="Offene Posten") action.triggered.connect(self.onViewOffenePosten) menu.addAction(action) menu.addSeparator() # Menüpunkt "Soll-Miete..." action = QAction(self, text="Soll-Miete...") action.triggered.connect(self.onViewSollMiete) menu.addAction(action) # Menüpunkt "Soll-Hausgeld..." action = QAction(self, text="Soll-Hausgeld...") action.triggered.connect(self.onViewSollHausgeld) menu.addAction(action) menu.addSeparator() def _createAbrechnungenMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Abrechnungen") self._menubar.addMenu(menu) action = QAction(self, text="Nebenkostenabrechnung...") action.triggered.connect(self.onViewNebenkostenabrechnung) menu.addAction(action) action = QAction(self, text="Hausgeldabrechnung...") action.triggered.connect(self.onViewHausgeldabrechnung) menu.addAction(action) def _createAnlageVMenu(self): menu = QtWidgets.QMenu(self._menubar, title="AnlageV") self._menubar.addMenu(menu) action = QAction(self, text="Anlagen V: Objektauswahl, Vorschau, Druck...") action.triggered.connect(self.onViewAnlageV) menu.addAction(action) # action = QAction( self, text="Anlagen V drucken..." ) # action.triggered.connect( self.onViewHausgeldabrechnung ) # menu.addAction( action ) def _createExtrasMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Extras") self._menubar.addMenu(menu) action = QAction(self, text="Geschäftsreise...") action.triggered.connect(self.onGeschaeftsreise) menu.addAction(action) action = QAction(self, text="Notizen...") action.triggered.connect(self.onNotizen) menu.addAction(action) menu.addSeparator() action = QAction(self, text="Sammelabgabenbescheid aufsplitten...") action.triggered.connect(self.onSammelabgabe) menu.addAction(action) menu.addSeparator() action = QAction(self, text="Renditevergleich...") action.triggered.connect(self.onRenditeVergleich) menu.addAction(action) menu.addSeparator() action = QAction(self, text="Exportiere aktive Tabelle in Calc") action.triggered.connect(self.onExportActiveTableView) menu.addAction(action) def _createSqlMenu(self): # Menü "ImmoCenter" menu = QtWidgets.QMenu(self._menubar, title="SQL") self._menubar.addMenu(menu) # Menüpunkt "Neue Abfrage action = QAction(self, text="Neue Datenbankabfrage") action.setShortcut(QKeySequence("Ctrl+n")) icon = QtGui.QIcon("../images/sql.xpm") action.setIcon(icon) action.triggered.connect(self.onNewSql) menu.addAction(action) #self._toolBar.addAction( action ) #Menüpunkt "Ganze Tabelle anzeigen" self._submenuShowTableContent = QtWidgets.QMenu( menu, title="Ganze Tabelle anzeigen") menu.addMenu(self._submenuShowTableContent) def _createShowViewsMenu(self): menu = QtWidgets.QMenu(self._menubar, title="Views") self._menubar.addMenu(menu) self._viewsMenu = menu def _addSumFields(self): self._toolBar.addWidget(self._createSumLabel()) self._toolBar.addWidget(self._createSumArtLabel("Miete", 30)) self._toolBar.addWidget(self._idSumMiete) self._toolBar.addWidget(self._createLabel("-", 20)) self._toolBar.addWidget(self._createSumLabel()) self._toolBar.addWidget(self._createSumArtLabel("Ausgaben", 50)) self._toolBar.addWidget(self._idSummeSonstAus) self._toolBar.addWidget(self._createLabel("-", 20)) self._toolBar.addWidget(self._createSumLabel()) self._toolBar.addWidget(self._createSumArtLabel("HGV", 40)) self._toolBar.addWidget(self._idSummeHGV) self._toolBar.addWidget(self._createLabel("=", 20)) self._toolBar.addWidget(self._idSaldo) self._toolBar.addWidget(self._createSpacer(20)) def _createSumDisplay(self, tooltip: str) -> IntDisplay: display = IntDisplay(self) display.setMaximumWidth(70) display.setEnabled(False) display.setToolTip(tooltip) return display def _createSumLabel(self) -> QLabel: lbl = QLabel(self, text="∑") lbl.setFont(self._summenfont) lbl.setMaximumWidth(15) return lbl def _createSumArtLabel(self, sumart: str, width: int) -> QLabel: lbl = QLabel(self, text=sumart) lbl.setFont(self._summenartfont) lbl.setMaximumWidth(width) return lbl def _createLabel(self, text: str, width: int) -> QLabel: lbl = QLabel(self, text=text) lbl.setMinimumWidth(width) lbl.setMaximumWidth(width) lbl.setAlignment(Qt.AlignCenter) return lbl def _createSpacer(self, width: int) -> QWidget: spacer = QWidget(self) spacer.setMinimumWidth(width) spacer.setMaximumWidth(width) return spacer def canShutdown(self) -> bool: if self._shutdownCallback: return self._shutdownCallback() def setLetzteBuchung(self, datum: str, text: str) -> None: try: y, m, d = getDateParts(datum) self._sdLetzteBuchung.setDate(y, m, d) except: pass self._leLetzteBuchung.setText(text) def setTabellenAuswahl(self, tables: List[str]) -> None: """ Fügt dem SubMenu "Ganze Tabelle anzeigen" soviele Tabellen-Namen hinzu wie in <tables> enthalten. :param tables: :return: """ n = len(tables) actions = [QAction(self._submenuShowTableContent) for i in range(n)] for i in range(n): act = actions[i] act.setText(tables[i]) #act.triggered.connect( lambda action=act: self.onShowTableContent(action) ) --> funktioniert nicht act.triggered.connect(partial(self.onShowTableContent, act)) #txt = act.text() #act.triggered.connect( lambda table=txt: self.showTableContent.emit( txt ) ) --> funktioniert nicht self._submenuShowTableContent.addAction(act) def getLetzteBuchung(self) -> Dict: """ :return: dictionary with keys "date" and "text" """ d = dict() d["datum"] = self._sdLetzteBuchung.getDate() d["text"] = self._leLetzteBuchung.text() return d def setShutdownCallback(self, callbackFnc) -> None: self._shutdownCallback = callbackFnc def setActionCallback(self, callbackFnc) -> None: self._actionCallbackFnc = callbackFnc def addOpenedDialog(self, name: str, data: Any): """ Fügt dem Views-Menü eine gerade geöffnete View hinzu :param name: :param data: :return: """ action = QAction(self._viewsMenu, text=name) action.setData(data) self._viewsMenu.addAction(action) #action.triggered.connect( lambda name=name, data=data: self.bringDialogToFront.emit( name, data ) ) action.triggered.connect(partial(self.onShowDialog, action)) def getOpenedDialogs(self) -> List[IccDialog]: return [a.data() for a in self._viewsMenu.actions()] def removeClosedDialog(self, name: str, data: Any): """ Entfernt den Eintrag <name> aus dem Views-Menü. :param name: Name der zu entfernenden View (entspricht dem Text des Menüpunktes) :param data: Wird zur Identifikation der View verwendet. :return: """ for a in self._viewsMenu.actions(): if a.data() == data: self._viewsMenu.removeAction(a) break return def doCallback(self, action: MainWindowAction, arg=None): if self._actionCallbackFnc: if arg: self._actionCallbackFnc(action, arg) else: self._actionCallbackFnc(action) def onNewWindow(self): self.doCallback(MainWindowAction.NEW_WINDOW) def onMieterwechsel(self): self.doCallback(MainWindowAction.MIETERWECHSEL) def onVerwalterwechsel(self): pass # def onChangeSollmiete( self ): # pass # # def onChangeSollhausgeld( self ): # pass # def onSaveActiveView( self ): # self.doCallback( MainWindowAction.SAVE_ACTIVE_VIEW ) def onSaveAll(self): self.doCallback(MainWindowAction.SAVE_ALL) def onExportDatabaseToServer(self): self.doCallback(MainWindowAction.EXPORT_DB_TO_SERVER) def onImportDatabaseFromServer(self): self.doCallback(MainWindowAction.IMPORT_DB_FROM_SERVER) def onFolgejahrEinrichten(self): self.doCallback(MainWindowAction.FOLGEJAHR) def onPrintActiveView(self): self.doCallback(MainWindowAction.PRINT_ACTIVE_VIEW) def onExit(self): self.doCallback(MainWindowAction.EXIT) def onViewMietzahlungen(self): self.doCallback(MainWindowAction.OPEN_MIETE_VIEW) def onViewHGVorauszahlungen(self): self.doCallback(MainWindowAction.OPEN_HGV_VIEW) def onViewSollMiete(self): self.doCallback(MainWindowAction.OPEN_SOLL_MIETE_VIEW) def onViewSollHausgeld(self): self.doCallback(MainWindowAction.OPEN_SOLL_HG_VIEW) def onViewNebenkostenabrechnung(self): self.doCallback(MainWindowAction.OPEN_NKA_VIEW) def onViewHausgeldabrechnung(self): self.doCallback(MainWindowAction.OPEN_HGA_VIEW) def onViewAnlageV(self): self.doCallback(MainWindowAction.OPEN_ANLAGEV_VIEW) def onExportActiveTableView(self): self.doCallback(MainWindowAction.EXPORT_CSV) def onNotizen(self): self.doCallback(MainWindowAction.NOTIZEN) def onSammelabgabe(self): self.doCallback(MainWindowAction.SAMMELABGABE_DETAIL) def onGeschaeftsreise(self): self.doCallback(MainWindowAction.OPEN_GESCHAEFTSREISE_VIEW) def onRenditeVergleich(self): self.doCallback(MainWindowAction.RENDITE_VIEW) def onViewRechnungen(self): self.doCallback(MainWindowAction.OPEN_SONST_EIN_AUS_VIEW) def onViewOffenePosten(self): self.doCallback(MainWindowAction.OPEN_OFFENE_POSTEN_VIEW) def onViewObjektStammdaten(self): self.doCallback(MainWindowAction.OPEN_OBJEKT_STAMMDATEN_VIEW) def onViewMietverhaeltnis(self): self.doCallback(MainWindowAction.OPEN_MIETVERH_VIEW) def onNewSql(self): pass def onShowTableContent(self, action): self.doCallback(MainWindowAction.SHOW_TABLE_CONTENT, action.text()) def onShowDialog(self, action): self.doCallback(MainWindowAction.BRING_DIALOG_TO_FRONT, action.data()) def showException(self, exception: str, moretext: str = None): print(exception) msg = QtWidgets.QMessageBox() msg.setIcon(QMessageBox.Critical) msg.setText(exception) if moretext: msg.setInformativeText(moretext) msg.setWindowTitle("Error") msg.exec_() def showInfo(self, title, msg): box = InfoBox(title, msg, "", "OK") box.exec_()
def __init__(self, enigma_api, cursor_handler): """Initializes Root QT window widgets :param enigma_api: {EnigmaAPI} Shared EnigmaAPI object """ super().__init__() # QT WINDOW SETTINGS ================================================== self.setWindowIcon(QIcon(BASE_DIR + "enigma_200px.png")) main_layout = QVBoxLayout(self) self.setLayout(main_layout) # SAVE ATTRIBUTES ===================================================== self.__enigma_api = enigma_api logging.info("Qt GUI initialized with EnigmaAPI settings:\n%s", str(enigma_api)) # MENU BAR ============================================================ menu = QMenuBar(self) save_load_menu = menu.addMenu("Save/load settings") save_load_menu.addAction("Save settings", self.save_config) save_load_menu.addAction("Load settings", self.load_config) menu.addAction("Export message", self.export_message) # ROTORS INDICATOR ==================================================== logging.info("Generating rotor and reflector indicators...") self.__rotors = _RotorsHandlerWidget( self, self.__enigma_api.positions, self.__enigma_api.generate_rotate_callback, self.__enigma_api.rotate_reflector, enigma_api, self.refresh_gui, enigma_api.reflector_position, ) # LIGHTBOARD FRAME ==================================================== logging.info("Adding Lightboard...") self.__lightboard = _LightboardWidget(self, self.__enigma_api.charset) # INPUT OUTPUT FOR ENCRYPTION/DECRYPTION ============================== logging.info("Adding I/O textboxes...") self.__output_textbox = _OutputTextBoxWidget( self, self.__lightboard.light_up, enigma_api.letter_group) self.__input_textbox = _InputTextBoxWidget( self, enigma_api.encrypt, self.__output_textbox.insert, self.__output_textbox, self.__rotors.set_positions, enigma_api.letter_group, enigma_api.revert_by, enigma_api.buffer_full, enigma_api.data()["charset"], cursor_handler) # PLUGBOARD BUTTONS =================================================== logging.info("Adding Plugboard button") self.__plug_button = QPushButton("Plugboard") self.__plug_button.setToolTip("Edit plugboard letter pairs") self.__plug_button.clicked.connect(self.__open_plugboard_window) # SHOW WIDGETS ======================================================== logging.info("Showing all widgets...") main_layout.addWidget(menu, alignment=Qt.AlignTop) main_layout.addWidget(self.__rotors, alignment=Qt.AlignBottom) main_layout.addWidget(self.__lightboard) main_layout.addWidget( QLabel("INPUT", self, styleSheet="font-size: 20px"), alignment=Qt.AlignCenter, ) main_layout.addWidget(self.__input_textbox) main_layout.addWidget( QLabel("OUTPUT", self, styleSheet="font-size: 20px"), alignment=Qt.AlignCenter, ) main_layout.addWidget(self.__output_textbox) main_layout.addWidget(self.__plug_button) self.refresh_gui() self.show()
class MyWidget(QWidget): MAX_SHOWN_LINES = 10 PROCESS_NUMBER = 2 STOP_THREADS = False # Files APP_DIR = 'app' HTML = '.html' XML = '.xml' TXT = '.txt' EXTENSIONS = [TXT, XML, HTML] def __init__(self): QWidget.__init__(self) # Configuration from CLI interface self.file_names = [] self.disable_logging = False self.result_filter = None self.filters = [] self.configuration = [] # Internal subprocess attributes # Shared queues, first for steps and second for processes self.queues = None self.worker = None self.processes = [] self.thread = None self.show_steps_thread = None self.web_views = [] # Create Menu bar self.setWindowTitle("Sec&Po testing framework") self.main_menu = QMenuBar() self.file_menu = self.main_menu.addMenu("File") self.configure_menu = self.main_menu.addMenu("Configure") # Set actions file_actions = self.create_file_actions() for action in file_actions: self.file_menu.addAction(action) manage_actions = self.create_management_actions() for action in manage_actions: self.configure_menu.addAction(action) # Create main window self.selected_files = QLabel( "Here will be printed some of the selected files") self.finish_button = QPushButton("&Run security and" " portability testing") # Connecting the signal self.finish_button.clicked.connect(self.run_testing) self.results_label = QLabel("No results") self.show_results_button = QPushButton("&Show results") self.show_results_button.clicked.connect(self.load_html_result) # Create file dialog self.file_dialog = QFileDialog(self) # fixme: debug for pycharm # self.file_dialog.setOption(QFileDialog.DontUseNativeDialog, True) self.file_dialog.setFileMode(QFileDialog.ExistingFiles) file_filters = ["All files files (*)"] for value in ProgramTypes: file_filters.append(value.name + " files (") for program_filters in next(iter(value.value.values())): file_filters[len(file_filters) - 1] += "*" + program_filters\ + " " file_filters[len(file_filters) - 1] += ")" self.file_dialog.setNameFilters(file_filters) self.file_dialog.setViewMode(QFileDialog.Detail) # Add proxy model to change default behaviour or file selector # todo: does not work with files only directories. # However selecting of both must be overridden with own QFileDialog # proxy = ProxyModel(self.file_dialog) # self.file_dialog.setProxyModel(proxy) # Set layout of elements self._set_layout() def _set_layout(self): self.layout = QFormLayout() self.layout.setFormAlignment(Qt.AlignHCenter | Qt.AlignTop) self.layout.setFieldGrowthPolicy(QFormLayout.FieldsStayAtSizeHint) self.layout.setVerticalSpacing(150) self.layout.setHorizontalSpacing(220) self.layout.setLabelAlignment(Qt.AlignRight) self.layout.addRow(self.main_menu) self.finish_button.setFixedSize(250, 65) self.layout.addRow(self.selected_files, self.finish_button) self.show_results_button.setFixedSize(250, 65) self.layout.addRow(self.results_label, self.show_results_button) self.setLayout(self.layout) def create_file_actions(self): openAct = QAction(QIcon("images/folder.png"), self.tr("&Open..."), self) openAct.setShortcuts(QKeySequence.Open) openAct.setStatusTip( self.tr("Open an existing file, " "files or directories")) openAct.triggered.connect(self.open) addFilter = QAction(QIcon("images/filter.png"), self.tr("&Add filter"), self) # addFilter.setShortcuts() addFilter.setStatusTip( self.tr("Select filter files to apply " " result filtering")) addFilter.triggered.connect(self.add_filter) return [openAct, addFilter] def create_management_actions(self): manageAct = QAction(QIcon("images/settings.png"), self.tr("&Manage filters"), self) # manageAct.setShortcuts(QKeySequence.Open) manageAct.setStatusTip(self.tr("Manage added filters")) manageAct.triggered.connect(self.manage_filters) docker_conf_action = QAction(QIcon("images/docker.png"), self.tr("&Docker"), self) # docker_conf_action.setShortcuts() docker_conf_action.setStatusTip(self.tr("Docker")) docker_conf_action.triggered.connect(self.docker_conf) apparmor_conf_action = QAction(QIcon("images/apparmor.png"), self.tr("&Apparmor"), self) # apparmor_conf_action.setShortcuts() apparmor_conf_action.setStatusTip(self.tr("Apparmor")) apparmor_conf_action.triggered.connect(self.apparmor_conf) seccomp_conf_action = QAction(QIcon("images/lock.png"), self.tr("&Seccomp"), self) # docker_conf_action.setShortcuts() seccomp_conf_action.setStatusTip(self.tr("Seccomp")) seccomp_conf_action.triggered.connect(self.seccomp_conf) return [ manageAct, docker_conf_action, apparmor_conf_action, seccomp_conf_action ] def add_filter(self): pass def manage_filters(self): output = subprocess.check_output(['secpo', '--list-filters']) dialog = QDialog(self) label = QLabel("Managed filters are: {}".format( output.decode('utf-8'))) # dialog.set widgets = QWidget() layout = QHBoxLayout() layout.addWidget(dialog) layout.addWidget(label) widgets.setLayout(layout) widgets.show() dialog.exec() pass def docker_conf(self): pass def apparmor_conf(self): pass def seccomp_conf(self): pass def check_result_dir(self, file_name): result_dir = file_name.parent / self.APP_DIR if result_dir.exists(): self.results_label.setText("Results present") def open(self): output = 'No files chosen!' self.file_names = [] cnt = 0 # fixme: running code from pycharm causes to not use # regular path system but symlinks # https://stackoverflow.com/questions/57646908/pyqt5-qfiledialog-is-not-returning-correct-paths-in-ubuntu if self.file_dialog.exec(): self.file_names = self.file_dialog.selectedFiles() if self.file_names: output = '' for file_name in self.file_names: if cnt < self.MAX_SHOWN_LINES and file_name: # Correct the path absolute_path = pathlib.Path(file_name) self.check_result_dir(absolute_path) output += str(pathlib.Path(absolute_path.parts [len(absolute_path.parts) - 2])\ / pathlib.Path(absolute_path.name)) + '\n' cnt += 1 self.selected_files.setText(output + "\n") def open_single_result_view(self, result_file): if isinstance(self.web_views[-1], QWebEngineView): self.web_views[-1].load(QUrl(result_file.as_uri())) self.web_views[-1].showMaximized() elif isinstance(self.web_views[-1], QTextBrowser): text_doc = QTextDocument() self.web_views[-1].setDocument(text_doc) self.web_views[-1].setSource( QUrl(result_file.as_uri(), QUrl.ParsingMode.TolerantMode)) self.web_views[-1].show() def load_html_result(self): for file in self.file_names: root_dir = pathlib.Path(file).parent root_dir /= self.APP_DIR if root_dir.exists(): for extension in self.EXTENSIONS: result_files = root_dir.glob('**/*' + extension) for result_file in result_files: if extension == self.HTML or extension == self.XML: self.web_views.append(QWebEngineView()) elif extension == self.TXT: self.web_views.append(QTextBrowser()) self.open_single_result_view(result_file) def show_steps(self, progress): while True: time.sleep(0.5) if not self.queues[0].empty(): steps = self.queues[0].get() progress.setValue(steps.value) if steps.value == len(self.file_names) * 2: self.thread.quit() self.check_result_dir(pathlib.Path(self.file_names[0])) break if self.STOP_THREADS: break def remove_processes(self): self.processes = [] def cancel(self): for process in self.processes: self.queues[1].put(process.pid) time.sleep(5) # Set stop threads to true to indicate end of program self.STOP_THREADS = True if self.thread: self.thread.quit() self.processes = [] def run_testing(self): if not self.file_names: self.selected_files.setText( "No files were selected. Go to menu and " "select files to test.") return # Create new queues every run self.queues = [ multiprocessing.Queue(maxsize=self.PROCESS_NUMBER), multiprocessing.Queue(maxsize=self.PROCESS_NUMBER) ] self.STOP_THREADS = False progress = QProgressDialog("Starting Docker and VMs", "Abort start", 0, len(self.file_names) * 2, self) progress.canceled.connect(self.cancel) progress.setWindowModality(Qt.WindowModal) # Create Qthread to show progress self.thread = QThread() self.worker = Worker(self, self.file_names, self.queues) self.worker.moveToThread(self.thread) # Custom signals connected to own functions for progress dialog self.show_steps_thread = threading.Thread(target=self.show_steps, args=(progress, )) self.show_steps_thread.start() # Thread start and stop signals connected with slots self.thread.started.connect(self.worker.run) self.thread.finished.connect(self.remove_processes) # Start thread and force to show progress dialog self.thread.start() progress.forceShow() def closeEvent(self, event: PySide2.QtGui.QCloseEvent): event.ignore() super(MyWidget, self).closeEvent(event) # Indicate end of program if self.thread: self.thread.quit() self.thread.wait() for process in self.processes: if process: if os.name == 'nt': process.send_signal(signal.CTRL_C_EVENT) else: process.send_signal(signal.SIGINT)
class MainWindow(QMainWindow): def __init__(self, application, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.printer_thread = WorkerThread() self.printer_thread.message.connect(self.standardOutputWritten) self.printer_thread.start() self.app = application self.app.setStyle("Fusion") self.__set_interface() self.__set_layouts() self.__set_stylesheet() self.__set_connections() self.__set_params() def closeEvent(self, event): self.printer_thread.stop() def __set_interface(self): self.button_width = 0.35 self.button_height = 0.05 self.setWindowTitle("GSI-RADS") self.__getScreenDimensions() self.setGeometry(self.left, self.top, self.width, self.height) self.setMaximumWidth(self.width) #self.setMaximumHeight(self.height) self.setMinimumWidth(self.width) self.setMinimumHeight(self.height) self.move(self.width / 2, self.height / 2) self.menu_bar = QMenuBar(self) self.menu_bar.setNativeMenuBar( False ) # https://stackoverflow.com/questions/25261760/menubar-not-showing-for-simple-qmainwindow-code-qt-creator-mac-os self.file_menu = self.menu_bar.addMenu('File') self.import_dicom_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/database-icon.png')), 'Import DICOM', self) self.import_dicom_action.setShortcut('Ctrl+D') self.file_menu.addAction(self.import_dicom_action) self.quit_action = QAction('Quit', self) self.quit_action.setShortcut("Ctrl+Q") self.file_menu.addAction(self.quit_action) self.settings_menu = self.menu_bar.addMenu('Settings') self.settings_seg_menu = self.settings_menu.addMenu("Segmentation...") self.settings_seg_preproc_menu = self.settings_seg_menu.addMenu( "Preprocessing...") self.settings_seg_preproc_menu_p1_action = QAction( "Brain-masking off (P1)", checkable=True) self.settings_seg_preproc_menu_p2_action = QAction( "Brain-masking on (P2)", checkable=True) self.settings_seg_preproc_menu_p2_action.setChecked(True) self.settings_seg_preproc_menu.addAction( self.settings_seg_preproc_menu_p1_action) self.settings_seg_preproc_menu.addAction( self.settings_seg_preproc_menu_p2_action) self.help_menu = self.menu_bar.addMenu('Help') self.readme_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/readme-icon.jpeg')), 'Tutorial', self) self.readme_action.setShortcut("Ctrl+R") self.help_menu.addAction(self.readme_action) self.about_action = QAction( QIcon( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/about-icon.png')), 'About', self) self.about_action.setShortcut("Ctrl+A") self.help_menu.addAction(self.about_action) self.help_action = QAction( QIcon.fromTheme("help-faq"), "Help", self ) # Default icons can be found here: https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html#guidelines self.help_action.setShortcut("Ctrl+J") self.help_menu.addAction(self.help_action) self.input_image_lineedit = QLineEdit() self.input_image_lineedit.setFixedWidth(self.width * (0.93 - self.button_width / 2)) self.input_image_lineedit.setFixedHeight(self.height * self.button_height) self.input_image_lineedit.setReadOnly(True) self.input_image_pushbutton = QPushButton('Input MRI') self.input_image_pushbutton.setFixedWidth(self.height * self.button_width) self.input_image_pushbutton.setFixedHeight(self.height * self.button_height) self.input_segmentation_lineedit = QLineEdit() self.input_segmentation_lineedit.setReadOnly(True) self.input_segmentation_lineedit.setFixedWidth( self.width * (0.93 - self.button_width / 2)) self.input_segmentation_lineedit.setFixedHeight(self.height * self.button_height) self.input_segmentation_pushbutton = QPushButton('Input segmentation') self.input_segmentation_pushbutton.setFixedWidth(self.height * self.button_width) self.input_segmentation_pushbutton.setFixedHeight(self.height * self.button_height) self.output_folder_lineedit = QLineEdit() self.output_folder_lineedit.setReadOnly(True) self.output_folder_lineedit.setFixedWidth( self.width * (0.93 - self.button_width / 2)) self.output_folder_lineedit.setFixedHeight(self.height * self.button_height) self.output_folder_pushbutton = QPushButton('Output destination') self.output_folder_pushbutton.setFixedWidth(self.height * self.button_width) self.output_folder_pushbutton.setFixedHeight(self.height * self.button_height) self.run_button = QPushButton('Run diagnosis') self.run_button.setFixedWidth(self.height * self.button_width) self.run_button.setFixedHeight(self.height * self.button_height) self.main_display_tabwidget = QTabWidget() self.tutorial_textedit = QPlainTextEdit() self.tutorial_textedit.setReadOnly(True) self.tutorial_textedit.setFixedWidth(self.width * 0.97) self.tutorial_textedit.setPlainText( "HOW TO USE THE SOFTWARE: \n" " 1) Click 'Input MRI...' to select from your file explorer the MRI scan to process (unique file).\n" " 1*) Alternatively, Click File > Import DICOM... if you wish to process an MRI scan as a DICOM sequence.\n" " 2) Click 'Output destination' to choose a directory where to save the results \n" " 3) (OPTIONAL) Click 'Input segmentation' to choose a tumor segmentation mask file, if nothing is provided the internal model with generate the segmentation automatically \n" " 4) Click 'Run diagnosis' to perform the analysis. The human-readable version will be displayed in the interface.\n" " \n" "NOTE: \n" "The output folder is populated automatically with the following: \n" " * The diagnosis results in human-readable text (report.txt) and Excel-ready format (report.csv).\n" " * The automatic segmentation masks of the brain and the tumor in the original patient space (input_brain_mask.nii.gz and input_tumor_mask.nii.gz).\n" " * The input volume and tumor segmentation mask in MNI space in the sub-directory named \'registration\'.\n" ) self.main_display_tabwidget.addTab(self.tutorial_textedit, 'Tutorial') self.prompt_lineedit = QPlainTextEdit() self.prompt_lineedit.setReadOnly(True) self.prompt_lineedit.setFixedWidth(self.width * 0.97) self.main_display_tabwidget.addTab(self.prompt_lineedit, 'Logging') self.results_textedit = QPlainTextEdit() self.results_textedit.setReadOnly(True) self.results_textedit.setFixedWidth(self.width * 0.97) self.main_display_tabwidget.addTab(self.results_textedit, 'Results') self.sintef_logo_label = QLabel() self.sintef_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/sintef-logo.png'))) self.sintef_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.sintef_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.sintef_logo_label.setScaledContents(True) self.stolavs_logo_label = QLabel() self.stolavs_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/stolavs-logo.png'))) self.stolavs_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.stolavs_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.stolavs_logo_label.setScaledContents(True) self.amsterdam_logo_label = QLabel() self.amsterdam_logo_label.setPixmap( QPixmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), 'images/amsterdam-logo.png'))) self.amsterdam_logo_label.setFixedWidth(0.95 * (self.width / 3)) self.amsterdam_logo_label.setFixedHeight( 1 * (self.height * self.button_height)) self.amsterdam_logo_label.setScaledContents(True) def __set_layouts(self): self.input_volume_hbox = QHBoxLayout() self.input_volume_hbox.addStretch(1) self.input_volume_hbox.addWidget(self.input_image_lineedit) self.input_volume_hbox.addWidget(self.input_image_pushbutton) self.input_volume_hbox.addStretch(1) self.input_seg_hbox = QHBoxLayout() self.input_seg_hbox.addStretch(1) self.input_seg_hbox.addWidget(self.input_segmentation_lineedit) self.input_seg_hbox.addWidget(self.input_segmentation_pushbutton) self.input_seg_hbox.addStretch(1) self.output_dir_hbox = QHBoxLayout() self.output_dir_hbox.addStretch(1) self.output_dir_hbox.addWidget(self.output_folder_lineedit) self.output_dir_hbox.addWidget(self.output_folder_pushbutton) self.output_dir_hbox.addStretch(1) self.run_action_hbox = QHBoxLayout() self.run_action_hbox.addStretch(1) self.run_action_hbox.addWidget(self.run_button) self.run_action_hbox.addStretch(1) self.dump_area_hbox = QHBoxLayout() self.dump_area_hbox.addStretch(1) self.dump_area_hbox.addWidget(self.main_display_tabwidget) self.dump_area_hbox.addStretch(1) self.logos_hbox = QHBoxLayout() self.logos_hbox.addStretch(1) self.logos_hbox.addWidget(self.sintef_logo_label) self.logos_hbox.addWidget(self.stolavs_logo_label) self.logos_hbox.addWidget(self.amsterdam_logo_label) self.logos_hbox.addStretch(1) self.main_vbox = QVBoxLayout() self.main_vbox.addWidget(self.menu_bar) #self.main_vbox.addStretch(1) self.main_vbox.addLayout(self.input_volume_hbox) self.main_vbox.addLayout(self.output_dir_hbox) self.main_vbox.addLayout(self.input_seg_hbox) self.main_vbox.addLayout(self.run_action_hbox) #self.main_vbox.addStretch(1) self.main_vbox.addLayout(self.dump_area_hbox) self.main_vbox.addLayout(self.logos_hbox) #self.main_vbox.addStretch(1) self.central_label = QLabel() self.central_label.setLayout(self.main_vbox) self.setCentralWidget(self.central_label) def __set_stylesheet(self): self.central_label.setStyleSheet( 'QLabel{background-color: qlineargradient(spread:pad, x1:0.5, y1:1, x2:0.5, y2:0, stop:0 rgba(207, 209, 207, 255), stop:1 rgba(230, 229, 230, 255));}' ) self.menu_bar.setStyleSheet(get_stylesheet('QMenuBar')) self.input_image_lineedit.setStyleSheet(get_stylesheet('QLineEdit')) self.input_image_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.input_segmentation_lineedit.setStyleSheet( get_stylesheet('QLineEdit')) self.input_segmentation_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.output_folder_lineedit.setStyleSheet(get_stylesheet('QLineEdit')) self.output_folder_pushbutton.setStyleSheet( get_stylesheet('QPushButton')) self.results_textedit.setStyleSheet(get_stylesheet('QTextEdit')) self.prompt_lineedit.setStyleSheet(get_stylesheet('QTextEdit')) self.run_button.setStyleSheet(get_stylesheet('QPushButton')) def __set_connections(self): self.run_button.clicked.connect(self.diagnose_main_wrapper) self.input_image_pushbutton.clicked.connect( self.run_select_input_image) self.input_segmentation_pushbutton.clicked.connect( self.run_select_input_segmentation) self.output_folder_pushbutton.clicked.connect( self.run_select_output_folder) self.readme_action.triggered.connect(self.readme_action_triggered) self.about_action.triggered.connect(self.about_action_triggered) self.quit_action.triggered.connect(self.quit_action_triggered) self.import_dicom_action.triggered.connect( self.import_dicom_action_triggered) self.help_action.triggered.connect(self.help_action_triggered) self.settings_seg_preproc_menu_p1_action.triggered.connect( self.settings_seg_preproc_menu_p1_action_triggered) self.settings_seg_preproc_menu_p2_action.triggered.connect( self.settings_seg_preproc_menu_p2_action_triggered) def __set_params(self): self.input_image_filepath = '' self.input_annotation_filepath = '' self.output_folderpath = '' def __getScreenDimensions(self): screen = self.app.primaryScreen() size = screen.size() self.left = size.width() / 2 self.top = size.height() / 2 self.width = 0.4 * size.width() self.height = 0.4 * size.height() def readme_action_triggered(self): popup = QMessageBox() popup.setWindowTitle('Tutorial') popup.setText( "HOW TO USE THE SOFTWARE: \n" " 1) Click 'Input MRI...' to select from your file explorer the MRI scan to process (unique file).\n" " 1*) Alternatively, Click File > Import DICOM... if you wish to process an MRI scan as a DICOM sequence.\n" " 2) Click 'Output destination' to choose a directory where to save the results \n" " 3) (OPTIONAL) Click 'Input segmentation' to choose a tumor segmentation mask file, if nothing is provided the internal model with generate the segmentation automatically \n" " 4) Click 'Run diagnosis' to perform the analysis. The human-readable version will be displayed in the interface.\n" " \n" "NOTE: \n" "The output folder is populated automatically with the following: \n" " * The diagnosis results in human-readable text (report.txt) and Excel-ready format (report.csv).\n" " * The automatic segmentation masks of the brain and the tumor in the original patient space (input_brain_mask.nii.gz and input_tumor_mask.nii.gz).\n" " * The input volume and tumor segmentation mask in MNI space in the sub-directory named \'registration\'.\n" ) popup.exec_() def about_action_triggered(self): popup = QMessageBox() popup.setWindowTitle('About') popup.setText( 'Software developed as part of a collaboration between: \n' ' * Departement of Health Research, SINTEF\n' ' * St. Olavs hospital, Trondheim University Hospital\n' ' * Amsterdam University Medical Center\n\n' 'Contact: David Bouget, Andre Pedersen\n\n' 'For questions about the software, please visit:\n' 'https://github.com/SINTEFMedtek/GSI-RADS\n' 'For questions about the methodological aspect, please refer to the original publication:\n' 'https://www.mdpi.com/2072-6694/13/12/2854/review_report') popup.exec_() def quit_action_triggered(self): self.printer_thread.stop() sys.exit() def diagnose_main_wrapper(self): self.run_diagnosis_thread = threading.Thread(target=self.run_diagnosis) self.run_diagnosis_thread.daemon = True # using daemon thread the thread is killed gracefully if program is abruptly closed self.run_diagnosis_thread.start() def run_diagnosis(self): if not os.path.exists(self.input_image_filepath) or not os.path.exists( self.output_folderpath): self.standardOutputWritten( 'Process could not be started - The 1st and 2nd above-fields must be filled in.\n' ) return self.run_button.setEnabled(False) self.prompt_lineedit.clear() self.main_display_tabwidget.setCurrentIndex(1) QApplication.processEvents( ) # to immidiently update GUI after button is clicked self.seg_preprocessing_scheme = 'P1' if self.settings_seg_preproc_menu_p1_action.isChecked( ) else 'P2' try: start_time = time.time() print('Initialize - Begin (Step 0/6)') from diagnosis.main import diagnose_main print('Initialize - End (Step 0/6)') print('Step runtime: {} seconds.'.format( np.round(time.time() - start_time, 3)) + "\n") diagnose_main( input_volume_filename=self.input_image_filepath, input_segmentation_filename=self.input_annotation_filepath, output_folder=self.output_folderpath, preprocessing_scheme=self.seg_preprocessing_scheme) except Exception as e: print('{}'.format(traceback.format_exc())) self.run_button.setEnabled(True) self.standardOutputWritten( 'Process could not be completed - Issue arose.\n') QApplication.processEvents() return self.run_button.setEnabled(True) results_filepath = os.path.join( ResourcesConfiguration.getInstance().output_folder, 'report.txt') self.results_textedit.setPlainText(open(results_filepath, 'r').read()) self.main_display_tabwidget.setCurrentIndex(2) def run_select_input_image(self): input_image_filedialog = QFileDialog() self.input_image_filepath = input_image_filedialog.getOpenFileName( self, 'Select input T1 MRI', '~', "Image files (*.nii *.nii.gz *.nrrd *.mha *.mhd)")[0] self.input_image_lineedit.setText(self.input_image_filepath) def run_select_input_segmentation(self): filedialog = QFileDialog() self.input_annotation_filepath = filedialog.getOpenFileName( self, 'Select input segmentation file', '~', "Image files (*.nii *.nii.gz)")[0] self.input_segmentation_lineedit.setText( self.input_annotation_filepath) def import_dicom_action_triggered(self): filedialog = QFileDialog() filedialog.setFileMode(QFileDialog.DirectoryOnly) self.input_image_filepath = filedialog.getExistingDirectory( self, 'Select DICOM folder', '~') self.input_image_lineedit.setText(self.input_image_filepath) def run_select_output_folder(self): filedialog = QFileDialog() filedialog.setFileMode(QFileDialog.DirectoryOnly) self.output_folderpath = filedialog.getExistingDirectory( self, 'Select output folder', '~') self.output_folder_lineedit.setText(self.output_folderpath) def standardOutputWritten(self, text): self.prompt_lineedit.moveCursor(QTextCursor.End) self.prompt_lineedit.insertPlainText(text) QApplication.processEvents() def help_action_triggered(self): # opens browser with specified url, directs user to Issues section of GitHub repo QDesktopServices.openUrl( QUrl("https://github.com/SINTEFMedtek/GSI-RADS/issues")) def settings_seg_preproc_menu_p1_action_triggered(self, status): if status: self.settings_seg_preproc_menu_p2_action.setChecked(False) else: self.settings_seg_preproc_menu_p2_action.setChecked(True) def settings_seg_preproc_menu_p2_action_triggered(self, status): if status: self.settings_seg_preproc_menu_p1_action.setChecked(False) else: self.settings_seg_preproc_menu_p1_action.setChecked(True)
def setUpMenus(self) -> None: menuBar = QMenuBar() fileMenu = menuBar.addMenu('File') importMenu = fileMenu.addMenu('Import') exportMenu = fileMenu.addMenu('Export') flowMenu = menuBar.addMenu('Flow') viewMenu = menuBar.addMenu('View') helpMenu = menuBar.addMenu('Help') aAppendEmpty = QAction('Add frame', fileMenu) aAppendEmpty.setStatusTip('Create an empty dataframe in the workbench') aQuit = QAction('Quit', fileMenu) aLoadCsv = OperationAction(CsvLoader, fileMenu, 'From csv', self.rect().center(), self.centralWidget().workbenchModel) self.aWriteCsv = OperationAction(CsvWriter, fileMenu, 'To csv', self.rect().center(), w=self.centralWidget().workbenchModel) aLoadPickle = OperationAction(PickleLoader, fileMenu, 'From pickle', self.rect().center(), self.centralWidget().workbenchModel) self.aWritePickle = OperationAction( PickleWriter, fileMenu, 'To pickle', self.mapToGlobal(self.rect().center()), w=self.centralWidget().workbenchModel) aCompareFrames = QAction('Compare dataframes', viewMenu) aLogDir = QAction('Open log directory', helpMenu) aClearLogs = QAction('Delete old logs', helpMenu) fileMenu.addActions([aAppendEmpty, aQuit]) exportMenu.addActions([self.aWriteCsv, self.aWritePickle]) importMenu.addActions([aLoadCsv, aLoadPickle]) self._aStartFlow = QAction('Execute', flowMenu) self._aResetFlow = QAction('Reset', flowMenu) aSaveFlow = QAction('Save', flowMenu) aLoadFlow = QAction('Load', flowMenu) flowMenu.addActions( [self._aStartFlow, self._aResetFlow, aSaveFlow, aLoadFlow]) viewMenu.addAction(aCompareFrames) helpMenu.addActions([aLogDir, aClearLogs]) self.setMenuBar(menuBar) # Tips aLoadCsv.setStatusTip('Load a csv file in the workbench') aLoadPickle.setStatusTip('Load a Pickle file in the workbench') self.aWriteCsv.setStatusTip('Write a dataframe to a csv file') self.aWritePickle.setStatusTip( 'Serializes a dataframe into a pickle file') aCompareFrames.setStatusTip('Open two dataframes side by side') self._aStartFlow.setStatusTip('Start flow-graph execution') self._aResetFlow.setStatusTip('Reset the node status in flow-graph') aLogDir.setStatusTip('Open the folder containing all logs') aClearLogs.setStatusTip('Delete older logs and keep the last 5') # Connect aAppendEmpty.triggered.connect( self.centralWidget().workbenchModel.appendEmptyRow) aQuit.triggered.connect(self.close) self._aStartFlow.triggered.connect( self.centralWidget().controller.executeFlow) self._aResetFlow.triggered.connect( self.centralWidget().controller.resetFlowStatus) aCompareFrames.triggered.connect(self.openComparePanel) aLogDir.triggered.connect(self.openLogDirectory) aClearLogs.triggered.connect(self.clearLogDir) aSaveFlow.triggered.connect(self.saveFlow) aLoadFlow.triggered.connect(self.readFlow) aLoadCsv.stateChanged.connect(self.operationStateChanged) aLoadPickle.stateChanged.connect(self.operationStateChanged) self.aWriteCsv.stateChanged.connect(self.operationStateChanged) self.aWritePickle.stateChanged.connect(self.operationStateChanged)
class MainWindow(QWidget): # noinspection PyUnresolvedReferences def __init__(self): super().__init__() self.setWindowTitle("Cobaya input generator for Cosmology") self.setStyleSheet("* {font-size:%s;}" % font_size) # Menu bar for defaults self.menubar = QMenuBar() defaults_menu = self.menubar.addMenu( '&Show defaults and bibliography for a component...') menu_actions = {} for kind in kinds: submenu = defaults_menu.addMenu(subfolders[kind]) components = get_available_internal_class_names(kind) menu_actions[kind] = {} for component in components: menu_actions[kind][component] = QAction(component, self) menu_actions[kind][component].setData((kind, component)) menu_actions[kind][component].triggered.connect( self.show_defaults) submenu.addAction(menu_actions[kind][component]) # Main layout self.menu_layout = QVBoxLayout() self.menu_layout.addWidget(self.menubar) self.setLayout(self.menu_layout) self.layout = QHBoxLayout() self.menu_layout.addLayout(self.layout) self.layout_left = QVBoxLayout() self.layout.addLayout(self.layout_left) self.layout_output = QVBoxLayout() self.layout.addLayout(self.layout_output) # LEFT: Options self.options = QWidget() self.layout_options = QVBoxLayout() self.options.setLayout(self.layout_options) self.options_scroll = QScrollArea() self.options_scroll.setWidget(self.options) self.options_scroll.setWidgetResizable(True) self.layout_left.addWidget(self.options_scroll) self.combos = dict() for group, fields in _combo_dict_text: group_box = QGroupBox(group) self.layout_options.addWidget(group_box) group_layout = QVBoxLayout(group_box) for a, desc in fields: self.combos[a] = QComboBox() # Combo box label only if not single element in group if len(fields) > 1: label = QLabel(desc) group_layout.addWidget(label) group_layout.addWidget(self.combos[a]) self.combos[a].addItems([ text(k, v) for k, v in getattr(input_database, a).items() ]) # PLANCK NAMES CHECKBOX TEMPORARILY DISABLED # if a == "theory": # # Add Planck-naming checkbox # self.planck_names = QCheckBox( # "Keep common parameter names " # "(useful for fast CLASS/CAMB switching)") # group_layout.addWidget(self.planck_names) # Connect to refreshers -- needs to be after adding all elements for field, combo in self.combos.items(): if field == "preset": combo.currentIndexChanged.connect(self.refresh_preset) else: combo.currentIndexChanged.connect(self.refresh) # self.planck_names.stateChanged.connect(self.refresh_keep_preset) # RIGHT: Output + buttons self.display_tabs = QTabWidget() self.display = {} for k in ["yaml", "python", "bibliography"]: self.display[k] = QTextEdit() self.display[k].setLineWrapMode(QTextEdit.NoWrap) self.display[k].setFontFamily("mono") self.display[k].setCursorWidth(0) self.display[k].setReadOnly(True) self.display_tabs.addTab(self.display[k], k) self.display["covmat"] = QWidget() covmat_tab_layout = QVBoxLayout() self.display["covmat"].setLayout(covmat_tab_layout) self.covmat_text = QLabel() self.covmat_text.setWordWrap(True) self.covmat_table = QTableWidget(0, 0) self.covmat_table.setEditTriggers( QAbstractItemView.NoEditTriggers) # ReadOnly! covmat_tab_layout.addWidget(self.covmat_text) covmat_tab_layout.addWidget(self.covmat_table) self.display_tabs.addTab(self.display["covmat"], "covariance matrix") self.layout_output.addWidget(self.display_tabs) # Buttons self.buttons = QHBoxLayout() self.save_button = QPushButton('Save as...', self) self.copy_button = QPushButton('Copy to clipboard', self) self.buttons.addWidget(self.save_button) self.buttons.addWidget(self.copy_button) self.save_button.released.connect(self.save_file) self.copy_button.released.connect(self.copy_clipb) self.layout_output.addLayout(self.buttons) self.save_dialog = QFileDialog() self.save_dialog.setFileMode(QFileDialog.AnyFile) self.save_dialog.setAcceptMode(QFileDialog.AcceptSave) self.read_settings() self.show() def read_settings(self): settings = get_settings() # noinspection PyArgumentList screen = QApplication.desktop().screenGeometry() h = min(screen.height() * 5 / 6., 900) size = QSize(min(screen.width() * 5 / 6., 1200), h) pos = settings.value("pos", None) savesize = settings.value("size", size) if savesize.width() > screen.width(): savesize.setWidth(size.width()) if savesize.height() > screen.height(): savesize.setHeight(size.height()) self.resize(savesize) if ((pos is None or pos.x() + savesize.width() > screen.width() or pos.y() + savesize.height() > screen.height())): self.move(screen.center() - self.rect().center()) else: self.move(pos) def write_settings(self): settings = get_settings() settings.setValue("pos", self.pos()) settings.setValue("size", self.size()) def closeEvent(self, event): self.write_settings() event.accept() def create_input(self): return create_input( get_comments=True, # planck_names=self.planck_names.isChecked(), **{ field: list(getattr(input_database, field))[combo.currentIndex()] for field, combo in self.combos.items() if field != "preset" }) @Slot() def refresh_keep_preset(self): self.refresh_display(self.create_input()) @Slot() def refresh(self): self.combos["preset"].blockSignals(True) self.combos["preset"].setCurrentIndex(0) self.combos["preset"].blockSignals(False) self.refresh_display(self.create_input()) @Slot() def refresh_preset(self): preset = list(getattr(input_database, "preset"))[self.combos["preset"].currentIndex()] if preset is input_database._none: return info = create_input( get_comments=True, # planck_names=self.planck_names.isChecked(), preset=preset) self.refresh_display(info) # Update combo boxes to reflect the preset values, without triggering update for k, v in input_database.preset[preset].items(): if k in [input_database._desc]: continue self.combos[k].blockSignals(True) self.combos[k].setCurrentIndex(self.combos[k].findText( text(v, getattr(input_database, k).get(v)))) self.combos[k].blockSignals(False) def refresh_display(self, info): QApplication.setOverrideCursor(Qt.WaitCursor) try: comments = info.pop(input_database._comment, None) comments_text = "\n# " + "\n# ".join(comments) except ( TypeError, # No comments AttributeError ): # Failed to generate info (returned str instead) comments_text = "" self.display["python"].setText("info = " + pformat(info) + comments_text) self.display["yaml"].setText( yaml_dump(sort_cosmetic(info)) + comments_text) self.display["bibliography"].setText( prettyprint_bib(*get_bib_info(info))) # Display covmat packages_path = resolve_packages_path() if not packages_path: self.covmat_text.setText( "\nIn order to find a covariance matrix, you need to define an external " "packages installation path, e.g. via the env variable %r.\n" % _packages_path_env) elif any(not os.path.isdir(d.format(**{_packages_path: packages_path})) for d in covmat_folders): self.covmat_text.setText( "\nThe external cosmological packages appear not to be installed where " "expected: %r\n" % packages_path) else: covmat_data = get_best_covmat(info, packages_path=packages_path) self.current_params_in_covmat = covmat_data["params"] self.current_covmat = covmat_data["covmat"] _, corrmat = cov_to_std_and_corr(self.current_covmat) self.covmat_text.setText( "\nCovariance file: %r\n\n" "NB: you do *not* need to save or copy this covariance matrix: " "it will be selected automatically.\n" % covmat_data["name"]) self.covmat_table.setRowCount(len(self.current_params_in_covmat)) self.covmat_table.setColumnCount(len( self.current_params_in_covmat)) self.covmat_table.setHorizontalHeaderLabels( list(self.current_params_in_covmat)) self.covmat_table.setVerticalHeaderLabels( list(self.current_params_in_covmat)) for i, pi in enumerate(self.current_params_in_covmat): for j, pj in enumerate(self.current_params_in_covmat): self.covmat_table.setItem( i, j, QTableWidgetItem("%g" % self.current_covmat[i, j])) if i != j: color = [ 256 * c for c in cmap_corr(corrmat[i, j] / 2 + 0.5)[:3] ] else: color = [255.99] * 3 self.covmat_table.item(i, j).setBackground(QColor(*color)) self.covmat_table.item(i, j).setForeground(QColor(0, 0, 0)) QApplication.restoreOverrideCursor() def save_covmat_txt(self, file_handle=None): """ Saved covmat to given file_handle. If none given, returns the text to be saved. """ return_txt = False if not file_handle: file_handle = io.BytesIO() return_txt = True np.savetxt(file_handle, self.current_covmat, header=" ".join(self.current_params_in_covmat)) if return_txt: return file_handle.getvalue().decode() @Slot() def save_file(self): ftype = next(k for k, w in self.display.items() if w is self.display_tabs.currentWidget()) ffilter = { "yaml": "Yaml files (*.yaml *.yml)", "python": "(*.py)", "bibliography": "(*.txt)", "covmat": "(*.covmat)" }[ftype] fsuffix = { "yaml": ".yaml", "python": ".py", "bibliography": ".txt", "covmat": ".covmat" }[ftype] fname, path = self.save_dialog.getSaveFileName(self.save_dialog, "Save input file", fsuffix, ffilter, os.getcwd()) if not fname.endswith(fsuffix): fname += fsuffix with open(fname, "w+", encoding="utf-8") as f: if self.display_tabs.currentWidget() == self.display["covmat"]: self.save_covmat_txt(f) else: f.write(self.display_tabs.currentWidget().toPlainText()) @Slot() def copy_clipb(self): if self.display_tabs.currentWidget() == self.display["covmat"]: self.clipboard.setText(self.save_covmat_txt()) else: self.clipboard.setText( self.display_tabs.currentWidget().toPlainText()) def show_defaults(self): kind, component = self.sender().data() self.current_defaults_diag = DefaultsDialog(kind, component, parent=self)
class MainWindow(QMainWindow): def __init__(self,*args,**kwargs): super(MainWindow,self).__init__(*args,**kwargs) self.ventanaHelp = HelpContentWindow() self.ventana = AddProductWindow(self) self.menuBar = QMenuBar(self) self.setMenuBar(self.menuBar) help_menu = self.menuBar.addMenu('&Ayuda') help_content = QAction('&Contenido',self) help_acerca = QAction('&Acerca de',self) help_menu.addAction(help_content) help_menu.addAction(help_acerca) help_content.triggered.connect(self.help_content_triggered) focus_in_signal = Signal() self.majorLayout = QHBoxLayout() self.layout = QVBoxLayout() self.table = QTableView() self.marco = QFrame() self.marco.setFrameStyle(QFrame.Box) self.marco.setLayout(self.layout) self.inputCommands = LineEdit() self.inputCommands.setText('Ingrese un comando') self.inputCommands.focus_in_signal.connect(self.focus_in_command) self.layout.addWidget(self.inputCommands) self.inputCommands.returnPressed.connect(self.command_agrega_producto) self.inputCommands.hide() self.input = QLineEdit() self.layout.addWidget(self.table) self.layout.addWidget(self.input) self.majorLayout.addWidget(self.marco) self.total = QLabel() self.total.setText('Total: 0.00') self.total.setStyleSheet("{padding:0 50; margin:0 100;}") self.majorLayout.addWidget(self.total) widget = QWidget() widget.setLayout(self.majorLayout) self.datos =[ ['ABCZ-1234', 'Paño esponja', 130.50], ['XYZC-2345', 'Escoba', 140.30], ] self.datosAnexos = [ ['ABCZ-1234', 'Paño esponja', 130.50], ['XYZC-2345', 'Escoba', 140.30], ['WXYZ-1234', 'Limpiador de pisos', 150.00], ['ABCD-1234', 'Bote de basura grande', 1000.00] ] self.model = TableModel(self.datos, ['SKU', 'Artículo', 'Precio']) self.table.setModel(self.model) self.table.setColumnWidth(1,315) self.setCentralWidget(widget) self.input.returnPressed.connect(self.add_datos) self.calculate_total() def help_content_triggered(self): self.ventanaHelp.display_help() def add_datos(self): texto = self.input.text() valor = self.method_in(self.datosAnexos, texto) if(valor is None): self.marco.setStyleSheet("QFrame{border: 1px solid red;}") self.input.setStyleSheet("border: 1px solid red;") self.inputCommands.setText('Ingrese un comando') self.inputCommands.show() else: self.marco.setStyleSheet("QFrame{border: 1px solid black;}") self.input.setStyleSheet("border: 1px solid black;") self.datos.append(valor) self.table.model().layoutChanged.emit() self.calculate_total() self.inputCommands.hide() def calculate_total(self): agregado = 0.0 for dato in self.datos: agregado += dato[2] self.total.setText('Total: ' + '{:.2f}'.format(agregado)) def method_in(self, data, text): for element in data: if element[0].casefold() == text.casefold(): return element return None def focus_in_command(self): self.inputCommands.setText('') def command_agrega_producto(self): valor = self.elige_comando(1) print(valor) if(valor == self.inputCommands.text()): self.ventana.display_info() else: print('Comando incorrecto') def comando_agregar(self): return 'A' def elige_comando(self, argument): return { 1: lambda: self.comando_agregar(), }.get(argument, lambda: 'Opción inválida')()