def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = TextEdit() # Setup the QTextEdit editor configuration self.editor.setAutoFormatting(QTextEdit.AutoAll) self.editor.selectionChanged.connect(self.update_format) # Initialize default font size. font = QFont('Times', 12) self.editor.setFont(font) # We need to repeat the size to init the current format. self.editor.setFontPointSize(12) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) # Uncomment to disable native menubar on Mac # self.menuBar().setNativeMenuBar(False) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.setShortcut(QKeySequence.Cut) cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") cut_action.setShortcut(QKeySequence.Copy) copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") cut_action.setShortcut(QKeySequence.Paste) paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") cut_action.setShortcut(QKeySequence.SelectAll) select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) format_toolbar = QToolBar("Format") format_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(format_toolbar) format_menu = self.menuBar().addMenu("&Format") # We need references to these actions/settings to update as selection changes, so attach to self. self.fonts = QFontComboBox() self.fonts.currentFontChanged.connect(self.editor.setCurrentFont) format_toolbar.addWidget(self.fonts) self.fontsize = QComboBox() self.fontsize.addItems([str(s) for s in FONT_SIZES]) # Connect to the signal producing the text of the current selection. Convert the string to float # and set as the pointsize. We could also use the index + retrieve from FONT_SIZES. self.fontsize.currentIndexChanged[str].connect( lambda s: self.editor.setFontPointSize(float(s))) format_toolbar.addWidget(self.fontsize) self.bold_action = QAction( QIcon(os.path.join('images', 'edit-bold.png')), "Bold", self) self.bold_action.setStatusTip("Bold") self.bold_action.setShortcut(QKeySequence.Bold) self.bold_action.setCheckable(True) self.bold_action.toggled.connect(lambda x: self.editor.setFontWeight( QFont.Bold if x else QFont.Normal)) format_toolbar.addAction(self.bold_action) format_menu.addAction(self.bold_action) self.italic_action = QAction( QIcon(os.path.join('images', 'edit-italic.png')), "Italic", self) self.italic_action.setStatusTip("Italic") self.italic_action.setShortcut(QKeySequence.Italic) self.italic_action.setCheckable(True) self.italic_action.toggled.connect(self.editor.setFontItalic) format_toolbar.addAction(self.italic_action) format_menu.addAction(self.italic_action) self.underline_action = QAction( QIcon(os.path.join('images', 'edit-underline.png')), "Underline", self) self.underline_action.setStatusTip("Underline") self.underline_action.setShortcut(QKeySequence.Underline) self.underline_action.setCheckable(True) self.underline_action.toggled.connect(self.editor.setFontUnderline) format_toolbar.addAction(self.underline_action) format_menu.addAction(self.underline_action) format_menu.addSeparator() self.alignl_action = QAction( QIcon(os.path.join('images', 'edit-alignment.png')), "Align left", self) self.alignl_action.setStatusTip("Align text left") self.alignl_action.setCheckable(True) self.alignl_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignLeft)) format_toolbar.addAction(self.alignl_action) format_menu.addAction(self.alignl_action) self.alignc_action = QAction( QIcon(os.path.join('images', 'edit-alignment-center.png')), "Align center", self) self.alignc_action.setStatusTip("Align text center") self.alignc_action.setCheckable(True) self.alignc_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignCenter)) format_toolbar.addAction(self.alignc_action) format_menu.addAction(self.alignc_action) self.alignr_action = QAction( QIcon(os.path.join('images', 'edit-alignment-right.png')), "Align right", self) self.alignr_action.setStatusTip("Align text right") self.alignr_action.setCheckable(True) self.alignr_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignRight)) format_toolbar.addAction(self.alignr_action) format_menu.addAction(self.alignr_action) self.alignj_action = QAction( QIcon(os.path.join('images', 'edit-alignment-justify.png')), "Justify", self) self.alignj_action.setStatusTip("Justify text") self.alignj_action.setCheckable(True) self.alignj_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignJustify)) format_toolbar.addAction(self.alignj_action) format_menu.addAction(self.alignj_action) format_group = QActionGroup(self) format_group.setExclusive(True) format_group.addAction(self.alignl_action) format_group.addAction(self.alignc_action) format_group.addAction(self.alignr_action) format_group.addAction(self.alignj_action) format_menu.addSeparator() # A list of all format-related widgets/actions, so we can disable/enable signals when updating. self._format_actions = [ self.fonts, self.fontsize, self.bold_action, self.italic_action, self.underline_action, # We don't need to disable signals for alignment, as they are paragraph-wide. ] # Initialize. self.update_format() self.update_title() self.show()
def initMenuBar(self): self.menu = self.menuBar() file_menu = self.menu.addMenu("File") file_menu.addAction(self.newGameAction) #file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement file_menu.addAction(self.saveGameAction) file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() #file_menu.addAction("Save As") # TODO : implement #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("Exit" , lambda: self.exit()) help_menu = self.menu.addMenu("Help") help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) help_menu.addAction("Discord", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) #help_menu.addAction("Troubleshooting Guide", lambda: webbrowser.open_new_tab(URLS["Troubleshooting"])) #help_menu.addAction("Modding Guide", lambda: webbrowser.open_new_tab(URLS["Modding"])) #help_menu.addSeparator() ----> Note from Khopa : I disable these links since it's not up to date for this branch #help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"])) help_menu.addAction("Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction) displayMenu = self.menu.addMenu("Display") tg_cp_visibility = QAction('Control Point', displayMenu) tg_cp_visibility.setCheckable(True) tg_cp_visibility.setChecked(True) tg_cp_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("cp", tg_cp_visibility.isChecked())) tg_go_visibility = QAction('Ground Objects', displayMenu) tg_go_visibility.setCheckable(True) tg_go_visibility.setChecked(True) tg_go_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("go", tg_go_visibility.isChecked())) tg_line_visibility = QAction('Lines', displayMenu) tg_line_visibility.setCheckable(True) tg_line_visibility.setChecked(True) tg_line_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule("lines", tg_line_visibility.isChecked())) tg_event_visibility = QAction('Events', displayMenu) tg_event_visibility.setCheckable(True) tg_event_visibility.setChecked(True) tg_event_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("events", tg_event_visibility.isChecked())) tg_sam_visibility = QAction('SAM Range', displayMenu) tg_sam_visibility.setCheckable(True) tg_sam_visibility.setChecked(True) tg_sam_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("sam", tg_sam_visibility.isChecked())) tg_flight_path_visibility = QAction('Flight Paths', displayMenu) tg_flight_path_visibility.setCheckable(True) tg_flight_path_visibility.setChecked(False) tg_flight_path_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("flight_paths", tg_flight_path_visibility.isChecked())) displayMenu.addAction(tg_go_visibility) displayMenu.addAction(tg_cp_visibility) displayMenu.addAction(tg_line_visibility) displayMenu.addAction(tg_event_visibility) displayMenu.addAction(tg_sam_visibility) displayMenu.addAction(tg_flight_path_visibility)
def initMenuBar(self): self.menu = self.menuBar() file_menu = self.menu.addMenu("&File") file_menu.addAction(self.newGameAction) file_menu.addAction(self.openAction) file_menu.addSeparator() file_menu.addAction(self.saveGameAction) file_menu.addAction(self.saveAsAction) file_menu.addSeparator() file_menu.addAction(self.showLiberationPrefDialogAction) file_menu.addSeparator() #file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working file_menu.addAction("E&xit" , lambda: self.exit()) displayMenu = self.menu.addMenu("&Display") tg_cp_visibility = QAction('&Control Point', displayMenu) tg_cp_visibility.setCheckable(True) tg_cp_visibility.setChecked(True) tg_cp_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("cp", tg_cp_visibility.isChecked())) tg_go_visibility = QAction('&Ground Objects', displayMenu) tg_go_visibility.setCheckable(True) tg_go_visibility.setChecked(True) tg_go_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("go", tg_go_visibility.isChecked())) tg_line_visibility = QAction('&Lines', displayMenu) tg_line_visibility.setCheckable(True) tg_line_visibility.setChecked(True) tg_line_visibility.toggled.connect( lambda: QLiberationMap.set_display_rule("lines", tg_line_visibility.isChecked())) tg_event_visibility = QAction('&Events', displayMenu) tg_event_visibility.setCheckable(True) tg_event_visibility.setChecked(True) tg_event_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("events", tg_event_visibility.isChecked())) tg_sam_visibility = QAction('&SAM Range', displayMenu) tg_sam_visibility.setCheckable(True) tg_sam_visibility.setChecked(True) tg_sam_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("sam", tg_sam_visibility.isChecked())) tg_flight_path_visibility = QAction('&Flight Paths', displayMenu) tg_flight_path_visibility.setCheckable(True) tg_flight_path_visibility.setChecked(False) tg_flight_path_visibility.toggled.connect(lambda: QLiberationMap.set_display_rule("flight_paths", tg_flight_path_visibility.isChecked())) displayMenu.addAction(tg_go_visibility) displayMenu.addAction(tg_cp_visibility) displayMenu.addAction(tg_line_visibility) displayMenu.addAction(tg_event_visibility) displayMenu.addAction(tg_sam_visibility) displayMenu.addAction(tg_flight_path_visibility) help_menu = self.menu.addMenu("&Help") help_menu.addAction("&Discord Server", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ")) help_menu.addAction("&Github Repository", lambda: webbrowser.open_new_tab("https://github.com/khopa/dcs_liberation")) help_menu.addAction("&Releases", lambda: webbrowser.open_new_tab("https://github.com/Khopa/dcs_liberation/releases")) help_menu.addAction("&Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"])) help_menu.addAction("&ED Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"])) help_menu.addAction("Report an &issue", lambda: webbrowser.open_new_tab(URLS["Issues"])) help_menu.addSeparator() help_menu.addAction(self.showAboutDialogAction)
class MainWindow(QMainWindow): def __init__(self, tedaCommandLine): super().__init__() self.tedaCommandLine = tedaCommandLine self.cmaps = ColorMaps() self.combobox = QComboBox() self.filename = None self.isMousePressed = False self.isCmdPressed = False self.cursor_coords = CoordinatesModel() self.scales_model = ScalesModel() fig = Figure(figsize=(14, 10)) fig.tight_layout() self.fits_image = FitsPlotter(figure=fig) fig.subplots_adjust(left=0, bottom=0.001, right=1, top=1, wspace=None, hspace=None) self.fits_image = FitsPlotterFitsFile(figure=fig, cmap_model=self.cmaps, scale_model=self.scales_model) self.central_widget = FigureCanvas(fig) self.setCentralWidget(self.central_widget) self.current_x_coord = 0 self.current_y_coord = 0 self.fullWidgetXcord = 0 self.fullWidgetYcord = 0 self.centralWidgetcordX = 0 self.centralWidgetcordY = 0 self.painterComponent = PainterComponent(self.fits_image) # self.painterComponent.startMovingEvents(self.central_widget) self.painterComponent.setCanvas(self.central_widget) self.scanObject = ScanToolbar(self) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.createDockWindows() if not self.tedaCommandLine.ignoreSettings: self.scaleWidget.readSlidersValues() # self.defineButtonsActions() self.setWindowTitle("TeDa") self.painterComponent.observe( lambda change: self.onAutoCenterChange(change), ['auto_center']) self.readWindowSettings() self.readAppState() self.updateHeaderData() self.dragging = draggingComponent.Dragging( widget=self, scale_widget=self.scaleWidget) self.activeLinearAdjustmentByMouseMovement() # Observing here may be to late for values loaded from settings e.g. via readAppState self.painterComponent.observe( lambda change: self.onCenterCircleChange(change), ['ccenter_x', 'ccenter_y']) self.painterComponent.observe( lambda change: self.onCenterCircleRadiusChange(change), ['cradius']) self.fits_image.observe(lambda change: self.onMouseMoveOnImage(change), ['mouse_xdata', 'mouse_ydata']) # self.cmaps.observe(lambda change: self.on_colormap_change(change)) self.full_view_widget.painterComponent.observe( lambda change: self.onRectangleInWidgetMove(change), ['viewX', 'viewY']) self.painterComponent.observe( lambda change: self.movingCentralWidget(change), ['movingViewX', 'movingViewY']) self.fits_image.observe(lambda change: self.onMouseZoomOnImage(change), ['viewBounaries_versionno']) # open last fits try: self.openLastFits() except FileNotFoundError: print('Błąd w odczycie lub brak ostatio wczytanego pliku') def closeEvent(self, event: PySide2.QtGui.QCloseEvent): self.writeAppState() self.writeWindowSettings() if not self.tedaCommandLine.ignoreSettings: self.scaleWidget.writeSlidersValues() super().closeEvent(event) def keyPressEvent(self, e): if e.key() == Qt.Key_Delete: self.deleteSelected() if e.key() == Qt.Key_R: action = self.dockRadialFit.toggleViewAction() if not action.isChecked(): action.trigger() if (self.cursor_coords.img_x != 0 and self.cursor_coords.img_x != None) and (self.cursor_coords.img_y != 0 and self.cursor_coords.img_y != None): self.painterComponent.add(self.cursor_coords.img_x, self.cursor_coords.img_y, type="circleCenter") self.painterComponent.paintAllShapes( self.central_widget.figure.axes[0]) if e.key() == Qt.Key_Control: self.isCmdPressed = True def keyReleaseEvent(self, event: PySide2.QtGui.QKeyEvent): if event.key() == Qt.Key_Control: self.isCmdPressed = False def canvasMousePressEvent(self, event): self.isMousePressed = not self.isMousePressed def mouseMoveEventOnCanvas(self, event): if self.isCmdPressed: if self.isMousePressed: self.dragging.mouseMoveEvent(event) def print_(self): document = self.textEdit.document() printer = QPrinter() dlg = QPrintDialog(printer, self) if dlg.exec_() != QDialog.Accepted: return document.print_(printer) self.statusBar().showMessage("Ready", 2000) def open_dialog(self): fileName, _ = QFileDialog.getOpenFileName(self, "Open Image", ".", "Fits files (*.fits)") if fileName: self.open_fits(fileName) def save(self): filename, _ = QFileDialog.getSaveFileName(self, "Choose a file name", '.', "HTML (*.html *.htm)") if not filename: return file = QFile(filename) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning( self, "Dock Widgets", "Cannot write file %s:\n%s." % (filename, file.errorString())) return out = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) out << self.textEdit.toHtml() QApplication.restoreOverrideCursor() self.statusBar().showMessage("Saved '%s'" % filename, 2000) def save_dialog(self): figure = self.central_widget.figure filetypes = figure.canvas.get_supported_filetypes_grouped() filterstr = ';;'.join([ k + ' (' + ' '.join(['*.' + ext for ext in v]) + ')' for k, v in filetypes.items() ]) dialog = QFileDialog.getSaveFileName( self, "Save Image As...", os.path.splitext(self.filename)[0], filterstr) if dialog[0] != "": try: self.central_widget.figure.savefig(dialog[0]) except ValueError: print("Unsupported format") def open_fits(self, fileName): """Opens specified FITS file and loads it to user interface""" self.fits_image.set_file(fileName) self.filename = fileName self.cursor_coords.set_wcs_from_fits( self.fits_image.header ) # TODO: one up and extract and set wcs in fits_image before plot self.fits_image.set_wcs(self.cursor_coords.wcs) self.fits_image.plot() self.radial_profile_widget.set_data(self.fits_image.data) self.radial_profile_iraf_widget.set_data(self.fits_image.data) self.updateHeaderData() self.zoom_view_widget.updateFits(self.fits_image) self.full_view_widget.updateFits(self.fits_image) self.saveLastFits() def saveLastFits(self): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("Files") settings.setValue("lastFile", self.filename) settings.endGroup() def openLastFits(self): if (self.tedaCommandLine.openFile is None): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("Files") filename = settings.value("lastFile") settings.endGroup() else: filename = self.tedaCommandLine.openFile if filename: self.open_fits(filename) def readAppState(self): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("WCS") self.wcsSexAct.setChecked(bool(settings.value("sexagesimal", True))) self.wcsGridAct.setChecked(bool(settings.value("grid", False))) settings.endGroup() settings.beginGroup("paint") self.painterComponent.auto_center = bool( settings.value("auto_center", True)) settings.endGroup() def writeAppState(self): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("WCS") settings.setValue("sexagesimal", self.wcsSexAct.isChecked()) settings.setValue("grid", self.wcsGridAct.isChecked()) settings.endGroup() settings.beginGroup("paint") settings.setValue("auto_center", self.painterComponent.auto_center) settings.endGroup() def undo(self): document = self.textEdit.document() document.undo() def insertCustomer(self, customer): if not customer: return def addParagraph(self, paragraph): if not paragraph: return def about(self): QMessageBox.about( self, "TeDa FITS Viewer", f"TeDa FITS Viewer {__version__} <br/>" "Authors: <ul> " "<li>Michał Brodniak</li>" "<li>Konrad Górski</li>" "<li>Mikołaj Kałuszyński</li>" "<li>Edward Lis</li>" "<li>Grzegorz Mroczkowski</li>" "</ul>" "Created by <a href='https://akond.com'>Akond Lab</a> for The " "<a href='https://araucaria.camk.edu.pl'>Araucaria Project</a><br/>" "Licence: MIT <br/>" "3rd party work used: " "<a href='https://material.io/resources/icons/'> Google Material Icons</a>, " "<a href='https://www.astropy.org'> AstroPy</a>, " "<a href='https://doc.qt.io/qtforpython/'> Qt5/PySide2</a>, " "<a href='https://www.scipy.org'> SciPy</a>, and other..." "<br/><br/>" "Visit the <a href='https://github.com/majkelx/teda'>project's GitHub page</a> for help" " and the issue tracker") def on_console_show(self): console.show(ax=self.fits_image.ax, window=self, data=self.fits_image.data, header=self.fits_image.header, wcs=self.cursor_coords.wcs) def on_sex_toggle(self): print('sex toggled to :', self.wcsSexAct.isChecked()) self.cursor_coords.wcs_sexagesimal = self.wcsSexAct.isChecked() def on_grid_toggle(self): self.fits_image.plot_grid = self.wcsGridAct.isChecked() def createActions(self): # ico1 = QPixmap('/Users/mka/projects/astro/teda/icons/png.png') # self.openAct = QAction(ico1, "&Open", self, shortcut=QKeySequence.Open, statusTip="Open FITS file", triggered=self.open) self.openAct = QAction(IconFactory.getIcon('note_add'), "&Open", self, shortcut=QKeySequence.Open, statusTip="Open FITS file", triggered=self.open_dialog) self.saveAct = QAction(IconFactory.getIcon('save'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save FITS view", triggered=self.save_dialog) self.quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Quit the application", triggered=self.close) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) self.qtConsoleAct = QAction('Python Console', self, statusTip="Open IPython console window", triggered=self.on_console_show) self.wcsSexAct = QAction( 'Sexagesimal', self, statusTip= "Format WCS coordinates as sexagesimal (RA in hour angle) instead of decimal deg" ) self.wcsSexAct.toggled.connect(self.on_sex_toggle) self.wcsSexAct.setCheckable(True) self.wcsGridAct = QAction( 'Show Grid', self, statusTip="Overlay WCS coordinates grid over image", ) self.wcsGridAct.setCheckable(True) self.wcsGridAct.toggled.connect(self.on_grid_toggle) self.prevHDUAct = QAction(IconFactory.getIcon('skip_previous'), 'Prev HDU', self, statusTip="Previous HDU", triggered=self.prevHDU) self.nextHDUAct = QAction(IconFactory.getIcon('skip_next'), 'Next HDU', self, statusTip="Next HDU", triggered=self.nextHDU) self.zoom4Act = QAction(IconFactory.getIcon("x4"), 'Zoom ×4', self, statusTip="Zoom ×4", triggered=self.setZoomButton4) self.zoom2Act = QAction(IconFactory.getIcon("x2"), 'Zoom ×2', self, statusTip="Zoom ×2", triggered=self.setZoomButton2) self.zoomHomeAct = QAction(IconFactory.getIcon('home'), 'Home', self, statusTip="Reset zoom an position", triggered=self.setZoomButtonHome) self.zoom05Act = QAction(IconFactory.getIcon("1-2"), 'Zoom 1/2', self, statusTip="Zoom 1/2", triggered=self.setZoomButton05) self.zoom025Act = QAction(IconFactory.getIcon("1-4"), 'Zoom 1/4', self, statusTip="Zoom 1/4", triggered=self.setZoomButton025) self.panningAct = QAction(IconFactory.getIcon('panning'), 'Panning', self, statusTip="Panning", triggered=self.changePanningStatus) self.circleAct = QAction(IconFactory.getIcon('circle'), 'Add Region', self, statusTip="Add Region", triggered=self.changeAddCircleStatus) self.centerCircleAct = QAction( IconFactory.getIcon('add_circle_outline'), 'Radial profile', self, statusTip="Radial profile with gaussoide fit [R]-key", triggered=self.changeAddCenterCircleStatus) self.autoCenterAct = QAction( 'Auto Center', self, statusTip="Automatically center cursor on star centroid", triggered=self.changeAutoCenter) self.deleteAct = QAction(IconFactory.getIcon('delete_forever'), 'Delete selected', self, statusTip="Delete selected [Del]-key", triggered=self.deleteSelected) self.slidersAct = QAction( IconFactory.getIcon('slider'), 'Dynamic Scale Sliders', self, statusTip='Show/Hide Dynamic Scale', triggered=self.dynamicScaleDockWidgetTriggerActions) self.panningAct.setCheckable(True) self.panningAct.setChecked(True) self.circleAct.setCheckable(True) self.autoCenterAct.setCheckable(True) self.autoCenterAct.setChecked(self.painterComponent.auto_center) self.centerCircleAct.setCheckable(True) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.scanObject.scanAct) self.fileMenu.addAction(self.scanObject.stopAct) self.fileMenu.addAction(self.scanObject.pauseAct) self.fileMenu.addAction(self.scanObject.resumeAct) self.fileMenu.addAction(self.scanObject.autopauseAct) self.fileMenu.addAction(self.scanObject.disabledautopauseAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.panningAct) self.editMenu.addAction(self.circleAct) self.editMenu.addAction(self.centerCircleAct) self.editMenu.addSeparator() self.editMenu.addAction(self.autoCenterAct) self.editMenu.addSeparator() self.editMenu.addAction(self.deleteAct) self.hduMenu = self.menuBar().addMenu("HDU") self.hduMenu.addAction(self.prevHDUAct) self.hduMenu.addAction(self.nextHDUAct) self.hduMenu.addSeparator() self.zoomMenu = self.menuBar().addMenu("Zoom") self.zoomMenu.addAction(self.zoom4Act) self.zoomMenu.addAction(self.zoom2Act) self.zoomMenu.addAction(self.zoomHomeAct) self.zoomMenu.addAction(self.zoom05Act) self.zoomMenu.addAction(self.zoom025Act) self.WcsMenu = self.menuBar().addMenu("W&CS") self.WcsMenu.addAction(self.wcsSexAct) self.WcsMenu.addSeparator() self.WcsMenu.addAction(self.wcsGridAct) self.viewMenu = self.menuBar().addMenu("&View") self.viewMenu.addAction(self.qtConsoleAct) self.viewMenu.addSeparator() self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File Toolbar") self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.hduToolBar = self.addToolBar("HDU Toolbar") self.hduToolBar.addAction(self.prevHDUAct) self.hduToolBar.addAction(self.nextHDUAct) self.scanToolBar = self.addToolBar("Scan Toolbar") self.scanToolBar.addAction(self.scanObject.scanAct) self.scanToolBar.addAction(self.scanObject.stopAct) self.scanToolBar.addAction(self.scanObject.pauseAct) self.scanToolBar.addAction(self.scanObject.resumeAct) self.scanToolBar.addAction(self.scanObject.autopauseAct) self.scanToolBar.addAction(self.scanObject.disabledautopauseAct) self.scanToolBar.hide() # self.infoToolBar = self.addToolBar("Info Toolbar") # self.mouse_x_label = QLabel('100.1') # self.mouse_y_label = QLabel('100.145') # self.infoToolBar.addWidget(QLabel('image x:')) # self.infoToolBar.addWidget(self.mouse_x_label) # self.infoToolBar.addWidget(QLabel('y:')) # self.infoToolBar.addWidget(self.mouse_y_label) # self.infoToolBar.hide() self.zoomToolBar = self.addToolBar("Zoom Toolbar") self.zoomToolBar.addAction(self.zoom4Act) self.zoomToolBar.addAction(self.zoom2Act) self.zoomToolBar.addAction(self.zoomHomeAct) self.zoomToolBar.addAction(self.zoom05Act) self.zoomToolBar.addAction(self.zoom025Act) self.mouseActionToolBar = self.addToolBar("Mouse Task Toolbar") self.mouseActionToolBar.addAction(self.panningAct) self.mouseActionToolBar.addAction(self.circleAct) self.mouseActionToolBar.addAction(self.centerCircleAct) self.mouseActionToolBar.addAction(self.deleteAct) self.sliderToolBar = self.addToolBar("Slider Toolbar") self.slidersAct.setChecked(True) self.sliderToolBar.addAction(self.slidersAct) self.viewMenu.addAction(self.fileToolBar.toggleViewAction()) self.viewMenu.addAction(self.hduToolBar.toggleViewAction()) self.viewMenu.addAction(self.scanToolBar.toggleViewAction()) # self.viewMenu.addAction(self.infoToolBar.toggleViewAction()) self.viewMenu.addAction(self.zoomToolBar.toggleViewAction()) self.viewMenu.addAction(self.mouseActionToolBar.toggleViewAction()) self.viewMenu.addAction(self.sliderToolBar.toggleViewAction()) self.viewMenu.addSeparator() def nextHDU(self): self.fits_image.changeHDU(True, 1) self.updateHeaderData() def prevHDU(self): self.fits_image.changeHDU(True, -1) self.updateHeaderData() def updateHeaderData(self): self.headerWidget.setHeader() self.prevHDUAct.setEnabled(self.fits_image._huds is not None and self.fits_image.hdu != 0) self.nextHDUAct.setEnabled( self.fits_image._huds is not None and self.fits_image.hdu != len(self.fits_image._huds) - 1) def setZoomButton4(self): self.setZoomButton(4, False) def setZoomButton2(self): self.setZoomButton(2, False) def setZoomButtonHome(self): self.setZoomButton(1, True) def setZoomButton05(self): self.setZoomButton(0.5, False) def setZoomButton025(self): self.setZoomButton(0.25, False) def setZoomButton(self, zoom: float, reset: bool): if self.fits_image.ax != None: self.fits_image.setZoom(zoom, reset) self.full_view_widget.updateMiniatureShape(self.fits_image.viewX, self.fits_image.viewY, self.fits_image.viewW, self.fits_image.viewH) def changePanningStatus(self): if self.panningAct.isChecked(): self.toogleOffRegionButtons() self.panningAct.toggle() self.painterComponent.stopPainting(self.central_widget) self.painterComponent.startMovingEvents(self.central_widget) else: self.painterComponent.stopPainting(self.central_widget) self.painterComponent.stopMovingEvents(self.central_widget) def changeAddCircleStatus(self): if self.circleAct.isChecked(): self.toogleOffRegionButtons() self.circleAct.toggle() self.painterComponent.startPainting(self.central_widget, "circle") else: self.painterComponent.stopPainting(self.central_widget) self.painterComponent.startMovingEvents(self.central_widget) self.panningAct.toggle() def changeAddCenterCircleStatus(self): if self.centerCircleAct.isChecked(): self.toogleOffRegionButtons() self.centerCircleAct.toggle() self.painterComponent.startPainting(self.central_widget, "circleCenter") else: self.painterComponent.stopPainting(self.central_widget) self.painterComponent.startMovingEvents(self.central_widget) self.panningAct.toggle() def changeAutoCenter(self): self.painterComponent.auto_center = self.autoCenterAct.isChecked() def deleteSelected(self): self.painterComponent.deleteSelectedShapes( self.central_widget.figure.axes[0]) def toogleOffRegionButtons(self): if self.panningAct.isChecked(): self.panningAct.toggle() if self.circleAct.isChecked(): self.circleAct.toggle() if self.centerCircleAct.isChecked(): self.centerCircleAct.toggle() self.painterComponent.stopPainting(self.central_widget) def createStatusBar(self): self.statusBar().showMessage("Ready") def createDockWindows(self): # Scale self.dynamic_scale_dock = QDockWidget("Dynamic Scale", self) self.dynamic_scale_dock.setObjectName("SCALE") self.dynamic_scale_dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.scaleWidget = ScaleWidget(self, scales_model=self.scales_model, cmap_model=self.cmaps) self.dynamic_scale_dock.setWidget(self.scaleWidget) self.addDockWidget(Qt.RightDockWidgetArea, self.dynamic_scale_dock) self.viewMenu.addAction(self.dynamic_scale_dock.toggleViewAction()) self.dynamic_scale_dock.setFloating(True) self.dynamic_scale_dock.hide() #radial profiles dock = QDockWidget("Radial Profile Fit", self) dock.setObjectName("RADIAL_PROFILE_IRAF") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.radial_profile_iraf_widget = IRAFRadialProfileWidget( self.fits_image.data) dock.setWidget(self.radial_profile_iraf_widget) self.addDockWidget(Qt.RightDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) self.dockRadialFit = dock dock = QDockWidget("Radial Profile Curve", self) dock.setObjectName("RADIAL_PROFILE") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.radial_profile_widget = RadialProfileWidget(self.fits_image.data) dock.setWidget(self.radial_profile_widget) self.addDockWidget(Qt.RightDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) dock.hide() #info panel dock = QDockWidget("Info", self) dock.setObjectName("INFO_PANEL") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.info_widget = InfoWidget(self) dock.setWidget(self.info_widget) self.addDockWidget(Qt.TopDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) # FITS headers dock = QDockWidget("FITS header", self) dock.setObjectName("FTIS_DATA") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.addDockWidget(Qt.TopDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) self.headerWidget = HeaderTableWidget(self) self.headerWidget.setColumnCount(2) self.headerWidget.setHorizontalHeaderItem(0, QTableWidgetItem("KEY")) self.headerWidget.setHorizontalHeaderItem(1, QTableWidgetItem("VALUE")) self.headerWidget.horizontalHeader().setStretchLastSection(1) self.headerWidget.setEditTriggers( QtWidgets.QTableWidget.NoEditTriggers) self.headerWidget.clearFocus() dock.setWidget(self.headerWidget) # full dock = QDockWidget("Full view", self) dock.setObjectName("FULL_VIEW") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.full_view_widget = FullViewWidget(self.fits_image) self.full_view_widget.fits_image.set_scale_model(self.scales_model) self.full_view_widget.fits_image.set_cmap_model(self.cmaps) dock.setWidget(self.full_view_widget) self.addDockWidget(Qt.TopDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) # zoom dock = QDockWidget("Zoom view", self) dock.setObjectName("ZOOM_VIEW") dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) self.zoom_view_widget = ZoomViewWidget(self.fits_image) self.zoom_view_widget.fits_image.set_scale_model(self.scales_model) self.zoom_view_widget.fits_image.set_cmap_model(self.cmaps) dock.setWidget(self.zoom_view_widget) self.addDockWidget(Qt.TopDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) # fileSelector dock = QDockWidget("Directory view", self) dock.setObjectName("DIRECTORY_VIEW") dock.setAllowedAreas(Qt.LeftDockWidgetArea) self.file_widget = FileSystemWidget(self) dock.setWidget(self.file_widget) self.addDockWidget(Qt.LeftDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) self.viewMenu.addSeparator() # def changeColor(self, color): # self.cmaps.set_active_color_map(color) # def on_colormap_change(self, change): # self.fits_image.cmap = self.cmaps.get_active_color_map() # self.fits_image.plot() # self.updateFitsInWidgets() def onAutoCenterChange(self, change): self.autoCenterAct.setChecked(change.new) def dynamicScaleDockWidgetTriggerActions(self): if self.dynamic_scale_dock.isHidden(): self.dynamic_scale_dock.show() else: self.dynamic_scale_dock.hide() def onCenterCircleChange(self, change): self.radial_profile_widget.set_centroid( self.painterComponent.ccenter_x, self.painterComponent.ccenter_y) self.radial_profile_iraf_widget.set_centroid( self.painterComponent.ccenter_x, self.painterComponent.ccenter_y) def onCenterCircleRadiusChange(self, change): self.radial_profile_widget.set_radius(self.painterComponent.cradius) self.radial_profile_iraf_widget.set_radius( self.painterComponent.cradius) def onRectangleInWidgetMove(self, change): changed = False if change.new is not None: changed = True if change.name == 'viewX': self.fullWidgetXcord = change.new elif change.name == 'viewY': self.fullWidgetYcord = change.new if changed: self.fits_image.moveToXYcords(self.fullWidgetXcord, self.fullWidgetYcord) def movingCentralWidget(self, change): changed = False if change.new is not None: changed = True if change.name == 'movingViewX': self.centralWidgetcordX = change.new elif change.name == 'movingViewY': self.centralWidgetcordY = change.new if changed: self.full_view_widget.updateMiniatureShapeXYonly( self.centralWidgetcordX, self.centralWidgetcordY) def onMouseMoveOnImage(self, change): display = '' val = 0 if change.new is not None: display = f'{change.new:f}' val = change.new if change.name == 'mouse_xdata': # self.mouse_x_label.setText(display) self.current_x_coord = val self.cursor_coords.set_img_x(change.new) elif change.name == 'mouse_ydata': # self.mouse_y_label.setText(display) self.current_y_coord = val self.cursor_coords.set_img_y(change.new) if display != '': self.zoom_view_widget.setXYofZoom(self.fits_image, self.current_x_coord, self.current_y_coord, self.fits_image.zoom) if not self.hasFocus(): self.setFocus() if self.scanObject.activeScan and self.scanObject.enableAutopause: #reser autopause if not self.scanObject.obserwableValue.autopauseFlag: self.scanObject.obserwableValue.autopauseFlag = True def activeLinearAdjustmentByMouseMovement(self): self.central_widget.mpl_connect('motion_notify_event', self.mouseMoveEventOnCanvas) self.central_widget.mpl_connect('button_press_event', self.canvasMousePressEvent) self.central_widget.mpl_connect('button_release_event', self.canvasMousePressEvent) def onMouseZoomOnImage(self, change): changed = False if change.new is not None: changed = True if changed: self.full_view_widget.updateMiniatureShape(self.fits_image.viewX, self.fits_image.viewY, self.fits_image.viewW, self.fits_image.viewH) def readWindowSettings(self): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("MainWindow") size, pos = settings.value("size"), settings.value("pos") settings.endGroup() if size is not None and pos is not None: print('settings: resize to {} and move to {}', size, pos) self.move(pos) # self.resize(size) print('Size reported ', self.size()) print('Size set ', size) self.resize(size) print('Size reported ', self.size()) else: self.resize(800, 600) geometry = settings.value("geometry") if geometry is not None: self.restoreGeometry(geometry) self.restoreState(settings.value("windowState")) self.headerWidget.readSettings(settings) self.file_widget.readSettings(settings) def writeWindowSettings(self): if self.tedaCommandLine.ignoreSettings: return settings = QSettings() settings.beginGroup("MainWindow") settings.setValue("size", self.size()) settings.setValue("pos", self.pos()) settings.endGroup() settings.setValue('geometry', self.saveGeometry()) settings.setValue('windowState', self.saveState()) self.headerWidget.writeSettings(settings) self.file_widget.writeSettings(settings)
class MVCPlaybackControlGUI(PlaybackControlConsole): """ GUI implementation of MVCPlaybackControlBase """ nameFiltersChanged = Signal("QStringList") def __init__(self, config): assertMainThread() super().__init__(config) # state self.preventSeek = False self.beginTime = None self.timeRatio = 1.0 # gui srv = Services.getService("MainWindow") config.configLoaded.connect(self.restoreState) config.configAboutToSave.connect(self.saveState) self.config = config playbackMenu = srv.menuBar().addMenu("&Playback") style = QApplication.style() self.actStart = QAction( QIcon.fromTheme("media-playback-start", style.standardIcon(QStyle.SP_MediaPlay)), "Start Playback", self) self.actPause = QAction( QIcon.fromTheme("media-playback-pause", style.standardIcon(QStyle.SP_MediaPause)), "Pause Playback", self) self.actPause.setEnabled(False) self.actStepFwd = QAction( QIcon.fromTheme("media-seek-forward", style.standardIcon(QStyle.SP_MediaSeekForward)), "Step Forward", self) self.actStepBwd = QAction( QIcon.fromTheme("media-seek-backward", style.standardIcon(QStyle.SP_MediaSeekBackward)), "Step Backward", self) self.actSeekEnd = QAction( QIcon.fromTheme("media-skip-forward", style.standardIcon(QStyle.SP_MediaSkipForward)), "Seek End", self) self.actSeekBegin = QAction( QIcon.fromTheme("media-skip-backward", style.standardIcon(QStyle.SP_MediaSkipBackward)), "Seek Begin", self) self.actSetTimeFactor = { r: QAction("x 1/%d" % (1 / r), self) if r < 1 else QAction("x %d" % r, self) for r in (1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8) } # pylint: disable=unnecessary-lambda # let's stay on the safe side and do not use emit as a slot... self.actStart.triggered.connect(self.startPlayback) self.actPause.triggered.connect(self.pausePlayback) self.actStepFwd.triggered.connect( lambda: self.stepForward(self.selectedStream())) self.actStepBwd.triggered.connect( lambda: self.stepBackward(self.selectedStream())) self.actSeekEnd.triggered.connect(self.seekEnd) self.actSeekBegin.triggered.connect(self.seekBeginning) # pylint: enable=unnecessary-lambda def setTimeFactor(newFactor): logger.debug("new time factor %f", newFactor) self.setTimeFactor(newFactor) for r in self.actSetTimeFactor: logger.debug("adding action for time factor %f", r) self.actSetTimeFactor[r].triggered.connect( functools.partial(setTimeFactor, r)) self.dockWidget = srv.newDockWidget("PlaybackControl", None, Qt.LeftDockWidgetArea) self.dockWidgetContents = QWidget(self.dockWidget) self.dockWidget.setWidget(self.dockWidgetContents) toolLayout = QBoxLayout(QBoxLayout.TopToBottom, self.dockWidgetContents) toolLayout.setContentsMargins(0, 0, 0, 0) toolBar = QToolBar() toolLayout.addWidget(toolBar) toolBar.addAction(self.actSeekBegin) toolBar.addAction(self.actStepBwd) toolBar.addAction(self.actStart) toolBar.addAction(self.actPause) toolBar.addAction(self.actStepFwd) toolBar.addAction(self.actSeekEnd) playbackMenu.addAction(self.actSeekBegin) playbackMenu.addAction(self.actStepBwd) playbackMenu.addAction(self.actStart) playbackMenu.addAction(self.actPause) playbackMenu.addAction(self.actStepFwd) playbackMenu.addAction(self.actSeekEnd) playbackMenu.addSeparator() for r in self.actSetTimeFactor: playbackMenu.addAction(self.actSetTimeFactor[r]) self.timeRatioLabel = QLabel("x 1") self.timeRatioLabel.addActions(list(self.actSetTimeFactor.values())) self.timeRatioLabel.setContextMenuPolicy(Qt.ActionsContextMenu) toolBar.addSeparator() toolBar.addWidget(self.timeRatioLabel) contentsLayout = QGridLayout() toolLayout.addLayout(contentsLayout, 10) # now we add a position view self.positionSlider = QSlider(Qt.Horizontal, self.dockWidgetContents) self.beginLabel = QLabel(parent=self.dockWidgetContents) self.beginLabel.setAlignment(Qt.AlignLeft | Qt.AlignCenter) self.currentLabel = QLabel(parent=self.dockWidgetContents) self.currentLabel.setAlignment(Qt.AlignHCenter | Qt.AlignCenter) self.endLabel = QLabel(parent=self.dockWidgetContents) self.endLabel.setAlignment(Qt.AlignRight | Qt.AlignCenter) contentsLayout.addWidget(self.beginLabel, 0, 0, alignment=Qt.AlignLeft) contentsLayout.addWidget(self.currentLabel, 0, 1, alignment=Qt.AlignHCenter) contentsLayout.addWidget(self.endLabel, 0, 2, alignment=Qt.AlignRight) contentsLayout.addWidget(self.positionSlider, 1, 0, 1, 3) self.positionSlider.setTracking(False) self.positionSlider.valueChanged.connect(self.onSliderValueChanged, Qt.DirectConnection) self.positionSlider.sliderMoved.connect(self.displayPosition) # file browser self.browser = BrowserWidget(self.dockWidget) self.nameFiltersChanged.connect(self._onNameFiltersChanged, Qt.QueuedConnection) contentsLayout.addWidget(self.browser, 3, 0, 1, 3) contentsLayout.setRowStretch(3, 100) self.browser.activated.connect(self.browserActivated) self.actShowAllFiles = QAction("Show all files") self.actShowAllFiles.setCheckable(True) self.actShowAllFiles.setChecked(False) self.actShowAllFiles.toggled.connect(self._onShowAllFiles) playbackMenu.addSeparator() playbackMenu.addAction(self.actShowAllFiles) self.actGroupStream = QActionGroup(self) self.actGroupStream.setExclusionPolicy( QActionGroup.ExclusionPolicy.ExclusiveOptional) playbackMenu.addSeparator() self.actGroupStreamMenu = playbackMenu.addMenu("Step Stream") self._selectedStream = None self.recentSeqs = [QAction() for i in range(10)] playbackMenu.addSeparator() recentMenu = playbackMenu.addMenu("Recent") for a in self.recentSeqs: a.setVisible(False) a.triggered.connect(self.openRecent) recentMenu.addAction(a) self._supportedFeaturesChanged(set(), set()) def __del__(self): logger.internal("deleting playback control") def _onNameFiltersChanged(self, nameFilt): self.browser.setFilter(nameFilt) if isinstance(nameFilt, str): nameFilt = [nameFilt] for a in self.recentSeqs: if a.isVisible(): m = QDir.match(nameFilt, Path(a.data()).name) a.setEnabled(m) def _onShowAllFiles(self, enabled): self.fileSystemModel.setNameFilterDisables(enabled) def _supportedFeaturesChanged(self, featureset, nameFilters): """ overwritten from MVCPlaybackControlBase. This function is called from multiple threads, but not at the same time. :param featureset: the current featureset :return: """ assertMainThread() self.featureset = featureset self.actStepFwd.setEnabled("stepForward" in featureset) self.actStepBwd.setEnabled("stepBackward" in featureset) self.actSeekBegin.setEnabled("seekBeginning" in featureset) self.actSeekEnd.setEnabled("seekEnd" in featureset) self.positionSlider.setEnabled("seekTime" in featureset) self.browser.setEnabled("setSequence" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) for f in self.actSetTimeFactor: self.actSetTimeFactor[f].setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) self.timeRatioLabel.setEnabled("setTimeFactor" in featureset) if "startPlayback" not in featureset: self.actStart.setEnabled(False) if "pausePlayback" not in featureset: self.actPause.setEnabled(False) logger.debug("current feature set: %s", featureset) logger.debug("Setting name filters of browser: %s", list(nameFilters)) self.nameFiltersChanged.emit(list(nameFilters)) super()._supportedFeaturesChanged(featureset, nameFilters) def scrollToCurrent(self): """ Scrolls to the current item in the browser :return: """ assertMainThread() c = self.browser.current() if c is not None: self.browser.scrollTo(c) def _sequenceOpened(self, filename, begin, end, streams): """ Notifies about an opened sequence. :param filename: the filename which has been opened :param begin: timestamp of sequence's first sample :param end: timestamp of sequence's last sample :param streams: list of streams in the sequence :return: None """ assertMainThread() self.beginTime = begin self.preventSeek = True self.positionSlider.setRange(0, (end - begin) // 1000000) self.preventSeek = False self.beginLabel.setText(self._timeToString(0)) self.endLabel.setText(self._timeToString((end - begin) // 1000000)) self._currentTimestampChanged(begin) try: self.browser.blockSignals(True) self.browser.setActive(filename) self.browser.scrollTo(filename) finally: self.browser.blockSignals(False) self._selectedStream = None for a in self.actGroupStream.actions(): logger.debug("Remove stream group action: %s", a.data()) self.actGroupStream.removeAction(a) for stream in streams: act = QAction(stream, self.actGroupStream) act.triggered.connect( lambda cstream=stream: self.setSelectedStream(cstream)) act.setCheckable(True) act.setChecked(False) logger.debug("Add stream group action: %s", act.data()) self.actGroupStreamMenu.addAction(act) QTimer.singleShot(250, self.scrollToCurrent) super()._sequenceOpened(filename, begin, end, streams) @staticmethod def _splitTime(milliseconds): hours = milliseconds // (60 * 60 * 1000) milliseconds -= hours * (60 * 60 * 1000) minutes = milliseconds // (60 * 1000) milliseconds -= minutes * (60 * 1000) seconds = milliseconds // 1000 milliseconds -= seconds * 1000 return hours, minutes, seconds, milliseconds @staticmethod def _timeToString(milliseconds): return "%02d:%02d:%02d.%03d" % ( MVCPlaybackControlGUI._splitTime(milliseconds)) def _currentTimestampChanged(self, currentTime): """ Notifies about a changed timestamp :param currentTime: the new current timestamp :return: None """ assertMainThread() if self.beginTime is None: self.currentLabel.setText("") else: sliderVal = (currentTime - self.beginTime ) // 1000000 # nanoseconds to milliseconds self.preventSeek = True self.positionSlider.setValue(sliderVal) self.preventSeek = False self.positionSlider.blockSignals(False) self.currentLabel.setEnabled(True) self.currentLabel.setText(self._timeToString(sliderVal)) super()._currentTimestampChanged(currentTime) def onSliderValueChanged(self, value): """ Slot called whenever the slider value is changed. :param value: the new slider value :return: """ assertMainThread() if self.beginTime is None or self.preventSeek: return if self.actStart.isEnabled(): ts = self.beginTime + value * 1000000 self.seekTime(ts) else: logger.warning("Can't seek while playing.") def displayPosition(self, value): """ Slot called when the slider is moved. Displays the position without actually seeking to it. :param value: the new slider value. :return: """ assertMainThread() if self.beginTime is None: return if self.positionSlider.isSliderDown(): ts = self.beginTime // 1000000 + value self.currentLabel.setEnabled(False) self.currentLabel.setText(self._timeToString(ts)) def _playbackStarted(self): """ Notifies about starting playback :return: None """ assertMainThread() self.actStart.setEnabled(False) if "pausePlayback" in self.featureset: self.actPause.setEnabled(True) super()._playbackStarted() def _playbackPaused(self): """ Notifies about pause playback :return: None """ assertMainThread() logger.debug("playbackPaused received") if "startPlayback" in self.featureset: self.actStart.setEnabled(True) self.actPause.setEnabled(False) super()._playbackPaused() def openRecent(self): """ Called when the user clicks on a recent sequence. :return: """ assertMainThread() action = self.sender() self.browser.setActive(action.data()) def browserActivated(self, filename): """ Called when the user activated a file. :param filename: the new filename :return: """ assertMainThread() if filename is not None and Path(filename).is_file(): foundIdx = None for i, a in enumerate(self.recentSeqs): if a.data() == filename: foundIdx = i if foundIdx is None: foundIdx = len(self.recentSeqs) - 1 for i in range(foundIdx, 0, -1): self.recentSeqs[i].setText(self.recentSeqs[i - 1].text()) self.recentSeqs[i].setData(self.recentSeqs[i - 1].data()) logger.debug("%d data: %s", i, self.recentSeqs[i - 1].data()) self.recentSeqs[i].setVisible( self.recentSeqs[i - 1].data() is not None) self.recentSeqs[0].setText(self.compressFileName(filename)) self.recentSeqs[0].setData(filename) self.recentSeqs[0].setVisible(True) self.setSequence(filename) def _timeRatioChanged(self, newRatio): """ Notifies about a changed playback time ratio, :param newRatio the new playback ratio as a float :return: None """ assertMainThread() self.timeRatio = newRatio logger.debug("new timeRatio: %f", newRatio) for r in [1 / 8, 1 / 4, 1 / 2, 1, 2, 4, 8]: if abs(newRatio / r - 1) < 0.01: self.timeRatioLabel.setText(("x 1/%d" % (1 / r)) if r < 1 else ("x %d" % r)) return self.timeRatioLabel.setText("%.2f" % newRatio) super()._timeRatioChanged(newRatio) def selectedStream(self): """ Returns the user-selected stream (for forward/backward stepping) :return: """ return self._selectedStream def setSelectedStream(self, stream): """ Sets the user-selected stream (for forward/backward stepping) :param stream the stream name. :return: """ self._selectedStream = stream def _defineProperties(self): propertyCollection = self.config.guiState() propertyCollection.defineProperty("PlaybackControl_showAllFiles", 0, "show all files setting") propertyCollection.defineProperty("PlaybackControl_folder", "", "current folder name") propertyCollection.defineProperty("PlaybackControl_recent", "", "recent opened sequences") def saveState(self): """ Saves the state of the playback control :return: """ assertMainThread() self._defineProperties() propertyCollection = self.config.guiState() showAllFiles = self.actShowAllFiles.isChecked() folder = self.browser.folder() logger.debug("Storing current folder: %s", folder) try: propertyCollection.setProperty("PlaybackControl_showAllFiles", int(showAllFiles)) propertyCollection.setProperty("PlaybackControl_folder", folder) recentFiles = [ a.data() for a in self.recentSeqs if a.data() is not None ] propertyCollection.setProperty("PlaybackControl_recent", "|".join(recentFiles)) except PropertyCollectionPropertyNotFound: pass def restoreState(self): """ Restores the state of the playback control from the given property collection :param propertyCollection: a PropertyCollection instance :return: """ assertMainThread() self._defineProperties() propertyCollection = self.config.guiState() showAllFiles = propertyCollection.getProperty( "PlaybackControl_showAllFiles") self.actShowAllFiles.setChecked(bool(showAllFiles)) folder = propertyCollection.getProperty("PlaybackControl_folder") if Path(folder).is_dir(): logger.debug("Setting current file: %s", folder) self.browser.setFolder(folder) recentFiles = propertyCollection.getProperty("PlaybackControl_recent") idx = 0 for f in recentFiles.split("|"): if f != "" and Path(f).is_file(): self.recentSeqs[idx].setData(f) self.recentSeqs[idx].setText(self.compressFileName(f)) self.recentSeqs[idx].setVisible(True) self.recentSeqs[idx].setEnabled(False) idx += 1 if idx >= len(self.recentSeqs): break for a in self.recentSeqs[idx:]: a.setData(None) a.setText("") a.setVisible(False) @staticmethod def compressFileName(filename): """ Compresses long path names with an ellipsis (...) :param filename: the original path name as a Path or string instance :return: the compressed path name as a string instance """ p = Path(filename) parts = tuple(p.parts) if len(parts) >= 6: p = Path(*parts[:2]) / "..." / Path(*parts[-2:]) return str(p)
class SystemTray(QSystemTrayIcon): def __init__(self, parent): super().__init__(QIcon("resources/translate-icon.svg"), ) self.parent = parent self.show() self.clear_action = QAction("Clear", self) self.enable_action = QAction("Enable clipboard translation", self) self.enable_action.setCheckable(True) self.show_panel_action = QAction("Show panel", self) self.show_panel_action.setCheckable(True) self.on_top_action = QAction("Always on top", self) self.on_top_action.setCheckable(True) self.not_fix_action = QAction("Move to avoid mouse", self) self.not_fix_action.setCheckable(True) self.follow_cursor_action = QAction("Move to follow mouse", self) self.follow_cursor_action.setCheckable(True) close_action = QAction("Exit", self) QObject.connect(self.clear_action, SIGNAL("triggered()"), self.parent.clear_button, SLOT("click()")) QObject.connect(self.on_top_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_on_top(bool)")) QObject.connect(self.show_panel_action, SIGNAL("triggered(bool)"), self.parent, SLOT("show_interface(bool)")) QObject.connect(self.not_fix_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_not_fix(bool)")) QObject.connect(self.enable_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_enable(bool)")) QObject.connect(self.follow_cursor_action, SIGNAL("triggered(bool)"), self.parent, SLOT("set_follow_cursor(bool)")) QObject.connect(close_action, SIGNAL("triggered()"), self.parent, SLOT("close()")) menu = QMenu() menu.addActions([ self.clear_action, self.enable_action, self.show_panel_action, self.on_top_action, self.not_fix_action, self.show_panel_action, self.follow_cursor_action, close_action ]) self.setContextMenu(menu) QObject.connect(menu, SIGNAL("aboutToShow()"), self, SLOT("refresh()")) def refresh(self): self.enable_action.setChecked(self.parent.is_enable) self.on_top_action.setChecked(self.parent.is_on_top) self.not_fix_action.setChecked(self.parent.is_not_fixed) self.show_panel_action.setChecked(self.parent.is_show_panel)
class mainWindow(QObject): signalRun = Signal(SpiderThread) signalRunApi = Signal(apiTester) def __init__(self): QObject.__init__(self) # must init parent QObject,if you want to use signal self.widget = QWidget() self.ipLabel = QLabel(self.widget) self.thread = QThread() self.worker = Worker() self.worker.moveToThread(self.thread) self.man = SpiderThread() self.api = apiTester() # 1 2:loop 3: time self.testStatus = [False,1,0,"ip","mac"] # advance config self.findCoreStop = True self.cleanCacheSet = False self.useApiTest = False self.showTestProgress = True self.saveTestLog = False self.saveLogPath = "" self.chromePath = "" self.showChrome = False self.coreDumpPath = "" # ui form self.passwordLabel = QLabel(self.widget) self.ipLineEdit = QLineEdit(self.widget) self.passwordLineEdit = QLineEdit(self.widget) self.startBtn = QPushButton(self.widget) self.stopBtn = QPushButton(self.widget) self.messageBox = QTextEdit(self.widget) self.messageBox.setReadOnly(True) self.userLabel = QLabel(self.widget) self.userLineEdit = QLineEdit(self.widget) self.intervalLabel = QLabel(self.widget) self.intervalSpinBox = QSpinBox(self.widget) self.loopLabel = QLabel(self.widget) self.loopSpinBox = QSpinBox(self.widget) self.intervalSpinBox.setRange(0,9999) self.loopSpinBox.setRange(1,9999) self.radioReboot = QRadioButton(self.widget) self.radioProvision = QRadioButton(self.widget) self.radioFactory = QRadioButton(self.widget) # self.apiCheckBox = QCheckBox(self.widget) self.menu = QMenu() self.gxpAction = QAction("Classic UI") self.grp2602Action = QAction("Ant Design UI") self.gxpAction.setCheckable(True) self.grp2602Action.setCheckable(True) self.menu.addAction(self.gxpAction) self.menu.addAction(self.grp2602Action) self.webLabel = QLabel(self.widget) self.webBtn = QPushButton(self.widget) self.webBtn.setMenu(self.menu) self.clearBtn = QPushButton(self.widget) self.messageList = deque() self.timer = QTimer() self.advanceBtn = QPushButton(self.widget) self.infoLabel = QLabel(self.widget) # provision widget self.provWidget = QWidget(self.widget) self.ver1Label = QLabel(self.provWidget) self.ver2Label = QLabel(self.provWidget) self.ver3Label = QLabel(self.provWidget) self.ver1LineEdit = QLineEdit(self.provWidget) self.ver2LineEdit = QLineEdit(self.provWidget) self.ver3LineEdit = QLineEdit(self.provWidget) self.dir1Label = QLabel(self.provWidget) self.dir2Label = QLabel(self.provWidget) self.dir3Label = QLabel(self.provWidget) self.dir1LineEdit = QLineEdit(self.provWidget) self.dir2LineEdit = QLineEdit(self.provWidget) self.dir3LineEdit = QLineEdit(self.provWidget) self.radioHttp = QRadioButton(self.provWidget) self.radioHttps = QRadioButton(self.provWidget) self.radioTftp = QRadioButton(self.provWidget) self.radioFtp = QRadioButton(self.provWidget) self.radioFtps = QRadioButton(self.provWidget) self.radioWindow = QRadioButton(self.provWidget) # advance widget self.advanceWidget = QWidget() self.checkCoreBox = QCheckBox(self.advanceWidget) self.cleanCache = QCheckBox(self.advanceWidget) self.checkSaveLogBox = QCheckBox(self.advanceWidget) self.selectDirBtn = QPushButton(self.advanceWidget) self.saveDirLabel = QLabel(self.advanceWidget) self.saveDirLabel.setStyleSheet("background:white") self.checkProgressBox = QCheckBox(self.advanceWidget) self.advanceOkBtn = QPushButton(self.advanceWidget) self.selectChromeBtn = QPushButton(self.advanceWidget) self.chromePathLabel = QLabel(self.advanceWidget) self.chromePathLabel.setStyleSheet("background:white") self.checkShowChromeBox = QCheckBox(self.advanceWidget) self.chromeLabel = QLabel(self.advanceWidget) self.pcapLabel = QLabel(self.advanceWidget) self.apiCheckBox = QCheckBox(self.advanceWidget) self.corePathLabel = QLabel(self.advanceWidget) self.corePathLabel.setStyleSheet("background:white") self.corePathBtn = QPushButton(self.advanceWidget) self.interfaceMenu = QComboBox(self.advanceWidget) self.interfaceMenu.addItem('Default') self.aiOptionBox= QCheckBox(self.advanceWidget) a = IFACES print(a) for i in a.keys(): print(a[i].description) self.interfaceMenu.addItem(a[i].description) # connect singal and slot self.startBtn.clicked.connect(self.clickedStarBtn) self.radioProvision.clicked.connect(self.clickedProvision) self.radioReboot.clicked.connect(self.clickedOthers) self.radioFactory.clicked.connect(self.clickedOthers) self.stopBtn.clicked.connect(self.clickedStopBtn) self.grp2602Action.triggered.connect(self.clickedGrp2602) self.gxpAction.triggered.connect(self.clickedGxpType) self.timer.timeout.connect(self.updateMessage) self.apiCheckBox.stateChanged.connect(self.apiTestBoxCheck) self.clearBtn.clicked.connect(self.clickedClearBtn) self.advanceBtn.clicked.connect(self.clickedAdvanceBtn) self.advanceOkBtn.clicked.connect(self.clickedAdvanceOkBtn) self.checkSaveLogBox.stateChanged.connect(self.saveLogBoxCheck) self.selectChromeBtn.clicked.connect(self.clickedSelectChromeBtn) self.selectDirBtn.clicked.connect(self.clickedSelectDirBtn) self.corePathBtn.clicked.connect(self.clickedCorePathBtn) self.worker.signalJobEnd.connect(self.slotTestStoped) self.worker.apiTestFinished.connect(self.slotTestStoped) self.signalRun.connect(self.worker.dowork) self.signalRunApi.connect(self.worker.doworkApi) # self.man.signalUpdateMessage.connect(self.pushMessage) def setupUI(self): # set text content /value self.widget.setWindowTitle("自动重启升降级测试工具") self.ipLabel.setText("初始IP:") self.passwordLabel.setText("密码:") self.startBtn.setText("开始测试") self.stopBtn.setText("停止测试") self.userLabel.setText("用户名:") self.intervalLabel.setText("间隔时间:") self.loopLabel.setText("测试次数:") self.intervalSpinBox.setValue(130) self.radioFactory.setText("恢复出厂") self.radioProvision.setText("升降级") self.radioReboot.setText("重启") self.ver1Label.setText("版本 1:") self.ver2Label.setText("版本 2:") self.ver3Label.setText("版本 3:") self.dir1Label.setText("路径 1:") self.dir2Label.setText("路径 2:") self.dir3Label.setText("路径 3:") self.radioHttp.setText("Http") self.radioHttps.setText("Https") self.radioTftp.setText("Tftp") self.radioFtp.setText("Ftp") self.radioFtps.setText("Ftps") self.apiCheckBox.setText("使用API测试,配置CoreDump下载路径") self.webLabel.setText("网页类型:") self.webBtn.setText("请选择UI类型") self.clearBtn.setText("清空输入") self.advanceWidget.setWindowTitle("高级设置") self.advanceBtn.setText("高级设置") self.checkCoreBox.setText("发现Core Dump时停止测试") self.cleanCache.setText("清除页面cache") self.checkSaveLogBox.setText("保存测试日志") self.selectDirBtn.setText("浏览") self.checkProgressBox.setText("显示底部状态条") self.advanceOkBtn.setText("OK") self.checkShowChromeBox.setText("测试时显示Chrome浏览器") self.selectChromeBtn.setText("浏览") self.chromeLabel.setText("Chrome浏览器路径") self.infoLabel.setText("未开始测试") self.pcapLabel.setText("Net Interface") self.corePathBtn.setText("浏览") self.radioWindow.setText("网页拖拽文件") # self.aiOptionBox.setText("AI") #init value self.saveDirLabel.hide() self.selectDirBtn.hide() self.gxpAction.setChecked(True) self.radioReboot.click() self.radioHttp.click() self.stopBtn.setEnabled(False) self.passwordLineEdit.setEchoMode(QLineEdit.PasswordEchoOnEdit) # set position------------------------------- xPos = 20 yPos = 30 colum2 = xPos +200 # line 1 self.ipLabel.move(xPos,yPos) self.intervalLabel.move(colum2,yPos) self.intervalSpinBox.move(colum2+60,yPos-2) self.ipLineEdit.move(xPos+50,yPos-2) # line 2 line2 = yPos +40 self.passwordLabel.move(xPos,line2) self.passwordLineEdit.move(xPos+50,line2-2) self.loopLabel.move(colum2,line2) self.loopSpinBox.move(colum2+60,line2-2) # line3 line3 = yPos +80 self.userLabel.move(xPos,line3) self.userLineEdit.move(xPos+50,line3-2) self.radioReboot.move(colum2,line3) self.radioFactory.move(colum2+60,line3) self.radioProvision.move(colum2,line3+30) self.webLabel.move(xPos,line3+40) self.webBtn.move(xPos+60,line3+35) # provWidget self.provWidget.resize(400,130) self.provWidget.move(xPos,line3+70) spaceY = 30 x = 0 y = 0 cl = 200 self.ver1Label.move(x,y) self.ver1LineEdit.move(x+50,y) self.ver2Label.move(x,y+spaceY) self.ver2LineEdit.move(x+50,y+spaceY) self.ver3Label.move(x,y+spaceY*2) self.ver3LineEdit.move(x+50,y+spaceY*2) self.dir1Label.move(cl,y) self.dir1LineEdit.move(cl+50,y) self.dir2Label.move(cl,y+spaceY) self.dir2LineEdit.move(cl+50,y+spaceY) self.dir3Label.move(cl,y+spaceY*2) self.dir3LineEdit.move(cl+50,y+spaceY*2) self.radioHttp.move(x,y+spaceY*3) self.radioHttps.move(x+50,y+spaceY*3) self.radioTftp.move(x+110,y+spaceY*3) self.radioFtp.move(x+160,y+spaceY*3) self.radioFtps.move(x+210,y+spaceY*3) self.radioWindow.move(x+265,y+spaceY*3) # advance widget self.advanceWidget.resize(300,400) x = 20 y = 20 space = 30 self.checkCoreBox.move(x,y) self.cleanCache.move(x,y+space) self.checkProgressBox.move(x,y+space*2) self.checkShowChromeBox.move(x,y+space*3) self.apiCheckBox.move(x,y+space*4) self.corePathBtn.move(x-2,y+space*5-8) self.corePathLabel.move(x+35,y+space*5-8) y += 40 self.chromeLabel.move(x,y+space*4+10) self.selectChromeBtn.move(x-2,y+space*5) self.chromePathLabel.move(x+35,y+space*5+2) self.checkSaveLogBox.move(x,y+space*6+10) self.selectDirBtn.move(x-2,y+space*7) self.saveDirLabel.move(x+35,y+space*7+2) self.advanceOkBtn.move(x+170,y+space*10+10) self.interfaceMenu.move(x-5,y+space*8+10) self.pcapLabel.move(x,y+space*8-2) # self.aiOptionBox.move(x, y+space*9+8) # set size self.messageBox.resize(373,155) # self.widget.resize(410,400) self.loopSpinBox.resize(60,25) self.intervalSpinBox.resize(60,25) self.webBtn.resize(100,25) self.saveDirLabel.resize(185,22) self.selectDirBtn.resize(32,24) self.selectChromeBtn.resize(32,24) self.chromePathLabel.resize(185,22) self.infoLabel.resize(400, 25) self.corePathBtn.resize(32,24) self.corePathLabel.resize(185,22) # self.provWidget.hide() self.changePosition(True) self.widget.show() self.loadCache() # ----------------end of setupUI --------------------- def changePosition(self,hide): xPos = 20 if hide: buttonLine = 200 self.widget.resize(420,415) else: buttonLine = 310 self.widget.resize(420,524) self.startBtn.move(xPos,buttonLine) self.stopBtn.move(xPos+90,buttonLine) self.clearBtn.move(xPos+180,buttonLine) self.advanceBtn.move(xPos+270,buttonLine) self.messageBox.move(xPos,buttonLine+30) boxH = self.messageBox.height() self.infoLabel.move(xPos,buttonLine+boxH+35) def setItemEnable(self,enable): self.provWidget.setEnabled(enable) self.ipLineEdit.setEnabled(enable) self.passwordLineEdit.setEnabled(enable) self.userLineEdit.setEnabled(enable) self.intervalSpinBox.setEnabled(enable) self.loopSpinBox.setEnabled(enable) self.radioFactory.setEnabled(enable) self.radioReboot.setEnabled(enable) self.radioProvision.setEnabled(enable) self.advanceBtn.setEnabled(enable) self.startBtn.setEnabled(enable) self.clearBtn.setEnabled(enable) if self.useApiTest: self.webBtn.setEnabled(False) else: self.webBtn.setEnabled(enable) self.stopBtn.setEnabled(not enable) def outputError(self,str): appstr = "<span style=\"color:red\">" appstr += str + "</span>" self.messageBox.append(appstr) def outputWarning(self,str): appstr = "<span style=\"color:orange\">" appstr += str + "</span>" self.messageBox.append(appstr) def loadCache(self): file = QFile("cache") if not file.open(QIODevice.ReadOnly | QIODevice.Text): return inStream = QTextStream(file) # ip self.ipLineEdit.setText(inStream.readLine()) # passwordLabel self.passwordLineEdit.setText(inStream.readLine()) # user self.userLineEdit.setText(inStream.readLine()) # ver1 self.ver1LineEdit.setText(inStream.readLine()) self.dir1LineEdit.setText(inStream.readLine()) # ver2 self.ver2LineEdit.setText(inStream.readLine()) self.dir2LineEdit.setText(inStream.readLine()) # ver3 self.ver3LineEdit.setText(inStream.readLine()) self.dir3LineEdit.setText(inStream.readLine()) self.intervalSpinBox.setValue(int(inStream.readLine())) self.loopSpinBox.setValue(int(inStream.readLine())) # web type button webType = inStream.readLine() if webType == "gxpAction": self.grp2602Action.setChecked(False) self.gxpAction.setChecked(True) self.webBtn.setText(self.gxpAction.text()) else: self.grp2602Action.setChecked(True) self.gxpAction.setChecked(False) self.webBtn.setText(self.grp2602Action.text()) testType = inStream.readLine() if testType == "reboot": self.radioReboot.setChecked(True) elif testType == "provision": self.radioProvision.setChecked(True) self.changePosition(False) self.provWidget.show() else: self.radioFactory.setChecked(True) serverType = inStream.readLine() if serverType == "Http": self.radioHttp.setChecked(True) elif serverType == "Https": self.radioHttps.setChecked(True) elif serverType == "Tftp": self.radioTftp.setChecked(True) elif serverType == "Ftp": self.radioFtp.setChecked(True) elif serverType == "Ftps": self.radioFtps.setChecked(True) else: self.radioWindow.setChecked(True) if inStream.readLine() == "True": self.findCoreStop = True else: self.findCoreStop = False if inStream.readLine() == "True": self.cleanCacheSet = True else: self.cleanCacheSet = False if inStream.readLine() == "True": self.useApiTest = True self.webBtn.setEnabled(False) else: self.useApiTest = False self.corePathBtn.hide() self.corePathLabel.hide() if inStream.readLine() == "True": self.showTestProgress = True else: self.showTestProgress = False self.infoLabel.hide() if inStream.readLine() == "True": self.showChrome = True else: self.showChrome = False self.chromePath = inStream.readLine() if inStream.readLine() == "True": self.saveTestLog = True else: self.saveTestLog = False self.saveLogPath = inStream.readLine() self.coreDumpPath = inStream.readLine() file.close() def saveCache(self): file = QFile("cache") if not file.open(QIODevice.WriteOnly): return content = self.ipLineEdit.text() + "\n" content += self.passwordLineEdit.text() + "\n" content += self.userLineEdit.text() + "\n" content += self.ver1LineEdit.text() + "\n" content += self.dir1LineEdit.text() + "\n" content += self.ver2LineEdit.text() + "\n" content += self.dir2LineEdit.text() + "\n" content += self.ver3LineEdit.text() + "\n" content += self.dir3LineEdit.text() + "\n" content += str(self.intervalSpinBox.value()) + "\n" content += str(self.loopSpinBox.value()) + "\n" if self.gxpAction.isChecked(): content += "gxpAction\n" else: content += "grp2602Action\n" if self.radioReboot.isChecked(): content += "reboot\n" elif self.radioProvision.isChecked(): content += "provision\n" else: content += "factory\n" if self.radioHttp.isChecked(): content += "Http\n" elif self.radioHttps.isChecked(): content += "Https\n" elif self.radioTftp.isChecked(): content += "Tftp\n" elif self.radioFtp.isChecked(): content += "Ftp\n" elif self.radioFtps.isChecked(): content += "Ftps\n" else : content += "Window\n" content += str(self.findCoreStop) + "\n" content += str(self.cleanCacheSet) + "\n" content += str(self.useApiTest) + "\n" content += str(self.showTestProgress) + "\n" content += str(self.showChrome) + "\n" content += self.chromePath +"\n" content += str(self.saveTestLog) +"\n" content += self.saveLogPath +"\n" content += self.coreDumpPath +"\n" byteArr = bytes(content,"utf-8") file.write(QByteArray(byteArr)) file.close() def checkBeforeRun(self): containError = False #---------check Ip address-------------- if self.ipLineEdit.text() == "": containError = True self.outputError("IP地址不能为空!") else: pattern = re.compile("^((1[0-9][0-9]\.)|(2[0-4][0-9]\.)|(25[0-5]\.)|([1-9][0-9]\.)|([0-9]\.)){3}((1[0-9][0-9])|(2[0-4][0-9])|(25[0-5])|([1-9][0-9])|([0-9]))$") if not pattern.search(self.ipLineEdit.text()): containError = True self.outputError("IP地址格式错误,检查是否含有多余空格!(仅支持IPV4)") #------------------------ if self.passwordLineEdit.text() == "": containError = True self.outputError("密码不能为空!") if self.userLineEdit.text() == "": containError = True self.outputError("用户名不能为空!") if self.intervalSpinBox.value() <= 40: self.outputWarning("间隔时间过短,可能对测试造成影响") if not self.radioProvision.isChecked() and not self.radioReboot.isChecked() and \ not self.radioFactory.isChecked(): containError = True self.outputError("必须选择测试方式(重启,升降级,恢复出厂)") # check provision ---------- if self.radioProvision.isChecked(): if self.ver1LineEdit.text() == "" or self.ver2LineEdit.text() == "" or \ self.dir1LineEdit.text() == "" or self.dir2LineEdit.text() == "": containError = True self.outputError("升降级测试至少填上前两个版本及其路径") bin_name = "" if os.path.exists(os.path.abspath("config.ini") ): f = open(os.path.abspath("config.ini") , "r") line = f.readline() while line: option = line.split("=") if option[0] == "firmbinname": if option[1].strip('"') != "": filenamebin = option[1].strip() bin_name = filenamebin.strip('"') pass line = f.readline() f.close() filename = os.path.join(self.dir1LineEdit.text(), bin_name) if not os.path.exists(filename) and self.radioWindow.isChecked(): containError = True self.outputError("firmware1 文件不存在!" + filename) filename = os.path.join(self.dir2LineEdit.text(), bin_name) if not os.path.exists(filename) and self.radioWindow.isChecked(): containError = True self.outputError("firmware2 文件不存在!" + filename) filename = os.path.join(self.dir3LineEdit.text(), bin_name) if self.ver3LineEdit.text() != "" and self.dir3LineEdit.text() == "": containError = True self.outputError("填写了版本3,但对应路径为空!") if self.dir3LineEdit.text() != "" and self.ver3LineEdit.text() == "": containError = True self.outputError("填写了路径3,但对应版本为空!") elif self.dir3LineEdit.text() != "" and self.radioWindow.isChecked() and not os.path.exists(filename): containError = True self.outputError("firmware3 文件不存在!" + filename) if not self.radioFtp.isChecked() and not self.radioFtps.isChecked() and \ not self.radioHttp.isChecked() and not self.radioHttps.isChecked() and \ not self.radioTftp.isChecked() and not self.radioWindow.isChecked(): containError = True self.outputError("升降级测试必须选择服务器类型(Tftp,Ftp,Ftps,Http,Https)") return containError def startTest(self): ip = self.ipLineEdit.text() passwd = self.passwordLineEdit.text() username = self.userLineEdit.text() ptime = self.intervalSpinBox.value() loop = self.loopSpinBox.value() modelType = self.webBtn.text() if self.gxpAction.isChecked(): device_type = "GXP21XX" elif self.grp2602Action.isChecked(): device_type = "GRP260X" if self.radioReboot.isChecked(): task_type = "reboot" elif self.radioProvision.isChecked(): task_type = "provision" text_ver1 = self.ver1LineEdit.text() text_ver2 = self.ver2LineEdit.text() text_ver3 = self.ver3LineEdit.text() text_dir1 = self.dir1LineEdit.text() text_dir2 = self.dir2LineEdit.text() text_dir3 = self.dir3LineEdit.text() prov_dict = {"ver1": text_ver1.strip(), "dir1": text_dir1.strip(), "ver2": text_ver2.strip(), "dir2": text_dir2.strip(), "ver3": text_ver3.strip(), "dir3": text_dir3.strip()} if self.radioHttp.isChecked(): self.man.update_prov_setting("HTTP", prov_dict) elif self.radioHttps.isChecked(): self.man.update_prov_setting("HTTPS", prov_dict) elif self.radioTftp.isChecked(): self.man.update_prov_setting("TFTP", prov_dict) elif self.radioFtp.isChecked(): self.man.update_prov_setting("FTP", prov_dict) elif self.radioFtps.isChecked(): self.man.update_prov_setting("FTPS", prov_dict) elif self.radioWindow.isChecked(): self.man.update_prov_setting("browser", prov_dict) else: task_type = "reset" coredump_stop = True headless_flag = False clean_cache = False if self.checkCoreBox.isChecked() == False: self.messageBox.append("Find core dump will not stop") coredump_stop = False if self.cleanCache.isChecked() == True: clean_cache = True if self.checkShowChromeBox.isChecked() == True or self.radioWindow.isChecked() == True: headless_flag = False else: headless_flag = True # ai_mode = False # if self.aiOptionBox.isChecked() == True: # ai_mode = True browser_path = "" if self.chromePathLabel.text() != "": browser_path = self.chromePathLabel.text() self.testStatus = [True,1,0,"ip",""] print(self.interfaceMenu.currentText()) self.man.setStatus(self.testStatus) self.man.setMessageList(self.messageList) self.man.update_setting(ip.strip(), username.strip(), passwd.strip(), device_type, \ task_type, loop, ptime, coredump_stop, headless_flag, browser_path, \ clean_cache, self.interfaceMenu.currentText(), False) def startApiTest(self): ip = self.ipLineEdit.text() passwd = self.passwordLineEdit.text() username = self.userLineEdit.text() ptime = self.intervalSpinBox.value() loop = self.loopSpinBox.value() testType = "Reboot" if self.radioProvision.isChecked(): testType = "Provision" self.api.setValue(ip,username,passwd,ptime,loop,testType) v1 = self.ver1LineEdit.text() v2 = self.ver2LineEdit.text() v3 = self.ver3LineEdit.text() d1 = self.dir1LineEdit.text() d2 = self.dir2LineEdit.text() d3 = self.dir3LineEdit.text() self.api.setVersion(v1,v2,v3,d1,d2,d3) self.api.setTestStatus(self.testStatus,self.messageList,self.coreDumpPath) if self.radioHttp.isChecked(): self.api.setServerType("http") elif self.radioHttps.isChecked(): self.api.setServerType("https") elif self.radioTftp.isChecked(): self.api.setServerType("tftp") elif self.radioFtp.isChecked(): self.api.setServerType("ftp") else: #self.radioFtps.isChecked() self.api.setServerType("ftps") self.api.setFoundCoreStop(self.findCoreStop) # slot --------------------------------- def apiTestBoxCheck(self,state): if state == 2: self.corePathBtn.show() self.corePathLabel.show() else: self.corePathBtn.hide() self.corePathLabel.hide() def clickedCorePathBtn(self): dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择Core Dump存放路径","/home") if dir != "": self.corePathLabel.setText(dir) self.coreDumpPath = dir def clickedSelectDirBtn(self): dir = QFileDialog.getExistingDirectory(self.advanceWidget,"选择日志存放路径","/home") if dir != "": self.saveDirLabel.setText(dir) self.saveLogPath = dir def clickedSelectChromeBtn(self): fileName = QFileDialog.getOpenFileName(self.advanceWidget,"选择谷歌浏览器","/home","Chrome (*.exe)") if fileName != "": self.chromePathLabel.setText(fileName[0]) self.chromePath = fileName[0] def saveLogBoxCheck(self,state): if state == 2: # checked self.selectDirBtn.show() self.saveDirLabel.show() else: self.selectDirBtn.hide() self.saveDirLabel.hide() def clickedAdvanceOkBtn(self): self.findCoreStop = self.checkCoreBox.isChecked() self.cleanCacheSet = self.cleanCache.isChecked() self.useApiTest = self.apiCheckBox.isChecked() self.showTestProgress = self.checkProgressBox.isChecked() self.saveTestLog = self.checkSaveLogBox.isChecked() self.saveLogPath = self.saveDirLabel.text() self.showChrome = self.checkShowChromeBox.isChecked() self.chromePath = self.chromePathLabel.text() self.coreDumpPath = self.corePathLabel.text() if self.useApiTest: self.webBtn.setEnabled(False) self.corePathBtn.show() self.corePathLabel.show() else: self.webBtn.setEnabled(True) self.corePathBtn.hide() self.corePathLabel.hide() if self.showTestProgress: self.infoLabel.show() else: self.infoLabel.hide() self.saveCache() self.advanceWidget.hide() def clickedAdvanceBtn(self): self.advanceWidget.hide() self.checkCoreBox.setChecked(self.findCoreStop) self.cleanCache.setChecked(self.cleanCacheSet) self.apiCheckBox.setChecked(self.useApiTest) self.checkProgressBox.setChecked(self.showTestProgress) self.checkSaveLogBox.setChecked(self.saveTestLog) self.saveDirLabel.setText(self.saveLogPath) self.checkShowChromeBox.setChecked(self.showChrome) self.chromePathLabel.setText(self.chromePath) self.corePathLabel.setText(self.coreDumpPath) self.advanceWidget.show() def slotTestStoped(self): self.testStatus[0] = False self.setItemEnable(True) self.timer.stop() self.updateMessage() self.thread.quit() # save Test log if self.saveTestLog: fileName = time.strftime("%Y_%m_%d.%H_%M_%S.",time.localtime()) if self.radioReboot.isChecked(): fileName += "reboot" elif self.radioProvision: fileName += "provision" else: fileName += "factoryReset" fileName += ".htm" if self.saveLogPath == "": self.outputWarning("日志地址没有设置,无法保存") else: fileName = self.saveLogPath + "\\" + fileName print(fileName) file = QFile(fileName) if not file.open(QIODevice.WriteOnly): self.outputError("打开文件错误,保存日志失败") return byteArr = bytes(self.messageBox.toHtml(),"utf-8") file.write(QByteArray(byteArr)) file.close() def clickedClearBtn(self): self.ipLineEdit.setText("") self.passwordLineEdit.setText("") self.userLineEdit.setText("") self.ver1LineEdit.setText("") self.dir1LineEdit.setText("") self.ver2LineEdit.setText("") self.dir2LineEdit.setText("") self.ver3LineEdit.setText("") self.dir3LineEdit.setText("") def clickedStarBtn(self): if self.checkBeforeRun(): return self.messageBox.clear() self.saveCache() self.messageBox.append("Init Setting...") self.setItemEnable(False) self.timer.start(500) self.thread.start() # deside use what to test if self.useApiTest: if self.radioFactory.isChecked(): self.outputWarning("Api not support Factory Reset, will test as Gxp type web driver") self.clickedGxpType() self.startTest() self.signalRun.emit(self.man) else: self.startApiTest() self.signalRunApi.emit(self.api) else: self.startTest() self.signalRun.emit(self.man) def clickedStopBtn(self): self.stopBtn.setEnabled(False) self.testStatus[0] = False self.man.quit() self.outputWarning("正在停止...") def clickedProvision(self): self.provWidget.show() self.changePosition(False) def clickedOthers(self): self.provWidget.hide() self.changePosition(True) def clickedGxpType(self): self.gxpAction.setChecked(True) self.grp2602Action.setChecked(False) self.webBtn.setText(self.gxpAction.text()) def clickedGrp2602(self): self.gxpAction.setChecked(False) self.grp2602Action.setChecked(True) self.webBtn.setText(self.grp2602Action.text()) def updateMessage(self): while len(self.messageList) >0: info = self.messageList.popleft() self.messageBox.append(info) if self.testStatus[0] == False: self.infoLabel.setText("未开始测试") else: info = "第" + str(self.testStatus[1]) + "次测试 " if self.testStatus[2] > 0: info += "等待:{} 秒".format( self.testStatus[2] ) else: info += "运行中" info += " " + self.testStatus[3] info += " " + self.testStatus[4] self.infoLabel.setText(info)
class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.resize(1280, 720) self.central_widget = QWidget(MainWindow) self.splitter = QSplitter(self.central_widget) self.splitter.setChildrenCollapsible(False) self.splitter.setOpaqueResize(True) self.main_grid_layout = QGridLayout(self.central_widget) self.main_grid_layout.addWidget(self.splitter) self.main_grid_layout.setSizeConstraint(QLayout.SetDefaultConstraint) self.tab_widget = QTabWidget(self.central_widget) self.tab_widget.setMinimumSize(QSize(500, 0)) self._set_up_component_tree_view() self.splitter.addWidget(self.tab_widget) self._set_up_3d_view() MainWindow.setCentralWidget(self.central_widget) self._set_up_menus(MainWindow) self.tab_widget.setCurrentIndex(0) QMetaObject.connectSlotsByName(MainWindow) self.splitter.setStretchFactor(0, 0) self.splitter.setStretchFactor(1, 1) def _set_up_3d_view(self): self.sceneWidget.setMinimumSize(QSize(600, 0)) self.splitter.addWidget(self.sceneWidget) def _set_up_component_tree_view(self): self.sceneWidget = InstrumentView(self.splitter) self.component_tree_view_tab = ComponentTreeViewTab( scene_widget=self.sceneWidget, parent=self ) self.tab_widget.addTab(self.component_tree_view_tab, "") def _set_up_menus(self, MainWindow: QObject): self.menu_bar = QMenuBar() self.menu_bar.setGeometry(QRect(0, 0, 1280, 720)) self.file_menu = QMenu(self.menu_bar) MainWindow.setMenuBar(self.menu_bar) self.status_bar = QStatusBar(MainWindow) MainWindow.setStatusBar(self.status_bar) self.open_json_file_action = QAction(MainWindow) self.open_json_file_action.setShortcut(QKeySequence("Ctrl+O")) self.export_to_filewriter_JSON_action = QAction(MainWindow) self.export_to_filewriter_JSON_action.setShortcut(QKeySequence("Ctrl+S")) self.file_menu.addAction(self.open_json_file_action) self.file_menu.addAction(self.export_to_filewriter_JSON_action) self.view_menu = QMenu(self.menu_bar) self.show_action_labels = QAction(MainWindow) self.show_action_labels.setCheckable(True) self.show_action_labels.setChecked(True) self.simple_tree_view = QAction(MainWindow) self.simple_tree_view.setCheckable(True) self.about_window = QAction(MainWindow) self.view_menu.addAction(self.about_window) self.view_menu.addAction(self.show_action_labels) self.view_menu.addAction(self.simple_tree_view) self.menu_bar.addAction(self.file_menu.menuAction()) self.menu_bar.addAction(self.view_menu.menuAction()) self._set_up_titles(MainWindow) def _set_up_titles(self, MainWindow): MainWindow.setWindowTitle("NeXus Constructor") self.tab_widget.setTabText( self.tab_widget.indexOf(self.component_tree_view_tab), "Nexus Structure" ) self.file_menu.setTitle("File") self.open_json_file_action.setText("Open File writer JSON file") self.export_to_filewriter_JSON_action.setText("Export to File writer JSON") self.view_menu.setTitle("View") self.show_action_labels.setText("Show Button Labels") self.simple_tree_view.setText("Use Simple Tree Model View") self.about_window.setText("About") self.menu_bar.setNativeMenuBar(False)
class MineSweeperWindow(QMainWindow): def __init__(self, mode): super().__init__() self.ms = mode self.init_ui() self.set_menu() def init_ui(self): """初始化游戏界面""" # 1.确定游戏界面的标题,大小和背景颜色 self.setObjectName('MainWindow') self.setWindowTitle('扫雷') self.setWindowIcon(QIcon(':/minesweeper.ico')) self.setFixedSize(50 * self.ms.length + 100, 50 * self.ms.width + 180) self.setStyleSheet('#MainWindow{background-color: #f6edd2}') self.remain_boom = QLCDNumber(2, self) self.remain_boom.move(50, 50) self.remain_boom.setFixedSize(60, 50) self.remain_boom.setStyleSheet( "border: 2px solid blue; color: red; background: black;") self.remain_boom.display( '{:>02d}'.format(self.ms.b_num if self.ms.b_num >= 0 else 0)) self.timer = QBasicTimer() self.second = 0 self.time = QLCDNumber(3, self) self.time.move(50 * self.ms.length - 40, 50) self.time.setFixedSize(90, 50) self.time.setStyleSheet( "border: 2px solid blue; color: red; background: black;") self.time.display('{:>03d}'.format(self.second)) self.btn = QPushButton(self) self.btn.move(25 * self.ms.length + 20, 50) self.btn.setFixedSize(50, 50) self.btn.setIcon(QIcon(':/普通.png')) self.btn.setIconSize(QSize(45, 45)) self.btn.setStyleSheet('QPushButton{border:None}') self.btn.clicked.connect(self.restart) self.over_signal = 0 self.rank = sqlite3.connect('rank.db') self.c = self.rank.cursor() def set_menu(self): bar = self.menuBar() game = bar.addMenu('游戏(&G)') more_info = bar.addMenu('更多(&M)') new_game = QAction('新游戏(&N)', self) new_game.setShortcut('Ctrl+N') new_game.triggered.connect(self.start) game.addAction(new_game) restart = QAction('重玩(&R)', self) restart.setShortcut('Ctrl+R') restart.triggered.connect(self.restart) game.addAction(restart) game.addSeparator() self.modes = QActionGroup(self) self.easy = QAction('简单(E)', self) self.easy.setCheckable(True) game.addAction(self.modes.addAction(self.easy)) self.medium = QAction('中等(M)', self) self.medium.setCheckable(True) game.addAction(self.modes.addAction(self.medium)) self.hard = QAction('困难(H)', self) self.hard.setCheckable(True) game.addAction(self.modes.addAction(self.hard)) self.modes.triggered.connect( lambda: self.set_mode(self.modes.checkedAction())) if isinstance(self.ms, EasyMode): self.easy.setChecked(True) elif isinstance(self.ms, MediumMode): self.medium.setChecked(True) elif isinstance(self.ms, HardMode): self.hard.setChecked(True) rank = QAction('排行耪(&R)', self) rank.triggered.connect(self.show_rank) more_info.addAction(rank) def paintEvent(self, e): """绘制游戏内容""" qp = QPainter() def draw_map(): """绘制版面""" # background = QPixmap(':/背景2.png') # qp.drawPixmap(self.rect(), background) # qp.setBrush(QColor('#e8ff9d')) # qp.drawRect(50, 130, 50 * self.ms.length, 50 * self.ms.width) for x in range(0, self.ms.length, 2): for y in range(0, self.ms.width, 2): qp.setBrush(QColor('#007a15')) qp.drawRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50) for y in range(1, self.ms.width, 2): qp.setBrush(QColor('#00701c')) qp.drawRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50) for x in range(1, self.ms.length, 2): for y in range(0, self.ms.width, 2): qp.setBrush(QColor('#00701c')) qp.drawRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50) for y in range(1, self.ms.width, 2): qp.setBrush(QColor('#007a15')) qp.drawRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50) # qp.setPen(QPen(QColor(111, 108, 108), 2, Qt.SolidLine)) # for x in range(self.ms.length + 1): # qp.drawLine(50 * (x + 1), 130, 50 * (x + 1), 50 * self.ms.width + 130) # for y in range(self.ms.width + 1): # qp.drawLine(50, 50 * (y + 1) + 80, 50 * self.ms.length + 50, 50 * (y + 1) + 80) def draw_blanks(): qp.setBrush(QColor('#f4f4f4')) for x in range(self.ms.length): for y in range(self.ms.width): if isinstance(self.ms.g_map[y][x], str): if self.ms.g_map[y][x] == '0$' or self.ms.g_map[y][ x] == '1$': # qp.setPen(QPen(QColor(219, 58, 58), 1, Qt.SolidLine)) # qp.setFont(QFont('Kai', 15)) flag = QPixmap(':/雷旗.png').scaled(50, 50) qp.drawPixmap( QRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50), flag) # qp.drawText(50 * (x + 1) + 18, 50 * (y + 1) + 115, '$') continue qp.setPen(QPen(QColor('black'), 1, Qt.SolidLine)) qp.setFont(QFont('Kai', 15)) qp.drawRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50) if self.ms.g_map[y][x] == '0': continue if self.ms.g_map[y][x] == '*': flag = QPixmap(':/土豆雷.png').scaled(50, 50) qp.drawPixmap( QRect(50 * (x + 1), 50 * (y + 1) + 80, 50, 50), flag) continue qp.setPen(QPen(QColor('black'), 5, Qt.SolidLine)) qp.drawText(50 * (x + 1) + 18, 50 * (y + 1) + 115, '{}'.format(self.ms.g_map[y][x])) qp.begin(self) draw_map() draw_blanks() qp.end() def mousePressEvent(self, e): """根据鼠标的动作,确定落子位置""" if self.over_signal == 1: return if e.button() in (Qt.LeftButton, Qt.RightButton): mouse_x = e.windowPos().x() mouse_y = e.windowPos().y() if 50 <= mouse_x <= 50 * self.ms.length + 50 and 130 <= mouse_y <= 50 * self.ms.width + 130: if self.ms.step == 0: self.timer.start(1000, self) self.tic = time_ns() game_x = int(mouse_x // 50) - 1 game_y = int((mouse_y - 80) // 50) - 1 else: return if e.buttons() == Qt.LeftButton | Qt.RightButton: self.ms.click_around(game_x, game_y) elif e.buttons() == Qt.LeftButton: self.ms.click(game_x, game_y) else: self.ms.mark_mine(game_x, game_y) if self.ms.boom: self.timer.stop() self.btn.setIcon(QIcon(':/哭脸.png')) self.btn.setIconSize(QSize(45, 45)) self.repaint(0, 0, 50 * self.ms.length + 100, 50 * self.ms.width + 180) self.over_signal = 1 return elif self.ms.game_judge(): self.timer.stop() self.toc = time_ns() self.btn.setIconSize(QSize(45, 45)) self.btn.setIcon(QIcon(':/笑脸.png')) self.repaint(0, 0, 50 * self.ms.length + 100, 50 * self.ms.width + 180) self.check_rank() self.over_signal = 1 return self.repaint(0, 0, 50 * self.ms.length + 100, 50 * self.ms.width + 180) self.remain_boom.display( '{:>02d}'.format(self.ms.b_num if self.ms.b_num >= 0 else 0)) def timerEvent(self, e) -> None: self.second += 1 self.time.display('{:>03d}'.format(self.second)) def set_mode(self, action: QAction): if action == self.easy: self.close() self.msw = MineSweeperWindow(EasyMode()) self.msw.show() elif action == self.medium: self.close() self.msw = MineSweeperWindow(MediumMode()) self.msw.show() elif action == self.hard: self.close() self.msw = MineSweeperWindow(HardMode()) self.msw.show() def show_rank(self): self.sk = ShowRank() self.sk.show() def start(self): self.close() self.a = Start() self.a.show() def restart(self): self.ms.refresh() self.repaint() self.btn.setIcon(QIcon(':/普通.png')) self.remain_boom.display( '{:>02d}'.format(self.ms.b_num if self.ms.b_num >= 0 else 0)) self.second = 0 self.timer.stop() self.time.display('{:>03d}'.format(self.second)) self.over_signal = 0 def check_rank(self): a_num = (self.toc - self.tic) / 10**9 out = subprocess.check_output("whoami").decode("gbk") name = re.search(r"\\(.+)\r\n", out) a_user = name.group(1) for i in range(5, 0, -1): if isinstance(self.ms, EasyMode): mode = "Easy" elif isinstance(self.ms, MediumMode): mode = "Medium" elif isinstance(self.ms, HardMode): mode = "Hard" else: return self.c.execute("SELECT * FROM {} WHERE id=?;".format(mode), (i, )) feedback = self.c.fetchone() if i == 5: if (not feedback[2]) or (feedback[2] > a_num): a_user, _ = QInputDialog.getText(self, "用户名", "请输入用户名:", QLineEdit.Normal, text=a_user) self.c.execute( "UPDATE {} SET user=?, time=? WHERE id=?;".format( mode), (a_user, a_num, i)) self.rank.commit() continue else: return else: if (not feedback[2]) or (feedback[2] > a_num): self.c.execute( "UPDATE {0} " "SET user = (SELECT user FROM {0} WHERE id=?), " "time = (SELECT time FROM {0} WHERE id=?)" "WHERE id=? ;".format(mode), (i, i, i + 1)) self.c.execute( "UPDATE {} SET user=?, time=? WHERE id=? ;".format( mode), (a_user, a_num, i)) self.rank.commit() else: return def closeEvent(self, e): self.rank.commit() self.rank.close()
class ConsoleWidget(QTextEdit): def __init__(self): super().__init__() self.setReadOnly(True) self.setWordWrapMode(QTextOption.NoWrap) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.setStyleSheet(''' QTextEdit { background-color: #2b2b2b; font-family: "Consolas"; font-size: 10pt; color: #bbbbbb; border: 0; white-space: pre; }''') self.handlerObj = RichTextHandlerObject() self.handler = RichTextHandler(self.handlerObj) self.handler.setFormatter(ConsoleFormatter()) self.handlerObj.wroteLine.connect(self.write) g.log.addHandler(self.handler) self.actionCopy = QAction( '&Copy', self, shortcut=QKeySequence.Copy, statusTip='Copy selected text to the clipboard', triggered=self.copy) self.actionSelectAll = QAction( 'Select &All', self, shortcut=QKeySequence.SelectAll, statusTip='Select all text in the output window', triggered=self.selectAll) self.actionScrollToEnd = QAction( 'Scroll to &End', self, statusTip='Move the cursor to the end of the output', triggered=self.scrollToEnd) self.actionWordWrap = QAction('&Word Wrap', self, statusTip='Toggle word wrap', checkable=True, triggered=self.toggleWordWrap) self.actionClear = QAction('C&lear', self, statusTip='Clear the output window', triggered=self.clear) @Slot(str) def write(self, line): self.append(line) self.scrollToEnd() def scrollToEnd(self): self.moveCursor(QTextCursor.End) self.moveCursor(QTextCursor.StartOfLine) def toggleWordWrap(self): if self.wordWrapMode() == QTextOption.NoWrap: self.setWordWrapMode(QTextOption.WordWrap) else: self.setWordWrapMode(QTextOption.NoWrap) def clear(self): self.setText('') self.scrollToEnd() def contextMenuEvent(self, event): menu = QMenu(self) menu.addAction(self.actionCopy) menu.addAction(self.actionSelectAll) menu.addAction(self.actionScrollToEnd) menu.addSeparator() self.actionWordWrap.setChecked( self.wordWrapMode() != QTextOption.NoWrap) menu.addAction(self.actionWordWrap) menu.addAction(self.actionClear) menu.exec_(event.globalPos())
def buttonActions(self, action: str) -> QAction: addAct = QAction(QIcon().fromTheme("list-add"), "&Add to collection...", self) addAct.setShortcut("Ctrl+A") addAct.setToolTip("Add to collection") addAct.triggered.connect(self.addToCollection) delText = "&Delete row" currentTab = self.tab.currentIndex() if 0 < currentTab < 4: if len(self.tableViewList[currentTab-1].selectedIndexes()) > 1: delText += "s" delAct = QAction(QIcon().fromTheme("edit-delete"), delText, self) delAct.setToolTip("Delete from collection") delAct.triggered.connect(self.deleteFromCollection) detAct = QAction(QIcon.fromTheme("text-x-generic-template"), "Details...", self) detAct.setToolTip("Open details side-panel") detAct.triggered.connect(self.tableViewList[currentTab-1].rowData) expAct = QAction(QIcon.fromTheme("text-x-generic-template"), "&Export as csv...", self) expAct.setShortcut("Ctrl+E") expAct.setToolTip("Export table as CSV file") expAct.triggered.connect(self.exportToCSV) impAct = QAction(QIcon.fromTheme("insert-object"), "&Import platform template...", self) impAct.setShortcut("Ctrl+I") impAct.setToolTip("Import games to database") impAct.triggered.connect(self.importToDatabase) stmAct = QAction(QIcon.fromTheme("insert-object"), "Import Steam Library...", self) stmAct.triggered.connect(self.importSteamLibrary) ownAct = QAction("Hide games not in collection", self) ownAct.setCheckable(True) ownAct.setChecked(True) ownAct.triggered.connect(self.toggleOwnedFilter) delNotOwned = QAction(QIcon().fromTheme("edit-delete"), "Remove items not in collection", self) delNotOwned.setToolTip("Remove items that are not owned from database") delNotOwned.triggered.connect(self.deleteNotOwned) aboutAct = QAction(QIcon.fromTheme("help-about"), "Abou&t", self) aboutAct.setToolTip("About Game Collection Manager") aboutAct.triggered.connect(self.about) exitAct = QAction(QIcon.fromTheme("application-exit"), "&Exit", self) exitAct.setShortcut("Ctrl+Q") exitAct.setToolTip("Exit application") exitAct.triggered.connect(self.close) infoAct = QAction("Debug: Print row info", self) infoAct.triggered.connect(self.info) fetchAct = QAction("Fetch info for all games...", self) fetchAct.setToolTip("Tries to fetch info for all games from MobyGames") fetchAct.triggered.connect(self.fetchInfo) valAct = QAction("Total value of collection", self) valAct.setToolTip("Rough estimate of the total value of collection") valAct.triggered.connect(self.totalValue) act = {"add": addAct, "del": delAct, "det": detAct, "export": expAct, "import": impAct, "steam": stmAct, "owned": ownAct, "delnotowned": delNotOwned, "about": aboutAct, "exit": exitAct, "info": infoAct, "fetch": fetchAct, "value": valAct} return act.get(action)
def main(): logging.info('main') app = QApplication(sys.argv) app.setQuitOnLastWindowClosed(False) appctxt = AppContext(app) sentry_sdk.init( "https://[email protected]/5210435", shutdown_timeout=5, default_integrations=False, # Either pyqt or pyinstaller do weird things with sentry, # need to explicitely specify these else sentry fails integrations=[ LoggingIntegration(), StdlibIntegration(), ExcepthookIntegration(), DedupeIntegration(), AtexitIntegration(), ModulesIntegration(), ArgvIntegration(), ThreadingIntegration(), ]) instance = SingleInstance() print('instance', instance) logger = get_logging(appctxt.build_settings['debug']) build_msg = "Production" if appctxt.is_frozen else "Development" logger.info( f"PWUploader, version: {appctxt.build_settings['version']}, {build_msg} build" ) logging.debug(f'config {CONFIG.as_dict()}') signal.signal(signal.SIGINT, signal.SIG_DFL) logo_path = appctxt.get_resource('logo.png') logging.debug(f'logo_path: {logo_path}') icon = QIcon(logo_path) tray = QSystemTrayIcon() tray.setIcon(icon) logging.debug('tray: %s', tray) tray.show() menu = QMenu() # left-click should just open the menu too def on_systray_activated(reason): if reason == 3: menu.popup(QCursor.pos()) tray.activated.connect(on_systray_activated) action0 = QAction(f"Version: v{appctxt.build_settings['version']}") menu.addAction(action0) action2 = QAction('settings') action2.triggered.connect(on_settings(appctxt)) menu.addAction(action2) action3 = QAction('resync files') def connect_missing_files(): upload_missing_files(appctxt, remote_config) action3.triggered.connect(connect_missing_files) menu.addAction(action3) action4 = QAction('open log dir') action4.triggered.connect(on_open_logdir) menu.addAction(action4) def toggle_always_running(state): if state: CONFIG.set('always_running', True) start_guardian_detached() else: CONFIG.set('always_running', False) kill_guardian() with open(CONFIG_FILE, 'w') as f: f.write(json.dumps(CONFIG.as_dict(), indent=2)) logging.info('config saved') action5 = QAction('always running', checkable=True) if CONFIG.get('always_running'): action5.setChecked(True) action5.triggered.connect(toggle_always_running) menu.addAction(action5) action1 = QAction("quit") action1.triggered.connect(on_quit) menu.addAction(action1) tray.setContextMenu(menu) # FIXME get this after app display if possible for i in range(10): api = PWAPI(appctxt, appctxt.build_settings['api_url'], CONFIG.get('api_token'), CONFIG.get('account_id')) remote_config = api.get_config() if 'detail' in remote_config: logging.error('Invalid remote config %s', remote_config) message = 'Unable to reach Pathology Watch API for authentication, are your API Token & Lab ID correct? Click ok to open settings.' response = QtWidgets.QMessageBox.question( None, 'API Error', message, QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.Cancel) if response == QtWidgets.QMessageBox.Cancel: sys.exit(0) settings_dialog = SettingsDialog(appctxt, CONFIG, CONFIG_FILE) settings_dialog.ui.setWindowModality(QtCore.Qt.ApplicationModal) settings_dialog.ui.exec_() else: break time.sleep(1) if remote_config is not None: clean_remote_config = { k: v for k, v in remote_config.items() if 'secret' not in k } logging.debug(f'remote_config {clean_remote_config}') # TODO verify remote_config if it's not set, check api token or pw connectivity else: logging.error( 'Uploader settings invalid or server isn\'t configured, contact [email protected]' ) sys.exit(1) # FIXME need to validate remote_config, config logging.info('Starting upload watcher') watcher_thread = WatcherThread(CONFIG.get('watch_dir'), trigger_upload) watcher_thread.finished.connect(app.exit) watcher_thread.start() logging.info('Starting heartbeat thread') heartbeat_thread = HeartbeatThread(appctxt, remote_config, CONFIG, upload_missing_files) heartbeat_thread.finished.connect(app.exit) heartbeat_thread.start() worker_threads = [] for i in range(appctxt.build_settings['n_upload_worker_threads']): logging.info(f'Starting worker {i}') worker_thread = WorkerThread(appctxt, remote_config, CONFIG, UPLOAD_QUEUE) worker_thread.finished.connect(app.exit) worker_thread.start() worker_threads.append(worker_thread) #def excepthook(exc_type, exc_value, exc_tb): # import traceback # tb = "".join(traceback.format_exception(exc_type, exc_value, exc_tb)) # logging.error("error caught: %s", str(tb)) # capture_exception(exc_type) #sys.excepthook = excepthook exit_code = -1 delay = 2 for i in range(5): logging.info('Starting') exit_code = app.exec_() if exit_code == 0: break logging.info(f'Exit loop {exit_code}, sleeping {delay}') time.sleep(delay) delay = delay**2 logging.info(f'Exited: {exit_code}') sys.exit(exit_code)
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) view = QGraphicsView() self.scene = QGraphicsScene() self.scene.setSceneRect(QRectF(0, 0, *WINDOW_SIZE)) felt = QBrush(QPixmap(os.path.join('images', 'felt.png'))) self.scene.setBackgroundBrush(felt) name = QGraphicsPixmapItem() name.setPixmap(QPixmap(os.path.join('images', 'ronery.png'))) name.setPos(QPointF(170, 375)) self.scene.addItem(name) view.setScene(self.scene) # Timer for the win animation only. self.timer = QTimer() self.timer.setInterval(5) self.timer.timeout.connect(self.win_animation) self.animation_event_cover = AnimationCover() self.scene.addItem(self.animation_event_cover) menu = self.menuBar().addMenu("&Game") deal_action = QAction( QIcon(os.path.join('images', 'playing-card.png')), "Deal...", self) deal_action.triggered.connect(self.restart_game) menu.addAction(deal_action) menu.addSeparator() deal1_action = QAction("1 card", self) deal1_action.setCheckable(True) deal1_action.triggered.connect(lambda: self.set_deal_n(1)) menu.addAction(deal1_action) deal3_action = QAction("3 card", self) deal3_action.setCheckable(True) deal3_action.setChecked(True) deal3_action.triggered.connect(lambda: self.set_deal_n(3)) menu.addAction(deal3_action) dealgroup = QActionGroup(self) dealgroup.addAction(deal1_action) dealgroup.addAction(deal3_action) dealgroup.setExclusive(True) menu.addSeparator() rounds3_action = QAction("3 rounds", self) rounds3_action.setCheckable(True) rounds3_action.setChecked(True) rounds3_action.triggered.connect(lambda: self.set_rounds_n(3)) menu.addAction(rounds3_action) rounds5_action = QAction("5 rounds", self) rounds5_action.setCheckable(True) rounds5_action.triggered.connect(lambda: self.set_rounds_n(5)) menu.addAction(rounds5_action) roundsu_action = QAction("Unlimited rounds", self) roundsu_action.setCheckable(True) roundsu_action.triggered.connect(lambda: self.set_rounds_n(None)) menu.addAction(roundsu_action) roundgroup = QActionGroup(self) roundgroup.addAction(rounds3_action) roundgroup.addAction(rounds5_action) roundgroup.addAction(roundsu_action) roundgroup.setExclusive(True) menu.addSeparator() quit_action = QAction("Quit", self) quit_action.triggered.connect(self.quit) menu.addAction(quit_action) self.deck = [] self.deal_n = 3 # Number of cards to deal each time self.rounds_n = 3 # Number of rounds (restacks) before end. for suit in SUITS: for value in range(1, 14): card = Card(value, suit) self.deck.append(card) self.scene.addItem(card) card.signals.doubleclicked.connect( lambda card=card: self.auto_drop_card(card)) self.setCentralWidget(view) self.setFixedSize(*WINDOW_SIZE) self.deckstack = DeckStack() self.deckstack.setPos(OFFSET_X, OFFSET_Y) self.scene.addItem(self.deckstack) # Set up the working locations. self.works = [] for n in range(7): stack = WorkStack() stack.setPos(OFFSET_X + CARD_SPACING_X * n, WORK_STACK_Y) self.scene.addItem(stack) self.works.append(stack) self.drops = [] # Set up the drop locations. for n in range(4): stack = DropStack() stack.setPos(OFFSET_X + CARD_SPACING_X * (3 + n), OFFSET_Y) stack.signals.complete.connect(self.check_win_condition) self.scene.addItem(stack) self.drops.append(stack) # Add the deal location. self.dealstack = DealStack() self.dealstack.setPos(OFFSET_X + CARD_SPACING_X, OFFSET_Y) self.scene.addItem(self.dealstack) # Add the deal click-trigger. dealtrigger = DealTrigger() dealtrigger.signals.clicked.connect(self.deal) self.scene.addItem(dealtrigger) self.shuffle_and_stack() self.setWindowTitle("Ronery") self.show()
class ViewMenu(QMenu): font_size_setting = 0 # 0, 1, 2 Small, Standard, Big def __init__(self, ui, menu_name: str = _('Ansicht')): super(ViewMenu, self).__init__(menu_name, ui) self.ui = ui # --- App style --- self.addSeparator().setText(_('Anwendungs-Stil')) style_grp = QActionGroup(self) self.default_style = QAction(_('Standard'), style_grp) self.default_style.setCheckable(True) self.default_style.triggered.connect(self.switch_default_style) self.dark_style = QAction(_('Dunkel'), style_grp) self.dark_style.setCheckable(True) self.dark_style.triggered.connect(self.switch_dark_style) self.addActions([self.default_style, self.dark_style]) # --- Font Size --- self.addSeparator().setText(_('Schrifgröße')) font_grp = QActionGroup(self) self.small_font = QAction(_('Klein'), font_grp) self.small_font.setCheckable(True) self.default_font = QAction(_('Standard'), font_grp) self.default_font.setCheckable(True) self.big_font = QAction(_('Groß'), font_grp) self.big_font.setCheckable(True) font_grp.triggered.connect(self.switch_font_size) self.addActions([self.small_font, self.default_font, self.big_font]) self.display_current_style() def display_current_style(self): """ Set action checked according to KnechtSettings """ if KnechtSettings.app.get('app_style') == 'fusion-dark': self.dark_style.setChecked(True) else: self.default_style.setChecked(True) if KnechtSettings.app['font_size'] == FontRsc.small_pixel_size: self.small_font.setChecked(True) self.font_size_setting = 0 elif KnechtSettings.app['font_size'] == FontRsc.regular_pixel_size: self.default_font.setChecked(True) self.font_size_setting = 1 elif KnechtSettings.app['font_size'] == FontRsc.big_pixel_size: self.big_font.setChecked(True) self.font_size_setting = 2 def switch_default_style(self): self.ui.app.set_default_style() self.set_app_font(self.font_size_setting) def switch_dark_style(self): self.ui.app.set_dark_style() self.set_app_font(self.font_size_setting) def switch_font_size(self, action: QAction): action.setChecked(True) if action is self.small_font: self.set_app_font(0) elif action is self.default_font: self.set_app_font(1) elif action is self.big_font: self.set_app_font(2) def set_app_font(self, size: int=0): self.font_size_setting = size if size == 0: font_size = FontRsc.small_pixel_size elif size == 1: font_size = FontRsc.regular_pixel_size elif size == 2: font_size = FontRsc.big_pixel_size KnechtSettings.app['font_size'] = font_size FontRsc.init(font_size) self.ui.app.setFont(FontRsc.regular) title = _('Neustart') msg = _('Anwendungstil geändert. Für eine vollständige Übernahme muss die Anwendung neu gestartet werden.<br>' 'Anwendung jetzt neustarten?') ok_btn = _('Neustarten') no_btn = _('Später neustarten..') msg_box = AskToContinue(self.ui) if msg_box.ask(title, msg, ok_btn, no_btn): restart_knecht_app(self.ui)
def init_ui(self): self.setWindowTitle('Trace Event Window') self.setGeometry(100, 100, 800, 600) bar = self.menuBar() file_ = bar.addMenu('File') export_log = QAction('Save to File', self, triggered=lambda: self.save_log()) options = bar.addMenu('Options') auto_refresh = QAction( 'Auto Refresh', self, checkable=True, triggered=lambda: self.timer.start(100) if auto_refresh.isChecked() else self.timer.stop()) auto_refresh.setChecked(True) options.addAction(auto_refresh) file_.addAction(export_log) vgrid = QVBoxLayout() grid = QHBoxLayout() self.tree = QTreeWidget() self.tree.setHeaderLabels(['Name']) self.top = [] self.lst = [] for n, event in enumerate(self.trace_events): word = event.split('_')[0] if word not in self.top: self.top.append(word) item = QTreeWidgetItem(self.tree) self.lst.append(item) item.setText(0, word) subitem = QTreeWidgetItem(item) subitem.setText(0, ' ' + event.split(' : ')[0]) # subitem.setCheckState(0, Qt.Unchecked) cbox = QCheckBox() cbox.stateChanged.connect(lambda state, text=subitem.text(0): self. handle_checked(state, text)) self.tree.setItemWidget(subitem, 0, cbox) # self.tree.setColumnWidth(0, 25) self.tracelist = QLabel() self.disp_output() self.traceview = QScrollArea() self.traceview.setWidget(self.tracelist) self.traceview.setWidgetResizable(True) search = QHBoxLayout() self.search_bar = QLineEdit(self) self.completer = QCompleter(self.top, self) self.completer.setCaseSensitivity(Qt.CaseInsensitive) self.search_bar.setCompleter(self.completer) search_button = QPushButton('Search') search_button.clicked.connect(lambda: self.tree.setCurrentItem( self.lst[self.top.index(self.search_bar.text())])) expand = QPushButton('▼') expand.setFixedSize(QSize(25, 25)) expand.clicked.connect(lambda: self.tree.expandAll()) collapse = QPushButton('▲') collapse.setFixedSize(QSize(25, 25)) collapse.clicked.connect(lambda: self.tree.collapseAll()) self.search_bar.returnPressed.connect(lambda: search_button.click()) search.addWidget(self.search_bar) search.addWidget(search_button) search.addWidget(expand) search.addWidget(collapse) self.digest = QLabel() vgrid.addLayout(search) vgrid.addWidget(self.tree) vgridwid = QWidget() vgridwid.setLayout(vgrid) split = QSplitter(Qt.Horizontal) split.addWidget(vgridwid) split.addWidget(self.traceview) split.setStretchFactor(1, 1) # grid.addLayout(vgrid) grid.addWidget(split) # grid.addWidget(self.tracelist) self.disp_output() center = QWidget() center.setLayout(grid) self.setCentralWidget(center) self.show()
class MainWindow(QMainWindow): def __init__(self, parent=None, flags=None): super().__init__(parent) self.project_filename = None self.project_widget = ProjectWidget(self) self.setCentralWidget(self.project_widget) self.project_widget.project.curves_collection_changed.connect( self.curvesCollectionChanged) self.createActions() self.createToolBars() self.createStatusBar() self.createMenus() self.createDockWindows() self.setWindowTitle("WD Wrapper") self.readWindowSettings() self.readAppState() # open last fits # try: # self.openLastFits() # except FileNotFoundError: # print('Can not open last file') @Slot(Container) def currentItemChanged(self, item: Container): if isinstance(item, ParentColumnContainer): item = item.parent() self.delCurveAct.setEnabled(isinstance(item, CurveContainer)) @Slot(CurveContainer) def curvesCollectionChanged(self, curves_model: CurvesModel): self.createPlotMenu(source=curves_model) # QMessageBox.information(self, 'collection changed', 'DEBUG: collection changed') @Slot(CurveContainer) def curvesPlotChanged(self, curves_model: CurvesModel): self.checkPlotInPlotMenu(source=curves_model) # QMessageBox.information(self, 'collection changed', 'DEBUG: collection changed') def closeEvent(self, event: PySide2.QtGui.QCloseEvent): self.writeAppState() self.writeWindowSettings() super().closeEvent(event) def keyPressEvent(self, e): # if e.key() == Qt.Key_Delete: # self.deleteSelected() pass def print_(self): document = self.textEdit.document() printer = QPrinter() dlg = QPrintDialog(printer, self) if dlg.exec_() != QDialog.Accepted: return document.print_(printer) self.statusBar().showMessage("Ready", 2000) @Slot() def open_lcin(self): file_name, _ = QFileDialog.getOpenFileName(self.centralWidget(), "Open lc.in", "./", "lc.in (*)") if file_name: try: self.project_widget.project.load_bundle(file_name) logger().info(f'lc.in file opened, path: {file_name}') except (ValueError, FileFormatNotSupportedError, FileFormatMultipleSetsError) as e: logger().exception( f'lc.in file f{file_name} loading failed. Input file format error', exc_info=e) msg = QMessageBox() msg.setWindowTitle('File loading failed') msg.setText('Input file format error:') msg.setInformativeText(str(e)) msg.exec_() @Slot() def save_lcin(self): file_name, _ = QFileDialog.getSaveFileName(self, "Save lc.in", "lc.in", "lc.in (*)") if file_name: self.project_widget.project.save_bundle(file_name) logger().info(f'lc.in file saved, path: {file_name}') @Slot() def open_prj(self): filename, _ = QFileDialog.getOpenFileName( self, 'Open Project', './', 'WD Wrapper Project (*.wdw)') if filename: if self.open_prj_from_file(filename): self.setWindowTitle(os.path.basename(filename)) self.project_filename = filename @Slot() def save_prj_as(self): filename, _ = QFileDialog.getSaveFileName( self, "Save Project", './project.wdw', "WD Wrapper Project (*.wdw)") if filename: self.project_filename = filename self.setWindowTitle(os.path.basename(filename)) self.save_prj_to_file(self.project_filename, mode='gui save as') @Slot() def save_prj(self): if self.project_filename is None: self.save_prj_as() else: self.save_prj_to_file(self.project_filename, mode='gui save') def save_prj_to_file(self, filename, mode): self.project_widget.project.save_project(filename, mode=mode) logger().info(f'Project saved, path: {filename}') def open_prj_from_file(self, filename): if self.project_widget.project.open_project(filename): logger().info(f'Project opened, path: {filename}') return True else: logger().error(f'Project opening failed, path: {filename}') return False def save(self): filename, _ = QFileDialog.getSaveFileName(self, "Choose a file name", '.', "HTML (*.html *.htm)") if not filename: return file = QFile(filename) if not file.open(QFile.WriteOnly | QFile.Text): QMessageBox.warning( self, "Dock Widgets", "Cannot write file %s:\n%s." % (filename, file.errorString())) return out = QTextStream(file) QApplication.setOverrideCursor(Qt.WaitCursor) out << self.textEdit.toHtml() QApplication.restoreOverrideCursor() self.statusBar().showMessage("Saved '%s'" % filename, 2000) def readAppState(self): # if self.tedaCommandLine.ignoreSettings: # return settings = QSettings() self.project_widget.restore_session(settings) # settings.beginGroup("WCS") # self.wcsSexAct.setChecked(bool(settings.value("sexagesimal", True))) # self.wcsGridAct.setChecked(bool(settings.value("grid", False))) # settings.endGroup() def writeAppState(self): # if self.tedaCommandLine.ignoreSettings: # return settings = QSettings() self.project_widget.save_session(settings) # settings.beginGroup("WCS") # settings.setValue("sexagesimal", self.wcsSexAct.isChecked()) # settings.setValue("grid", self.wcsGridAct.isChecked()) # settings.endGroup() def undo(self): document = self.textEdit.document() document.undo() def about(self): QMessageBox.about( self, "WD Wrapper", f"WD Wrapper {__version__} <br/>" "Authors: <ul> " "<li>Mikołaj Kałuszyński</li>" "</ul>" "Created by <a href='https://akond.com'>Akond Lab</a> for The Araucaria Project" ) # def on_console_show(self): # self.console.show(window=self, ) def createActions(self): self.openlcinAct = QAction(IconFactory.getIcon('open_lc'), "&Open lc.in", self, shortcut=QKeySequence.Open, statusTip="Open lc.in file", triggered=self.open_lcin) self.savelcinAct = QAction(IconFactory.getIcon('save_lc'), "&Save lc.in as", self, shortcut=QKeySequence.Save, statusTip="Save lc.in file", triggered=self.save_lcin) self.openProjectAct = QAction(IconFactory.getIcon('open_prj'), "Open &project", self, statusTip="Open project", triggered=self.open_prj) self.saveProjectAct = QAction(IconFactory.getIcon('save_prj'), "S&ave project", self, statusTip="Save project", triggered=self.save_prj) self.saveProjectAsAct = QAction(IconFactory.getIcon('save_prj'), "S&ave project as...", self, statusTip="Save project to new file", triggered=self.save_prj_as) self.autoSaveProjectAct = QAction( None, "Auto save project", self, statusTip="Automatically save project") self.autoSaveProjectAct.setCheckable(True) self.autoSaveProjectAct.setChecked(False) self.addLightCurveAct = QAction(IconFactory.getIcon('note_add'), "Add &LC", self, statusTip="Add light curve", triggered=self.project_widget.add_lc) self.addRvCurveAct = QAction(IconFactory.getIcon('note_add'), "Add &RV", self, statusTip="Add radial velocity curve", triggered=self.project_widget.add_rv) self.delCurveAct = QAction(IconFactory.getIcon('note_del'), "Delete Curve", self, statusTip="Delete selected curve", triggered=self.project_widget.del_curve) self.delCurveAct.setEnabled(False) self.quitAct = QAction("&Quit", self, shortcut="Ctrl+Q", statusTip="Quit the application", triggered=self.close) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) # self.qtConsoleAct = QAction('Python Console', self, # statusTip="Open IPython console window", triggered=self.on_console_show) # self.wcsSexAct = QAction('Sexagesimal', self, # statusTip="Format WCS coordinates as sexagesimal (RA in hour angle) instead of decimal deg") # self.wcsSexAct.toggled.connect(self.on_sex_toggle) # self.wcsSexAct.setCheckable(True) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.openlcinAct) self.fileMenu.addAction(self.savelcinAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.openProjectAct) self.fileMenu.addAction(self.saveProjectAct) self.fileMenu.addAction(self.saveProjectAsAct) self.fileMenu.addAction(self.autoSaveProjectAct) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAct) self.curvesMenu = self.menuBar().addMenu("&Curves") self.curvesMenu.addAction(self.addLightCurveAct) self.curvesMenu.addAction(self.addRvCurveAct) self.plotMenu = self.menuBar().addMenu("&Plot") self.viewMenu = self.menuBar().addMenu("&View") # self.viewMenu.addAction(self.qtConsoleAct) # self.viewMenu.addSeparator() self.viewMenu.addAction(self.fileToolBar.toggleViewAction()) self.viewMenu.addAction(self.curvesToolBar.toggleViewAction()) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File Toolbar") self.fileToolBar.setObjectName("FILE TOOLBAR") self.fileToolBar.addAction(self.openProjectAct) self.fileToolBar.addAction(self.saveProjectAct) self.fileToolBar.addAction(self.openlcinAct) self.fileToolBar.addAction(self.savelcinAct) self.curvesToolBar = self.addToolBar("Curves Toolbar") self.curvesToolBar.setObjectName("CURVES TOOLBAR") self.curvesToolBar.addAction(self.addLightCurveAct) self.curvesToolBar.addAction(self.addRvCurveAct) self.curvesToolBar.addAction(self.delCurveAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def createDockWindows(self): # Info dock = QDockWidget('Details', self) dock.setObjectName("DETAILS") dock.setAllowedAreas(Qt.AllDockWidgetAreas) self.detailsWidget = InfoPanelWidget(self) self.project_widget.treeCurrentItemChanged.connect( self.detailsWidget.setItem) self.project_widget.treeCurrentItemChanged.connect( self.currentItemChanged) dock.setWidget(self.detailsWidget) self.addDockWidget(Qt.RightDockWidgetArea, dock) self.viewMenu.addAction(dock.toggleViewAction()) # dock.setFloating(True) # dock.hide() # Scale # dock = QDockWidget("Dynamic Scale", self) # dock.setObjectName("SCALE") # dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) # self.scaleWidget = ScaleWidget(self, scales_model=self.scales_model, cmap_model=self.cmaps) # dock.setWidget(self.scaleWidget) # self.addDockWidget(Qt.RightDockWidgetArea, dock) # self.viewMenu.addAction(dock.toggleViewAction()) # dock.setFloating(True) # dock.hide() # #radial profiles # dock = QDockWidget("Radial Profile Fit", self) # dock.setObjectName("RADIAL_PROFILE_IRAF") # dock.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea | Qt.TopDockWidgetArea) # self.radial_profile_iraf_widget = IRAFRadialProfileWidget(self.fits_image.data) # dock.setWidget(self.radial_profile_iraf_widget) # self.addDockWidget(Qt.RightDockWidgetArea, dock) # self.viewMenu.addAction(dock.toggleViewAction()) # self.dockRadialFit = dock def createPlotMenu(self, source: CurvesModel): self.plotMenu.clear() light_curves = [] rv_curves = [] for c in source.curves_iter(): if c.is_rv(): rv_curves.append(c) else: light_curves.append(c) for c in light_curves + [None] + rv_curves: if c is None: self.plotMenu.addSeparator() else: action = ConnectedCheckableAction(c.objectName()) connector = TraitletsModelConnector('plot') action.model_connector = connector connector.connect_model(c.content) self.plotMenu.addAction(action) # a = self.plotMenu.addAction(c.objectName()) # a.setData(id(c)) # a.setCheckable(True) # c.content.observe(lambda change: self.curvesPlotChanged(source)) # self.checkPlotInPlotMenu(source) # def _plotMenuCurvesActions(self) -> Mapping[int, QAction]: # """plotMenu actions indexed by Data: CurveContainer's id """ # return {a.data(): a for a in self.plotMenu.actions()} # def checkPlotInPlotMenu(self, source: CurvesModel): # actions = self._plotMenuCurvesActions() # for c in source.curves_iter(): # try: # menu_action = actions[id(c)] # menu_action.setChecked(c.plot) # except KeyError: # pass # noinspection PyTypeChecker def readWindowSettings(self): # if self.tedaCommandLine.ignoreSettings: # return settings = QSettings() settings.beginGroup("MainWindow") size, pos = settings.value("size"), settings.value("pos") settings.endGroup() if size is not None and pos is not None: print('settings: resize to {} and move to {}', size, pos) self.move(pos) # self.resize(size) print('Size reported ', self.size()) print('Size set ', size) self.resize(size) print('Size reported ', self.size()) else: self.resize(800, 600) geometry = settings.value("geometry") if geometry is not None: self.restoreGeometry(geometry) self.restoreState(settings.value("windowState")) def writeWindowSettings(self): # if self.tedaCommandLine.ignoreSettings: # return settings = QSettings() settings.beginGroup("MainWindow") settings.setValue("size", self.size()) settings.setValue("pos", self.pos()) settings.endGroup() settings.setValue('geometry', self.saveGeometry()) settings.setValue('windowState', self.saveState())
class MainWindow(QMainWindow): def __init__(self, parent=None): super().__init__(parent) self.setWindowIcon(QIcon(":/icons/apps/16/tabulator.svg")) self._recentDocuments = [] self._actionRecentDocuments = [] self._keyboardShortcutsDialog = None self._preferences = Preferences() self._preferences.loadSettings() self._createActions() self._createMenus() self._createToolBars() self._loadSettings() self._updateActions() self._updateActionFullScreen() self._updateMenuOpenRecent() # Central widget self._documentArea = QMdiArea() self._documentArea.setViewMode(QMdiArea.TabbedView) self._documentArea.setTabsMovable(True) self._documentArea.setTabsClosable(True) self.setCentralWidget(self._documentArea) self._documentArea.subWindowActivated.connect(self._onDocumentWindowActivated) def closeEvent(self, event): if True: # Store application properties and preferences self._saveSettings() self._preferences.saveSettings() event.accept() else: event.ignore() def _loadSettings(self): settings = QSettings() # Recent documents size = settings.beginReadArray("RecentDocuments") for idx in range(size-1, -1, -1): settings.setArrayIndex(idx) canonicalName = QFileInfo(settings.value("Document")).canonicalFilePath() self._updateRecentDocuments(canonicalName) settings.endArray() # Application properties: Geometry geometry = settings.value("Application/Geometry", QByteArray()) if self._preferences.restoreApplicationGeometry() else QByteArray() if not geometry.isEmpty(): self.restoreGeometry(geometry) else: availableGeometry = self.screen().availableGeometry() self.resize(availableGeometry.width() * 2/3, availableGeometry.height() * 2/3) self.move((availableGeometry.width() - self.width()) / 2, (availableGeometry.height() - self.height()) / 2) # Application properties: State state = settings.value("Application/State", QByteArray()) if self._preferences.restoreApplicationState() else QByteArray() if not state.isEmpty(): self.restoreState(state) else: self._toolbarApplication.setVisible(True) self._toolbarDocument.setVisible(True) self._toolbarEdit.setVisible(True) self._toolbarTools.setVisible(True) self._toolbarView.setVisible(False) self._toolbarHelp.setVisible(False) def _saveSettings(self): settings = QSettings() # Recent documents if not self._preferences.restoreRecentDocuments(): self._recentDocuments.clear() settings.remove("RecentDocuments") settings.beginWriteArray("RecentDocuments") for idx in range(len(self._recentDocuments)): settings.setArrayIndex(idx) settings.setValue("Document", self._recentDocuments[idx]) settings.endArray() # Application properties: Geometry geometry = self.saveGeometry() if self._preferences.restoreApplicationGeometry() else QByteArray() settings.setValue("Application/Geometry", geometry) # Application properties: State state = self.saveState() if self._preferences.restoreApplicationState() else QByteArray() settings.setValue("Application/State", state) def _createActions(self): # # Actions: Application self._actionAbout = QAction(self.tr("About {0}").format(QApplication.applicationName()), self) self._actionAbout.setObjectName("actionAbout") self._actionAbout.setIcon(QIcon(":/icons/apps/16/tabulator.svg")) self._actionAbout.setIconText(self.tr("About")) self._actionAbout.setToolTip(self.tr("Brief description of the application")) self._actionAbout.triggered.connect(self._onActionAboutTriggered) self._actionColophon = QAction(self.tr("Colophon"), self) self._actionColophon.setObjectName("actionColophon") self._actionColophon.setToolTip(self.tr("Lengthy description of the application")) self._actionColophon.triggered.connect(self._onActionColophonTriggered) self._actionPreferences = QAction(self.tr("Preferences…"), self) self._actionPreferences.setObjectName("actionPreferences") self._actionPreferences.setIcon(QIcon.fromTheme("configure", QIcon(":/icons/actions/16/application-configure.svg"))) self._actionPreferences.setToolTip(self.tr("Customize the appearance and behavior of the application")) self._actionPreferences.triggered.connect(self._onActionPreferencesTriggered) self._actionQuit = QAction(self.tr("Quit"), self) self._actionQuit.setObjectName("actionQuit") self._actionQuit.setIcon(QIcon.fromTheme("application-exit", QIcon(":/icons/actions/16/application-exit.svg"))) self._actionQuit.setShortcut(QKeySequence.Quit) self._actionQuit.setToolTip(self.tr("Quit the application")) self._actionQuit.triggered.connect(self.close) # # Actions: Document self._actionNew = QAction(self.tr("New"), self) self._actionNew.setObjectName("actionNew") self._actionNew.setIcon(QIcon.fromTheme("document-new", QIcon(":/icons/actions/16/document-new.svg"))) self._actionNew.setShortcut(QKeySequence.New) self._actionNew.setToolTip(self.tr("Create new document")) self._actionNew.triggered.connect(self._onActionNewTriggered) self._actionOpen = QAction(self.tr("Open…"), self) self._actionOpen.setObjectName("actionOpen") self._actionOpen.setIcon(QIcon.fromTheme("document-open", QIcon(":/icons/actions/16/document-open.svg"))) self._actionOpen.setShortcut(QKeySequence.Open) self._actionOpen.setToolTip(self.tr("Open an existing document")) self._actionOpen.triggered.connect(self._onActionOpenTriggered) self._actionOpenRecentClear = QAction(self.tr("Clear List"), self) self._actionOpenRecentClear.setObjectName("actionOpenRecentClear") self._actionOpenRecentClear.setToolTip(self.tr("Clear document list")) self._actionOpenRecentClear.triggered.connect(self._onActionOpenRecentClearTriggered) self._actionSave = QAction(self.tr("Save"), self) self._actionSave.setObjectName("actionSave") self._actionSave.setIcon(QIcon.fromTheme("document-save", QIcon(":/icons/actions/16/document-save.svg"))) self._actionSave.setShortcut(QKeySequence.Save) self._actionSave.setToolTip(self.tr("Save document")) self._actionSave.triggered.connect(self._onActionSaveTriggered) self._actionSaveAs = QAction(self.tr("Save As…"), self) self._actionSaveAs.setObjectName("actionSaveAs") self._actionSaveAs.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg"))) self._actionSaveAs.setShortcut(QKeySequence.SaveAs) self._actionSaveAs.setToolTip(self.tr("Save document under a new name")) self._actionSaveAs.triggered.connect(self._onActionSaveAsTriggered) self._actionSaveAsDelimiterColon = QAction(self.tr("Colon"), self) self._actionSaveAsDelimiterColon.setObjectName("actionSaveAsDelimiterColon") self._actionSaveAsDelimiterColon.setCheckable(True) self._actionSaveAsDelimiterColon.setToolTip(self.tr("Save document with colon as delimiter under a new name")) self._actionSaveAsDelimiterColon.setData("colon") self._actionSaveAsDelimiterColon.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("colon") ) self._actionSaveAsDelimiterComma = QAction(self.tr("Comma"), self) self._actionSaveAsDelimiterComma.setObjectName("actionSaveAsDelimiterComma") self._actionSaveAsDelimiterComma.setCheckable(True) self._actionSaveAsDelimiterComma.setToolTip(self.tr("Save document with comma as delimiter under a new name")) self._actionSaveAsDelimiterComma.setData("comma") self._actionSaveAsDelimiterComma.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("comma") ) self._actionSaveAsDelimiterSemicolon = QAction(self.tr("Semicolon"), self) self._actionSaveAsDelimiterSemicolon.setObjectName("actionSaveAsDelimiterSemicolon") self._actionSaveAsDelimiterSemicolon.setCheckable(True) self._actionSaveAsDelimiterSemicolon.setToolTip(self.tr("Save document with semicolon as delimiter under a new name")) self._actionSaveAsDelimiterSemicolon.setData("semicolon") self._actionSaveAsDelimiterSemicolon.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("semicolon") ) self._actionSaveAsDelimiterTab = QAction(self.tr("Tab"), self) self._actionSaveAsDelimiterTab.setObjectName("actionSaveAsDelimiterTab") self._actionSaveAsDelimiterTab.setCheckable(True) self._actionSaveAsDelimiterTab.setToolTip(self.tr("Save document with tab as delimiter under a new name")) self._actionSaveAsDelimiterTab.setData("tab") self._actionSaveAsDelimiterTab.triggered.connect(lambda: self._onActionSaveAsDelimiterTriggered("tab") ) self._actionSaveAsDelimiter = QActionGroup(self) self._actionSaveAsDelimiter.setObjectName("actionSaveAsDelimiter") self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterColon) self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterComma) self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterSemicolon) self._actionSaveAsDelimiter.addAction(self._actionSaveAsDelimiterTab) self._actionSaveCopyAs = QAction(self.tr("Save Copy As…"), self) self._actionSaveCopyAs.setObjectName("actionSaveCopyAs") self._actionSaveCopyAs.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg"))) self._actionSaveCopyAs.setToolTip(self.tr("Save copy of document under a new name")) self._actionSaveCopyAs.triggered.connect(self._onActionSaveCopyAsTriggered) self._actionSaveAll = QAction(self.tr("Save All"), self) self._actionSaveAll.setObjectName("actionSaveAll") self._actionSaveAll.setIcon(QIcon.fromTheme("document-save-all", QIcon(":/icons/actions/16/document-save-all.svg"))) self._actionSaveAll.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_L)) self._actionSaveAll.setToolTip(self.tr("Save all documents")) self._actionSaveAll.triggered.connect(self._onActionSaveAllTriggered) self._actionClose = QAction(self.tr("Close"), self) self._actionClose.setObjectName("actionClose") self._actionClose.setIcon(QIcon.fromTheme("document-close", QIcon(":/icons/actions/16/document-close.svg"))) self._actionClose.setShortcut(QKeySequence.Close) self._actionClose.setToolTip(self.tr("Close document")) self._actionClose.triggered.connect(self._onActionCloseTriggered) self._actionCloseOther = QAction(self.tr("Close Other"), self) self._actionCloseOther.setObjectName("actionCloseOther") self._actionCloseOther.setToolTip(self.tr("Close all other documents")) self._actionCloseOther.triggered.connect(self._onActionCloseOtherTriggered) self._actionCloseAll = QAction(self.tr("Close All"), self) self._actionCloseAll.setObjectName("actionCloseAll") self._actionCloseAll.setShortcut(QKeySequence(Qt.CTRL + Qt.SHIFT + Qt.Key_W)) self._actionCloseAll.setToolTip(self.tr("Close all documents")) self._actionCloseAll.triggered.connect(self._onActionCloseAllTriggered) # # Actions: View self._actionFullScreen = QAction(self) self._actionFullScreen.setObjectName("actionFullScreen") self._actionFullScreen.setIconText(self.tr("Full Screen")) self._actionFullScreen.setCheckable(True) self._actionFullScreen.setShortcuts([QKeySequence(Qt.Key_F11), QKeySequence.FullScreen]) self._actionFullScreen.triggered.connect(self._onActionFullScreenTriggered) self._actionTitlebarFullPath = QAction(self.tr("Show Path in Titlebar"), self) self._actionTitlebarFullPath.setObjectName("actionTitlebarFullPath") self._actionTitlebarFullPath.setCheckable(True) self._actionTitlebarFullPath.setChecked(True) self._actionTitlebarFullPath.setToolTip(self.tr("Display the full path of the document in the titlebar")) self._actionTitlebarFullPath.triggered.connect(self._onActionTitlebarFullPathTriggered) self._actionToolbarApplication = QAction(self.tr("Show Application Toolbar"), self) self._actionToolbarApplication.setObjectName("actionToolbarApplication") self._actionToolbarApplication.setCheckable(True) self._actionToolbarApplication.setToolTip(self.tr("Display the Application toolbar")) self._actionToolbarApplication.toggled.connect(lambda checked: self._toolbarApplication.setVisible(checked)) self._actionToolbarDocument = QAction(self.tr("Show Document Toolbar"), self) self._actionToolbarDocument.setObjectName("actionToolbarDocument") self._actionToolbarDocument.setCheckable(True) self._actionToolbarDocument.setToolTip(self.tr("Display the Document toolbar")) self._actionToolbarDocument.toggled.connect(lambda checked: self._toolbarDocument.setVisible(checked)) self._actionToolbarEdit = QAction(self.tr("Show Edit Toolbar"), self) self._actionToolbarEdit.setObjectName("actionToolbarEdit") self._actionToolbarEdit.setCheckable(True) self._actionToolbarEdit.setToolTip(self.tr("Display the Edit toolbar")) self._actionToolbarEdit.toggled.connect(lambda checked: self._toolbarEdit.setVisible(checked)) self._actionToolbarTools = QAction(self.tr("Show Tools Toolbar"), self) self._actionToolbarTools.setObjectName("actionToolbarTools") self._actionToolbarTools.setCheckable(True) self._actionToolbarTools.setToolTip(self.tr("Display the Tools toolbar")) self._actionToolbarTools.toggled.connect(lambda checked: self._toolbarTools.setVisible(checked)) self._actionToolbarView = QAction(self.tr("Show View Toolbar"), self) self._actionToolbarView.setObjectName("actionToolbarView") self._actionToolbarView.setCheckable(True) self._actionToolbarView.setToolTip(self.tr("Display the View toolbar")) self._actionToolbarView.toggled.connect(lambda checked: self._toolbarView.setVisible(checked)) self._actionToolbarHelp = QAction(self.tr("Show Help Toolbar"), self) self._actionToolbarHelp.setObjectName("actionToolbarHelp") self._actionToolbarHelp.setCheckable(True) self._actionToolbarHelp.setToolTip(self.tr("Display the Help toolbar")) self._actionToolbarHelp.toggled.connect(lambda checked: self._toolbarHelp.setVisible(checked)) # # Actions: Help self._actionKeyboardShortcuts = QAction(self.tr("Keyboard Shortcuts"), self) self._actionKeyboardShortcuts.setObjectName("actionKeyboardShortcuts") self._actionKeyboardShortcuts.setIcon(QIcon.fromTheme("help-keyboard-shortcuts", QIcon(":/icons/actions/16/help-keyboard-shortcuts.svg"))) self._actionKeyboardShortcuts.setIconText(self.tr("Shortcuts")) self._actionKeyboardShortcuts.setToolTip(self.tr("List of all keyboard shortcuts")) self._actionKeyboardShortcuts.triggered.connect(self._onActionKeyboardShortcutsTriggered) def _createMenus(self): # Menu: Application menuApplication = self.menuBar().addMenu(self.tr("Application")) menuApplication.setObjectName("menuApplication") menuApplication.addAction(self._actionAbout) menuApplication.addAction(self._actionColophon) menuApplication.addSeparator() menuApplication.addAction(self._actionPreferences) menuApplication.addSeparator() menuApplication.addAction(self._actionQuit) # # Menu: Document self._menuOpenRecent = QMenu(self.tr("Open Recent"), self) self._menuOpenRecent.setObjectName("menuOpenRecent") self._menuOpenRecent.setIcon(QIcon.fromTheme("document-open-recent", QIcon(":/icons/actions/16/document-open-recent.svg"))) self._menuOpenRecent.setToolTip(self.tr("Open a document which was recently opened")) self._menuSaveAsDelimiter = QMenu(self.tr("Save As with Delimiter…"), self) self._menuSaveAsDelimiter.setObjectName("menuSaveAsDelimiter") self._menuSaveAsDelimiter.setIcon(QIcon.fromTheme("document-save-as", QIcon(":/icons/actions/16/document-save-as.svg"))) self._menuSaveAsDelimiter.setToolTip(self.tr("Save document with specific delimiter under a new name")) self._menuSaveAsDelimiter.addActions(self._actionSaveAsDelimiter.actions()) menuDocument = self.menuBar().addMenu(self.tr("Document")) menuDocument.setObjectName("menuDocument") menuDocument.addAction(self._actionNew) menuDocument.addSeparator() menuDocument.addAction(self._actionOpen) menuDocument.addMenu(self._menuOpenRecent) menuDocument.addSeparator() menuDocument.addAction(self._actionSave) menuDocument.addAction(self._actionSaveAs) menuDocument.addMenu(self._menuSaveAsDelimiter) menuDocument.addAction(self._actionSaveCopyAs) menuDocument.addAction(self._actionSaveAll) menuDocument.addSeparator() menuDocument.addAction(self._actionClose) menuDocument.addAction(self._actionCloseOther) menuDocument.addAction(self._actionCloseAll) # Menu: Edit menuEdit = self.menuBar().addMenu(self.tr("Edit")) menuEdit.setObjectName("menuEdit") # Menu: Tools menuTools = self.menuBar().addMenu(self.tr("Tools")) menuTools.setObjectName("menuTools") # Menu: View menuView = self.menuBar().addMenu(self.tr("View")) menuView.setObjectName("menuView") menuView.addAction(self._actionFullScreen) menuView.addSeparator() menuView.addAction(self._actionTitlebarFullPath) menuView.addSeparator() menuView.addAction(self._actionToolbarApplication) menuView.addAction(self._actionToolbarDocument) menuView.addAction(self._actionToolbarEdit) menuView.addAction(self._actionToolbarTools) menuView.addAction(self._actionToolbarView) menuView.addAction(self._actionToolbarHelp) # Menu: Help menuHelp = self.menuBar().addMenu(self.tr("Help")) menuHelp.setObjectName("menuHelp") menuHelp.addAction(self._actionKeyboardShortcuts) def _createToolBars(self): # Toolbar: Application self._toolbarApplication = self.addToolBar(self.tr("Application Toolbar")) self._toolbarApplication.setObjectName("toolbarApplication") self._toolbarApplication.addAction(self._actionAbout) self._toolbarApplication.addAction(self._actionPreferences) self._toolbarApplication.addSeparator() self._toolbarApplication.addAction(self._actionQuit) self._toolbarApplication.visibilityChanged.connect(lambda visible: self._actionToolbarApplication.setChecked(visible)) # Toolbar: Document self._toolbarDocument = self.addToolBar(self.tr("Document Toolbar")) self._toolbarDocument.setObjectName("toolbarDocument") self._toolbarDocument.addAction(self._actionNew) self._toolbarDocument.addAction(self._actionOpen) self._toolbarDocument.addSeparator() self._toolbarDocument.addAction(self._actionSave) self._toolbarDocument.addAction(self._actionSaveAs) self._toolbarDocument.addSeparator() self._toolbarDocument.addAction(self._actionClose) self._toolbarDocument.visibilityChanged.connect(lambda visible: self._actionToolbarDocument.setChecked(visible)) # Toolbar: Edit self._toolbarEdit = self.addToolBar(self.tr("Edit Toolbar")) self._toolbarEdit.setObjectName("toolbarEdit") self._toolbarEdit.visibilityChanged.connect(lambda visible: self._actionToolbarEdit.setChecked(visible)) # Toolbar: Tools self._toolbarTools = self.addToolBar(self.tr("Tools Toolbar")) self._toolbarTools.setObjectName("toolbarTools") self._toolbarTools.visibilityChanged.connect(lambda visible: self._actionToolbarTools.setChecked(visible)) # Toolbar: View self._toolbarView = self.addToolBar(self.tr("View Toolbar")) self._toolbarView.setObjectName("toolbarView") self._toolbarView.addAction(self._actionFullScreen) self._toolbarView.visibilityChanged.connect(lambda visible: self._actionToolbarView.setChecked(visible)) # Toolbar: Help self._toolbarHelp = self.addToolBar(self.tr("Help Toolbar")) self._toolbarHelp.setObjectName("toolbarHelp") self._toolbarHelp.addAction(self._actionKeyboardShortcuts) self._toolbarHelp.visibilityChanged.connect(lambda visible: self._actionToolbarHelp.setChecked(visible)) def _updateActions(self, subWindowCount=0): hasDocument = subWindowCount >= 1 hasDocuments = subWindowCount >= 2 # Actions: Document self._actionSave.setEnabled(hasDocument) self._actionSaveAs.setEnabled(hasDocument) self._menuSaveAsDelimiter.setEnabled(hasDocument) self._actionSaveCopyAs.setEnabled(hasDocument) self._actionSaveAll.setEnabled(hasDocument) self._actionClose.setEnabled(hasDocument) self._actionCloseOther.setEnabled(hasDocuments) self._actionCloseAll.setEnabled(hasDocument) def _updateActionFullScreen(self): if not self.isFullScreen(): self._actionFullScreen.setText(self.tr("Full Screen Mode")) self._actionFullScreen.setIcon(QIcon.fromTheme("view-fullscreen", QIcon(":/icons/actions/16/view-fullscreen.svg"))) self._actionFullScreen.setChecked(False) self._actionFullScreen.setToolTip(self.tr("Display the window in full screen")) else: self._actionFullScreen.setText(self.tr("Exit Full Screen Mode")) self._actionFullScreen.setIcon(QIcon.fromTheme("view-restore", QIcon(":/icons/actions/16/view-restore.svg"))) self._actionFullScreen.setChecked(True) self._actionFullScreen.setToolTip(self.tr("Exit the full screen mode")) def _updateActionRecentDocuments(self): # Add items to the list, if necessary for idx in range(len(self._actionRecentDocuments)+1, self._preferences.maximumRecentDocuments()+1): actionRecentDocument = QAction(self) actionRecentDocument.setObjectName(f"actionRecentDocument_{idx}") actionRecentDocument.triggered.connect(lambda data=actionRecentDocument.data(): self._onActionOpenRecentDocumentTriggered(data)) self._actionRecentDocuments.append(actionRecentDocument) # Remove items from the list, if necessary while len(self._actionRecentDocuments) > self._preferences.maximumRecentDocuments(): self._actionRecentDocuments.pop() # Update items for idx in range(len(self._actionRecentDocuments)): text = None data = None show = False if idx < len(self._recentDocuments): text = self.tr("{0} [{1}]").format(QFileInfo(self._recentDocuments[idx]).fileName(), self._recentDocuments[idx]) data = self._recentDocuments[idx] show = True self._actionRecentDocuments[idx].setText(text) self._actionRecentDocuments[idx].setData(data) self._actionRecentDocuments[idx].setVisible(show) def _updateMenuOpenRecent(self): self._menuOpenRecent.clear() if self._preferences.maximumRecentDocuments() > 0: # Document list wanted; show the menu self._menuOpenRecent.menuAction().setVisible(True) if len(self._recentDocuments) > 0: # Document list has items; enable the menu self._menuOpenRecent.setEnabled(True) self._menuOpenRecent.addActions(self._actionRecentDocuments) self._menuOpenRecent.addSeparator() self._menuOpenRecent.addAction(self._actionOpenRecentClear) else: # Document list is empty; disable the menu self._menuOpenRecent.setEnabled(False) else: # No document list wanted; hide the menu self._menuOpenRecent.menuAction().setVisible(False) def _updateTitleBar(self): title = None document = self._activeDocument() if document: title = document.canonicalName() if self._actionTitlebarFullPath.isChecked() and document.canonicalName() else document.documentTitle() self.setWindowTitle(title) def _onActionAboutTriggered(self): dialog = AboutDialog(self) dialog.exec_() def _onActionColophonTriggered(self): dialog = ColophonDialog(self) dialog.exec_() def _onActionPreferencesTriggered(self): dialog = PreferencesDialog(self) dialog.setPreferences(self._preferences) dialog.exec_() self._preferences = dialog.preferences() self._updateRecentDocuments(None) self._updateMenuOpenRecent() def _onActionNewTriggered(self): self._loadDocument("") def _onActionOpenTriggered(self): fileNames = QFileDialog.getOpenFileNames(self, self.tr("Open Document"), QStandardPaths.writableLocation(QStandardPaths.HomeLocation), self.tr("CSV Files (*.csv);;All Files (*.*)"))[0] for fileName in fileNames: self._openDocument(fileName) def _onActionOpenRecentDocumentTriggered(self, canonicalName): pass # self.openDocument(canonicalName) def _onActionOpenRecentClearTriggered(self): self._recentDocuments.clear() self._updateRecentDocuments(None) self._updateMenuOpenRecent() def _onActionSaveTriggered(self): pass def _onActionSaveAsTriggered(self): pass def _onActionSaveAsDelimiterTriggered(self, delimiter): pass def _onActionSaveCopyAsTriggered(self): pass def _onActionSaveAllTriggered(self): pass def _onActionCloseTriggered(self): self._documentArea.closeActiveSubWindow() def _onActionCloseOtherTriggered(self): for subWindow in self._documentArea.subWindowList(): if subWindow != self._documentArea.activeSubWindow(): subWindow.close() def _onActionCloseAllTriggered(self): self._documentArea.closeAllSubWindows() def _onActionFullScreenTriggered(self): if not self.isFullScreen(): self.setWindowState(self.windowState() | Qt.WindowFullScreen) else: self.setWindowState(self.windowState() & ~Qt.WindowFullScreen) self._updateActionFullScreen() def _onActionTitlebarFullPathTriggered(self): self._updateTitleBar() def _onActionKeyboardShortcutsTriggered(self): if not self._keyboardShortcutsDialog: self._keyboardShortcutsDialog = KeyboardShortcutsDialog(self) self._keyboardShortcutsDialog.show() self._keyboardShortcutsDialog.raise_() self._keyboardShortcutsDialog.activateWindow() def _onDocumentWindowActivated(self, subWindow): # Update the application window self._updateActions(len(self._documentArea.subWindowList())) self._updateTitleBar() if not subWindow: return def _onDocumentAboutToClose(self, canonicalName): # Workaround to show subwindows always maximized for subWindow in self._documentArea.subWindowList(): if not subWindow.isMaximized(): subWindow.showMaximized() # Update menu items without the emitter self._updateActions(len(self._documentArea.subWindowList()) - 1) def _createDocument(self): document = Document() document.setPreferences(self._preferences) document.aboutToClose.connect(self._onDocumentAboutToClose) subWindow = self._documentArea.addSubWindow(document) subWindow.setWindowIcon(QIcon()) subWindow.showMaximized() return document def _createDocumentIndex(self, canonicalName): fileName = QFileInfo(canonicalName).fileName() canonicalIndex = 0 for subWindow in self._documentArea.subWindowList(): if QFileInfo(subWindow.widget().canonicalName()).fileName() == fileName: if subWindow.widget().canonicalIndex() > canonicalIndex: canonicalIndex = subWindow.widget().canonicalIndex() return canonicalIndex + 1 def _findDocumentWindow(self, canonicalName): for subWindow in self._documentArea.subWindowList(): if subWindow.widget().canonicalName() == canonicalName: return subWindow return None def _activeDocument(self): subWindow = self._documentArea.activeSubWindow() return subWindow.widget() if subWindow else None def _openDocument(self, fileName): canonicalName = QFileInfo(fileName).canonicalFilePath() subWindow = self._findDocumentWindow(canonicalName) if subWindow: # Given document is already loaded; activate the subwindow self._documentArea.setActiveSubWindow(subWindow) # Update list of recent documents self._updateRecentDocuments(canonicalName) self._updateMenuOpenRecent() return True return self._loadDocument(canonicalName); def _loadDocument(self, canonicalName): document = self._createDocument() succeeded = document.load(canonicalName) if succeeded: document.setCanonicalIndex(self._createDocumentIndex(canonicalName)) document.updateDocumentTitle() document.show() # Update list of recent documents self._updateRecentDocuments(canonicalName) self._updateMenuOpenRecent() # Update the application window self._updateActions(len(self._documentArea.subWindowList())) self._updateTitleBar() else: document.close() return succeeded def _updateRecentDocuments(self, canonicalName): if canonicalName: while canonicalName in self._recentDocuments: self._recentDocuments.remove(canonicalName) self._recentDocuments.insert(0, canonicalName) # Remove items from the list, if necessary while len(self._recentDocuments) > self._preferences.maximumRecentDocuments(): self._recentDocuments.pop() self._updateActionRecentDocuments()
def __init__(self): QMainWindow.__init__(self) signal.signal(signal.SIGINT, self.exit_app) self.setWindowTitle("Steam Account Switcher") self.setMinimumSize(300, 200) self.resize(300, 300) # Logo self.switcher_logo = QIcon("logo.png") self.setWindowIcon(self.switcher_logo) if platform.system() == "Windows": # windows taskbar app icon fix import ctypes win_appid = 'github.tommis.steam_account_switcher' ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID( win_appid) from steamswitcher import SteamSwitcher self.switcher = SteamSwitcher() self.args = self.switcher.args self.main_widget = QWidget() if self.args.gui or self.switcher.settings.get( "show_on_startup", True) and not self.args.no_gui: self.show() elif self.args.no_gui and self.args.no_tray: self.exit_app() self.menu_bar = self.menuBar() self.file_menu = self.menu_bar.addMenu(_("File")) self.settings_menu = self.menu_bar.addMenu(_("Settings")) self.size_menu = self.menu_bar.addMenu(_("Size")) refresh_action = QAction(_("Refresh"), self) import_action = QAction(_("Import accounts"), self) open_skinsdir_action = QAction(_("Skins dir"), self) about_action = QAction(_("About"), self) exit_action = QAction(_("Exit"), self) refresh_action.triggered.connect(Accounts.steamapi_refresh) import_action.triggered.connect( lambda: DialogImportAccount.import_accounts_dialog(self)) open_skinsdir_action.triggered.connect(self.open_skinsdir) about_action.triggered.connect(lambda: DialogAbout.about_dialog(self)) exit_action.triggered.connect(self.exit_app) refresh_action.setShortcut("F5") exit_action.setShortcut("Ctrl+Q") self.file_menu.addActions([ refresh_action, import_action, open_skinsdir_action, about_action ]) self.file_menu.addSeparator() self.file_menu.addAction(exit_action) set_steamapi_key = QAction(_("Set steamapi key"), self) show_avatars = QAction(_("Show avatars"), self, checkable=True) use_systemtray = QAction(_("Use systemtray"), self, checkable=True) after_login_menu = QMenu(_("After login")) after_login_behaviour_group = QActionGroup(after_login_menu) nothing_behaviour = QAction(_('Nothing'), after_login_behaviour_group, checkable=True, data="nothing") close_behaviour = QAction(_('Close'), after_login_behaviour_group, checkable=True, data="close") minimize_behaviour = QAction(_('Minimize to taskbar'), after_login_behaviour_group, checkable=True, data="minimize") minimize_tray_behaviour = QAction(_('Minimize to tray'), after_login_behaviour_group, checkable=True, data="minimize_tray") after_login_menu.addActions([ nothing_behaviour, close_behaviour, minimize_behaviour, minimize_tray_behaviour ]) behaviour_switcher = { "close": lambda: close_behaviour.setChecked(True), "minimize": lambda: minimize_behaviour.setChecked(True), "minimize_tray": lambda: minimize_tray_behaviour.setChecked(True) } behaviour_switcher.get(self.switcher.settings["behavior_after_login"], lambda: nothing_behaviour.setChecked(True))() after_login_menu.triggered.connect(self.set_after_login_action) self.systemtray(self.main_widget) set_steamapi_key.triggered.connect(lambda: self.steamapi_key_dialog()) show_avatars.triggered.connect(lambda: self.set_show_avatars()) use_systemtray.triggered.connect(lambda: self.set_use_systemtray()) self.settings_menu.addAction(set_steamapi_key) self.settings_menu.addSeparator() self.settings_menu.addActions([show_avatars, use_systemtray]) self.settings_menu.addMenu(after_login_menu) show_avatars.setChecked(self.switcher.settings.get("show_avatars")) use_systemtray.setChecked(self.switcher.settings.get("use_systemtray")) set_size_small = QAction(_("Small"), self) set_size_medium = QAction(_("Medium"), self) set_size_large = QAction(_("Large"), self) set_size_small.triggered.connect(lambda: self.set_size("small")) set_size_medium.triggered.connect(lambda: self.set_size("medium")) set_size_large.triggered.connect(lambda: self.set_size("large")) self.size_menu.addActions( [set_size_small, set_size_medium, set_size_large]) set_size_small.setShortcut("Ctrl+1") set_size_medium.setShortcut("Ctrl+2") set_size_large.setShortcut("Ctrl+3") self.add_button = QPushButton(_("Add account")) self.edit_button = QPushButton(_("Edit account")) self.edit_button.setDisabled(True) self.buttons = QHBoxLayout() self.buttons.addWidget(self.add_button) self.buttons.addWidget(self.edit_button) self.layout = QVBoxLayout() self.main_widget.setLayout(self.layout) self.accounts_list = QListWidget() self.accounts_list.setDragDropMode(QAbstractItemView.InternalMove) self.layout.addWidget(self.accounts_list) self.layout.addLayout(self.buttons) self.layout.setSpacing(10) self.accounts_list.setSpacing(1) self.import_accounts_window = QDialog() self.load_accounts() def edit_button_enabled(): if self.accounts_list.selectedItems(): self.edit_button.setEnabled(True) else: self.edit_button.setEnabled(False) # Signals and Slots self.add_button.clicked.connect(lambda: self.account_dialog(True)) self.edit_button.clicked.connect(lambda: self.account_dialog(False)) self.accounts_list.itemSelectionChanged.connect(edit_button_enabled) self.accounts_list.doubleClicked.connect(lambda: self.steam_login( self.accounts_list.currentIndex().data(5))) self.accounts_list.setContextMenuPolicy(Qt.CustomContextMenu) self.accounts_list.customContextMenuRequested.connect( lambda: RightClickMenu.show_rightclick_menu(self)) #self.accounts_list.layoutChanged.connect(lambda: self.account_reordered) #self.accounts_list.dropEvent(self.dropEvent(QDropEvent)) self.setCentralWidget(self.main_widget) if self.args.no_tray: print("test") elif self.switcher.settings.get("use_systemtray") or self.args.tray: self.tray_icon.show() if self.switcher.first_run or self.args.first_run: self.steamapi_key_dialog() elif not self.switcher.first_run and \ not self.is_valid_steampi_key(self.switcher.settings["steam_api_key"]): self.tray_icon.showMessage("No api key", "Set the steam web api key.", self.switcher_logo)
class PuppetMaster(QMainWindow): requestEditMode = Signal(bool) def __init__(self, parent=None, editMode=bool(False)): super(PuppetMaster, self).__init__(parent) self._parent = parent self._model = list() self.editMode = editMode self.setMouseTracking(True) self.setFocusPolicy(Qt.StrongFocus) self.setWindowTitle('PuppetMaster') self.setMinimumHeight(1) self.setMinimumWidth(1) self.setContextMenuPolicy(Qt.PreventContextMenu) # Main tabs self.tab = CanvasGraphicsViewTab(parent=self) self.tab.requestEditMode.connect(self.set_edit) self.setCentralWidget(self.tab) # Parameter Widget self.parameter = Parameters(parent=self) self.parameterDock = QDockWidget(": Parameters ::", self) self.parameterDock.setObjectName('Parameters') self.parameterDock.setFeatures(QDockWidget.DockWidgetClosable) self.parameterDock.setWidget(self.parameter) self.parameterDock.setTitleBarWidget(QWidget(self.parameterDock)) self.parameterDock.setVisible(self.editMode) self.addDockWidget(Qt.BottomDockWidgetArea, self.parameterDock) self.tab.onSelection.connect(self.update_parameters) self.parameter.onChangeBGColor.connect(self.tab.update_bg_color) self.parameter.onChangeFontColor.connect(self.tab.update_font_color) self.parameter.onChangeFontSize.connect(self.tab.update_font_size) self.parameter.onChangeText.connect(self.tab.update_text) self.parameter.onChangeShape.connect(self.tab.update_shape) # maya Signal on selection items self.idx = OpenMaya.MEventMessage.addEventCallback( "SelectionChanged", self.tab.maya_selection) # Menu self.setMenuBar(self.create_menu()) self.set_edit(self.editMode) def update_parameters(self, node=PickNode): shape = node.Shape if isinstance(node, PickNode) else str() self.parameter.update_param(text=node.toPlainText(), fontSize=node.font().pointSize(), fontColor=node.defaultTextColor(), bgColor=node.Background, shapeName=shape) def create_menu(self): window_menu = QMenuBar(self) file_menu = window_menu.addMenu("&File") new_action = QAction("&New...", self) new_action.setShortcut('Ctrl+N') new_action.setShortcutContext(Qt.WidgetShortcut) new_action.setStatusTip('Create a new Set') new_action.triggered.connect(self.tab.new_tab) file_menu.addAction(new_action) open_action = QAction("&Open...", self) open_action.setShortcut('Ctrl+O') open_action.setShortcutContext(Qt.WidgetShortcut) open_action.setStatusTip('Open a new set') open_action.triggered.connect(self.tab.open_set) file_menu.addAction(open_action) refresh_action = QAction("&Refresh...", self) refresh_action.setStatusTip('Refresh the current sets') refresh_action.triggered.connect(self.tab.refresh_set) file_menu.addAction(refresh_action) file_menu.addSeparator() save_action = QAction("&Save...", self) save_action.setShortcut('Ctrl+S') save_action.setShortcutContext(Qt.WidgetShortcut) save_action.setStatusTip('Save the current tab') save_action.triggered.connect(self.tab.save_set) file_menu.addAction(save_action) saveAs_action = QAction("&Save As...", self) saveAs_action.setShortcut('Ctrl+Shift+S') saveAs_action.setShortcutContext(Qt.WidgetShortcut) saveAs_action.setStatusTip('Save the current tab as a new Set') saveAs_action.triggered.connect(self.tab.saveAs_set) file_menu.addAction(saveAs_action) saveAsTemp_action = QAction("&Save As Template...", self) saveAsTemp_action.setStatusTip( 'Save the current tab as a new Template') saveAsTemp_action.triggered.connect(self.tab.saveAsTemplate_set) file_menu.addAction(saveAsTemp_action) file_menu.addSeparator() rename_action = QAction("&Rename Tab...", self) rename_action.setStatusTip('Rename the current Tab') rename_action.triggered.connect(self.tab.rename_set) file_menu.addAction(rename_action) close_action = QAction("&Close Tab...", self) close_action.setShortcut('Ctrl+Shift+W') close_action.setShortcutContext(Qt.WidgetShortcut) close_action.setStatusTip('Close the current Tab') close_action.triggered.connect(self.tab.closeCurrentTab) file_menu.addAction(close_action) picker_menu = window_menu.addMenu("&Picker") self.edit_action = QAction("&Edit Mode", self) self.edit_action.setStatusTip('Toggle between view and edit mode.') self.edit_action.triggered.connect(self.edit_toggle) self.edit_action.setCheckable(True) self.edit_action.setChecked(self.editMode) picker_menu.addAction(self.edit_action) picker_menu.addSeparator() self.background_action = QAction("&Change Background...", self) self.background_action.setEnabled(self.editMode) self.background_action.setStatusTip('Change the background.') self.background_action.triggered.connect(self.tab.set_background) picker_menu.addAction(self.background_action) self.namespace_action = QAction("&Change Namespace...", self) self.namespace_action.setEnabled(self.editMode) self.namespace_action.setStatusTip('Change the namespace.') self.namespace_action.triggered.connect(self.tab.set_namespace) picker_menu.addAction(self.namespace_action) help_menu = window_menu.addMenu("&Help") wiki_action = QAction("&About PuppetMaster...", self) wiki_action.setStatusTip('Open Wiki page') wiki_action.triggered.connect(self.wiki_open) help_menu.addAction(wiki_action) return window_menu def edit_toggle(self): self.set_edit(not self.editMode) def wiki_open(self): webbrowser.open_new_tab( 'https://github.com/Bernardrouhi/PuppetMaster/wiki') def force_load(self, paths=list): self.tab.force_load(paths) def findAndLoad(self, names=list()): ''' Find the names in PuppetMaster folder and load them Parameters ---------- names: (list) List of dictionaries of name and namespace. ''' if names: self.tab.findAndLoad(names) def destroyMayaSignals(self): # destroy the connection OpenMaya.MMessage.removeCallback(self.idx) def get_edit(self): return self.editMode def set_edit(self, value=bool): self.editMode = value self.background_action.setEnabled(self.editMode) self.namespace_action.setEnabled(self.editMode) self.parameterDock.setVisible(self.editMode) self.edit_action.setChecked(self.editMode) self.tab.Edit = self.editMode # Notification if not self.editMode: warningMes( "PUPPETMASTER-INFO: Out of 'Edit Mode' you won't be able to create/edit/move or delete any node." ) else: warningMes( "PUPPETMASTER-INFO: In 'Edit Mode' command buttons won't run any commands." ) Edit = property(get_edit, set_edit) def keyPressEvent(self, event): if event.modifiers() == (Qt.ControlModifier | Qt.ShiftModifier): if event.key() == Qt.Key_W: self.tab.closeCurrentTab() elif event.key() == Qt.Key_S: self.tab.saveAs_set() elif event.modifiers() == Qt.ControlModifier: if event.key() == Qt.Key_S: self.tab.save_set() elif event.key() == Qt.Key_N: self.tab.new_tab() elif event.key() == Qt.Key_O: self.tab.open_set() else: super(PuppetMaster, self).keyPressEvent(event)
def __init__(self, bind_address: str, port: int, filesToRead: [str]): """ :param bind_address: address to bind to when listening for live connections. :param port: port to bind to when listening for live connections. :param filesToRead: replay files to open. """ super().__init__() # TODO : Rework into a class if we add more options later. self.options = { "focusNewTab": True, # Useful whenever we are getting flooded with connections (or scanned), and we only want to monitor one at a time. "closeTabOnCtrlW": True # Allow user to toggle Ctrl+W passthrough. } self.liveWindow = LiveWindow(bind_address, port, self.updateCountSignal, self.options) self.replayWindow = ReplayWindow(self.options) self.tabManager = QTabWidget() self.tabManager.addTab(self.liveWindow, "Live connections") self.tabManager.addTab(self.replayWindow, "Replays") self.setCentralWidget(self.tabManager) self.updateCountSignal.connect(self.updateTabConnectionCount) # File menu openAction = QAction("Open...", self) openAction.setShortcut("Ctrl+O") openAction.setStatusTip("Open a replay file") openAction.triggered.connect(self.onOpenFile) # Command menu windowsRAction = QAction("Windows+R", self) windowsRAction.setShortcut("Ctrl+Alt+R") windowsRAction.setStatusTip("Send a Windows+R key sequence") windowsRAction.triggered.connect(lambda: self.sendKeySequence([Qt.Key.Key_Meta, Qt.Key.Key_R])) windowsLAction = QAction("Windows+L", self) windowsLAction.setShortcut("Ctrl+Alt+L") windowsLAction.setStatusTip("Send a Windows+L key sequence") windowsLAction.triggered.connect(lambda: self.sendKeySequence([Qt.Key.Key_Meta, Qt.Key.Key_L])) windowsEAction = QAction("Windows+E", self) windowsEAction.setShortcut("Ctrl+Alt+E") windowsEAction.setStatusTip("Send a Windows+E key sequence") windowsEAction.triggered.connect(lambda: self.sendKeySequence([Qt.Key.Key_Meta, Qt.Key.Key_E])) typeTextAction = QAction("Type text...", self) typeTextAction.setShortcut("Ctrl+Alt+T") typeTextAction.setStatusTip("Simulate typing on the keyboard") typeTextAction.triggered.connect(self.sendText) # Options menu focusTabAction = QAction("Focus new connections", self) focusTabAction.setCheckable(True) focusTabAction.setChecked(self.options.get("focusNewTab")) focusTabAction.triggered.connect(lambda: self.toggleFocusNewTab()) closeTabOnCtrlW = QAction("Close current tab on Ctrl+W", self) closeTabOnCtrlW.setCheckable(True) closeTabOnCtrlW.setChecked(self.options.get("closeTabOnCtrlW")) closeTabOnCtrlW.triggered.connect(lambda: self.toggleCloseTabOnCtrlW()) # Create menu menuBar = self.menuBar() fileMenu = menuBar.addMenu("File") fileMenu.addAction(openAction) commandMenu = menuBar.addMenu("Command") commandMenu.addAction(windowsRAction) commandMenu.addAction(windowsLAction) commandMenu.addAction(windowsEAction) commandMenu.addAction(typeTextAction) optionsMenu = menuBar.addMenu("Options") optionsMenu.addAction(focusTabAction) optionsMenu.addAction(closeTabOnCtrlW) for fileName in filesToRead: self.replayWindow.openFile(fileName)
class MainWindow(QMainWindow): def __init__(self, font=QtGui.QFontMetrics(QtGui.QFont()), screen_size=QtCore.QSize()): super().__init__() self.screen = screen_size self.font_metric = font self.setWindowTitle('OpenMC Plot Explorer') def loadGui(self): self.pixmap = None self.zoom = 100 self.loadModel() # Create viewing area self.frame = QScrollArea(self) cw = QWidget() self.frame.setCornerWidget(cw) self.frame.setAlignment(QtCore.Qt.AlignCenter) self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.setCentralWidget(self.frame) # connect pinch gesture (OSX) self.grabGesture(QtCore.Qt.PinchGesture) # Create plot image self.plotIm = PlotImage(self.model, self.frame, self) self.frame.setWidget(self.plotIm) # Dock self.dock = DomainDock(self.model, self.font_metric, self) self.dock.setObjectName("Domain Options Dock") self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock) # Tally Dock self.tallyDock = TallyDock(self.model, self.font_metric, self) self.tallyDock.update() self.tallyDock.setObjectName("Tally Options Dock") self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.tallyDock) # Color DialogtallyDock self.colorDialog = ColorDialog(self.model, self.font_metric, self) self.colorDialog.hide() # Tools self.exportDataDialog = ExportDataDialog(self.model, self.font_metric, self) # Restore Window Settings self.restoreWindowSettings() # Create menubar self.createMenuBar() self.updateEditMenu() # Status Bar self.coord_label = QLabel() self.statusBar().addPermanentWidget(self.coord_label) self.coord_label.hide() # Keyboard overlay self.shortcutOverlay = ShortcutsOverlay(self) self.shortcutOverlay.hide() # Load Plot self.statusBar().showMessage('Generating Plot...') self.dock.updateDock() self.tallyDock.update() self.colorDialog.updateDialogValues() self.statusBar().showMessage('') # Timer allows GUI to render before plot finishes loading QtCore.QTimer.singleShot(0, self.plotIm.generatePixmap) QtCore.QTimer.singleShot(0, self.showCurrentView) def event(self, event): # use pinch event to update zoom if isinstance(event, QGestureEvent): pinch = event.gesture(QtCore.Qt.PinchGesture) self.editZoom(self.zoom * pinch.scaleFactor()) if isinstance(event, QKeyEvent) and hasattr(self, "shortcutOverlay"): self.shortcutOverlay.event(event) return super().event(event) def show(self): super().show() self.plotIm._resize() def toggleShortcuts(self): if self.shortcutOverlay.isVisible(): self.shortcutOverlay.close() else: self.shortcutOverlay.move(0, 0) self.shortcutOverlay.resize(self.width(), self.height()) self.shortcutOverlay.show() # Create and update menus: def createMenuBar(self): self.mainMenu = self.menuBar() # File Menu self.reloadModelAction = QAction("&Reload model...", self) self.reloadModelAction.setShortcut("Ctrl+Shift+R") self.reloadModelAction.setToolTip("Reload current model") self.reloadModelAction.setStatusTip("Reload current model") reload_connector = partial(self.loadModel, reload=True) self.reloadModelAction.triggered.connect(reload_connector) self.saveImageAction = QAction("&Save Image As...", self) self.saveImageAction.setShortcut("Ctrl+Shift+S") self.saveImageAction.setToolTip('Save plot image') self.saveImageAction.setStatusTip('Save plot image') self.saveImageAction.triggered.connect(self.saveImage) self.saveViewAction = QAction("Save &View...", self) self.saveViewAction.setShortcut(QtGui.QKeySequence.Save) self.saveViewAction.setStatusTip('Save current view settings') self.saveViewAction.triggered.connect(self.saveView) self.openAction = QAction("&Open View...", self) self.openAction.setShortcut(QtGui.QKeySequence.Open) self.openAction.setToolTip('Open saved view settings') self.openAction.setStatusTip('Open saved view settings') self.openAction.triggered.connect(self.openView) self.quitAction = QAction("&Quit", self) self.quitAction.setShortcut(QtGui.QKeySequence.Quit) self.quitAction.setToolTip('Quit OpenMC Plot Explorer') self.quitAction.setStatusTip('Quit OpenMC Plot Explorer') self.quitAction.triggered.connect(self.close) self.exportDataAction = QAction('E&xport...', self) self.exportDataAction.setToolTip('Export model and tally data VTK') self.setStatusTip('Export current model and tally data to VTK') self.exportDataAction.triggered.connect(self.exportTallyData) if not _HAVE_VTK: self.exportDataAction.setEnabled(False) self.exportDataAction.setToolTip( "Disabled: VTK Python module is not installed") self.fileMenu = self.mainMenu.addMenu('&File') self.fileMenu.addAction(self.reloadModelAction) self.fileMenu.addAction(self.saveImageAction) self.fileMenu.addAction(self.exportDataAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.saveViewAction) self.fileMenu.addAction(self.openAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAction) # Data Menu self.openStatePointAction = QAction("&Open statepoint...", self) self.openStatePointAction.setToolTip('Open statepoint file') self.openStatePointAction.triggered.connect(self.openStatePoint) self.dataMenu = self.mainMenu.addMenu('D&ata') self.dataMenu.addAction(self.openStatePointAction) self.updateDataMenu() # Edit Menu self.applyAction = QAction("&Apply Changes", self) self.applyAction.setShortcut("Ctrl+Return") self.applyAction.setToolTip('Generate new view with changes applied') self.applyAction.setStatusTip('Generate new view with changes applied') self.applyAction.triggered.connect(self.applyChanges) self.undoAction = QAction('&Undo', self) self.undoAction.setShortcut(QtGui.QKeySequence.Undo) self.undoAction.setToolTip('Undo') self.undoAction.setStatusTip('Undo last plot view change') self.undoAction.setDisabled(True) self.undoAction.triggered.connect(self.undo) self.redoAction = QAction('&Redo', self) self.redoAction.setDisabled(True) self.redoAction.setToolTip('Redo') self.redoAction.setStatusTip('Redo last plot view change') self.redoAction.setShortcut(QtGui.QKeySequence.Redo) self.redoAction.triggered.connect(self.redo) self.restoreAction = QAction("&Restore Default Plot", self) self.restoreAction.setShortcut("Ctrl+R") self.restoreAction.setToolTip('Restore to default plot view') self.restoreAction.setStatusTip('Restore to default plot view') self.restoreAction.triggered.connect(self.restoreDefault) self.editMenu = self.mainMenu.addMenu('&Edit') self.editMenu.addAction(self.applyAction) self.editMenu.addSeparator() self.editMenu.addAction(self.undoAction) self.editMenu.addAction(self.redoAction) self.editMenu.addSeparator() self.editMenu.addAction(self.restoreAction) self.editMenu.addSeparator() self.editMenu.aboutToShow.connect(self.updateEditMenu) # Edit -> Basis Menu self.xyAction = QAction('&xy ', self) self.xyAction.setCheckable(True) self.xyAction.setShortcut('Alt+X') self.xyAction.setToolTip('Change to xy basis') self.xyAction.setStatusTip('Change to xy basis') xy_connector = partial(self.editBasis, 'xy', apply=True) self.xyAction.triggered.connect(xy_connector) self.xzAction = QAction('x&z ', self) self.xzAction.setCheckable(True) self.xzAction.setShortcut('Alt+Z') self.xzAction.setToolTip('Change to xz basis') self.xzAction.setStatusTip('Change to xz basis') xz_connector = partial(self.editBasis, 'xz', apply=True) self.xzAction.triggered.connect(xz_connector) self.yzAction = QAction('&yz ', self) self.yzAction.setCheckable(True) self.yzAction.setShortcut('Alt+Y') self.yzAction.setToolTip('Change to yz basis') self.yzAction.setStatusTip('Change to yz basis') yz_connector = partial(self.editBasis, 'yz', apply=True) self.yzAction.triggered.connect(yz_connector) self.basisMenu = self.editMenu.addMenu('&Basis') self.basisMenu.addAction(self.xyAction) self.basisMenu.addAction(self.xzAction) self.basisMenu.addAction(self.yzAction) self.basisMenu.aboutToShow.connect(self.updateBasisMenu) # Edit -> Color By Menu self.cellAction = QAction('&Cell', self) self.cellAction.setCheckable(True) self.cellAction.setShortcut('Alt+C') self.cellAction.setToolTip('Color by cell') self.cellAction.setStatusTip('Color plot by cell') cell_connector = partial(self.editColorBy, 'cell', apply=True) self.cellAction.triggered.connect(cell_connector) self.materialAction = QAction('&Material', self) self.materialAction.setCheckable(True) self.materialAction.setShortcut('Alt+M') self.materialAction.setToolTip('Color by material') self.materialAction.setStatusTip('Color plot by material') material_connector = partial(self.editColorBy, 'material', apply=True) self.materialAction.triggered.connect(material_connector) self.temperatureAction = QAction('&Temperature', self) self.temperatureAction.setCheckable(True) self.temperatureAction.setShortcut('Alt+T') self.temperatureAction.setToolTip('Color by temperature') self.temperatureAction.setStatusTip('Color plot by temperature') temp_connector = partial(self.editColorBy, 'temperature', apply=True) self.temperatureAction.triggered.connect(temp_connector) self.densityAction = QAction('&Density', self) self.densityAction.setCheckable(True) self.densityAction.setShortcut('Alt+D') self.densityAction.setToolTip('Color by density') self.densityAction.setStatusTip('Color plot by density') density_connector = partial(self.editColorBy, 'density', apply=True) self.densityAction.triggered.connect(density_connector) self.colorbyMenu = self.editMenu.addMenu('&Color By') self.colorbyMenu.addAction(self.cellAction) self.colorbyMenu.addAction(self.materialAction) self.colorbyMenu.addAction(self.temperatureAction) self.colorbyMenu.addAction(self.densityAction) self.colorbyMenu.aboutToShow.connect(self.updateColorbyMenu) self.editMenu.addSeparator() # Edit -> Other Options self.maskingAction = QAction('Enable &Masking', self) self.maskingAction.setShortcut('Ctrl+M') self.maskingAction.setCheckable(True) self.maskingAction.setToolTip('Toggle masking') self.maskingAction.setStatusTip('Toggle whether masking is enabled') masking_connector = partial(self.toggleMasking, apply=True) self.maskingAction.toggled.connect(masking_connector) self.editMenu.addAction(self.maskingAction) self.highlightingAct = QAction('Enable High&lighting', self) self.highlightingAct.setShortcut('Ctrl+L') self.highlightingAct.setCheckable(True) self.highlightingAct.setToolTip('Toggle highlighting') self.highlightingAct.setStatusTip('Toggle whether ' 'highlighting is enabled') highlight_connector = partial(self.toggleHighlighting, apply=True) self.highlightingAct.toggled.connect(highlight_connector) self.editMenu.addAction(self.highlightingAct) self.overlapAct = QAction('Enable Overlap Coloring', self) self.overlapAct.setShortcut('Ctrl+P') self.overlapAct.setCheckable(True) self.overlapAct.setToolTip('Toggle overlapping regions') self.overlapAct.setStatusTip('Toggle display of overlapping ' 'regions when enabled') overlap_connector = partial(self.toggleOverlaps, apply=True) self.overlapAct.toggled.connect(overlap_connector) self.editMenu.addAction(self.overlapAct) self.outlineAct = QAction('Enable Domain Outlines', self) self.outlineAct.setShortcut('Ctrl+U') self.outlineAct.setCheckable(True) self.outlineAct.setToolTip('Display Cell/Material Boundaries') self.outlineAct.setStatusTip('Toggle display of domain ' 'outlines when enabled') outline_connector = partial(self.toggleOutlines, apply=True) self.outlineAct.toggled.connect(outline_connector) self.editMenu.addAction(self.outlineAct) # View Menu self.dockAction = QAction('Hide &Dock', self) self.dockAction.setShortcut("Ctrl+D") self.dockAction.setToolTip('Toggle dock visibility') self.dockAction.setStatusTip('Toggle dock visibility') self.dockAction.triggered.connect(self.toggleDockView) self.tallyDockAction = QAction('Tally &Dock', self) self.tallyDockAction.setShortcut("Ctrl+T") self.tallyDockAction.setToolTip('Toggle tally dock visibility') self.tallyDockAction.setStatusTip('Toggle tally dock visibility') self.tallyDockAction.triggered.connect(self.toggleTallyDockView) self.zoomAction = QAction('&Zoom...', self) self.zoomAction.setShortcut('Alt+Shift+Z') self.zoomAction.setToolTip('Edit zoom factor') self.zoomAction.setStatusTip('Edit zoom factor') self.zoomAction.triggered.connect(self.editZoomAct) self.viewMenu = self.mainMenu.addMenu('&View') self.viewMenu.addAction(self.dockAction) self.viewMenu.addAction(self.tallyDockAction) self.viewMenu.addSeparator() self.viewMenu.addAction(self.zoomAction) self.viewMenu.aboutToShow.connect(self.updateViewMenu) # Window Menu self.mainWindowAction = QAction('&Main Window', self) self.mainWindowAction.setCheckable(True) self.mainWindowAction.setToolTip('Bring main window to front') self.mainWindowAction.setStatusTip('Bring main window to front') self.mainWindowAction.triggered.connect(self.showMainWindow) self.colorDialogAction = QAction('Color &Options', self) self.colorDialogAction.setCheckable(True) self.colorDialogAction.setToolTip('Bring Color Dialog to front') self.colorDialogAction.setStatusTip('Bring Color Dialog to front') self.colorDialogAction.triggered.connect(self.showColorDialog) # Keyboard Shortcuts Overlay self.keyboardShortcutsAction = QAction("&Keyboard Shortcuts...", self) self.keyboardShortcutsAction.setShortcut("?") self.keyboardShortcutsAction.setToolTip("Display Keyboard Shortcuts") self.keyboardShortcutsAction.setStatusTip("Display Keyboard Shortcuts") self.keyboardShortcutsAction.triggered.connect(self.toggleShortcuts) self.windowMenu = self.mainMenu.addMenu('&Window') self.windowMenu.addAction(self.mainWindowAction) self.windowMenu.addAction(self.colorDialogAction) self.windowMenu.addAction(self.keyboardShortcutsAction) self.windowMenu.aboutToShow.connect(self.updateWindowMenu) def updateEditMenu(self): changed = self.model.currentView != self.model.defaultView self.restoreAction.setDisabled(not changed) self.maskingAction.setChecked(self.model.currentView.masking) self.highlightingAct.setChecked(self.model.currentView.highlighting) self.outlineAct.setChecked(self.model.currentView.outlines) num_previous_views = len(self.model.previousViews) self.undoAction.setText('&Undo ({})'.format(num_previous_views)) num_subsequent_views = len(self.model.subsequentViews) self.redoAction.setText('&Redo ({})'.format(num_subsequent_views)) def updateBasisMenu(self): self.xyAction.setChecked(self.model.currentView.basis == 'xy') self.xzAction.setChecked(self.model.currentView.basis == 'xz') self.yzAction.setChecked(self.model.currentView.basis == 'yz') def updateColorbyMenu(self): cv = self.model.currentView self.cellAction.setChecked(cv.colorby == 'cell') self.materialAction.setChecked(cv.colorby == 'material') self.temperatureAction.setChecked(cv.colorby == 'temperature') self.densityAction.setChecked(cv.colorby == 'density') def updateViewMenu(self): if self.dock.isVisible(): self.dockAction.setText('Hide &Dock') else: self.dockAction.setText('Show &Dock') def updateWindowMenu(self): self.colorDialogAction.setChecked(self.colorDialog.isActiveWindow()) self.mainWindowAction.setChecked(self.isActiveWindow()) # Menu and shared methods def loadModel(self, reload=False): if reload: self.resetModels() else: # create new plot model self.model = PlotModel() self.restoreModelSettings() # update plot and model settings self.updateRelativeBases() self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) if reload: loader_thread = Thread(target=_openmcReload) loader_thread.start() while loader_thread.is_alive(): self.statusBar().showMessage("Reloading model...") QApplication.processEvents() self.plotIm.model = self.model self.applyChanges() def saveImage(self): filename, ext = QFileDialog.getSaveFileName(self, "Save Plot Image", "untitled", "Images (*.png)") if filename: if "." not in filename: filename += ".png" self.plotIm.figure.savefig(filename, transparent=True) self.statusBar().showMessage('Plot Image Saved', 5000) def saveView(self): filename, ext = QFileDialog.getSaveFileName(self, "Save View Settings", "untitled", "View Settings (*.pltvw)") if filename: if "." not in filename: filename += ".pltvw" saved = { 'version': self.model.version, 'current': self.model.currentView } with open(filename, 'wb') as file: pickle.dump(saved, file) def openView(self): filename, ext = QFileDialog.getOpenFileName(self, "Open View Settings", ".", "*.pltvw") if filename: try: with open(filename, 'rb') as file: saved = pickle.load(file) except Exception: message = 'Error loading plot settings' saved = {'version': None, 'current': None} if saved['version'] == self.model.version: self.model.activeView = saved['current'] self.dock.updateDock() self.colorDialog.updateDialogValues() self.applyChanges() message = '{} settings loaded'.format(filename) else: message = 'Error loading plot settings. Incompatible model.' self.statusBar().showMessage(message, 5000) def openStatePoint(self): # check for an alread-open statepoint if self.model.statepoint: msg_box = QMessageBox() msg_box.setText("Please close the current statepoint file before " "opening a new one.") msg_box.setIcon(QMessageBox.Information) msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec_() return filename, ext = QFileDialog.getOpenFileName(self, "Open StatePoint", ".", "statepoint*.h5") if filename: try: self.model.openStatePoint(filename) message = 'Opened statepoint file: {}' except (FileNotFoundError, OSError): message = 'Error opening statepoint file: {}' msg_box = QMessageBox() msg = "Could open statepoint file: \n\n {} \n" msg_box.setText(msg.format(filename)) msg_box.setIcon(QMessageBox.Warning) msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec_() finally: self.statusBar().showMessage(message.format(filename), 5000) self.updateDataMenu() self.tallyDock.update() def closeStatePoint(self): # remove the statepoint object and update the data menu filename = self.model.statepoint.filename self.model.statepoint = None self.model.currentView.selectedTally = None self.model.activeView.selectedTally = None msg = "Closed statepoint file {}".format(filename) self.statusBar().showMessage(msg) self.updateDataMenu() self.tallyDock.selectTally() self.tallyDock.update() self.plotIm.updatePixmap() def updateDataMenu(self): if self.model.statepoint: self.closeStatePointAction = QAction("&Close statepoint", self) self.closeStatePointAction.setToolTip("Close current statepoint") self.closeStatePointAction.triggered.connect(self.closeStatePoint) self.dataMenu.addAction(self.closeStatePointAction) elif hasattr(self, "closeStatePointAction"): self.dataMenu.removeAction(self.closeStatePointAction) def applyChanges(self): if self.model.activeView != self.model.currentView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.subsequentViews = [] self.plotIm.generatePixmap() self.resetModels() self.showCurrentView() self.statusBar().showMessage('') else: self.statusBar().showMessage('No changes to apply.', 3000) def undo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.undo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.previousViews: self.undoAction.setDisabled(True) self.redoAction.setDisabled(False) self.statusBar().showMessage('') def redo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.redo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.subsequentViews: self.redoAction.setDisabled(True) self.undoAction.setDisabled(False) self.statusBar().showMessage('') def restoreDefault(self): if self.model.currentView != self.model.defaultView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.activeView.adopt_plotbase(self.model.defaultView) self.plotIm.generatePixmap() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() self.model.subsequentViews = [] self.statusBar().showMessage('') def editBasis(self, basis, apply=False): self.model.activeView.basis = basis self.dock.updateBasis() if apply: self.applyChanges() def editColorBy(self, domain_kind, apply=False): self.model.activeView.colorby = domain_kind self.dock.updateColorBy() self.colorDialog.updateColorBy() if apply: self.applyChanges() def toggleOverlaps(self, state, apply=False): self.model.activeView.color_overlaps = bool(state) self.colorDialog.updateOverlap() if apply: self.applyChanges() def editColorMap(self, colormap_name, property_type, apply=False): self.model.activeView.colormaps[property_type] = colormap_name self.plotIm.updateColorMap(colormap_name, property_type) self.colorDialog.updateColorMaps() if apply: self.applyChanges() def editColorbarMin(self, min_val, property_type, apply=False): av = self.model.activeView current = av.user_minmax[property_type] av.user_minmax[property_type] = (min_val, current[1]) self.colorDialog.updateColorMinMax() self.plotIm.updateColorMinMax(property_type) if apply: self.applyChanges() def editColorbarMax(self, max_val, property_type, apply=False): av = self.model.activeView current = av.user_minmax[property_type] av.user_minmax[property_type] = (current[0], max_val) self.colorDialog.updateColorMinMax() self.plotIm.updateColorMinMax(property_type) if apply: self.applyChanges() def toggleColorbarScale(self, state, property, apply=False): av = self.model.activeView av.color_scale_log[property] = bool(state) # temporary, should be resolved diferently in the future cv = self.model.currentView cv.color_scale_log[property] = bool(state) self.plotIm.updateColorbarScale() if apply: self.applyChanges() def toggleUserMinMax(self, state, property): av = self.model.activeView av.use_custom_minmax[property] = bool(state) if av.user_minmax[property] == (0.0, 0.0): av.user_minmax[property] = copy.copy(av.data_minmax[property]) self.plotIm.updateColorMinMax('temperature') self.plotIm.updateColorMinMax('density') self.colorDialog.updateColorMinMax() def toggleDataIndicatorCheckBox(self, state, property, apply=False): av = self.model.activeView av.data_indicator_enabled[property] = bool(state) cv = self.model.currentView cv.data_indicator_enabled[property] = bool(state) self.plotIm.updateDataIndicatorVisibility() if apply: self.applyChanges() def toggleMasking(self, state, apply=False): self.model.activeView.masking = bool(state) self.colorDialog.updateMasking() if apply: self.applyChanges() def toggleHighlighting(self, state, apply=False): self.model.activeView.highlighting = bool(state) self.colorDialog.updateHighlighting() if apply: self.applyChanges() def toggleDockView(self): if self.dock.isVisible(): self.dock.hide() if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() - self.dock.width(), self.height()) else: self.dock.setVisible(True) if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() + self.dock.width(), self.height()) self.resizePixmap() self.showMainWindow() def toggleTallyDockView(self): if self.tallyDock.isVisible(): self.tallyDock.hide() if not self.isMaximized() and not self.tallyDock.isFloating(): self.resize(self.width() - self.tallyDock.width(), self.height()) else: self.tallyDock.setVisible(True) if not self.isMaximized() and not self.tallyDock.isFloating(): self.resize(self.width() + self.tallyDock.width(), self.height()) self.resizePixmap() self.showMainWindow() def editZoomAct(self): percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:", self.dock.zoomBox.value(), 25, 2000) if ok: self.dock.zoomBox.setValue(percent) def editZoom(self, value): self.zoom = value self.resizePixmap() self.dock.zoomBox.setValue(value) def showMainWindow(self): self.raise_() self.activateWindow() def showColorDialog(self): self.colorDialog.show() self.colorDialog.raise_() self.colorDialog.activateWindow() def showExportDialog(self): self.exportDataDialog.show() self.exportDataDialog.raise_() self.exportDataDialog.activateWindow() # Dock methods: def editSingleOrigin(self, value, dimension): self.model.activeView.origin[dimension] = value def editPlotAlpha(self, value): self.model.activeView.domainAlpha = value def editPlotVisibility(self, value): self.model.activeView.domainVisible = bool(value) def toggleOutlines(self, value, apply=False): self.model.activeView.outlines = bool(value) self.dock.updateOutlines() if apply: self.applyChanges() def editWidth(self, value): self.model.activeView.width = value self.onRatioChange() self.dock.updateWidth() def editHeight(self, value): self.model.activeView.height = value self.onRatioChange() self.dock.updateHeight() def toggleAspectLock(self, state): self.model.activeView.aspectLock = bool(state) self.onRatioChange() self.dock.updateAspectLock() def editVRes(self, value): self.model.activeView.v_res = value self.dock.updateVRes() def editHRes(self, value): self.model.activeView.h_res = value self.onRatioChange() self.dock.updateHRes() # Color dialog methods: def editMaskingColor(self): current_color = self.model.activeView.maskBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.maskBackground = new_color self.colorDialog.updateMaskingColor() def editHighlightColor(self): current_color = self.model.activeView.highlightBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.highlightBackground = new_color self.colorDialog.updateHighlightColor() def editAlpha(self, value): self.model.activeView.highlightAlpha = value def editSeed(self, value): self.model.activeView.highlightSeed = value def editOverlapColor(self, apply=False): current_color = self.model.activeView.overlap_color dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.overlap_color = new_color self.colorDialog.updateOverlapColor() if apply: self.applyChanges() def editBackgroundColor(self, apply=False): current_color = self.model.activeView.domainBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.domainBackground = new_color self.colorDialog.updateBackgroundColor() if apply: self.applyChanges() def resetColors(self): self.model.resetColors() self.colorDialog.updateDialogValues() self.applyChanges() # Tally dock methods def editSelectedTally(self, event): av = self.model.activeView if event is None or event == "None" or event == "": av.selectedTally = None else: av.selectedTally = int(event.split()[1]) self.tallyDock.selectTally(event) def editTallyValue(self, event): av = self.model.activeView av.tallyValue = event def updateScores(self, state): self.tallyDock.updateScores() def updateNuclides(self, state): self.tallyDock.updateNuclides() def toggleTallyVisibility(self, state, apply=False): av = self.model.activeView av.tallyDataVisible = bool(state) if apply: self.applyChanges() def toggleTallyLogScale(self, state, apply=False): av = self.model.activeView av.tallyDataLogScale = bool(state) if apply: self.applyChanges() def toggleTallyMaskZero(self, state): av = self.model.activeView av.tallyMaskZeroValues = bool(state) def editTallyAlpha(self, value, apply=False): av = self.model.activeView av.tallyDataAlpha = value if apply: self.applyChanges() def toggleTallyContours(self, state): av = self.model.activeView av.tallyContours = bool(state) def editTallyContourLevels(self, value): av = self.model.activeView av.tallyContourLevels = value def toggleTallyDataIndicator(self, state, apply=False): av = self.model.activeView av.tallyDataIndicator = bool(state) if apply: self.applyChanges() def toggleTallyDataClip(self, state): av = self.model.activeView av.clipTallyData = bool(state) def toggleTallyDataUserMinMax(self, state, apply=False): av = self.model.activeView av.tallyDataUserMinMax = bool(state) self.tallyDock.tallyColorForm.setMinMaxEnabled(bool(state)) if apply: self.applyChanges() def editTallyDataMin(self, value, apply=False): av = self.model.activeView av.tallyDataMin = value if apply: self.applyChanges() def editTallyDataMax(self, value, apply=False): av = self.model.activeView av.tallyDataMax = value if apply: self.applyChanges() def editTallyDataColormap(self, cmap, apply=False): av = self.model.activeView av.tallyDataColormap = cmap if apply: self.applyChanges() def updateTallyMinMax(self): self.tallyDock.updateMinMax() # Plot image methods def editPlotOrigin(self, xOr, yOr, zOr=None, apply=False): if zOr is not None: self.model.activeView.origin = [xOr, yOr, zOr] else: origin = [None, None, None] origin[self.xBasis] = xOr origin[self.yBasis] = yOr origin[self.zBasis] = self.model.activeView.origin[self.zBasis] self.model.activeView.origin = origin self.dock.updateOrigin() if apply: self.applyChanges() def revertDockControls(self): self.dock.revertToCurrent() def editDomainColor(self, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials current_color = domain[id].color dlg = QColorDialog(self) if isinstance(current_color, tuple): dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) elif isinstance(current_color, str): current_color = openmc.plots._SVG_COLORS[current_color] dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] domain[id].color = new_color self.applyChanges() def toggleDomainMask(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].masked = bool(state) self.applyChanges() def toggleDomainHighlight(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].highlight = bool(state) self.applyChanges() # Helper methods: def restoreWindowSettings(self): settings = QtCore.QSettings() self.resize(settings.value("mainWindow/Size", QtCore.QSize(800, 600))) self.move( settings.value("mainWindow/Position", QtCore.QPoint(100, 100))) self.restoreState(settings.value("mainWindow/State")) self.colorDialog.resize( settings.value("colorDialog/Size", QtCore.QSize(400, 500))) self.colorDialog.move( settings.value("colorDialog/Position", QtCore.QPoint(600, 200))) is_visible = settings.value("colorDialog/Visible", 0) # some versions of PySide will return None rather than the default value if is_visible is None: is_visible = False else: is_visible = bool(int(is_visible)) self.colorDialog.setVisible(is_visible) def restoreModelSettings(self): if os.path.isfile("plot_settings.pkl"): with open('plot_settings.pkl', 'rb') as file: model = pickle.load(file) # do not replace model if the version is out of date if model.version != self.model.version: print("WARNING: previous plot settings are for a different " "version of the GUI. They will be ignored.") wrn_msg = "Existing version: {}, Current GUI version: {}" print(wrn_msg.format(model.version, self.model.version)) return try: self.model.statepoint = model.statepoint except OSError: msg_box = QMessageBox() msg = "Could open statepoint file: \n\n {} \n" msg_box.setText(msg.format(self.model.statepoint.filename)) msg_box.setIcon(QMessageBox.Warning) msg_box.setStandardButtons(QMessageBox.Ok) msg_box.exec_() self.model.statepoint = None self.model.currentView = model.currentView self.model.activeView = copy.deepcopy(model.currentView) self.model.previousViews = model.previousViews self.model.subsequentViews = model.subsequentViews def resetModels(self): self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) self.cellsModel.beginResetModel() self.cellsModel.endResetModel() self.materialsModel.beginResetModel() self.materialsModel.endResetModel() self.colorDialog.updateDomainTabs() def showCurrentView(self): self.updateScale() self.updateRelativeBases() self.plotIm.updatePixmap() if self.model.previousViews: self.undoAction.setDisabled(False) if self.model.subsequentViews: self.redoAction.setDisabled(False) else: self.redoAction.setDisabled(True) self.adjustWindow() def updateScale(self): cv = self.model.currentView self.scale = (cv.h_res / cv.width, cv.v_res / cv.height) def updateRelativeBases(self): cv = self.model.currentView self.xBasis = 0 if cv.basis[0] == 'x' else 1 self.yBasis = 1 if cv.basis[1] == 'y' else 2 self.zBasis = 3 - (self.xBasis + self.yBasis) def adjustWindow(self): self.setMaximumSize(self.screen.width(), self.screen.height()) def onRatioChange(self): av = self.model.activeView if av.aspectLock: ratio = av.width / max(av.height, .001) av.v_res = int(av.h_res / ratio) self.dock.updateVRes() def showCoords(self, xPlotPos, yPlotPos): cv = self.model.currentView if cv.basis == 'xy': coords = ("({}, {}, {})".format(round(xPlotPos, 2), round(yPlotPos, 2), round(cv.origin[2], 2))) elif cv.basis == 'xz': coords = ("({}, {}, {})".format(round(xPlotPos, 2), round(cv.origin[1], 2), round(yPlotPos, 2))) else: coords = ("({}, {}, {})".format(round(cv.origin[0], 2), round(xPlotPos, 2), round(yPlotPos, 2))) self.coord_label.setText('{}'.format(coords)) def resizePixmap(self): self.plotIm._resize() self.plotIm.adjustSize() def moveEvent(self, event): self.adjustWindow() def resizeEvent(self, event): self.plotIm._resize() self.adjustWindow() self.updateScale() if self.shortcutOverlay.isVisible(): self.shortcutOverlay.resize(self.width(), self.height()) def closeEvent(self, event): settings = QtCore.QSettings() settings.setValue("mainWindow/Size", self.size()) settings.setValue("mainWindow/Position", self.pos()) settings.setValue("mainWindow/State", self.saveState()) settings.setValue("colorDialog/Size", self.colorDialog.size()) settings.setValue("colorDialog/Position", self.colorDialog.pos()) visible = int(self.colorDialog.isVisible()) settings.setValue("colorDialog/Visible", visible) openmc.lib.finalize() self.saveSettings() def saveSettings(self): if len(self.model.previousViews) > 10: self.model.previousViews = self.model.previousViews[-10:] if len(self.model.subsequentViews) > 10: self.model.subsequentViews = self.model.subsequentViews[-10:] with open('plot_settings.pkl', 'wb') as file: if self.model.statepoint: self.model.statepoint.close() pickle.dump(self.model, file) def exportTallyData(self): # show export tool dialog self.showExportDialog()
class UIMainWindow(object): """Container class to hold all UI-related creation methods. Must be sublcassed. """ def create_ui(self): """Setup main UI elements, dock widgets, UI-related elements, etc. """ log.debug('Loading UI') # Undo Stack self.undo_stack = QUndoStack(self) self.undo_stack.setUndoLimit(100) # Object navigation history self.obj_history = deque([], config.MAX_OBJ_HISTORY) app = QApplication.instance() base_font = QFont() base_font.fromString(self.prefs['base_font']) app.setFont(base_font) # Object class table widget # classTable = QTableView(self) classTable = classtable.TableView(self) classTable.setObjectName("classTable") classTable.setAlternatingRowColors(True) classTable.setFrameShape(QFrame.StyledPanel) classTable_font = QFont() classTable_font.fromString(self.prefs['class_table_font']) classTable.setFont(classTable_font) fm = classTable.fontMetrics() classTable.setWordWrap(True) classTable.setEditTriggers(QAbstractItemView.EditKeyPressed | QAbstractItemView.DoubleClicked | QAbstractItemView.AnyKeyPressed | QAbstractItemView.SelectedClicked) # classTable.horizontalHeader().setMovable(True) # classTable.verticalHeader().setMovable(False) classTable.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) classTable.verticalHeader().setSectionResizeMode(QHeaderView.Interactive) classTable.horizontalHeader().setDefaultSectionSize(self.prefs['default_column_width']) classTable.verticalHeader().setDefaultSectionSize(fm.height() + 0) classTable.setSelectionMode(QAbstractItemView.ExtendedSelection) classTable.setContextMenuPolicy(Qt.CustomContextMenu) classTable.customContextMenuRequested.connect(self.custom_table_context_menu) # Create table model and proxy layers for transposing and filtering self.classTableModel = classtable.IDFObjectTableModel(classTable) self.transposeableModel = classtable.TransposeProxyModel(self.classTableModel) self.transposeableModel.setSourceModel(self.classTableModel) self.sortableModel = classtable.SortFilterProxyModel(self.transposeableModel) self.sortableModel.setSourceModel(self.transposeableModel) # Assign model to table (enable sorting FIRST) # table.setSortingEnabled(True) # Disable for now, CRUD actions won't work! classTable.setModel(self.sortableModel) # Connect some signals selection_model = classTable.selectionModel() selection_model.selectionChanged.connect(self.table_selection_changed) scroll_bar = classTable.verticalScrollBar() scroll_bar.valueChanged.connect(self.scroll_changed) # These are currently broken # classTable.horizontalHeader().sectionMoved.connect(self.moveObject) # classTable.verticalHeader().sectionMoved.connect(self.moveObject) # Object class tree widget classTreeDockWidget = QDockWidget("Object Classes and Counts", self) classTreeDockWidget.setObjectName("classTreeDockWidget") classTreeDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) classTree = QTreeView(classTreeDockWidget) classTree.setUniformRowHeights(True) classTree.setAllColumnsShowFocus(True) classTree.setRootIsDecorated(False) classTree.setExpandsOnDoubleClick(True) classTree.setIndentation(15) classTree.setAnimated(True) classTree_font = QFont() classTree_font.fromString(self.prefs['class_tree_font']) classTree.setFont(classTree_font) classTree.setAlternatingRowColors(True) classTree.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel) palette = classTree.palette() palette.setColor(QPalette.Highlight, Qt.darkCyan) classTree.setPalette(palette) class_tree_window = QWidget(classTreeDockWidget) class_tree_dock_layout_v = QVBoxLayout() class_tree_dock_layout_h = QHBoxLayout() class_tree_dock_layout_v.setContentsMargins(0, 8, 0, 0) class_tree_dock_layout_h.setContentsMargins(0, 0, 0, 0) class_tree_filter_edit = QLineEdit(classTreeDockWidget) class_tree_filter_edit.setPlaceholderText("Filter Classes") class_tree_filter_edit.textChanged.connect(self.treeFilterRegExpChanged) class_tree_filter_cancel = QPushButton("Clear", classTreeDockWidget) class_tree_filter_cancel.setMaximumWidth(45) class_tree_filter_cancel.clicked.connect(self.clearTreeFilterClicked) class_tree_dock_layout_h.addWidget(class_tree_filter_edit) class_tree_dock_layout_h.addWidget(class_tree_filter_cancel) class_tree_dock_layout_v.addLayout(class_tree_dock_layout_h) class_tree_dock_layout_v.addWidget(classTree) class_tree_window.setLayout(class_tree_dock_layout_v) classTreeDockWidget.setWidget(class_tree_window) classTreeDockWidget.setContentsMargins(0,0,0,0) # Comments widget commentDockWidget = QDockWidget("Comments", self) commentDockWidget.setObjectName("commentDockWidget") commentDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) commentView = UndoRedoTextEdit(commentDockWidget, self) commentView.setLineWrapMode(QTextEdit.FixedColumnWidth) commentView.setLineWrapColumnOrWidth(499) commentView.setFrameShape(QFrame.StyledPanel) commentView_font = QFont() commentView_font.fromString(self.prefs['comments_font']) commentView.setFont(commentView_font) commentDockWidget.setWidget(commentView) # Info and help widget infoDockWidget = QDockWidget("Info", self) infoDockWidget.setObjectName("infoDockWidget") infoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) infoView = QTextEdit(infoDockWidget) infoView.setFrameShape(QFrame.StyledPanel) infoView.setReadOnly(True) infoDockWidget.setWidget(infoView) # Node list and jump menu widget refDockWidget = QDockWidget("Field References", self) refDockWidget.setObjectName("refDockWidget") refDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) ref_model = reftree.ReferenceTreeModel(None, refDockWidget) refView = QTreeView(refDockWidget) refView.setModel(ref_model) refView.setUniformRowHeights(True) refView.setRootIsDecorated(False) refView.setIndentation(15) refView.setColumnWidth(0, 160) refView.setFrameShape(QFrame.StyledPanel) refDockWidget.setWidget(refView) refView.doubleClicked.connect(self.ref_tree_double_clicked) # Logging and debugging widget logDockWidget = QDockWidget("Log Viewer", self) logDockWidget.setObjectName("logDockWidget") logDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) logView = QPlainTextEdit(logDockWidget) logView.setLineWrapMode(QPlainTextEdit.NoWrap) logView.setReadOnly(True) logView_font = QFont() logView_font.fromString(self.prefs['base_font']) logView.setFont(logView_font) logView.ensureCursorVisible() logDockWidget.setWidget(logView) # Undo view widget undoDockWidget = QDockWidget("Undo History", self) undoDockWidget.setObjectName("undoDockWidget") undoDockWidget.setAllowedAreas(Qt.AllDockWidgetAreas) undoView = QUndoView(self.undo_stack) undoDockWidget.setWidget(undoView) # Define corner docking behaviour self.setDockNestingEnabled(True) self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) # Assign main widget and dock widgets to QMainWindow self.setCentralWidget(classTable) self.addDockWidget(Qt.LeftDockWidgetArea, classTreeDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, commentDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, infoDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, refDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, logDockWidget) self.addDockWidget(Qt.RightDockWidgetArea, undoDockWidget) # Store widgets for access by other objects self.classTable = classTable self.commentView = commentView self.infoView = infoView self.classTree = classTree self.logView = logView self.undoView = undoView self.refView = refView self.filterTreeBox = class_tree_filter_edit # Store docks for access by other objects self.commentDockWidget = commentDockWidget self.infoDockWidget = infoDockWidget self.classTreeDockWidget = classTreeDockWidget self.logDockWidget = logDockWidget self.undoDockWidget = undoDockWidget self.refDockWidget = refDockWidget # Perform other UI-related initialization tasks self.center() self.setUnifiedTitleAndToolBarOnMac(True) self.setWindowIcon(QIcon(':/images/logo.png')) # Status bar setup self.statusBar().showMessage('Status: Ready') self.unitsLabel = QLabel() self.unitsLabel.setAlignment(Qt.AlignCenter) self.unitsLabel.setMinimumSize(self.unitsLabel.sizeHint()) self.unitsLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.unitsLabel) self.pathLabel = QLabel() self.pathLabel.setAlignment(Qt.AlignCenter) self.pathLabel.setMinimumSize(self.pathLabel.sizeHint()) self.pathLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.pathLabel) self.versionLabel = QLabel() self.versionLabel.setAlignment(Qt.AlignCenter) self.versionLabel.setMinimumSize(self.versionLabel.sizeHint()) self.versionLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) self.statusBar().addPermanentWidget(self.versionLabel) self.progressBarIDF = QProgressBar() self.progressBarIDF.setAlignment(Qt.AlignCenter) self.progressBarIDF.setMaximumWidth(200) self.statusBar().addPermanentWidget(self.progressBarIDF) self.clipboard = QApplication.instance().clipboard() self.obj_clipboard = [] self.setStyleSheet(""" QToolTip { background-color: gray; color: white; border: black solid 1px } # QMenu { # background-color: rgbf(0.949020, 0.945098, 0.941176); # color: rgb(255,255,255); # } # QMenu::item::selected { # background-color: rgbf(0.949020, 0.945098, 0.941176); # } """) def create_tray_menu(self): """Creates an icon and menu for the system tray """ # Menu for the system tray self.trayIconMenu = QMenu(self) self.trayIconMenu.addAction(self.minimizeAction) self.trayIconMenu.addAction(self.maximizeAction) self.trayIconMenu.addAction(self.restoreAction) self.trayIconMenu.addSeparator() self.trayIconMenu.addAction(self.exitAct) # System tray itself self.trayIcon = QSystemTrayIcon(self) self.trayIcon.setContextMenu(self.trayIconMenu) self.trayIcon.setIcon(QIcon(':/images/logo.png')) self.trayIcon.setToolTip('IDF+') self.trayIcon.show() def create_actions(self): """Creates appropriate actions for use in menus and toolbars. """ self.newAct = QAction(QIcon(':/images/new1.png'), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", iconVisibleInMenu=True, triggered=self.new_file) self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", iconVisibleInMenu=True, triggered=self.open_file) self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", iconVisibleInMenu=True, triggered=self.save) self.saveFormatAct = QAction(QIcon(':/images/save.png'), "&Format && Save", self, shortcut=QKeySequence('Ctrl+Shift+F'), statusTip="Format File and Save to disk", iconVisibleInMenu=True, triggered=self.format_save) self.saveAsAct = QAction(QIcon(':/images/saveas.png'), "Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", iconVisibleInMenu=True, triggered=self.save_as) self.exitAct = QAction(QIcon(':/images/quit.png'), "E&xit", self, shortcut=QKeySequence('Ctrl+Q'), iconVisibleInMenu=True, statusTip="Exit the application", triggered=self.closeAllWindows) self.cutObjAct = QAction(QIcon(':/images/cut.png'), "Cu&t Object", self, shortcut=QKeySequence.Cut, statusTip="Cut current selection's contents to clipboard", iconVisibleInMenu=True, triggered=self.cutObject, iconText='Cut Obj') self.copyAct = QAction(QIcon(':/images/copy.png'), "&Copy Selected Values", self, statusTip="Copy current selection's contents to clipboard", iconVisibleInMenu=True, triggered=self.copySelected) self.pasteAct = QAction(QIcon(':/images/paste.png'), "&Paste Selected Values", self, statusTip="Paste clipboard into current selection", iconVisibleInMenu=True, triggered=self.pasteSelected) self.pasteExtAct = QAction(QIcon(':/images/paste.png'), "&Paste from External", self, shortcut=QKeySequence('Ctrl+Shift+v'), statusTip="Paste from external program", iconVisibleInMenu=True, triggered=self.paste_from_external) self.transposeAct = QAction("Transpose", self, shortcut=QKeySequence('Ctrl+t'), statusTip="Transpose rows and columns in object display", triggered=self.transpose_table) self.newObjAct = QAction(QIcon(':/images/new2.png'), "New Object", self, shortcut=QKeySequence('Ctrl+Shift+n'), statusTip="Create new object in current class", iconVisibleInMenu=True, triggered=self.newObject, iconText='New Obj') self.copyObjAct = QAction(QIcon(':/images/copy.png'), "Copy Object", self, shortcut=QKeySequence.Copy, statusTip="Copy the current Object(s)", iconVisibleInMenu=True, triggered=self.copyObject, iconText='Copy Obj') self.pasteObjAct = QAction(QIcon(':/images/paste.png'), "Paste Object", self, shortcut=QKeySequence.Paste, statusTip="Paste the currently copies Object(s)", iconVisibleInMenu=True, triggered=self.pasteObject, iconText='Paste Obj') self.dupObjAct = QAction(QIcon(':/images/copy.png'), "Duplicate Object", self, shortcut=QKeySequence('Shift+Ctrl+d'), statusTip="Duplicate the current Object(s)", iconVisibleInMenu=True, triggered=self.duplicateObject, iconText='Dup Obj') self.delObjAct = QAction(QIcon(':/images/delete.png'), "Delete Object", self, shortcut=QKeySequence.Delete, statusTip="Delete the current Object(s)", iconVisibleInMenu=True, triggered=self.deleteObject, iconText='Del Obj') self.undoAct = QAction(QIcon(':/images/undo.png'), "&Undo", self, shortcut=QKeySequence.Undo, statusTip="Undo previous action", iconVisibleInMenu=True, triggered=self.undo_stack.undo) self.redoAct = QAction(QIcon(':/images/redo.png'), "&Redo", self, shortcut=QKeySequence.Redo, statusTip="Redo previous action", iconVisibleInMenu=True, triggered=self.undo_stack.redo) self.groupAct = QAction("Hide Groups in Class Tree", self, shortcut=QKeySequence('Ctrl+g'), triggered=self.toggle_groups, checkable=True) # self.navForwardAct = QAction("Forward", self, # shortcut=QKeySequence('Ctrl+Plus'), # statusTip="Go forward to the next object", # triggered=self.navForward) # # self.navBackAct = QAction("Back", self, # shortcut=QKeySequence('Ctrl+Minus'), # statusTip="Go back to the previous object", # triggered=self.navBack) self.showInFolderAct = QAction(QIcon(':/images/new.png'), "&Show in folder", self, shortcut=QKeySequence('Ctrl+Shift+t'), statusTip="Open location of current file", iconVisibleInMenu=True) self.showInFolderAct.triggered.connect(lambda: self.show_in_folder()) self.epDocGettingStartedAction = QAction("EnergyPlus Getting Started", self, triggered=self.energy_plus_docs) self.epDocIORefAction = QAction("EnergyPlus I/O Reference", self, triggered=self.energy_plus_docs) self.epDocOutputDetailsAction = QAction("EnergyPlus Output Details and Examples", self, triggered=self.energy_plus_docs) self.epDocEngineeringRefAction = QAction("EnergyPlus Engineering Reference", self, triggered=self.energy_plus_docs) self.epDocAuxiliaryProgsAction = QAction("EnergyPlus Auxiliary Programs", self, triggered=self.energy_plus_docs) self.epDocEMSGuideAction = QAction("EnergyPlus EMS Application Guide", self, triggered=self.energy_plus_docs) self.epDocComplianceAction = QAction("Using EnergyPlus for Compliance", self, triggered=self.energy_plus_docs) self.epDocInterfaceAction = QAction("External Interface Application Guide", self, triggered=self.energy_plus_docs) self.epDocTipsTricksAction = QAction("Tips and Tricks Using EnergyPlus", self, triggered=self.energy_plus_docs) self.epDocPlantGuideAction = QAction("EnergyPlus Plant Application Guide", self, triggered=self.energy_plus_docs) self.epDocAcknowledgmentsAction = QAction("EnergyPlus Acknowledgments", self, triggered=self.energy_plus_docs) self.openInEditorAct = QAction(QIcon(':/images/new.png'), "&Open in text editor", self, shortcut=QKeySequence('Ctrl+e'), statusTip="Open current file in default editor", iconVisibleInMenu=True, triggered=self.open_in_text_editor) self.helpAct = QAction("&EnergyPlus Help (Online)", self, statusTip="Show the EnergyPlus' help", triggered=self.energyplus_help) self.aboutAct = QAction("&About IDF+", self, statusTip="Show the application's About box", triggered=self.about) self.clearRecentAct = QAction("Clear Recent", self, statusTip="Clear recent files", triggered=self.clear_recent) self.minimizeAction = QAction("Mi&nimize", self, triggered=self.hide) self.maximizeAction = QAction("Ma&ximize", self, triggered=self.showMaximized) self.restoreAction = QAction("&Restore", self, triggered=self.showNormal) self.showPrefsAction = QAction("&Preferences", self, triggered=self.show_prefs_dialog) self.showSearchAction = QAction("&Search && Replace", self, shortcut=QKeySequence('Ctrl+f'), triggered=self.show_search_dialog) self.findThisAct = QAction("Find This", self, triggered=self.find_this) self.jumpFilterGeometry = QAction("Include Geometry", self, triggered=self.jump_to_filter_geometry, checkable=True) self.setIPUnitsAction = QAction("&IP Units", self, triggered=self.toggle_units, checkable=True) self.setSIUnitsAction = QAction("&SI Units", self, triggered=self.toggle_units, checkable=True) self.classWithObjsAction = QAction("Show Only Classes With Objects", self, shortcut=QKeySequence('Ctrl+l'), statusTip="Show Only Classes With Objects", triggered=self.toggle_full_tree, checkable=True) self.fillRightAction = QAction("Fill right", self, shortcut=QKeySequence('Ctrl+d'), statusTip="Fill right", triggered=self.fill_right) self.logDockWidgetAct = self.logDockWidget.toggleViewAction() self.transposeAct.setEnabled(False) self.setSIUnitsAction.setChecked(True) self.undoAct.setEnabled(False) self.redoAct.setEnabled(False) self.saveAct.setEnabled(False) self.undo_stack.canUndoChanged.connect(self.toggle_can_undo) self.undo_stack.canRedoChanged.connect(self.toggle_can_redo) self.logDockWidgetAct.toggled.connect(self.start_log_watcher) def toggle_can_undo(self): if self.undo_stack.canUndo(): new_state = True else: new_state = False self.undoAct.setEnabled(new_state) self.set_dirty(new_state) def toggle_can_redo(self): if self.undo_stack.canRedo(): new_state = True else: new_state = False self.redoAct.setEnabled(new_state) def create_menus(self): """Create all required items for menus. """ # File Menu self.fileMenu = self.menuBar().addMenu("&File") self.fileMenuActions = (self.newAct, self.openAct, self.saveAct, self.saveAsAct, self.saveFormatAct, None, self.exitAct) self.update_file_menu() self.fileMenu.aboutToShow.connect(self.update_file_menu) # Edit Menu self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.undoAct) self.editMenu.addAction(self.redoAct) self.editMenu.addSeparator().setText('Objects') self.editMenu.addAction(self.newObjAct) self.editMenu.addAction(self.dupObjAct) self.editMenu.addSeparator() self.editMenu.addAction(self.cutObjAct) self.editMenu.addAction(self.copyObjAct) self.editMenu.addAction(self.pasteObjAct) self.editMenu.addAction(self.pasteExtAct) self.editMenu.addSeparator() self.editMenu.addAction(self.delObjAct) self.editMenu.addSeparator().setText('Values') self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.editMenu.addSeparator() self.editMenu.addAction(self.fillRightAction) self.editMenu.addSeparator() self.editMenu.addAction(self.showSearchAction) # Tools Menu self.toolsMenu = self.menuBar().addMenu("&Tools") self.toolsMenu.addAction(self.showInFolderAct) self.toolsMenu.addAction(self.openInEditorAct) self.toolsMenu.addSeparator() self.toolsMenu.addAction(self.showPrefsAction) # View Menu self.viewMenu = self.menuBar().addMenu("&View") action_group = QActionGroup(self) self.viewMenu.addAction(action_group.addAction(self.setSIUnitsAction)) self.viewMenu.addAction(action_group.addAction(self.setIPUnitsAction)) self.viewMenu.addSeparator().setText('Dockable Widgets') self.viewMenu.addAction(self.classTreeDockWidget.toggleViewAction()) self.viewMenu.addAction(self.infoView.parent().toggleViewAction()) self.viewMenu.addAction(self.commentView.parent().toggleViewAction()) self.viewMenu.addAction(self.logDockWidgetAct) self.viewMenu.addAction(self.undoView.parent().toggleViewAction()) self.viewMenu.addSeparator().setText('Toolbars') self.viewMenu.addAction(self.fileToolBar.toggleViewAction()) self.viewMenu.addAction(self.editToolBar.toggleViewAction()) # self.viewMenu.addAction(self.navToolBar.toggleViewAction()) self.viewMenu.addAction(self.filterToolBar.toggleViewAction()) self.viewMenu.addSeparator() self.viewMenu.addAction(self.classWithObjsAction) self.viewMenu.addAction(self.groupAct) self.viewMenu.addSeparator() self.viewMenu.addAction(self.transposeAct) # Jump Menu self.jumpToMenu = self.menuBar().addMenu("&Jump") self.update_jump_menu() self.jumpToMenu.aboutToShow.connect(self.update_jump_menu) self.jumpFilterGeometry.setEnabled(False) # Help Menu self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.helpAct) self.helpMenu.addAction(self.aboutAct) self.helpMenu.addSeparator() self.helpMenu.addAction(self.epDocGettingStartedAction) self.helpMenu.addAction(self.epDocIORefAction) self.helpMenu.addAction(self.epDocOutputDetailsAction) self.helpMenu.addAction(self.epDocEngineeringRefAction) self.helpMenu.addAction(self.epDocAuxiliaryProgsAction) self.helpMenu.addAction(self.epDocEMSGuideAction) self.helpMenu.addAction(self.epDocComplianceAction) self.helpMenu.addAction(self.epDocInterfaceAction) self.helpMenu.addAction(self.epDocTipsTricksAction) self.helpMenu.addAction(self.epDocPlantGuideAction) self.helpMenu.addAction(self.epDocAcknowledgmentsAction) def create_tool_bars(self): """Creates the necessary toolbars. """ # File Toolbar self.fileToolBar = self.addToolBar("File Toolbar") self.fileToolBar.setObjectName('fileToolbar') self.fileToolBar.addAction(self.newAct) self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.fileToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # Edit Toolbar self.editToolBar = self.addToolBar("Edit Toolbar") self.editToolBar.setObjectName('editToolbar') self.editToolBar.addAction(self.undoAct) self.editToolBar.addAction(self.redoAct) self.editToolBar.addAction(self.newObjAct) self.editToolBar.addAction(self.dupObjAct) self.editToolBar.addAction(self.delObjAct) self.editToolBar.addAction(self.cutObjAct) self.editToolBar.addAction(self.copyObjAct) self.editToolBar.addAction(self.pasteObjAct) self.editToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # Object history navigation toolbar # self.navToolBar = self.addToolBar("Navigation Toolbar") # self.navToolBar.setObjectName('viewToolBar') # self.navToolBar.addAction(self.navForwardAct) # self.navToolBar.addAction(self.navBackAct) # self.navToolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) # Object filter toolbar self.filterToolBar = self.addToolBar("Filter Toolbar") self.filterToolBar.setObjectName('filterToolBar') self.filterBox = QLineEdit() self.filterBox.setPlaceholderText("Filter Objects") self.filterBox.setMaximumWidth(160) self.filterBox.setFixedWidth(160) # filterLabel = QLabel("Filter Obj:", self) # filterLabel.setBuddy(self.filterBox) # self.filterToolBar.addWidget(filterLabel) self.filterBox.textChanged.connect(self.tableFilterRegExpChanged) self.filterBox.textChanged.connect(self.treeFilterRegExpChanged) clearFilterButton = QPushButton('Clear') clearFilterButton.setMaximumWidth(45) clearFilterButton.clicked.connect(self.clearFilterClicked) self.filterToolBar.addWidget(self.filterBox) self.filterToolBar.addWidget(clearFilterButton) self.caseSensitivity = QCheckBox('Case Sensitive') self.caseSensitivity.stateChanged.connect(self.caseSensitivityChanged) self.filterToolBar.addWidget(self.caseSensitivity) self.filterToolBar.addSeparator() self.filterToolBar.addAction(self.transposeAct) def create_shortcuts(self): """Creates keyboard shortcuts. """ # QShortcut(QKeySequence('Ctrl+l'), self).activated.connect(self.toggle_full_tree) # QShortcut(QKeySequence('Ctrl+d'), self).activated.connect(self.fill_right) # QShortcut(QKeySequence('Ctrl+d'), self).activated.connect(self.fill_right) # def createAction(self, text, slot=None, shortcut=None, icon=None, # tip=None, checkable=False, signal="triggered()"): # action = QAction(text, self) # if icon is not None: # action.setIcon(QIcon(":/%s.png" % icon)) # if shortcut is not None: # action.setShortcut(shortcut) # if tip is not None: # action.setToolTip(tip) # action.setStatusTip(tip) # if slot is not None: # self.connect(action, QtCore.SIGNAL(signal), slot) # if checkable: # action.setCheckable(True) # return action # def custom_table_context_menu(self, position): # Create a menu and populate it with actions menu = QMenu(self) menu.addAction(self.undoAct) menu.addAction(self.redoAct) menu.addSeparator() menu.addAction(self.copyObjAct) menu.addAction(self.dupObjAct) menu.addAction(self.delObjAct) menu.addAction(self.newObjAct) menu.addAction(self.cutObjAct) menu.addSeparator() menu.addMenu(self.jumpToMenu) menu.addSeparator() menu.addAction(self.findThisAct) menu.popup(self.classTable.viewport().mapToGlobal(position)) self.mouse_position = position def reset_progress_bar(self): self.progressBarIDF.hide() def center(self): """Called to center the window on the screen on startup. """ screen = QDesktopWidget().screenGeometry() size = self.geometry() self.move((screen.width() - size.width()) / 2, (screen.height() - size.height()) / 2) def show_prefs_dialog(self): """Handles showing the settings dialog and setting its values. """ dlg = PrefsDialog(self, self.prefs) if dlg.exec_(): # Refresh the table view to take into account any new prefs self.load_table_view(self.current_obj_class) # Clear the idd cache if requested if self.prefs.get('clear_idd_cache', False) == True: self.clear_idd_cache() # Update preferences if various flags apply if not self.idf: return options_dict = dict() if self.prefs.get('apply_default_save_behaviour', False) == True: options_dict.update({'sort_order': self.prefs.get('sort_order'), 'special_formatting': self.prefs.get('special_formatting')}) # if self.prefs.get('apply_default_units_behaviour', False) == True: # options_dict.update({'save_units': self.prefs.get('save_units')}) # if self.prefs.get('apply_default_hidden_class_behaviour', False) == True: # options_dict.update({'save_hidden_classes': self.prefs.get('save_hidden_classes')}) if options_dict: self.idf.set_options(options_dict) self.set_dirty(True) def show_search_dialog(self): """Opens the search dialog. """ SearchReplaceDialog(self).show() def find_this(self): """Searches for fields with similar content. """ index = self.classTable.indexAt(self.mouse_position) text = self.classTable.model().data(index, Qt.EditRole) if text: SearchReplaceDialog(self, initial_query=text).show()
def __init__(self, quarterwidget): QObject.__init__(self, quarterwidget) #QObject.__init__(quarterwidget) self._quarterwidget = quarterwidget self._rendermanager = self._quarterwidget.getSoRenderManager() self.contextmenu = QMenu() self.functionsmenu = QMenu("Functions") self.rendermenu = QMenu("Render Mode") self.stereomenu = QMenu("Stereo Mode") self.transparencymenu = QMenu("Transparency Type") self.functionsgroup = QActionGroup(self) self.stereomodegroup = QActionGroup(self) self.rendermodegroup = QActionGroup(self) self.transparencytypegroup = QActionGroup(self) self.rendermodes = [] self.rendermodes.append((SoRenderManager.AS_IS, "as is")) self.rendermodes.append((SoRenderManager.WIREFRAME, "wireframe")) self.rendermodes.append( (SoRenderManager.WIREFRAME_OVERLAY, "wireframe overlay")) self.rendermodes.append((SoRenderManager.POINTS, "points")) self.rendermodes.append((SoRenderManager.HIDDEN_LINE, "hidden line")) self.rendermodes.append((SoRenderManager.BOUNDING_BOX, "bounding box")) self.stereomodes = [] self.stereomodes.append((SoRenderManager.MONO, "mono")) self.stereomodes.append((SoRenderManager.ANAGLYPH, "anaglyph")) self.stereomodes.append((SoRenderManager.QUAD_BUFFER, "quad buffer")) self.stereomodes.append( (SoRenderManager.INTERLEAVED_ROWS, "interleaved rows")) self.stereomodes.append( (SoRenderManager.INTERLEAVED_COLUMNS, "interleaved columns")) self.transparencytypes = [] self.transparencytypes.append((SoGLRenderAction.NONE, "none")) self.transparencytypes.append( (SoGLRenderAction.SCREEN_DOOR, "screen door")) self.transparencytypes.append((SoGLRenderAction.ADD, "add")) self.transparencytypes.append( (SoGLRenderAction.DELAYED_ADD, "delayed add")) self.transparencytypes.append( (SoGLRenderAction.SORTED_OBJECT_ADD, "sorted object add")) self.transparencytypes.append((SoGLRenderAction.BLEND, "blend")) self.transparencytypes.append( (SoGLRenderAction.DELAYED_BLEND, "delayed blend")) self.transparencytypes.append( (SoGLRenderAction.SORTED_OBJECT_BLEND, "sorted object blend")) self.transparencytypes.append( (SoGLRenderAction.SORTED_OBJECT_SORTED_TRIANGLE_ADD, "sorted object sorted triangle add")) self.transparencytypes.append( (SoGLRenderAction.SORTED_OBJECT_SORTED_TRIANGLE_BLEND, "sorted object sorted triangle blend")) self.transparencytypes.append( (SoGLRenderAction.SORTED_LAYERS_BLEND, "sorted layers blend")) self.rendermodeactions = [] for first, second in self.rendermodes: action = QAction(second, self) action.setCheckable(True) action.setChecked(self._rendermanager.getRenderMode() == first) action.setData(first) self.rendermodeactions.append(action) self.rendermodegroup.addAction(action) self.rendermenu.addAction(action) self.stereomodeactions = [] for first, second in self.stereomodes: action = QAction(second, self) action.setCheckable(True) action.setChecked(self._rendermanager.getStereoMode() == first) action.setData(first) self.stereomodeactions.append(action) self.stereomodegroup.addAction(action) self.stereomenu.addAction(action) self.transparencytypeactions = [] for first, second in self.transparencytypes: action = QAction(second, self) action.setCheckable(True) action.setChecked(self._rendermanager.getGLRenderAction(). getTransparencyType() == first) action.setData(first) self.transparencytypeactions.append(action) self.transparencytypegroup.addAction(action) self.transparencymenu.addAction(action) viewall = QAction("View All", self) seek = QAction("Seek", self) self.functionsmenu.addAction(viewall) self.functionsmenu.addAction(seek) self.connect(seek, QtCore.SIGNAL("triggered(bool)"), self.seek) self.connect(viewall, QtCore.SIGNAL("triggered(bool)"), self.viewAll) self.connect(self.rendermodegroup, QtCore.SIGNAL("triggered(QAction *)"), self.changeRenderMode) self.connect(self.stereomodegroup, QtCore.SIGNAL("triggered(QAction *)"), self.changeStereoMode) self.connect(self.transparencytypegroup, QtCore.SIGNAL("triggered(QAction *)"), self.changeTransparencyType) self.contextmenu.addMenu(self.functionsmenu) self.contextmenu.addMenu(self.rendermenu) self.contextmenu.addMenu(self.stereomenu) self.contextmenu.addMenu(self.transparencymenu)
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = QPlainTextEdit( ) # Could also use a QTextEdit and set self.editor.setAcceptRichText(False) # Setup the QTextEdit editor configuration fixedfont = QFontDatabase.systemFont(QFontDatabase.FixedFont) fixedfont.setPointSize(12) self.editor.setFont(fixedfont) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show()
def __init__(self, widget): QMainWindow.__init__(self) self.setWindowTitle("Backend Discord GUI") grad = QPalette() gradient = QConicalGradient(QPointF(700, -10), 195) gradient.setColorAt(0.0, QColor(30, 30, 30)) gradient.setColorAt(0.5, QColor(50, 50, 50)) gradient.setColorAt(0.97, QColor(50, 13, 150)) gradient.setColorAt(1.0, QColor(106, 13, 173)) gradient.setSpread(QGradient.RepeatSpread) grad.setBrush(QPalette.Window, QBrush(gradient)) self.setPalette(grad) # Status Bar self.statusbar = self.statusBar() self.statusbar.showMessage("Preparing Window...") qtRectangle = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) # Menu self.menu = self.menuBar() file_menu = self.menu.addMenu("File") new_bot_proj = QAction("New Bot Project", self) new_bot_proj.setShortcut("Ctrl+P") new_bot_proj.setStatusTip('Create a new Bot') new_bot_proj.triggered.connect(self.do_nothing) new_bot_file = QAction("New Bot File", self) new_bot_file.setShortcut("Ctrl+N") new_bot_file.setStatusTip('Create a new Command file') new_bot_file.triggered.connect(self.do_nothing) new_int_play = QAction("New Interactive Playground", self) new_int_play.setStatusTip('Create a Test App') new_int_play.triggered.connect(self.do_nothing) open_bot_proj = QAction("Open Bot Project", self) open_bot_proj.setShortcut("Ctrl+O") open_bot_proj.setStatusTip('Open a Bot Project') open_bot_proj.triggered.connect(self.do_nothing) open_bot_file = QAction("Open Bot File", self) open_bot_file.setStatusTip('Open a Command file') open_bot_file.triggered.connect(self.do_nothing) open_bot_iplay = QAction("Open Interactive Playground", self) open_bot_iplay.setStatusTip('Open a Test App') open_bot_iplay.triggered.connect(self.do_nothing) add_bot_file = QAction("Add Bot File", self) add_bot_file.setStatusTip('Add existing Command file') add_bot_file.triggered.connect(self.do_nothing) save_bot_proj = QAction("Save Bot Project", self) save_bot_proj.setStatusTip('Save a Bot Project') save_bot_proj.triggered.connect(self.do_nothing) save_bot_file = QAction("Save Bot File", self) save_bot_file.setStatusTip('Save a Command file') save_bot_file.triggered.connect(self.do_nothing) save_all = QAction("Save All", self) save_all.setShortcut("Ctrl+S") save_all.triggered.connect(self.do_nothing) close_editor = QAction("Close Editor", self) close_editor.setStatusTip('Close the editor only') close_editor.triggered.connect(self.do_nothing) close_workspace = QAction("Close Workspace", self) close_workspace.setShortcut("Ctrl+Q") close_workspace.setStatusTip('Close everything') close_workspace.triggered.connect(self.do_nothing) file_menu.addAction(new_bot_proj) file_menu.addAction(new_bot_file) file_menu.addAction(new_int_play) file_menu.addSeparator() file_menu.addAction(open_bot_proj) file_menu.addAction(open_bot_file) file_menu.addAction(open_bot_iplay) file_menu.addAction(add_bot_file) file_menu.addSeparator() file_menu.addAction(save_bot_proj) file_menu.addAction(save_bot_file) file_menu.addAction(save_all) file_menu.addSeparator() file_menu.addAction(close_editor) file_menu.addAction(close_workspace) edit_menu = self.menu.addMenu("Edit") find_file = QAction("Find File", self) find_file.setShortcut("Ctrl+F") find_file.setStatusTip("Find a Command") find_file.triggered.connect(self.do_nothing) file_settings = QAction("Open Find Settings", self) file_settings.setShortcut("Ctrl+Shift+S") file_settings.setStatusTip("Find a Command") file_settings.triggered.connect(self.do_nothing) toggle_file = QAction("Toggle File", self, checkable=True) toggle_file.setStatusTip("Toggles a Command file to be used") toggle_file.setChecked(True) toggle_file.triggered.connect(self.do_nothing) delete_file = QAction("Delete File", self) delete_file.setShortcut("Ctrl+Backspace") delete_file.setStatusTip("Delete a Command file") delete_file.triggered.connect(self.do_nothing) remove_file = QAction("Remove File", self) remove_file.setStatusTip("Removes a Command file from workspace") remove_file.triggered.connect(self.do_nothing) edit_menu.addAction(find_file) edit_menu.addAction(file_settings) edit_menu.addAction(toggle_file) edit_menu.addSeparator() edit_menu.addAction(delete_file) edit_menu.addAction(remove_file) viewMenu = self.menu.addMenu('View') command_palette = QAction("Command Palette", self) command_palette.setShortcut("Ctrl+Shift+P") command_palette.setStatusTip("Delete a Command file") command_palette.triggered.connect(self.do_nothing) open_view = QAction("Open View", self) open_view.setStatusTip("Delete a Command file") open_view.triggered.connect(self.do_nothing) appearance_settings = QAction("Appearance Settings", self) appearance_settings.setStatusTip("Delete a Command file") appearance_settings.triggered.connect(self.do_nothing) output_console = QAction("Output Console", self) output_console.setStatusTip("Delete a Command file") output_console.triggered.connect(self.do_nothing) debug_console = QAction("Debug Console", self) debug_console.setStatusTip("Delete a Command file") debug_console.triggered.connect(self.do_nothing) error_log = QAction("Error Log", self) error_log.setStatusTip("Delete a Command file") error_log.triggered.connect(self.do_nothing) view_logs = QAction("View All Logs", self) view_logs.setStatusTip("Delete a Command file") view_logs.triggered.connect(self.do_nothing) viewStatAct = QAction('View statusbar', self, checkable=True) viewStatAct.setStatusTip('View statusbar') viewStatAct.setChecked(True) viewStatAct.triggered.connect(self.toggleMenu) viewMenu.addAction(command_palette) viewMenu.addAction(open_view) viewMenu.addSeparator() viewMenu.addAction(appearance_settings) viewMenu.addSeparator() viewMenu.addAction(output_console) viewMenu.addAction(debug_console) viewMenu.addAction(error_log) viewMenu.addAction(view_logs) viewMenu.addSeparator() viewMenu.addAction(viewStatAct) runMenu = self.menu.addMenu('Run') start = QAction("Start", self) start.setShortcut("Ctrl+R") start.setStatusTip("Begin to start Command") start.triggered.connect(self.do_nothing) stop = QAction("Stop", self) stop.setShortcut("Ctrl+T") stop.setStatusTip("Terminate the Command") stop.triggered.connect(self.do_nothing) test_file = QAction("Test File", self) test_file.setStatusTip("Test the Command open on test server") test_file.triggered.connect(self.do_nothing) test_server = QAction("Test Server Connect", self) test_server.setShortcut("Ctrl+Shift+R") test_server.setStatusTip("Test server connection") test_server.triggered.connect(self.do_nothing) test_ping = QAction("Test Activity Ping", self) test_ping.setStatusTip("Test Bot API connection") test_ping.triggered.connect(self.do_nothing) test_commands = QAction("Test Ping Commands", self) test_commands.setStatusTip("Test all commands received") test_commands.triggered.connect(self.do_nothing) open_test_logs = QAction("View Test Logs", self) open_test_logs.setStatusTip("Open the logs for testing") open_test_logs.triggered.connect(self.do_nothing) runMenu.addAction(start) runMenu.addAction(stop) runMenu.addSeparator() runMenu.addAction(test_file) runMenu.addAction(test_server) runMenu.addAction(test_ping) runMenu.addAction(test_commands) runMenu.addAction(open_test_logs) terminalMenu = self.menu.addMenu('Terminal') start_bot = QAction("Start Bot", self) start_bot.setStatusTip("Begin to start Bot") start_bot.triggered.connect(self.do_nothing) stop_bot = QAction("Stop Bot", self) stop_bot.setStatusTip("Terminate the Bot") stop_bot.triggered.connect(self.do_nothing) terminal_logs = QAction("View Terminal Logs", self) terminal_logs.setStatusTip("Opens the Terminal logs") terminal_logs.triggered.connect(self.do_nothing) python_discord = QAction("Build Task as Python Discord", self) python_discord.setStatusTip( "Builds the Bot in Python Programming Language") python_discord.triggered.connect(self.do_nothing) javascript_discord = QAction("Build Task as Javascript Discord", self) javascript_discord.setStatusTip( "Builds the Bot in Javascript Programming Language") javascript_discord.triggered.connect(self.do_nothing) discordGUI_discord = QAction("Build Task as Discord-GUI", self) discordGUI_discord.setShortcut("Ctrl+Shift+W") discordGUI_discord.setStatusTip( "Builds the Bot in Discord-GUI Readable Language") discordGUI_discord.triggered.connect(self.do_nothing) build_errors = QAction("View Build Errors", self) build_errors.setStatusTip("Opens the Build Error Log") build_errors.triggered.connect(self.do_nothing) terminalMenu.addAction(start_bot) terminalMenu.addAction(stop_bot) terminalMenu.addSeparator() terminalMenu.addAction(terminal_logs) terminalMenu.addSeparator() terminalMenu.addAction(python_discord) terminalMenu.addAction(javascript_discord) terminalMenu.addAction(discordGUI_discord) terminalMenu.addAction(build_errors) windowMenu = self.menu.addMenu('Window') hide = QAction("Hide", self) hide.setStatusTip("Hide the Window") hide.triggered.connect(self.do_nothing) minimise = QAction("Minimise", self) minimise.setStatusTip("Minimise the Window") minimise.triggered.connect(self.do_nothing) maximise = QAction("Maximise", self) maximise.setStatusTip("Maximise the Window") maximise.triggered.connect(self.do_nothing) settings = QAction("Discord-GUI Settings", self) settings.setShortcut("Ctrl+L") settings.setStatusTip("Opens Discord-GUI's Settings") settings.triggered.connect(self.do_nothing) directory = QAction("Open Discord-GUI Directory", self) directory.setStatusTip("Goto the Discord-GUI Directory") directory.triggered.connect(self.do_nothing) windowMenu.addAction(hide) windowMenu.addAction(minimise) windowMenu.addAction(maximise) windowMenu.addSeparator() windowMenu.addAction(settings) windowMenu.addAction(directory) helpMenu = self.menu.addMenu('Help') welcome = QAction("Welcome", self) welcome.setStatusTip("Displays Welcome Message") welcome.triggered.connect(self.do_nothing) int_play = QAction("Interactive Playground", self) int_play.setStatusTip("Displays Interactive Playground Help") int_play.triggered.connect(self.do_nothing) docs = QAction("Documentation", self) docs.setStatusTip("Redirects to the Documentation of the app") docs.triggered.connect(self.do_nothing) keys_shorts = QAction("Keyboard Shortcuts", self) keys_shorts.setStatusTip("Displays Keyboard Shortcuts") keys_shorts.triggered.connect(self.do_nothing) report_issue = QAction("Report Issue", self) report_issue.setStatusTip("Redirects to Reporting an Issue") report_issue.triggered.connect(self.do_nothing) more_help = QAction("More Help", self) more_help.setStatusTip("Redirects to More Help Page") more_help.triggered.connect(self.do_nothing) helpMenu.addAction(welcome) helpMenu.addAction(int_play) helpMenu.addAction(docs) helpMenu.addSeparator() helpMenu.addAction(keys_shorts) helpMenu.addAction(report_issue) helpMenu.addAction(more_help) self.setCentralWidget(widget)
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) layout = QVBoxLayout() self.editor = TextEdit() # Setup the QTextEdit editor configuration self.editor.setAutoFormatting(QTextEdit.AutoAll) self.editor.selectionChanged.connect(self.update_format) # Initialize default font size. font = QFont('Times', 12) self.editor.setFont(font) # We need to repeat the size to init the current format. self.editor.setFontPointSize(12) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) # Uncomment to disable native menubar on Mac # self.menuBar().setNativeMenuBar(False) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction( QIcon(os.path.join('images', 'blue-folder-open-document.png')), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) save_file_action = QAction(QIcon(os.path.join('images', 'disk.png')), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) saveas_file_action = QAction( QIcon(os.path.join('images', 'disk--pencil.png')), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) print_action = QAction(QIcon(os.path.join('images', 'printer.png')), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction( QIcon(os.path.join('images', 'arrow-curve-180-left.png')), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) redo_action = QAction(QIcon(os.path.join('images', 'arrow-curve.png')), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) edit_menu.addSeparator() cut_action = QAction(QIcon(os.path.join('images', 'scissors.png')), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.setShortcut(QKeySequence.Cut) cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) copy_action = QAction( QIcon(os.path.join('images', 'document-copy.png')), "Copy", self) copy_action.setStatusTip("Copy selected text") cut_action.setShortcut(QKeySequence.Copy) copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) paste_action = QAction( QIcon(os.path.join('images', 'clipboard-paste-document-text.png')), "Paste", self) paste_action.setStatusTip("Paste from clipboard") cut_action.setShortcut(QKeySequence.Paste) paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) select_action = QAction( QIcon(os.path.join('images', 'selection-input.png')), "Select all", self) select_action.setStatusTip("Select all text") cut_action.setShortcut(QKeySequence.SelectAll) select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) edit_menu.addSeparator() wrap_action = QAction( QIcon(os.path.join('images', 'arrow-continue.png')), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) format_toolbar = QToolBar("Format") format_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(format_toolbar) format_menu = self.menuBar().addMenu("&Format") # We need references to these actions/settings to update as selection changes, so attach to self. self.fonts = QFontComboBox() self.fonts.currentFontChanged.connect(self.editor.setCurrentFont) format_toolbar.addWidget(self.fonts) self.fontsize = QComboBox() self.fontsize.addItems([str(s) for s in FONT_SIZES]) # Connect to the signal producing the text of the current selection. Convert the string to float # and set as the pointsize. We could also use the index + retrieve from FONT_SIZES. self.fontsize.currentIndexChanged[str].connect( lambda s: self.editor.setFontPointSize(float(s))) format_toolbar.addWidget(self.fontsize) self.bold_action = QAction( QIcon(os.path.join('images', 'edit-bold.png')), "Bold", self) self.bold_action.setStatusTip("Bold") self.bold_action.setShortcut(QKeySequence.Bold) self.bold_action.setCheckable(True) self.bold_action.toggled.connect(lambda x: self.editor.setFontWeight( QFont.Bold if x else QFont.Normal)) format_toolbar.addAction(self.bold_action) format_menu.addAction(self.bold_action) self.italic_action = QAction( QIcon(os.path.join('images', 'edit-italic.png')), "Italic", self) self.italic_action.setStatusTip("Italic") self.italic_action.setShortcut(QKeySequence.Italic) self.italic_action.setCheckable(True) self.italic_action.toggled.connect(self.editor.setFontItalic) format_toolbar.addAction(self.italic_action) format_menu.addAction(self.italic_action) self.underline_action = QAction( QIcon(os.path.join('images', 'edit-underline.png')), "Underline", self) self.underline_action.setStatusTip("Underline") self.underline_action.setShortcut(QKeySequence.Underline) self.underline_action.setCheckable(True) self.underline_action.toggled.connect(self.editor.setFontUnderline) format_toolbar.addAction(self.underline_action) format_menu.addAction(self.underline_action) format_menu.addSeparator() self.alignl_action = QAction( QIcon(os.path.join('images', 'edit-alignment.png')), "Align left", self) self.alignl_action.setStatusTip("Align text left") self.alignl_action.setCheckable(True) self.alignl_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignLeft)) format_toolbar.addAction(self.alignl_action) format_menu.addAction(self.alignl_action) self.alignc_action = QAction( QIcon(os.path.join('images', 'edit-alignment-center.png')), "Align center", self) self.alignc_action.setStatusTip("Align text center") self.alignc_action.setCheckable(True) self.alignc_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignCenter)) format_toolbar.addAction(self.alignc_action) format_menu.addAction(self.alignc_action) self.alignr_action = QAction( QIcon(os.path.join('images', 'edit-alignment-right.png')), "Align right", self) self.alignr_action.setStatusTip("Align text right") self.alignr_action.setCheckable(True) self.alignr_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignRight)) format_toolbar.addAction(self.alignr_action) format_menu.addAction(self.alignr_action) self.alignj_action = QAction( QIcon(os.path.join('images', 'edit-alignment-justify.png')), "Justify", self) self.alignj_action.setStatusTip("Justify text") self.alignj_action.setCheckable(True) self.alignj_action.triggered.connect( lambda: self.editor.setAlignment(Qt.AlignJustify)) format_toolbar.addAction(self.alignj_action) format_menu.addAction(self.alignj_action) format_group = QActionGroup(self) format_group.setExclusive(True) format_group.addAction(self.alignl_action) format_group.addAction(self.alignc_action) format_group.addAction(self.alignr_action) format_group.addAction(self.alignj_action) format_menu.addSeparator() # A list of all format-related widgets/actions, so we can disable/enable signals when updating. self._format_actions = [ self.fonts, self.fontsize, self.bold_action, self.italic_action, self.underline_action, # We don't need to disable signals for alignment, as they are paragraph-wide. ] # Initialize. self.update_format() self.update_title() self.show() def block_signals(self, objects, b): for o in objects: o.blockSignals(b) def update_format(self): """ Update the font format toolbar/actions when a new text selection is made. This is neccessary to keep toolbars/etc. in sync with the current edit state. :return: """ # Disable signals for all format widgets, so changing values here does not trigger further formatting. self.block_signals(self._format_actions, True) self.fonts.setCurrentFont(self.editor.currentFont()) # Nasty, but we get the font-size as a float but want it was an int self.fontsize.setCurrentText(str(int(self.editor.fontPointSize()))) self.italic_action.setChecked(self.editor.fontItalic()) self.underline_action.setChecked(self.editor.fontUnderline()) self.bold_action.setChecked(self.editor.fontWeight() == QFont.Bold) self.alignl_action.setChecked(self.editor.alignment() == Qt.AlignLeft) self.alignc_action.setChecked( self.editor.alignment() == Qt.AlignCenter) self.alignr_action.setChecked(self.editor.alignment() == Qt.AlignRight) self.alignj_action.setChecked( self.editor.alignment() == Qt.AlignJustify) self.block_signals(self._format_actions, False) def dialog_critical(self, s): dlg = QMessageBox(self) dlg.setText(s) dlg.setIcon(QMessageBox.Critical) dlg.show() def file_open(self): path, _ = QFileDialog.getOpenFileName( self, "Open file", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)") try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.dialog_critical(str(e)) else: self.path = path # Qt will automatically try and guess the format as txt/html self.editor.setText(text) self.update_title() def file_save(self): if self.path is None: # If we do not have a path, we need to use Save As. return self.file_saveas() text = self.editor.toHtml() if splitext( self.path) in HTML_EXTENSIONS else self.editor.toPlainText() try: with open(self.path, 'w') as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) def file_saveas(self): path, _ = QFileDialog.getSaveFileName( self, "Save file", "", "HTML documents (*.html);Text documents (*.txt);All files (*.*)") if not path: # If dialog is cancelled, will return '' return text = self.editor.toHtml() if splitext( path) in HTML_EXTENSIONS else self.editor.toPlainText() try: with open(path, 'w') as f: f.write(text) except Exception as e: self.dialog_critical(str(e)) else: self.path = path self.update_title() def file_print(self): dlg = QPrintDialog() if dlg.exec_(): self.editor.print_(dlg.printer()) def update_title(self): self.setWindowTitle( "%s - Megasolid Idiom" % (os.path.basename(self.path) if self.path else "Untitled")) def edit_toggle_wrap(self): self.editor.setLineWrapMode(1 if self.editor.lineWrapMode() == 0 else 0)
def __init__(self, *args, **kwargs): super(Notepad, self).__init__(*args, **kwargs) self.setWindowIcon(QIcon('arti.PNG')) self.setGeometry(200, 100, 700, 400) layout = QVBoxLayout() self.editor = QPlainTextEdit() self.editor.setFont(QFont('Roboto', 12)) # self.path holds the path of the currently open file. # If none, we haven't got a file open yet (or creating new). self.path = None layout.addWidget(self.editor) container = QWidget() container.setLayout(layout) self.setCentralWidget(container) self.status = QStatusBar() self.setStatusBar(self.status) file_toolbar = QToolBar("File") file_toolbar.setIconSize(QSize(14, 14)) self.addToolBar(file_toolbar) file_menu = self.menuBar().addMenu("&File") open_file_action = QAction(QIcon(), "Open file...", self) open_file_action.setStatusTip("Open file") open_file_action.triggered.connect(self.file_open) file_menu.addAction(open_file_action) file_toolbar.addAction(open_file_action) open_file_action.setShortcut(QKeySequence('Ctrl+O')) save_file_action = QAction(QIcon(), "Save", self) save_file_action.setStatusTip("Save current page") save_file_action.triggered.connect(self.file_save) file_menu.addAction(save_file_action) file_toolbar.addAction(save_file_action) save_file_action.setShortcut(QKeySequence('Ctrl+S')) saveas_file_action = QAction(QIcon(), "Save As...", self) saveas_file_action.setStatusTip("Save current page to specified file") saveas_file_action.triggered.connect(self.file_saveas) file_menu.addAction(saveas_file_action) file_toolbar.addAction(saveas_file_action) saveas_file_action.setShortcut(QKeySequence('Ctrl+Shift+S')) print_action = QAction(QIcon(), "Print...", self) print_action.setStatusTip("Print current page") print_action.triggered.connect(self.file_print) file_menu.addAction(print_action) file_toolbar.addAction(print_action) print_action.setShortcut(QKeySequence('Ctrl+P')) edit_toolbar = QToolBar("Edit") edit_toolbar.setIconSize(QSize(16, 16)) self.addToolBar(edit_toolbar) edit_menu = self.menuBar().addMenu("&Edit") undo_action = QAction(QIcon(), "Undo", self) undo_action.setStatusTip("Undo last change") undo_action.triggered.connect(self.editor.undo) edit_menu.addAction(undo_action) undo_action.setShortcut(QKeySequence('Ctrl+Z')) redo_action = QAction(QIcon(), "Redo", self) redo_action.setStatusTip("Redo last change") redo_action.triggered.connect(self.editor.redo) edit_toolbar.addAction(redo_action) edit_menu.addAction(redo_action) redo_action.setShortcut(QKeySequence('Ctrl+Y')) edit_menu.addSeparator() cut_action = QAction(QIcon(), "Cut", self) cut_action.setStatusTip("Cut selected text") cut_action.triggered.connect(self.editor.cut) edit_toolbar.addAction(cut_action) edit_menu.addAction(cut_action) cut_action.setShortcut(QKeySequence('Ctrl+X')) copy_action = QAction(QIcon(), "Copy", self) copy_action.setStatusTip("Copy selected text") copy_action.triggered.connect(self.editor.copy) edit_toolbar.addAction(copy_action) edit_menu.addAction(copy_action) copy_action.setShortcut(QKeySequence('Ctrl+C')) paste_action = QAction(QIcon(), "Paste", self) paste_action.setStatusTip("Paste from clipboard") paste_action.triggered.connect(self.editor.paste) edit_toolbar.addAction(paste_action) edit_menu.addAction(paste_action) paste_action.setShortcut(QKeySequence('Ctrl+V')) select_action = QAction(QIcon(), "Select all", self) select_action.setStatusTip("Select all text") select_action.triggered.connect(self.editor.selectAll) edit_menu.addAction(select_action) select_action.setShortcut(QKeySequence('Ctrl+A')) edit_menu.addSeparator() wrap_action = QAction(QIcon(), "Wrap text to window", self) wrap_action.setStatusTip("Toggle wrap text to window") wrap_action.setCheckable(True) wrap_action.setChecked(True) wrap_action.triggered.connect(self.edit_toggle_wrap) edit_menu.addAction(wrap_action) self.update_title() self.show()
class MainWindow(QMainWindow): def __init__(self, config): super(MainWindow, self).__init__() QFontDatabase.addApplicationFont('../resources/fonts/poppins/Poppins-Medium.ttf') QFontDatabase.addApplicationFont('../resources/fonts/source code pro/SourceCodePro-Regular.ttf') QFontDatabase.addApplicationFont('../resources/fonts/asap/Asap-Regular.ttf') self.ui = Ui_MainWindow() self.ui.setupUi(self) if MainConsole.main_console is not None: self.ui.scripts_console_splitter.addWidget(MainConsole.main_console) self.ui.scripts_console_splitter.setSizes([350, 350]) self.ui.splitter.setSizes([120, 800]) self.setWindowTitle('Ryven') self.setWindowIcon(QIcon('../resources/pics/program_icon2.png')) self.load_stylesheet('dark') self.ui.scripts_tab_widget.removeTab(0) # menu actions self.flow_design_actions = [] self.setup_menu_actions() # shortcuts save_shortcut = QShortcut(QKeySequence.Save, self) save_shortcut.activated.connect(self.on_save_project_triggered) import_nodes_shortcut = QShortcut(QKeySequence('Ctrl+i'), self) import_nodes_shortcut.activated.connect(self.on_import_nodes_triggered) # clear temp folder if not os.path.exists('temp'): os.mkdir('temp') for f in os.listdir('temp'): os.remove('temp/'+f) # GENERAL ATTRIBUTES self.scripts = [] self.custom_nodes = [] self.all_nodes = [SetVariable_Node(), GetVariable_Node(), Val_Node(), Result_Node()] self.package_names = [] # holds NI subCLASSES for imported nodes: self.all_node_instance_classes = { self.all_nodes[0]: SetVar_NodeInstance, self.all_nodes[1]: GetVar_NodeInstance, self.all_nodes[2]: Val_NodeInstance, self.all_nodes[3]: Result_NodeInstance } # (key: node obj, val: NI subclass) (used in Flow) # custom subclasses for input widgets # {node : {str: PortInstanceWidget-subclass}} (used in PortInstance) self.custom_node_input_widget_classes = {} # UI self.scripts_list_widget = ScriptsListWidget(self, self.scripts) self.ui.scripts_scrollArea.setWidget(self.scripts_list_widget) self.ui.add_new_script_pushButton.clicked.connect(self.create_new_script_button_pressed) self.ui.new_script_name_lineEdit.returnPressed.connect(self.create_new_script_LE_return_pressed) if config['config'] == 'create plain new project': self.try_to_create_new_script() elif config['config'] == 'open project': print('importing packages...') self.import_packages(config['required packages']) print('loading project...') self.parse_project(config['content']) print('finished') print(''' CONTROLS placing: right mouse selecting: left mouse panning: middle mouse saving: ctrl+s ''') Design.set_flow_theme() Design.set_flow_theme() # temporary # the double call is just a temporary fix for an issue I will address in a future release. # Problem: because the signal emitted when setting a flow theme is directly connected to the according slots # in NodeInstance as well as NodeInstance_TitleLabel, the NodeInstance's slot (which starts an animation which # uses the title label's current and theme dependent color) could get called before the title # label's slot has been called to reinitialize this color. This results in wrong color end points for the # title label when activating animations. # This is pretty nasty since I cannot think of a nice fix for this issue other that not letting the slot # methods be called directly from the emitted signal but instead through a defined procedure like before. # maybe this will be necessary due to scheduling issues when loading flows # for s in self.scripts: # s.flow.viewport().update() self.resize(1500, 800) def setup_menu_actions(self): # flow designs for d in Design.flow_themes: design_action = QAction(d.name, self) self.ui.menuFlow_Design_Style.addAction(design_action) design_action.triggered.connect(self.on_design_action_triggered) self.flow_design_actions.append(design_action) self.ui.actionImport_Nodes.triggered.connect(self.on_import_nodes_triggered) self.ui.actionSave_Project.triggered.connect(self.on_save_project_triggered) self.ui.actionEnableDebugging.triggered.connect(self.on_enable_debugging_triggered) self.ui.actionDisableDebugging.triggered.connect(self.on_disable_debugging_triggered) self.ui.actionSave_Pic_Viewport.triggered.connect(self.on_save_scene_pic_viewport_triggered) self.ui.actionSave_Pic_Whole_Scene_scaled.triggered.connect(self.on_save_scene_pic_whole_triggered) # performance mode self.action_set_performance_mode_fast = QAction('Fast', self) self.action_set_performance_mode_fast.setCheckable(True) self.action_set_performance_mode_pretty = QAction('Pretty', self) self.action_set_performance_mode_pretty.setCheckable(True) performance_mode_AG = QActionGroup(self) performance_mode_AG.addAction(self.action_set_performance_mode_fast) performance_mode_AG.addAction(self.action_set_performance_mode_pretty) self.action_set_performance_mode_fast.setChecked(True) performance_mode_AG.triggered.connect(self.on_performance_mode_changed) performance_menu = QMenu('Performance Mode', self) performance_menu.addAction(self.action_set_performance_mode_fast) performance_menu.addAction(self.action_set_performance_mode_pretty) self.ui.menuView.addMenu(performance_menu) # animations self.action_set_animation_active = QAction('Enabled', self) self.action_set_animation_active.setCheckable(True) self.action_set_animations_inactive = QAction('Disabled', self) self.action_set_animations_inactive.setCheckable(True) animation_enabled_AG = QActionGroup(self) animation_enabled_AG.addAction(self.action_set_animation_active) animation_enabled_AG.addAction(self.action_set_animations_inactive) self.action_set_animation_active.setChecked(True) animation_enabled_AG.triggered.connect(self.on_animation_enabling_changed) animations_menu = QMenu('Animations', self) animations_menu.addAction(self.action_set_animation_active) animations_menu.addAction(self.action_set_animations_inactive) self.ui.menuView.addMenu(animations_menu) def load_stylesheet(self, ss): ss_content = '' try: f = open('../resources/stylesheets/'+ss+'.txt') ss_content = f.read() f.close() finally: Design.ryven_stylesheet = ss_content self.setStyleSheet(ss_content) def on_performance_mode_changed(self, action): if action == self.action_set_performance_mode_fast: Design.set_performance_mode('fast') else: Design.set_performance_mode('pretty') def on_animation_enabling_changed(self, action): if action == self.action_set_animation_active: Design.animations_enabled = True else: Design.animations_enabled = False def on_design_action_triggered(self): index = self.flow_design_actions.index(self.sender()) Design.set_flow_theme(Design.flow_themes[index]) def on_enable_debugging_triggered(self): Debugger.enable() def on_disable_debugging_triggered(self): Debugger.disable() def on_save_scene_pic_viewport_triggered(self): """Saves a picture of the currently visible viewport.""" if len(self.scripts) == 0: return file_path = QFileDialog.getSaveFileName(self, 'select file', '', 'PNG(*.png)')[0] img = self.scripts[self.ui.scripts_tab_widget.currentIndex()].flow.get_viewport_img() img.save(file_path) def on_save_scene_pic_whole_triggered(self): """Saves a picture of the whole currently visible scene.""" if len(self.scripts) == 0: return file_path = QFileDialog.getSaveFileName(self, 'select file', '', 'PNG(*.png)')[0] img = self.scripts[self.ui.scripts_tab_widget.currentIndex()].flow.get_whole_scene_img() img.save(file_path) def create_new_script_button_pressed(self): self.try_to_create_new_script(name=self.ui.new_script_name_lineEdit.text()) def create_new_script_LE_return_pressed(self): self.try_to_create_new_script(name=self.ui.new_script_name_lineEdit.text()) def try_to_create_new_script(self, name='fancy script', config=None): """Tries to create a new script with a given name. If the name is already used or '', it fails.""" if len(name) == 0: return for s in self.scripts: if s.name == name: return new_script = Script(self, name, config) new_script.name_changed.connect(self.rename_script) self.ui.scripts_tab_widget.addTab(new_script.widget, new_script.name) self.scripts.append(new_script) self.scripts_list_widget.recreate_ui() def rename_script(self, script, new_name): self.ui.scripts_tab_widget.setTabText(self.scripts.index(script), new_name) script.name = new_name def delete_script(self, script): index = self.scripts.index(script) self.ui.scripts_tab_widget.removeTab(index) del self.scripts[index] def on_import_nodes_triggered(self): file_path = QFileDialog.getOpenFileName(self, 'select nodes file', '../packages', 'Ryven Packages(*.rpc)',)[0] if file_path != '': self.import_nodes_package(file_path) def import_packages(self, packages_list): for p in packages_list: self.import_nodes_package(p) def import_nodes_package(self, file_path): j_str = '' try: f = open(file_path) j_str = f.read() f.close() except FileExistsError or FileNotFoundError: Debugger.debug('couldn\'t open file') return # don't import a package twice if it already has been imported filename = os.path.splitext(os.path.basename(file_path)) if filename in self.package_names: return # Important: translate the package first (metacore files -> src code files) PackageTranslator = self.get_class_from_file(file_path='../Ryven_PackageTranslator', file_name='Ryven_PackageTranslator', class_name='PackageTranslator') package_translator = PackageTranslator(os.path.dirname(os.path.abspath(file_path))) self.parse_nodes(j_str, package_path=os.path.dirname(file_path), package_name=os.path.splitext(os.path.basename(file_path))[0])# self.package_names.append(filename) def parse_nodes(self, j_str, package_path, package_name): """Parses the nodes from a node package in JSON format. Here, all the classes get imported and for every node a Node object with specific attribute values gets created.""" import json # strict=False is necessary to allow 'control characters' like '\n' for newline when loading the json j_obj = json.loads(j_str, strict=False) Debugger.debug(j_obj['type']) if j_obj['type'] != 'Ryven nodes package' and j_obj['type'] != 'vyScriptFP nodes package': # old syntax return # package_title = j_obj['title'] # package_description = j_obj['description'] j_nodes_list = j_obj['nodes'] num_nodes = len(j_nodes_list) for ni in range(num_nodes): j_node = j_nodes_list[ni] self.parse_node(j_node, package_name, package_path) Debugger.debug(len(self.custom_nodes), 'nodes imported') def parse_node(self, j_node, package_name, package_path): new_node = Node() # loading the node's specifications which get finally set below after importing the classes node_title = j_node['title'] node_class_name = j_node['class name'] node_description = j_node['description'] node_type = j_node['type'] node_has_main_widget = j_node['has main widget'] node_main_widget_pos = j_node['widget position'] if node_has_main_widget else None node_design_style = j_node['design style'] node_color = j_node['color'] # Every node has a custom module name which differs from it's name to prevent import issues when using # multiple (different) Nodes with same titles. For further info: see node manager node_module_name = j_node['module name'] module_name_separator = '___' # CUSTOM CLASS IMPORTS ---------------------------------------------------------------------------- # creating all the necessary path variables here for all potentially imported classes # IMPORT NODE INSTANCE SUBCLASS node_instance_class_file_path = package_path + '/nodes/' + node_module_name + '/' node_instance_widgets_file_path = node_instance_class_file_path + '/widgets' node_instance_filename = node_module_name # the NI file's name is just the 'module name' new_node_instance_class = self.get_class_from_file(file_path=node_instance_class_file_path, file_name=node_instance_filename, class_name=node_class_name + '_NodeInstance') self.all_node_instance_classes[new_node] = new_node_instance_class # IMPORT MAIN WIDGET if node_has_main_widget: main_widget_filename = node_module_name + module_name_separator + 'main_widget' new_node.main_widget_class = self.get_class_from_file(file_path=node_instance_widgets_file_path, file_name=main_widget_filename, class_name=node_class_name + '_NodeInstance_MainWidget') # IMPORT CUSTOM INPUT WIDGETS # I need to create the dict for the node's potential custom input widgets already here self.custom_node_input_widget_classes[new_node] = {} for w_name in j_node['custom input widgets']: input_widget_filename = node_module_name + module_name_separator + w_name custom_widget_class = self.get_class_from_file(file_path=node_instance_widgets_file_path, file_name=input_widget_filename, class_name=w_name + '_PortInstanceWidget') self.custom_node_input_widget_classes[new_node][w_name] = custom_widget_class # --------------------------------------------------------------------------------------------------- j_n_inputs = j_node['inputs'] inputs = [] num_inputs = len(j_n_inputs) for ii in range(num_inputs): # loading info j_input = j_n_inputs[ii] i_type = j_input['type'] i_label = j_input['label'] i_has_widget = None i_widget_name = '' i_widget_pos = None if i_type == 'data': i_has_widget = j_input['has widget'] if i_has_widget: i_widget_name = j_input['widget name'] i_widget_pos = j_input['widget position'] # creating port new_input = NodePort() new_input.type_ = i_type new_input.label = i_label if i_has_widget: new_input.widget_name = i_widget_name new_input.widget_pos = i_widget_pos inputs.append(new_input) j_n_outputs = j_node['outputs'] outputs = [] num_outputs = len(j_n_outputs) for oi in range(num_outputs): # loading info j_output = j_n_outputs[oi] o_type = j_output['type'] o_label = j_output['label'] # creating port new_output = NodePort() new_output.type_ = o_type new_output.label = o_label outputs.append(new_output) # setting the Node's attributes new_node.title = node_title new_node.description = node_description new_node.type_ = node_type new_node.package = package_name new_node.has_main_widget = node_has_main_widget if node_has_main_widget: new_node.main_widget_pos = node_main_widget_pos new_node.design_style = node_design_style new_node.color = QColor(node_color) new_node.inputs = inputs new_node.outputs = outputs self.custom_nodes.append(new_node) self.all_nodes.append(new_node) def get_class_from_file(self, file_path, file_name, class_name): """Returns a class with a given name from a file for instantiation by importing the module. Used for all the dynamically imported classes: - NodeInstances - A NodeInstance's main widget - A NodeInstance's custom input widgets """ # Debugger.debug(file_path) # Debugger.debug(file_name) # Debugger.debug(class_name) sys.path.append(file_path) try: new_module = __import__(file_name, fromlist=[class_name]) except ModuleNotFoundError as e: QMessageBox.warning(self, 'Missing Python module', str(e)) sys.exit() new_class = getattr(new_module, class_name) return new_class def parse_project(self, j_obj): if j_obj['general info']['type'] != 'Ryven project file': return for s in j_obj['scripts']: # fill flows self.try_to_create_new_script(config=s) def on_save_project_triggered(self): file_name = QFileDialog.getSaveFileName(self, 'select location and give file name', '../saves', 'Ryven Project(*.rpo)')[0] if file_name != '': self.save_project(file_name) def save_project(self, file_name): import json file = None try: if os.path.exists(file_name): os.remove(file_name) file = open(file_name, 'w') except FileNotFoundError: Debugger.debug('couldn\'t open file') return general_project_info_dict = {'type': 'Ryven project file'} scripts_data = [] for script in self.scripts: scripts_data.append(script.get_json_data()) whole_project_dict = {'general info': general_project_info_dict, 'scripts': scripts_data} json_str = json.dumps(whole_project_dict) Debugger.debug(json_str) file.write(json_str) file.close()
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setWindowTitle('OpenMC Plot Explorer') self.restored = False self.pixmap = None self.zoom = 100 self.model = PlotModel() self.restoreModelSettings() self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) # Plot Image self.plotIm = PlotImage(self.model, FM, self) self.frame = QScrollArea(self) self.frame.setAlignment(QtCore.Qt.AlignCenter) self.frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.frame.setWidget(self.plotIm) self.setCentralWidget(self.frame) # Dock self.dock = OptionsDock(self.model, FM, self) self.dock.setObjectName("OptionsDock") self.addDockWidget(QtCore.Qt.RightDockWidgetArea, self.dock) # Color Dialog self.colorDialog = ColorDialog(self.model, FM, self) self.colorDialog.hide() # Restore Window Settings self.restoreWindowSettings() # Create menubar self.createMenuBar() # Status Bar self.coordLabel = QLabel() self.statusBar().addPermanentWidget(self.coordLabel) self.coordLabel.hide() # Load Plot self.statusBar().showMessage('Generating Plot...') self.dock.updateDock() self.colorDialog.updateDialogValues() if self.restored: self.model.updateIDs() self.showCurrentView() else: # Timer allows GUI to render before plot finishes loading QtCore.QTimer.singleShot(0, self.model.generatePlot) QtCore.QTimer.singleShot(0, self.showCurrentView) # Create and update menus: def createMenuBar(self): self.mainMenu = self.menuBar() # File Menu self.saveImageAction = QAction("&Save Image As...", self) self.saveImageAction.setShortcut("Ctrl+Shift+S") self.saveImageAction.setToolTip('Save plot image') self.saveImageAction.setStatusTip('Save plot image') self.saveImageAction.triggered.connect(self.saveImage) self.saveViewAction = QAction("Save &View Settings...", self) self.saveViewAction.setShortcut(QtGui.QKeySequence.Save) self.saveViewAction.setStatusTip('Save current view settings') self.saveViewAction.triggered.connect(self.saveView) self.openAction = QAction("&Open View Settings...", self) self.openAction.setShortcut(QtGui.QKeySequence.Open) self.openAction.setToolTip('Open saved view settings') self.openAction.setStatusTip('Open saved view settings') self.openAction.triggered.connect(self.openView) self.quitAction = QAction("&Quit", self) self.quitAction.setShortcut(QtGui.QKeySequence.Quit) self.quitAction.setToolTip('Quit OpenMC Plot Explorer') self.quitAction.setStatusTip('Quit OpenMC Plot Explorer') self.quitAction.triggered.connect(self.close) self.fileMenu = self.mainMenu.addMenu('&File') self.fileMenu.addAction(self.saveImageAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.saveViewAction) self.fileMenu.addAction(self.openAction) self.fileMenu.addSeparator() self.fileMenu.addAction(self.quitAction) # Edit Menu self.applyAction = QAction("&Apply Changes", self) self.applyAction.setShortcut("Shift+Return") self.applyAction.setToolTip('Generate new view with changes applied') self.applyAction.setStatusTip('Generate new view with changes applied') self.applyAction.triggered.connect(self.applyChanges) self.undoAction = QAction('&Undo', self) self.undoAction.setShortcut(QtGui.QKeySequence.Undo) self.undoAction.setToolTip('Undo') self.undoAction.setStatusTip('Undo last plot view change') self.undoAction.setDisabled(True) self.undoAction.triggered.connect(self.undo) self.redoAction = QAction('&Redo', self) self.redoAction.setDisabled(True) self.redoAction.setToolTip('Redo') self.redoAction.setStatusTip('Redo last plot view change') self.redoAction.setShortcut(QtGui.QKeySequence.Redo) self.redoAction.triggered.connect(self.redo) self.restoreAction = QAction("&Restore Default Plot", self) self.restoreAction.setShortcut("Ctrl+R") self.restoreAction.setToolTip('Restore to default plot view') self.restoreAction.setStatusTip('Restore to default plot view') self.restoreAction.triggered.connect(self.restoreDefault) self.editMenu = self.mainMenu.addMenu('&Edit') self.editMenu.addAction(self.applyAction) self.editMenu.addSeparator() self.editMenu.addAction(self.undoAction) self.editMenu.addAction(self.redoAction) self.editMenu.addSeparator() self.editMenu.addAction(self.restoreAction) self.editMenu.addSeparator() self.editMenu.aboutToShow.connect(self.updateEditMenu) # Edit -> Basis Menu self.xyAction = QAction('&xy ', self) self.xyAction.setCheckable(True) self.xyAction.setShortcut('Alt+X') self.xyAction.setToolTip('Change to xy basis') self.xyAction.setStatusTip('Change to xy basis') self.xyAction.triggered.connect( lambda: self.editBasis('xy', apply=True)) self.xzAction = QAction('x&z ', self) self.xzAction.setCheckable(True) self.xzAction.setShortcut('Alt+Z') self.xzAction.setToolTip('Change to xz basis') self.xzAction.setStatusTip('Change to xz basis') self.xzAction.triggered.connect( lambda: self.editBasis('xz', apply=True)) self.yzAction = QAction('&yz ', self) self.yzAction.setCheckable(True) self.yzAction.setShortcut('Alt+Y') self.yzAction.setToolTip('Change to yz basis') self.yzAction.setStatusTip('Change to yz basis') self.yzAction.triggered.connect( lambda: self.editBasis('yz', apply=True)) self.basisMenu = self.editMenu.addMenu('&Basis') self.basisMenu.addAction(self.xyAction) self.basisMenu.addAction(self.xzAction) self.basisMenu.addAction(self.yzAction) self.basisMenu.aboutToShow.connect(self.updateBasisMenu) # Edit -> Color By Menu self.cellAction = QAction('&Cell', self) self.cellAction.setCheckable(True) self.cellAction.setShortcut('Alt+C') self.cellAction.setToolTip('Color by cell') self.cellAction.setStatusTip('Color plot by cell') self.cellAction.triggered.connect( lambda: self.editColorBy('cell', apply=True)) self.materialAction = QAction('&Material', self) self.materialAction.setCheckable(True) self.materialAction.setShortcut('Alt+M') self.materialAction.setToolTip('Color by material') self.materialAction.setStatusTip('Color plot by material') self.materialAction.triggered.connect( lambda: self.editColorBy('material', apply=True)) self.colorbyMenu = self.editMenu.addMenu('&Color By') self.colorbyMenu.addAction(self.cellAction) self.colorbyMenu.addAction(self.materialAction) self.colorbyMenu.aboutToShow.connect(self.updateColorbyMenu) self.editMenu.addSeparator() self.maskingAction = QAction('Enable &Masking', self) self.maskingAction.setShortcut('Ctrl+M') self.maskingAction.setCheckable(True) self.maskingAction.setToolTip('Toggle masking') self.maskingAction.setStatusTip('Toggle whether masking is enabled') self.maskingAction.triggered[bool].connect( lambda bool=bool: self.toggleMasking(bool, apply=True)) self.editMenu.addAction(self.maskingAction) self.highlightingAct = QAction('Enable High&lighting', self) self.highlightingAct.setShortcut('Ctrl+L') self.highlightingAct.setCheckable(True) self.highlightingAct.setToolTip('Toggle highlighting') self.highlightingAct.setStatusTip( 'Toggle whether highlighting is enabled') self.highlightingAct.triggered[bool].connect( lambda bool=bool: self.toggleHighlighting(bool, apply=True)) self.editMenu.addAction(self.highlightingAct) # View Menu self.dockAction = QAction('Hide &Dock', self) self.dockAction.setShortcut("Ctrl+D") self.dockAction.setToolTip('Toggle dock visibility') self.dockAction.setStatusTip('Toggle dock visibility') self.dockAction.triggered.connect(self.toggleDockView) self.zoomAction = QAction('&Zoom...', self) self.zoomAction.setShortcut('Alt+Shift+Z') self.zoomAction.setToolTip('Edit zoom factor') self.zoomAction.setStatusTip('Edit zoom factor') self.zoomAction.triggered.connect(self.editZoomAct) self.viewMenu = self.mainMenu.addMenu('&View') self.viewMenu.addAction(self.dockAction) self.viewMenu.addSeparator() self.viewMenu.addAction(self.zoomAction) self.viewMenu.aboutToShow.connect(self.updateViewMenu) # Window Menu self.mainWindowAction = QAction('&Main Window', self) self.mainWindowAction.setShortcut('Alt+W') self.mainWindowAction.setCheckable(True) self.mainWindowAction.setToolTip('Bring main window to front') self.mainWindowAction.setStatusTip('Bring main window to front') self.mainWindowAction.triggered.connect(self.showMainWindow) self.colorDialogAction = QAction('Color &Options', self) self.colorDialogAction.setShortcut('Alt+D') self.colorDialogAction.setCheckable(True) self.colorDialogAction.setToolTip('Bring Color Dialog to front') self.colorDialogAction.setStatusTip('Bring Color Dialog to front') self.colorDialogAction.triggered.connect(self.showColorDialog) self.windowMenu = self.mainMenu.addMenu('&Window') self.windowMenu.addAction(self.mainWindowAction) self.windowMenu.addAction(self.colorDialogAction) self.windowMenu.aboutToShow.connect(self.updateWindowMenu) def updateEditMenu(self): changed = self.model.currentView != self.model.defaultView self.restoreAction.setDisabled(not changed) self.maskingAction.setChecked(self.model.currentView.masking) self.highlightingAct.setChecked(self.model.currentView.highlighting) self.undoAction.setText(f'&Undo ({len(self.model.previousViews)})') self.redoAction.setText(f'&Redo ({len(self.model.subsequentViews)})') def updateBasisMenu(self): self.xyAction.setChecked(self.model.currentView.basis == 'xy') self.xzAction.setChecked(self.model.currentView.basis == 'xz') self.yzAction.setChecked(self.model.currentView.basis == 'yz') def updateColorbyMenu(self): cv = self.model.currentView self.cellAction.setChecked(cv.colorby == 'cell') self.materialAction.setChecked(cv.colorby == 'material') def updateViewMenu(self): if self.dock.isVisible(): self.dockAction.setText('Hide &Dock') else: self.dockAction.setText('Show &Dock') def updateWindowMenu(self): self.colorDialogAction.setChecked(self.colorDialog.isActiveWindow()) self.mainWindowAction.setChecked(self.isActiveWindow()) # Menu and shared methods: def saveImage(self): filename, ext = QFileDialog.getSaveFileName(self, "Save Plot Image", "untitled", "Images (*.png *.ppm)") if filename: if "." not in filename: self.pixmap.save(filename + ".png") else: self.pixmap.save(filename) self.statusBar().showMessage('Plot Image Saved', 5000) def saveView(self): filename, ext = QFileDialog.getSaveFileName(self, "Save View Settings", "untitled", "View Settings (*.pltvw)") if filename: if "." not in filename: filename += ".pltvw" saved = { 'default': self.model.defaultView, 'current': self.model.currentView } with open(filename, 'wb') as file: pickle.dump(saved, file) def openView(self): filename, ext = QFileDialog.getOpenFileName(self, "Open View Settings", ".", "*.pltvw") if filename: try: with open(filename, 'rb') as file: saved = pickle.load(file) except Exception: message = 'Error loading plot settings' saved = {'default': None, 'current': None} if saved['default'] == self.model.defaultView: self.model.activeView = saved['current'] self.dock.updateDock() self.applyChanges() message = f'{filename} settings loaded' else: message = 'Error loading plot settings. Incompatible model.' self.statusBar().showMessage(message, 5000) def applyChanges(self): if self.model.activeView != self.model.currentView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.subsequentViews = [] self.model.generatePlot() self.resetModels() self.showCurrentView() else: self.statusBar().showMessage('No changes to apply.', 3000) def undo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.undo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.previousViews: self.undoAction.setDisabled(True) self.redoAction.setDisabled(False) def redo(self): self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.redo() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() if not self.model.subsequentViews: self.redoAction.setDisabled(True) self.undoAction.setDisabled(False) def restoreDefault(self): if self.model.currentView != self.model.defaultView: self.statusBar().showMessage('Generating Plot...') QApplication.processEvents() self.model.storeCurrent() self.model.activeView = copy.deepcopy(self.model.defaultView) self.model.generatePlot() self.resetModels() self.showCurrentView() self.dock.updateDock() self.colorDialog.updateDialogValues() self.model.subsequentViews = [] def editBasis(self, basis, apply=False): self.model.activeView.basis = basis self.dock.updateBasis() if apply: self.applyChanges() def editColorBy(self, domain_kind, apply=False): self.model.activeView.colorby = domain_kind self.dock.updateColorBy() self.colorDialog.updateColorBy() if apply: self.applyChanges() def toggleMasking(self, state, apply=False): self.model.activeView.masking = bool(state) self.colorDialog.updateMasking() if apply: self.applyChanges() def toggleHighlighting(self, state, apply=False): self.model.activeView.highlighting = bool(state) self.colorDialog.updateHighlighting() if apply: self.applyChanges() def toggleDockView(self): if self.dock.isVisible(): self.dock.hide() if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() - self.dock.width(), self.height()) else: self.dock.setVisible(True) if not self.isMaximized() and not self.dock.isFloating(): self.resize(self.width() + self.dock.width(), self.height()) self.resizePixmap() self.showMainWindow() def editZoomAct(self): percent, ok = QInputDialog.getInt(self, "Edit Zoom", "Zoom Percent:", self.dock.zoomBox.value(), 25, 2000) if ok: self.dock.zoomBox.setValue(percent) def editZoom(self, value): self.zoom = value self.resizePixmap() self.dock.zoomBox.setValue(value) def showMainWindow(self): self.raise_() self.activateWindow() def showColorDialog(self): self.colorDialog.show() self.colorDialog.raise_() self.colorDialog.activateWindow() # Dock methods: def editSingleOrigin(self, value, dimension): self.model.activeView.origin[dimension] = value def editWidth(self, value): self.model.activeView.width = value self.onRatioChange() self.dock.updateWidth() def editHeight(self, value): self.model.activeView.height = value self.onRatioChange() self.dock.updateHeight() def toggleAspectLock(self, state): self.model.activeView.aspectLock = bool(state) self.onRatioChange() self.dock.updateAspectLock() def editVRes(self, value): self.model.activeView.vRes = value self.dock.updateVRes() def editHRes(self, value): self.model.activeView.hRes = value self.onRatioChange() self.dock.updateHRes() # Color dialog methods: def editMaskingColor(self): current_color = self.model.activeView.maskBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.maskBackground = new_color self.colorDialog.updateMaskingColor() def editHighlightColor(self): current_color = self.model.activeView.highlightBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.highlightBackground = new_color self.colorDialog.updateHighlightColor() def editAlpha(self, value): self.model.activeView.highlightAlpha = value def editSeed(self, value): self.model.activeView.highlightSeed = value def editBackgroundColor(self, apply=False): current_color = self.model.activeView.plotBackground dlg = QColorDialog(self) dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] self.model.activeView.plotBackground = new_color self.colorDialog.updateBackgroundColor() if apply: self.applyChanges() # Plot image methods def editPlotOrigin(self, xOr, yOr, zOr=None, apply=False): if zOr != None: self.model.activeView.origin = [xOr, yOr, zOr] else: self.model.activeView.origin[self.xBasis] = xOr self.model.activeView.origin[self.yBasis] = yOr self.dock.updateOrigin() if apply: self.applyChanges() def revertDockControls(self): self.dock.revertToCurrent() def editDomainColor(self, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials current_color = domain[id].color dlg = QColorDialog(self) if isinstance(current_color, tuple): dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) elif isinstance(current_color, str): current_color = openmc.plots._SVG_COLORS[current_color] dlg.setCurrentColor(QtGui.QColor.fromRgb(*current_color)) if dlg.exec_(): new_color = dlg.currentColor().getRgb()[:3] domain[id].color = new_color self.applyChanges() def toggleDomainMask(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].masked = bool(state) self.applyChanges() def toggleDomainHighlight(self, state, kind, id): if kind == 'Cell': domain = self.model.activeView.cells else: domain = self.model.activeView.materials domain[id].highlighted = bool(state) self.applyChanges() # Helper methods: def restoreWindowSettings(self): settings = QtCore.QSettings() self.resize(settings.value("mainWindow/Size", QtCore.QSize(800, 600))) self.move( settings.value("mainWindow/Position", QtCore.QPoint(100, 100))) self.restoreState(settings.value("mainWindow/State")) self.colorDialog.resize( settings.value("colorDialog/Size", QtCore.QSize(400, 500))) self.colorDialog.move( settings.value("colorDialog/Position", QtCore.QPoint(600, 200))) self.colorDialog.setVisible( bool(settings.value("colorDialog/Visible", 0))) def restoreModelSettings(self): if os.path.isfile("plot_settings.pkl"): with open('plot_settings.pkl', 'rb') as file: model = pickle.load(file) if model.defaultView == self.model.defaultView: self.model.currentView = model.currentView self.model.activeView = copy.deepcopy(model.currentView) self.model.previousViews = model.previousViews self.model.subsequentViews = model.subsequentViews if os.path.isfile('plot_ids.binary') \ and os.path.isfile('plot.ppm'): self.restored = True def resetModels(self): self.cellsModel = DomainTableModel(self.model.activeView.cells) self.materialsModel = DomainTableModel(self.model.activeView.materials) self.cellsModel.beginResetModel() self.cellsModel.endResetModel() self.materialsModel.beginResetModel() self.materialsModel.endResetModel() self.colorDialog.updateDomainTabs() def showCurrentView(self): self.pixmap = QtGui.QPixmap('plot.ppm') self.resizePixmap() self.updateScale() self.updateRelativeBases() if self.model.previousViews: self.undoAction.setDisabled(False) if self.model.subsequentViews: self.redoAction.setDisabled(False) else: self.redoAction.setDisabled(True) self.statusBar().showMessage('Done', 1000) self.adjustWindow() def updateScale(self): cv = self.model.currentView self.scale = (self.pixmap.width() / cv.width, self.pixmap.height() / cv.height) def updateRelativeBases(self): cv = self.model.currentView self.xBasis = 0 if cv.basis[0] == 'x' else 1 self.yBasis = 1 if cv.basis[1] == 'y' else 2 def adjustWindow(self): self.screen = app.desktop().screenGeometry() self.setMaximumSize(self.screen.width(), self.screen.height()) def onRatioChange(self): av = self.model.activeView if av.aspectLock: ratio = av.width / max(av.height, .001) av.vRes = int(av.hRes / ratio) self.dock.updateVRes() def showCoords(self, xPlotPos, yPlotPos): cv = self.model.currentView if cv.basis == 'xy': coords = (f"({round(xPlotPos, 2)}, {round(yPlotPos, 2)}, " f"{round(cv.origin[2], 2)})") elif cv.basis == 'xz': coords = (f"({round(xPlotPos, 2)}, {round(cv.origin[1], 2)}, " f"{round(yPlotPos, 2)})") else: coords = (f"({round(cv.origin[0], 2)}, {round(xPlotPos, 2)}, " f"{round(yPlotPos, 2)})") self.coordLabel.setText(f'{coords}') def resizePixmap(self): z = self.zoom / 100 self.plotIm.setPixmap( self.pixmap.scaled(self.frame.width() * z, self.frame.height() * z, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)) self.plotIm.adjustSize() def moveEvent(self, event): self.adjustWindow() def resizeEvent(self, event): if self.pixmap: self.adjustWindow() self.resizePixmap() self.updateScale() def closeEvent(self, event): settings = QtCore.QSettings() settings.setValue("mainWindow/Size", self.size()) settings.setValue("mainWindow/Position", self.pos()) settings.setValue("mainWindow/State", self.saveState()) settings.setValue("colorDialog/Size", self.colorDialog.size()) settings.setValue("colorDialog/Position", self.colorDialog.pos()) visible = int(self.colorDialog.isVisible()) settings.setValue("colorDialog/Visible", visible) if len(self.model.previousViews) > 10: self.model.previousViews = self.model.previousViews[-10:] if len(self.model.subsequentViews) > 10: self.model.subsequentViews = self.model.subsequentViews[-10:] with open('plot_settings.pkl', 'wb') as file: pickle.dump(self.model, file)