def init_toolbar(self): # Init ToolBar toolbar = QToolBar("My main toolbar") self.addToolBar(toolbar) toolbar.setContextMenuPolicy( Qt.PreventContextMenu) # prevent users from hiding toolbar toolbar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) """Actions""" # terminate App action_exit_app = self.actionExit action_exit_app.triggered.connect(qApp.quit) # Open Single Dcm action_openOneImg = self.actionOpen_Single_Image action_openOneImg.triggered.connect(self.open_one_dcm) # Scan folder for dcm action_openSeries = self.actionOpen_Series_Folder action_openSeries.triggered.connect(self.openDirUpdateView) # Parse DICOMDIR action_ParseDcmDir = self.actionParse_DICOMDIR action_ParseDcmDir.triggered.connect(self.parseDICOMDIR) """" Set Keyboard Short Cut for actions""" action_openOneImg.setShortcut(QKeySequence.Open) """Link actions to ToolBar as Buttons""" toolbar.addAction(action_openOneImg) toolbar.addAction(action_openSeries) toolbar.addAction(action_ParseDcmDir) """"Set ToolBar Buttons Icon""" action_openOneImg.setIcon(QApplication.style().standardIcon( QStyle.SP_ComputerIcon)) action_openSeries.setIcon(QApplication.style().standardIcon( QStyle.SP_DialogOpenButton)) action_ParseDcmDir.setIcon(QApplication.style().standardIcon( QStyle.SP_DriveCDIcon))
def __init__(self, parent=None): super().__init__(parent) self.mTile = None self.mMapDocument = None self.mMapScene = MapScene(self) self.mMapView = MapView(self, MapViewMode.NoStaticContents) self.mToolManager = ToolManager(self) self.mApplyingChanges = False self.mSynchronizing = False self.setObjectName("TileCollisionEditor") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setSpacing(0) layout.setContentsMargins(5, 5, 5, 5) self.mMapView.setScene(self.mMapScene) self.mMapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mMapView.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) rectangleObjectsTool = CreateRectangleObjectTool(self) ellipseObjectsTool = CreateEllipseObjectTool(self) polygonObjectsTool = CreatePolygonObjectTool(self) polylineObjectsTool = CreatePolylineObjectTool(self) toolBar = QToolBar(self) toolBar.setMovable(False) toolBar.setFloatable(False) toolBar.setContextMenuPolicy(Qt.ActionsContextMenu) self.mToolManager = ToolManager(self) toolBar.addAction( self.mToolManager.registerTool(ObjectSelectionTool(self))) toolBar.addAction(self.mToolManager.registerTool( EditPolygonTool(self))) toolBar.addAction(self.mToolManager.registerTool(rectangleObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(ellipseObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polygonObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polylineObjectsTool)) self.setCentralWidget(self.mMapView) self.addToolBar(toolBar) self.mMapScene.setSelectedTool(self.mToolManager.selectedTool()) self.mToolManager.selectedToolChanged.connect(self.setSelectedTool) zoomComboBox = QComboBox() self.statusBar().addPermanentWidget(zoomComboBox) zoomable = self.mMapView.zoomable() zoomable.connectToComboBox(zoomComboBox) undoShortcut = QShortcut(QKeySequence.Undo, self) redoShortcut = QShortcut(QKeySequence.Redo, self) cutShortcut = QShortcut(QKeySequence.Cut, self) copyShortcut = QShortcut(QKeySequence.Copy, self) pasteShortcut = QShortcut(QKeySequence.Paste, self) deleteShortcut = QShortcut(QKeySequence.Delete, self) deleteShortcut2 = QShortcut(QKeySequence.Back, self) undoShortcut.activated.connect(self.undo) redoShortcut.activated.connect(self.redo) cutShortcut.activated.connect(self.cut) copyShortcut.activated.connect(self.copy) pasteShortcut.activated.connect(self.paste) deleteShortcut.activated.connect(self.delete_) deleteShortcut2.activated.connect(self.delete_) self.retranslateUi() self.resize(300, 300) Utils.restoreGeometry(self)
def __init__(self, parent = None): super().__init__(parent) self.mTile = None self.mMapDocument = None self.mMapScene = MapScene(self) self.mMapView = MapView(self, MapViewMode.NoStaticContents) self.mToolManager = ToolManager(self) self.mApplyingChanges = False self.mSynchronizing = False self.setObjectName("TileCollisionEditor") widget = QWidget(self) layout = QVBoxLayout(widget) layout.setSpacing(0) layout.setContentsMargins(5, 5, 5, 5) self.mMapView.setScene(self.mMapScene) self.mMapView.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mMapView.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) rectangleObjectsTool = CreateRectangleObjectTool(self) ellipseObjectsTool = CreateEllipseObjectTool(self) polygonObjectsTool = CreatePolygonObjectTool(self) polylineObjectsTool = CreatePolylineObjectTool(self) toolBar = QToolBar(self) toolBar.setMovable(False) toolBar.setFloatable(False) toolBar.setContextMenuPolicy(Qt.ActionsContextMenu) self.mToolManager = ToolManager(self) toolBar.addAction(self.mToolManager.registerTool(ObjectSelectionTool(self))) toolBar.addAction(self.mToolManager.registerTool(EditPolygonTool(self))) toolBar.addAction(self.mToolManager.registerTool(rectangleObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(ellipseObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polygonObjectsTool)) toolBar.addAction(self.mToolManager.registerTool(polylineObjectsTool)) self.setCentralWidget(self.mMapView) self.addToolBar(toolBar) self.mMapScene.setSelectedTool(self.mToolManager.selectedTool()) self.mToolManager.selectedToolChanged.connect(self.setSelectedTool) zoomComboBox = QComboBox() self.statusBar().addPermanentWidget(zoomComboBox) zoomable = self.mMapView.zoomable() zoomable.connectToComboBox(zoomComboBox) undoShortcut = QShortcut(QKeySequence.Undo, self) redoShortcut = QShortcut(QKeySequence.Redo, self) cutShortcut = QShortcut(QKeySequence.Cut, self) copyShortcut = QShortcut(QKeySequence.Copy, self) pasteShortcut = QShortcut(QKeySequence.Paste, self) deleteShortcut = QShortcut(QKeySequence.Delete, self) deleteShortcut2 = QShortcut(QKeySequence.Back, self) undoShortcut.activated.connect(self.undo) redoShortcut.activated.connect(self.redo) cutShortcut.activated.connect(self.cut) copyShortcut.activated.connect(self.copy) pasteShortcut.activated.connect(self.paste) deleteShortcut.activated.connect(self.delete_) deleteShortcut2.activated.connect(self.delete_) self.retranslateUi() self.resize(300, 300) Utils.restoreGeometry(self)
class SessionManager(QMainWindow): def __init__(self, parent=None): QListWidget.__init__(self, parent) self.setWindowTitle(tr("Saved Sessions")) self.setWindowFlags(Qt.Dialog) hideAction = QAction(self) hideAction.setShortcuts(["Esc", "Ctrl+W"]) hideAction.triggered.connect(self.hide) self.addAction(hideAction) self.sessionList = QListWidget(self) self.sessionList.itemActivated.connect(self.loadSession) self.setCentralWidget(self.sessionList) self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.loadButton = QPushButton(tr("&Load"), self) self.loadButton.clicked.connect( lambda: self.loadSession(self.sessionList.currentItem())) self.toolBar.addWidget(self.loadButton) self.saveButton = QPushButton(tr("&Save"), self) self.saveButton.clicked.connect(saveSessionManually) self.saveButton.clicked.connect(self.refresh) self.toolBar.addWidget(self.saveButton) deleteAction = QAction(self) deleteAction.setShortcut("Del") deleteAction.triggered.connect(self.delete) self.addAction(deleteAction) def refresh(self): self.sessionList.clear() if os.path.exists(settings.session_folder): sessions = os.listdir(settings.session_folder) for session in sessions: self.sessionList.addItem(session) def delete(self): if self.sessionList.hasFocus(): try: os.remove( os.path.join(settings.session_folder, self.sessionList.currentItem().text())) except: pass self.refresh() def show(self): self.refresh() QMainWindow.show(self) def loadSession(self, item): if os.path.exists(settings.session_folder): loadSession(os.path.join(settings.session_folder, item.text())) self.hide()
class SessionManager(QMainWindow): def __init__(self, parent=None): QListWidget.__init__(self, parent) self.setWindowTitle(tr("Saved Sessions")) self.setWindowFlags(Qt.Dialog) hideAction = QAction(self) hideAction.setShortcuts(["Esc", "Ctrl+W"]) hideAction.triggered.connect(self.hide) self.addAction(hideAction) self.sessionList = QListWidget(self) self.sessionList.itemActivated.connect(self.loadSession) self.setCentralWidget(self.sessionList) self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.loadButton = QPushButton(tr("&Load"), self) self.loadButton.clicked.connect(lambda: self.loadSession(self.sessionList.currentItem())) self.toolBar.addWidget(self.loadButton) self.saveButton = QPushButton(tr("&Save"), self) self.saveButton.clicked.connect(saveSessionManually) self.saveButton.clicked.connect(self.refresh) self.toolBar.addWidget(self.saveButton) deleteAction = QAction(self) deleteAction.setShortcut("Del") deleteAction.triggered.connect(self.delete) self.addAction(deleteAction) def refresh(self): self.sessionList.clear() if os.path.exists(settings.session_folder): sessions = os.listdir(settings.session_folder) for session in sessions: self.sessionList.addItem(session) def delete(self): if self.sessionList.hasFocus(): try: os.remove(os.path.join(settings.session_folder, self.sessionList.currentItem().text())) except: pass self.refresh() def show(self): self.refresh() QMainWindow.show(self) def loadSession(self, item): if os.path.exists(settings.session_folder): loadSession(os.path.join(settings.session_folder, item.text())) self.hide()
def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.setWindowTitle("My Awesome App") label = QLabel("This is a PyQt5 window!") label.setAlignment(Qt.AlignCenter) self.setCentralWidget(label) toolbar = QToolBar("My main toolbar") toolbar.setIconSize(QSize(16, 16)) self.addToolBar(toolbar) path_to_icon1 = 'fugue-icons-3/icons/bug.png' button_action = QAction(QIcon(path_to_icon1), "Your button", self) button_action.setStatusTip("This is Bug Button") button_action.triggered.connect(self.onMyToolBarButtonClick) button_action.setCheckable(True) toolbar.addAction(button_action) toolbar.setContextMenuPolicy( Qt.PreventContextMenu ) #Stop from toolbar disapearing from a right clicking it toolbar.addSeparator() path_to_icon2 = 'fugue-icons-3/icons/beer.png' button_action2 = QAction(QIcon(path_to_icon2), "Your button2", self) button_action2.setStatusTip("This is Beer Button") button_action2.triggered.connect(self.onMyToolBarButtonClick) button_action2.setCheckable(True) toolbar.addAction(button_action2) toolbar.addWidget(QLabel("Hello")) box = QCheckBox() box.stateChanged.connect(self.clickBox) toolbar.addWidget(box) path_to_icon3 = 'fugue-icons-3/icons/prohibition.png' action4 = QAction(QIcon(path_to_icon3), "Quit", self) action4.triggered.connect(app.quit) toolbar.addAction(action4) self.setStatusBar(QStatusBar(self))
class MainWindow(QMainWindow): """ Main UI window Class creates window elements, fills main menu with items. If you need to access to some existing menu items - check action path in the class constructor, than use next code: :: core.actionManager().action("mFile/aOpen").setEnabled(True) core.actionManager().action("mFile/aOpen").triggered.connect(self.myCoolMethod) MainWindow instance is accessible as: :: from enki.core.core import core core.mainwindow() Created by the core """ hideAllWindows = pyqtSignal() """ hideAllWindows() **Signal** emitted, when user toggled "Hide all" . Dock widgets are closed automatically, but other widgets, i.e. search widget, must catch this signal and close themselves. """ # pylint: disable=W0105 directoryDropt = pyqtSignal(str) """ directoryDropt() **Signal** emitted, when user drag-n-dropt directory to main windowd. FileBrowser shows directory """ # pylint: disable=W0105 _STATE_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_state.bin") _GEOMETRY_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_geometry.bin") def __init__(self): QMainWindow.__init__(self) self._queuedMessageToolBar = None self._createdMenuPathes = [] self._createdActions = [] self._addedDockWidgets = [] if hasattr(self, 'setUnifiedTitleAndToolBarOnMac'): # missing on some PyQt5 versions self.setUnifiedTitleAndToolBarOnMac(True) self.setIconSize(QSize(16, 16)) self.setAcceptDrops(True) # Set corner settings for dock widgets self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) self.setWindowTitle(self.defaultTitle()) # overwriten by workspace when file or it's modified state changes self.setWindowIcon(QIcon(':/enkiicons/logo/enki.svg')) # Create top tool bar self._topToolBar = QToolBar("topToolBar") self._topToolBar.setObjectName("topToolBar") self._topToolBar.setMovable(False) self._topToolBar.setIconSize(QSize(16, 16)) self._topToolBar.setContextMenuPolicy(Qt.PreventContextMenu) # to avoid possibility to hide the toolbar # Create menu bar self._menuBar = ActionMenuBar(self, core.actionManager()) self._initMenubarAndStatusBarLayout() # create central layout widget = QWidget(self) self._centralLayout = QVBoxLayout(widget) self._centralLayout.setContentsMargins(0, 0, 0, 0) self.setCentralWidget(widget) self.setStyleSheet('QMainWindow::separator{width: 4px}') def _initActions(self): """ Public method for actionManager. """ self._createMenuStructure() core.actionManager().action('mView/aOpenMainMenu').triggered.connect(self._openMainMenu) core.actionManager().action('mFile/aQuit').triggered.connect(self.onQuit) def terminate(self): """Explicitly called destructor """ if self._queuedMessageToolBar: self.removeToolBar(self._queuedMessageToolBar) del self._queuedMessageToolBar for act in self._createdActions: core.actionManager().removeAction(act) for menuPath in self._createdMenuPathes[::-1]: core.actionManager().removeMenu(menuPath) @staticmethod def _isMenuEmbeddedToTaskBar(): """On Unity (Ubuntu) and MacOS menu bar is embedded to task bar """ return 'UBUNTU_MENUPROXY' in os.environ or \ os.environ.get('XDG_CURRENT_DESKTOP') == 'Unity' or \ 'darwin' == sys.platform def _initMenubarAndStatusBarLayout(self): """Create top widget and put it on its place """ if not 'darwin' == sys.platform: # on Ubuntu toolbar, docs and editor area look as one widget. Ugly # Therefore it is separated with line. On Mac seems OK # I can't predict, how it will look on other platforms, therefore line is used for all, except Mac toolBarStyleSheet = "QToolBar {border: 0; border-bottom-width: 1; border-bottom-style: solid}""" self._topToolBar.setStyleSheet(toolBarStyleSheet) area = Qt.BottomToolBarArea if self._isMenuEmbeddedToTaskBar() else Qt.TopToolBarArea self.addToolBar(area, self._topToolBar) if self._isMenuEmbeddedToTaskBar(): # separate menu bar self.setMenuBar(self._menuBar) else: # menubar, statusbar and editor tool bar on one line self._menuBar.setAutoFillBackground(False) menuBarStyleSheet = """ QMenuBar {background-color: transparent; color: %s} QMenuBar::item:!selected {background: transparent;} """ % self.palette().color(QPalette.WindowText).name() self._menuBar.setStyleSheet(menuBarStyleSheet) self._menuBar.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self._topToolBar.addWidget(self._menuBar) # Create status bar self._statusBar = _StatusBar(self) self._topToolBar.addWidget(self._statusBar) def _initQueuedMessageToolBar(self): from enki.core.queued_msg_tool_bar import QueuedMessageToolBar self._queuedMessageToolBar = QueuedMessageToolBar(self) area = Qt.TopToolBarArea if self._isMenuEmbeddedToTaskBar() else Qt.BottomToolBarArea self.addToolBar(area, self._queuedMessageToolBar) self._queuedMessageToolBar.setVisible(False) def _createMenuStructure(self): """Fill menu bar with items. The majority of items are not connected to the slots, Connections made by module, which implements menu item functionality, but, all items are in one place, because it's easier to create clear menu layout """ # create menubar menus and actions def menu(path, name, icon, visible=True): """Subfunction for create a menu in the main menu""" menuObject = core.actionManager().addMenu(path, name) if icon: menuObject.setIcon(QIcon(':/enkiicons/' + icon)) self._createdMenuPathes.append(path) if not visible: menuObject.setVisible(False) def action(path, name, icon, shortcut, tooltip, enabled, checkable=False): # pylint: disable=R0913 """Subfunction for create an action in the main menu""" if icon: # has icon actObject = core.actionManager().addAction(path, name, QIcon(':/enkiicons/' + icon), shortcut) else: actObject = core.actionManager().addAction(path, name, shortcut=shortcut) if tooltip: actObject.setStatusTip(tooltip) actObject.setEnabled(enabled) actObject.setCheckable(checkable) self._createdActions.append(actObject) def separator(menu): """Subfunction for insert separator to the menu""" core.actionManager().action(menu).menu().addSeparator() # pylint: disable=C0301 # enable long lines for menu items # Menu or action path Name Icon Shortcut Hint enabled checkable menu ("mFile", "File" , "" ) action("mFile/aOpenProject", "Open Pro&ject..." , "open.png", "Shift+Ctrl+O" ,"Open a project" , True) separator("mFile") menu ("mFile/mUndoClose", "Undo Close" , "recents.png") separator("mFile") action("mFile/aNew", "&New file..." , "new.png", 'Ctrl+N', "New file" , True) action("mFile/aOpen", "&Open..." , "open.png", "Ctrl+O" , "Open a file" , True) menu ("mFile/mSave", "&Save" , "save.png" ) action("mFile/mSave/aCurrent", "&Save" , "save.png" , "Ctrl+S" , "Save the current file" , False) action("mFile/mSave/aSaveAs", "Save As..." , "save.png" , "Ctrl+Alt+S" , "" , False) action("mFile/mSave/aAll", "Save &All" , "saveall.png", 'Shift+Ctrl+S', "Save all files" , False) menu ("mFile/mReload", "&Reload" , "reload.png" ) action("mFile/mReload/aCurrent", "Reload" , "reload.png" , 'F5', "Reload the current file", False) action("mFile/mReload/aAll", "Reload All" , "reload.png" , 'Shift+F5', "Reload all files" , True) menu ("mFile/mClose", "&Close" , "close.png" ) action("mFile/mClose/aCurrent", "&Close" , "close.png", "Ctrl+W", "Close the current file" , False) action("mFile/mClose/aAll", "Close &All" , "closeall.png", 'Shift+Ctrl+W', "Close all files" , False) menu ("mFile/mFileSystem", "File System" , "filesystem.png") action("mFile/mFileSystem/aRename", "Rename" , "edit.png", '', "Rename current file" , False) if platform.system() != 'Windows': action("mFile/mFileSystem/aToggleExecutable", "Make executable" , "", '', "Toggle executable mode" , False) separator("mFile") action("mFile/aQuit", "Quit" , "quit.png", 'Ctrl+Q', "Quit" , True) separator("mFile") menu ("mView", "View" , "" ) action("mView/aShowIncorrectIndentation", "Show incorrect indentation", "", "", "" , False, True) action("mView/aShowAnyWhitespaces", "Show any whitespace", "", "", "" , False, True) separator("mView") action("mView/aHideAll", "Hide all / Restore" , "", "Shift+Esc", "Hide all widgets" , True) action("mView/aOpenMainMenu", "Open main menu" , "", "F10", "" , True) separator("mView") menu ("mEdit", "Edit" , "" ) action("mEdit/aStripTrailingWhitespace", "Strip trailing whitespace when saving", "", "", "" , True, True) separator("mEdit") menu ("mEdit/mCopyPasteLines", "Copy-paste lines" , "" ) menu ("mEdit/mIndentation", "Indentation" , "" ) separator("mEdit") action("mEdit/aEnableVimMode", "Enable Vim mode" , "", "", "" , False, True) menu ("mNavigation", "Navigation" , "" ) action("mNavigation/aFocusCurrentDocument", "Focus to editor" , "text.png", "Ctrl+Return", "Focus current document" , False) menu ("mNavigation/mSearchReplace", "&Search && Replace" , "search-replace-directory.png") menu ("mNavigation/mBookmarks", "&Bookmarks" , "bookmark.png") separator("mNavigation"), action("mNavigation/aNext", "&Next file" , "next.png", "Ctrl+PgDown", "Next file" , False) action("mNavigation/aPrevious", "&Previous file" , "previous.png", "Ctrl+PgUp", "Previous file" , False) separator("mNavigation") action("mNavigation/aGoto", "Go go line..." , "goto.png", "Ctrl+G", "Go to line..." , False) menu ("mNavigation/mFileBrowser", "File browser" , ':/enkiicons/open.png', visible=False) menu ("mNavigation/mScroll", "Scroll file" , '') menu ("mSettings", "Settings" , "" ) menu ("mTools", "Tools" , "" ) menu ("mHelp", "Help" , "" ) @pyqtSlot() def _openMainMenu(self): fileMenu = core.actionManager().menu('mFile') self._menuBar.setActiveAction(fileMenu.menuAction()) def menuBar(self): """Reference to menuBar """ return self._menuBar def topToolBar(self): """Top tool bar. Contains main menu, position indicator, etc """ return self._topToolBar def statusBar(self): """Return main window status bar. It is located on the top tool bar """ return self._statusBar def setWorkspace(self, workspace): """Set central widget of the main window. Normally called only by core when initializing system """ self._centralLayout.addWidget(workspace) self.setFocusProxy(workspace) def defaultTitle(self): """Default title. Contains name and version """ return "%s v.%s" % (enki.core.defines.PACKAGE_NAME, enki.core.defines.PACKAGE_VERSION) def centralLayout(self): """Layout of the central widget. Contains Workspace and search widget """ return self._centralLayout def appendMessage(self, text, timeoutMs=10000): """Append message to the queue. It will be shown as non-modal at the bottom of the window. Use such notifications, which are too long or too important for status bar but, not so important, to interrupt an user with QMessageBox """ if self._queuedMessageToolBar is None: self._initQueuedMessageToolBar() self._queuedMessageToolBar.appendMessage(text, timeoutMs) def closeEvent(self, event): """NOT A PUBLIC API Close event handler. Shows save files dialog. Cancels close, if dialog was rejected """ # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): event.ignore() return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QMainWindow.closeEvent(self, event) def onQuit(self): # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QApplication.quit() def _saveByteArray(self, path, title, data): """Load data, show error and return None if failed""" try: with open(path, 'wb') as f: f.write(data) except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical(None, self.tr("Cannot save {}".format(title)), self.tr("Cannot create file '%s'\nError: %s" % (path, error))) return def _loadByteArray(self, path, title): """Load data, show error and return None if failed""" if os.path.exists(path): try: with open(path, 'rb') as f: return f.read() except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical(None, self.tr("Cannot restore {}".format(title)), self.tr("Cannot read file '%s'\nError: %s" % (path, error))) return None def _saveState(self): """Save window state to main_window_state.bin file in the config directory """ self._saveByteArray(self._STATE_FILE, "main window state", self.saveState()) def loadState(self): """Restore window state from main_window_state.bin and config. Called by the core after all plugins had been initialized """ self._restoreGeometry() state = self._loadByteArray(self._STATE_FILE, "main window state") if state is not None: self.restoreState(state) else: # no state, first start self.showMaximized() for dock in self.findChildren(DockWidget): dock.show() def _saveGeometry(self): """Save window geometry to the config file """ self._saveByteArray(self._GEOMETRY_FILE, "main window geometry", self.saveGeometry()) def _restoreGeometry(self): """Restore window geometry to the config file """ geometry = self._loadByteArray(self._GEOMETRY_FILE, "main window geometry") if geometry is not None: self.restoreGeometry(geometry) def sizeHint(self): return QSize(900, 560) def dragEnterEvent(self, event): """QMainWindow method reimplementation. Say, that we are ready to accept dragged urls """ if event.mimeData().hasUrls(): # accept drag event.acceptProposedAction() # default handler QMainWindow.dragEnterEvent(self, event) def dropEvent(self, event): """QMainWindow method reimplementation. Open dropt files """ if event.mimeData().hasUrls(): for url in event.mimeData().urls(): localFile = url.toLocalFile() if os.path.isfile(localFile): core.workspace().openFile(localFile) elif os.path.isdir(localFile): self.directoryDropt.emit(localFile) # default handler QMainWindow.dropEvent(self, event) def addDockWidget(self, area, dock): pass # not a plugin API method """Add dock widget to previous position, if known. Otherwise add to specified area """ assert not dock in self._addedDockWidgets self._addedDockWidgets.append(dock) if not self.restoreDockWidget(dock): QMainWindow.addDockWidget(self, area, dock) """ Scroll view to make the cursor visible. Otherwise cursor can disappear from the viewport. QTimer is used because ensureCursorVisible works only after the dock has been drawn. A bad fix for #319 """ QTimer.singleShot(0, self._ensureCursorVisible) def _ensureCursorVisible(self): # When the timer fires, first check that there's still a workspace/document. if core.workspace() is not None: document = core.workspace().currentDocument() if document is not None: document.qutepart.ensureCursorVisible def removeDockWidget(self, dock): pass # not a plugin API method """Remove dock widget""" assert dock in self._addedDockWidgets self._addedDockWidgets.remove(dock) QMainWindow.removeDockWidget(self, dock) def restoreState(self, state): pass # not a plugin API method """Restore state shows widgets, which exist but shall not be installed on main window """ QMainWindow.restoreState(self, state) for dock in self.findChildren(DockWidget): if not dock in self._addedDockWidgets: dock.hide()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__() self.setWindowTitle("RainbowBB") self.setParent(parent) self.initUI() def initUI(self): self.setStyleSheet( "QCheckBox { background: palette(window); border-radius: 4px; padding: 2px; margin-right: 2px; }" ) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(stylesheet % (create_gradient("pastel"), )) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(self.toolBar) self.reverseBox = QCheckBox("&Reverse", self) self.reverseBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.reverseBox) self.byWordBox = QCheckBox("By &word", self) self.toolBar.addWidget(self.byWordBox) self.bounceBox = QCheckBox("&Bounce", self) self.bounceBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.bounceBox) self.sizeList = QComboBox(self) self.sizeList.addItem("None") for num in range(1, 8): self.sizeList.addItem(str(num)) self.toolBar.addWidget(self.sizeList) self.cycleList = QComboBox(self) self.toolBar.addWidget(self.cycleList) self.cycleList.currentIndexChanged.connect(self.updateGradient) self.loadCycles() self.convertButton = QPushButton("&Convert", self) self.convertButton.clicked.connect(self.convert) self.toolBar.addWidget(self.convertButton) self.reloadButton = QPushButton("Reload", self) self.reloadButton.setShortcut("Alt+Shift+R") self.reloadButton.clicked.connect(self.loadCycles) self.toolBar.addWidget(self.reloadButton) self.inputDock = QDockWidget("Input", self) self.inputDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.inputDock.setContextMenuPolicy(Qt.CustomContextMenu) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputDock) self.inputField = QTextEdit(self) self.inputField.setAcceptRichText(False) self.inputDock.setWidget(self.inputField) self.outputField = QTextEdit(self) self.outputField.setReadOnly(True) self.setCentralWidget(self.outputField) def updateGradient(self, index=None): if not index: index = self.cycleList.currentIndex() self.toolBar.setStyleSheet(stylesheet % (create_gradient( self.cycleList.itemText(index), self.reverseBox.isChecked(), self.bounceBox.isChecked()), )) def loadCycles(self): rainbowbb.load_cycles() self.cycleList.clear() self.cycleList.addItem("pastel") for cycle in sorted(list(rainbowbb.cycles.keys())): if cycle != "pastel": self.cycleList.addItem(cycle) def show(self): self.setVisible(True) self.inputField.setFocus() def convert(self): self.outputField.setPlainText( rainbowbb.size( rainbowbb.colorize(self.inputField.toPlainText(), self.cycleList.currentText(), self.reverseBox.isChecked(), not self.byWordBox.isChecked(), self.bounceBox.isChecked()), self.sizeList.currentText() if self.sizeList.currentText() != "None" else None))
class ClearHistoryDialog(QMainWindow): def __init__(self, parent=None): super(ClearHistoryDialog, self).__init__(parent) self.setWindowFlags(Qt.Dialog) self.setWindowTitle(tr("Clear Data")) closeWindowAction = QAction(self) closeWindowAction.setShortcuts(["Esc", "Ctrl+W", "Ctrl+Shift+Del"]) closeWindowAction.triggered.connect(self.close) self.addAction(closeWindowAction) self.contents = QWidget() self.layout = QVBoxLayout() self.contents.setLayout(self.layout) self.setCentralWidget(self.contents) label = QLabel(tr("What to clear:"), self) self.layout.addWidget(label) self.dataType = QComboBox(self) self.dataType.addItem(tr("History")) self.dataType.addItem(tr("Cookies")) self.dataType.addItem(tr("Cache")) self.dataType.addItem(tr("Persistent Storage")) self.dataType.addItem(tr("Everything")) self.layout.addWidget(self.dataType) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(common.blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.clearHistoryButton = QPushButton(tr("Clear"), self) self.clearHistoryButton.clicked.connect(self.clearHistory) self.toolBar.addWidget(self.clearHistoryButton) self.closeButton = QPushButton(tr("Close"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) def show(self): self.setVisible(True) def display(self): self.show() self.activateWindow() def clearHistory(self): clear_everything = (self.dataType.currentIndex() == 5) if self.dataType.currentIndex() == 0 or clear_everything: data.clearHistory() if self.dataType.currentIndex() == 1 or clear_everything: data.clearCookies() if self.dataType.currentIndex() == 2 or clear_everything: network.clear_cache() path = settings.offline_cache_folder if os.path.isdir(path): if sys.platform.startswith("win"): try: subprocess.Popen(["rd", path]) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass if self.dataType.currentIndex() == 3 or clear_everything: for subpath in ("WebpageIcons.db", "LocalStorage", "Databases",): path = os.path.abspath(os.path.join(settings.settings_folder, subpath)) if os.path.isfile(path): try: os.remove(path) except: pass elif os.path.isdir(path): if sys.platform.startswith("win"): try: subprocess.Popen(["rd", path]) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass
class SearchEditor(QMainWindow): searchChanged = Signal(str) searchManager = SearchManager() def __init__(self, parent=None): super(SearchEditor, self).__init__(parent) self.setWindowFlags(Qt.FramelessWindowHint | Qt.Popup) self.parent = parent self.setWindowTitle(tr('Search Editor')) self.styleSheet = "QMainWindow { background: palette(window); border: 1px solid palette(dark); }" self.setStyleSheet(self.styleSheet) try: self.setWindowIcon(common.app_icon) except: pass closeWindowAction = QAction(self) closeWindowAction.setShortcuts(["Ctrl+W", "Ctrl+Shift+K"]) closeWindowAction.triggered.connect(self.close) self.addAction(closeWindowAction) self.entryBar = QToolBar(self) self.entryBar.setIconSize(QSize(16, 16)) self.entryBar.setStyleSheet(common.blank_toolbar) self.entryBar.setContextMenuPolicy(Qt.CustomContextMenu) self.entryBar.setMovable(False) self.addToolBar(self.entryBar) eLabel = QLabel(" " + tr('New expression:'), self) self.entryBar.addWidget(eLabel) self.expEntry = custom_widgets.LineEdit(self) self.expEntry.returnPressed.connect(self.addSearch) self.entryBar.addWidget(self.expEntry) self.addSearchButton = QToolButton(self) self.addSearchButton.setText(tr("Add")) self.addSearchButton.setIcon(common.complete_icon("list-add")) self.addSearchButton.clicked.connect(self.addSearch) self.entryBar.addWidget(self.addSearchButton) self.engineList = QListWidget(self) self.engineList.setAlternatingRowColors(True) self.engineList.itemClicked.connect(self.applySearch) self.engineList.itemActivated.connect(self.applySearch) self.engineList.itemActivated.connect(self.close) self.setCentralWidget(self.engineList) self.takeSearchAction = QAction(self) self.takeSearchAction.triggered.connect(self.takeSearch) self.takeSearchAction.setShortcut("Del") self.addAction(self.takeSearchAction) self.hideAction = QAction(self) self.hideAction.triggered.connect(self.hide) self.hideAction.setShortcut("Esc") self.addAction(self.hideAction) self.reload_() def show(self): self.setVisible(True) y = QDesktopWidget() self.move(max(0, QCursor.pos().x() - self.width()), min(QCursor.pos().y(), y.height() - self.height())) y.deleteLater() def primeDisplay(self): self.reload_() self.expEntry.setFocus() def focusOutEvent(self, e): e.accept() self.hide() def reload_(self): self.engineList.clear() keys = self.searchManager.searchEngines.keys() if type(keys) is not list: keys = [key for key in keys] for name in sorted(keys): keyword = "None" if self.searchManager.searchEngines[name][0] != "": keyword = self.searchManager.searchEngines[name][0] self.engineList.addItem("%s\nKeyword: %s" % (name, keyword)) for item in range(0, self.engineList.count()): if self.searchManager.searchEngines[unicode(self.engineList.item(item).text()).split("\n")[0]][1] == self.searchManager.currentSearch: self.engineList.setCurrentItem(self.engineList.item(item)) self.searchChanged.emit(str(unicode(self.engineList.item(item).text()).split("\n")[0])) break def addSearch(self): if "%s" in self.expEntry.text(): name = QInputDialog.getText(self, tr('Query'), tr('Enter a name here:')) if name[1] and name[0] != "": name = name[0] keyword = QInputDialog.getText(self, tr('Query'), tr('Enter a keyword here:'))[0] self.searchManager.add(name, self.expEntry.text(), keyword) self.expEntry.clear() self.reload_() else: QMessageBox.warning(self, tr('Error'), tr('Search expression must contain a <b>%s</b> to indicate the search terms.')) def applySearch(self, item=False, old=False): if item: try: unicode(item.text()).split("\n")[0] except: notificationMessage(tr('searchError')) else: self.searchManager.change(unicode(item.text()).split("\n")[0]) if item != None: self.searchChanged.emit(str(unicode(item.text()).split("\n")[0])) def takeSearch(self): self.searchManager.remove(unicode(self.engineList.currentItem().text()).split("\n")[0]) self.reload_()
class LicenseDialog(QMainWindow): def __init__(self, parent=None): super(LicenseDialog, self).__init__(parent) self.resize(420, 320) self.setWindowTitle(tr("Credits & Licensing")) self.setWindowFlags(Qt.Dialog) self.readme = "" self.license = "" self.thanks = "" self.authors = "" self.tabWidget = QTabWidget(self) self.setCentralWidget(self.tabWidget) for folder in (app_folder, os.path.dirname(app_folder)): for fname in os.listdir(folder): if fname.startswith("LICENSE"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.license = f.read() f.close() elif fname.startswith("THANKS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.thanks = f.read() f.close() elif fname.startswith("AUTHORS"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.authors = f.read() f.close() elif fname.startswith("README"): try: f = open(os.path.join(folder, fname), "r") except: pass else: self.readme = f.read() f.close() self.readmeView = ReadOnlyTextEdit(self) self.readmeView.setText(self.readme) self.tabWidget.addTab(self.readmeView, tr("&README")) self.authorsView = ReadOnlyTextEdit(self) self.authorsView.setText(self.authors) self.tabWidget.addTab(self.authorsView, tr("&Authors")) self.thanksView = ReadOnlyTextEdit(self) self.thanksView.setText(self.thanks) self.tabWidget.addTab(self.thanksView, tr("&Thanks")) self.licenseView = ReadOnlyTextEdit(self) self.licenseView.setText(self.license) self.tabWidget.addTab(self.licenseView, tr("&License")) closeAction = QAction(self) closeAction.setShortcuts(["Esc", "Ctrl+W"]) closeAction.triggered.connect(self.hide) self.addAction(closeAction) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.BottomToolBarArea, self.toolBar) self.toolBar.addWidget(HorizontalExpander(self)) self.closeButton = QPushButton(tr("&OK"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) self.closeButton.setFocus()
class ListLayout(QWidget, CueLayout): NAME = 'List Layout' DESCRIPTION = ''' This layout organize the cues in a list: <ul> <li>Unlimited cues; <li>Side panel with playing media-cues; <li>Cues can be moved in the list; <li>Keyboard control; </ul>''' HEADER = ['', 'Cue', 'Duration', 'Progress'] # I need to redefine those from CueLayout key_pressed = CueLayout.key_pressed cue_added = CueLayout.cue_added cue_removed = CueLayout.cue_removed focus_changed = CueLayout.focus_changed def __init__(self, app, **kwds): super().__init__(**kwds) self.mainWindow = app.mainWindow self.menuLayout = self.mainWindow.menuLayout self._cue_items = [] self._playing_widgets = {} self._context_item = None self._show_dbmeter = config['ListLayout']['ShowDbMeters'] == 'True' self._show_seek = config['ListLayout']['ShowSeek'] == 'True' self._accurate_time = config['ListLayout']['ShowAccurate'] == 'True' self._auto_next = config['ListLayout']['AutoNext'] == 'True' # Add layout-specific menus self.showDbMeterAction = QAction(self) self.showDbMeterAction.setCheckable(True) self.showDbMeterAction.setChecked(self._show_dbmeter) self.showDbMeterAction.triggered.connect(self.set_dbmeter_visible) self.showSeekAction = QAction(self) self.showSeekAction.setCheckable(True) self.showSeekAction.setChecked(self._show_seek) self.showSeekAction.triggered.connect(self.set_seek_visible) self.accurateTimingAction = QAction(self) self.accurateTimingAction.setCheckable(True) self.accurateTimingAction.setChecked(self._accurate_time) self.accurateTimingAction.triggered.connect(self.set_accurate_time) self.autoNextAction = QAction(self) self.autoNextAction.setCheckable(True) self.autoNextAction.setChecked(self._auto_next) self.autoNextAction.triggered.connect(self.set_auto_next) self.menuLayout.addAction(self.showDbMeterAction) self.menuLayout.addAction(self.showSeekAction) self.menuLayout.addAction(self.accurateTimingAction) self.menuLayout.addAction(self.autoNextAction) # Add a toolbar to MainWindow self.toolBar = QToolBar(self.mainWindow) self.toolBar.setContextMenuPolicy(QtCore.Qt.PreventContextMenu) self.playAction = QAction(self) self.playAction.setIcon(QIcon.fromTheme("media-playback-start")) self.playAction.triggered.connect(self.play_current) self.pauseAction = QAction(self) self.pauseAction.setIcon(QIcon.fromTheme("media-playback-pause")) self.pauseAction.triggered.connect(self.pause_current) self.stopAction = QAction(self) self.stopAction.setIcon(QIcon.fromTheme("media-playback-stop")) self.stopAction.triggered.connect(self.stop_current) self.stopAllAction = QAction(self) self.stopAllAction.font().setBold(True) self.stopAllAction.triggered.connect(self.stop_all) self.pauseAllAction = QAction(self) self.pauseAllAction.font().setBold(True) self.pauseAllAction.triggered.connect(self.pause_all) self.restartAllAction = QAction(self) self.restartAllAction.font().setBold(True) self.restartAllAction.triggered.connect(self.restart_all) self.toolBar.addAction(self.playAction) self.toolBar.addAction(self.pauseAction) self.toolBar.addAction(self.stopAction) self.toolBar.addSeparator() self.toolBar.addAction(self.stopAllAction) self.toolBar.addAction(self.pauseAllAction) self.toolBar.addAction(self.restartAllAction) self.mainWindow.addToolBar(self.toolBar) self.hLayout = QHBoxLayout(self) self.hLayout.setContentsMargins(5, 5, 5, 5) # On the left (cue list) self.listView = ListWidget(self) self.listView.context_event.connect(self.context_event) self.listView.key_event.connect(self.onKeyPressEvent) self.listView.drop_move_event.connect(self.move_cue_at) self.listView.drop_copy_event.connect(self._copy_cue_at) self.listView.setHeaderLabels(self.HEADER) self.listView.header().setSectionResizeMode(QHeaderView.Fixed) self.listView.header().setSectionResizeMode(self.HEADER.index('Cue'), QHeaderView.Stretch) self.listView.setColumnWidth(0, 40) self.hLayout.addWidget(self.listView) self.vLayout = QVBoxLayout() self.vLayout.setContentsMargins(0, 0, 0, 0) self.vLayout.setSpacing(2) # On the right (playing media-cues) self.label = QLabel('Playing', self) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setStyleSheet('font-size: 17pt; font-weight: bold;') self.vLayout.addWidget(self.label) self.playView = QListWidget(self) self.playView.setMinimumWidth(300) self.playView.setMaximumWidth(300) self.playView.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn) self.playView.setFocusPolicy(QtCore.Qt.NoFocus) self.playView.setSelectionMode(QAbstractItemView.NoSelection) self.vLayout.addWidget(self.playView) self.hLayout.addLayout(self.vLayout) # Add cue preferences widgets self.add_settings_section(MediaCueGeneral, MediaCue) self.add_settings_section(Appearance) # Context menu actions self.edit_action = QAction(self) self.edit_action.triggered.connect(self.edit_context_cue) self.remove_action = QAction(self) self.remove_action.triggered.connect(self.remove_context_cue) self.select_action = QAction(self) self.select_action.triggered.connect(self.select_context_cue) self.add_context_item(self.edit_action) self.sep1 = self.add_context_separator() self.add_context_item(self.remove_action) self.add_context_item(self.select_action) self.retranslateUi() def retranslateUi(self): self.showDbMeterAction.setText("Show Db-meters") self.showSeekAction.setText("Show seek bars") self.accurateTimingAction.setText('Accurate timing') self.autoNextAction.setText('Auto select next cue') self.playAction.setText("Go") self.pauseAction.setText("Pause") self.stopAction.setText("Stop") self.stopAllAction.setText("Stop All") self.pauseAllAction.setText("Pause All") self.restartAllAction.setText("Restart All") self.edit_action.setText('Edit option') self.remove_action.setText('Remove') self.select_action.setText('Select') def current_cue(self): item = self.current_item() if item is not None: return item.cue def current_item(self): if len(self._cue_items) > 0: return self._cue_items[self.listView.currentIndex().row()] def select_context_cue(self): self._context_item.select() def __add_cue__(self, cue, index): if isinstance(cue, MediaCue): item = MediaItem(cue, self.listView) # Use weak-references for avoid cyclic-references with lambda(s) wself = weakref.ref(self) wcue = weakref.ref(cue) cue.media.on_play.connect(lambda: wself().show_playing(wcue())) cue.media.interrupted.connect(lambda: wself().hide_playing(wcue())) cue.media.stopped.connect(lambda: wself().hide_playing(wcue())) cue.media.error.connect(lambda: wself().hide_playing(wcue())) cue.media.eos.connect(lambda: wself().hide_playing(wcue())) elif isinstance(cue, ActionCue): item = ActionItem(cue) item.cue = cue else: raise Exception('Cue type not supported') item.setFlags(item.flags() & ~QtCore.Qt.ItemIsDropEnabled) if index is None or (index < 0 or index >= len(self._cue_items)): cue['index'] = len(self._cue_items) self.listView.addTopLevelItem(item) self._cue_items.append(item) else: self.listView.insertTopLevelItem(index, item) self._cue_items.insert(index, item) for n in range(index, len(self._cue_items)): self._cue_items[n].cue['index'] = n if isinstance(item, MediaItem): item.init() if len(self._cue_items) == 1: self.listView.setCurrentItem(item) self.listView.setFocus() self.listView.resizeColumnToContents(1) self.cue_added.emit(cue) def set_accurate_time(self, enable): self._accurate_time = enable for i in range(self.playView.count()): widget = self.playView.itemWidget(self.playView.item(i)) widget.set_accurate_time(enable) for item in self._cue_items: if isinstance(item, MediaItem): item.set_accurate_time(enable) def set_auto_next(self, enable): self._auto_next = enable def set_seek_visible(self, visible): self._show_seek = visible for i in range(self.playView.count()): widget = self.playView.itemWidget(self.playView.item(i)) widget.set_seek_visible(visible) def set_dbmeter_visible(self, visible): self._show_dbmeter = visible for i in range(self.playView.count()): widget = self.playView.itemWidget(self.playView.item(i)) widget.set_dbmeter_visible(visible) def show_playing(self, cue): if cue not in self._playing_widgets: media_time = self._cue_items[cue['index']].media_time widget = PlayingMediaWidget(cue, media_time, self.playView) widget.set_dbmeter_visible(self._show_dbmeter) widget.set_seek_visible(self._show_seek) widget.set_accurate_time(self._accurate_time) list_item = QListWidgetItem() list_item.setSizeHint(widget.size()) self.playView.addItem(list_item) self.playView.setItemWidget(list_item, widget) self._playing_widgets[cue] = list_item def hide_playing(self, cue): if cue in self._playing_widgets: list_item = self._playing_widgets.pop(cue) widget = self.playView.itemWidget(list_item) row = self.playView.indexFromItem(list_item).row() self.playView.removeItemWidget(list_item) self.playView.takeItem(row) widget.destroy_widget() def onKeyPressEvent(self, e): if e.key() == QtCore.Qt.Key_Space: if qApp.keyboardModifiers() == Qt.ShiftModifier: cue = self.current_cue() if cue is not None: self.edit_cue(cue) elif qApp.keyboardModifiers() == Qt.ControlModifier: item = self.current_item() if item is not None: item.select() else: cue = self.current_cue() if cue is not None: cue.execute() if self._auto_next: nextitem = self.listView.currentIndex().row() + 1 if nextitem < self.listView.topLevelItemCount(): nextitem = self.listView.topLevelItem(nextitem) self.listView.setCurrentItem(nextitem) else: self.key_pressed.emit(e) e.accept() def play_current(self): cue = self.current_cue() if isinstance(cue, MediaCue): cue.media.play() else: cue.execute() def pause_current(self): cue = self.current_cue() if isinstance(cue, MediaCue): cue.media.pause() def stop_current(self): cue = self.current_cue() if isinstance(cue, MediaCue): cue.media.stop() def context_event(self, event): item = self.listView.itemAt(event.pos()) if item is not None: index = self.listView.indexOfTopLevelItem(item) self._context_item = self._cue_items[index] self.show_context_menu(event.globalPos()) event.accept() def remove_context_cue(self): self.remove_cue(self.get_context_cue()) def edit_context_cue(self): self.edit_cue(self.get_context_cue()) def stop_all(self): for item in self._cue_items: if isinstance(item.cue, MediaCue): item.cue.media.stop() def pause_all(self): for item in self._cue_items: if isinstance(item.cue, MediaCue): item.cue.media.pause() def restart_all(self): for item in self._cue_items: if isinstance(item.cue, MediaCue): if item.cue.media.state == Media.PAUSED: item.cue.media.play() def __remove_cue__(self, cue): index = cue['index'] self.listView.takeTopLevelItem(index) self._cue_items.pop(index) if isinstance(cue, MediaCue): cue.media.interrupt() if cue in self._playing_widgets: self.hide_playing(self._playing_widgets[cue]) for item in self._cue_items[index:]: item.cue['index'] = item.cue['index'] - 1 cue.finalize() self.cue_removed.emit(cue) def move_cue_at(self, old_index, index): self.move_cue(self._cue_items[old_index].cue, index) def _copy_cue_at(self, old_index, index): newcue = CueFactory.clone_cue(self._cue_items[old_index].cue) self.add_cue(newcue, index) def __move_cue__(self, cue, index): item = self._cue_items.pop(cue['index']) self._cue_items.insert(index, item) self.listView.setCurrentItem(item) if isinstance(item, MediaItem): item.init() for n, item in enumerate(self._cue_items): item.cue['index'] = n def get_cues(self, cue_class=Cue): # i -> item return [i.cue for i in self._cue_items if isinstance(i.cue, cue_class)] def get_cue_at(self, index): if index < len(self._cue_items): return self._cue_items[index].cue def get_cue_by_id(self, cue_id): for item in self._cue_items: if item.cue.cue_id() == cue_id: return item.cue def get_selected_cues(self, cue_class=Cue): cues = [] for item in self._cue_items: if item.selected and isinstance(item.cue, cue_class): cues.append(item.cue) return cues def clear_layout(self): while len(self._cue_items) > 0: self.__remove_cue__(self._cue_items[-1].cue) def destroy_layout(self): self.clear_layout() self.menuLayout.clear() self.mainWindow.removeToolBar(self.toolBar) self.toolBar.deleteLater() # Remove context-items self.remove_context_item(self.edit_action) self.remove_context_item(self.sep1) self.remove_context_item(self.remove_action) self.remove_context_item(self.select_action) # Remove settings-sections self.remove_settings_section(Appearance) self.remove_settings_section(MediaCueGeneral) # !! Delete the layout references !! self.deleteLater() def get_context_cue(self): return self._context_item.cue def select_all(self): for item in self._cue_items: if not item.selected: item.select() def deselect_all(self): for item in self._cue_items: if item.selected: item.select() def invert_selection(self): for item in self._cue_items: item.select()
def __init__(self, parent=None): """Defaults the application window to be 500X500""" super(MyMainWindow, self).__init__(parent=parent) self.left = 500 self.top = 500 self.width = 500 self.height = 500 self.ui_widget = UIWidget() self.popup_settings = PopupSettings(self.ui_widget.graphix) self.glossary = Glossary(2) self.save_rules = SaveRules(self.ui_widget) self.getting_started = GettingStarted() self.init_window() toolbar = QToolBar("Settings Toolbar") toolbar.setIconSize(QSize(16,16)) self.addToolBar(toolbar) toolbar.setMovable(False) toolbar.setContextMenuPolicy(Qt.PreventContextMenu) self.button_action = QAction("2D", self) self.button_action.setStatusTip("Click to use 2D L-Systems!") self.button_action.triggered.connect(self.toggle_dim2D) self.button_action2 = QAction("3D", self) self.button_action2.setStatusTip("Click to use 3D L-Systems!") self.button_action2.triggered.connect(self.toggle_dim3D) self.save_action = QAction('Save Rules', self) self.save_action.setStatusTip("Click to save the grammar of the L-System!") self.save_action.triggered.connect(lambda: self.save_rules.show()) self.glossary_action = QAction("Glossary", self) self.glossary_action.setStatusTip("Click to see the glossary!") self.glossary_action.triggered.connect(lambda: self.glossary.show()) self.tutorial = QAction("Tutorial", self) self.tutorial.setStatusTip("Click here to see how to get started!") self.tutorial.triggered.connect(lambda: self.getting_started.show()) self.reset_zoom_button = QAction("Reset Zoom",self) self.reset_zoom_button.setStatusTip("Click here to reset zoom!") self.reset_zoom_button.triggered.connect(self.reset_zoom) self.reset_txtbox_button = QAction("Reset Rules",self) self.reset_txtbox_button.setStatusTip("Click here to clear the rules!") self.reset_txtbox_button.triggered.connect(self.ui_widget.reset_input_boxes) if sys.platform == 'win32' : self.screenshot_button = QAction("Screenshot",self) self.screenshot_button.setStatusTip("Click here to take a screenshot!") self.screenshot_button.triggered.connect(self.screenshot) toolbar.addAction(self.screenshot_button) toolbar.addAction(self.tutorial) toolbar.addAction(self.glossary_action) toolbar.addAction(self.save_action) toolbar.addAction(self.reset_zoom_button) toolbar.addAction(self.reset_txtbox_button) toolbar.addSeparator() toolbar.addAction(self.button_action) toolbar.addAction(self.button_action2) self.button_action.setCheckable(True) self.button_action2.setCheckable(True) self.button_action.setChecked(True) self.setStatusBar(QStatusBar(self)) self.setCentralWidget(self.ui_widget)
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.setObjectName("myGitClone") root = QFileInfo.path(QFileInfo(QCoreApplication.arguments()[0])) print("root", root) self.icon = QIcon(f"{root}/favicon.ico") self.setWindowIcon(self.icon) self.setGeometry(0, 0, 800, 600) self.setAttribute(Qt.WA_DeleteOnClose) self.setMinimumSize(400, 300) self.setDocumentMode(True) self.settings = QSettings('Axel Schneider', self.objectName()) self.createStatusbar() self.createActions() self.createWidgets() QMetaObject.connectSlotsByName(self) self.readSettings() self.statusbar.showMessage("Ready") self.setWindowTitle("myGitClone") ### process ### shell settings self.process = QProcess(self) self.process.setProcessChannelMode(QProcess.MergedChannels) self.process.readyReadStandardError.connect(lambda: self.msg("Error")) self.process.started.connect(lambda: self.msg("starting shell")) self.process.finished.connect(lambda: self.msg("shell ended")) ### widgets ### def createWidgets(self): self.username = "" self.url = "https://github.com/%s?tab=repositories" % self.username self.repoList = [] self.gitList = [] self.dlFolder = QDir.homePath() + "/Downloads" ### table ### self.lb = QTableWidget() self.lb.setColumnCount(2) self.lb.setColumnWidth(0, 60) self.lb.horizontalHeader().setStretchLastSection(True) self.lb.setHorizontalHeaderItem(0, QTableWidgetItem("Select")) self.lb.setHorizontalHeaderItem(1, QTableWidgetItem("Repository Name")) ### username field ### self.uname = QLineEdit("") self.uname.setFixedWidth(180) self.uname.setPlaceholderText("insert user name") self.uname.returnPressed.connect(self.listRepos) ### get repos button ### self.uBtn = QPushButton(QIcon(self.icon), "get Repos List") self.uBtn.setToolTip("get all repos from user") self.uBtn.clicked.connect(self.listRepos) ### get repos button ### self.dlBtn = QPushButton(QIcon.fromTheme("download"), "download selected Repos") self.dlBtn.setToolTip("download selected repos from user") self.dlBtn.setFixedWidth(180) self.dlBtn.clicked.connect(self.create_dl_list) ### Layout self.ubox = QHBoxLayout() self.ubox.addWidget(self.uname) self.ubox.addWidget(self.uBtn) self.ubox.addStretch(1) self.ubox.addWidget(self.dlBtn) self.layout = QVBoxLayout() self.wid = QWidget() self.layout.addLayout(self.ubox) self.layout.addWidget(self.lb) self.wid.setLayout(self.layout) self.setCentralWidget(self.wid) ### actions ### def createActions(self): self.tbar = QToolBar() self.tbar.setIconSize(QSize(16, 16)) self.tbar.setMovable(False) self.tbar.setToolButtonStyle(0) self.tbar.setContextMenuPolicy(Qt.PreventContextMenu) self.tbar.setObjectName("tbar") self.addToolBar(self.tbar) self.actionSettings = QAction(self, triggered=self.appSettings, toolTip="set output directory") icon = QIcon.fromTheme("preferences-system") self.actionSettings.setIcon(icon) self.actionSettings.setObjectName("actionSettings") self.actionAbout = QAction(self, triggered=self.aboutApp) icon = QIcon.fromTheme("help-about") self.actionAbout.setIcon(icon) self.tbar.addAction(self.actionSettings) self.tbar.addAction(self.actionAbout) ### statusbar### def createStatusbar(self): self.statusbar = QStatusBar(self) font = QFont() font.setPointSize(7) self.statusbar.setFont(font) self.statusbar.setObjectName("statusbar") self.setStatusBar(self.statusbar) ### get username from textfield def changeUsername(self): self.username = self.uname.text() self.url = "https://github.com/%s?tab=repositories" % self.username ### get user repos ## def listRepos(self): self.changeUsername() if self.username == "": self.msgBox("please type a username") else: self.lb.setRowCount(0) self.repoList = [] repositories = self.get_repositories() print("%s %s" % ("get repos from", self.username)) self.repoList = list(repositories) self.fillTable() self.msg("repos loaded") ### fill table with user repos def fillTable(self): self.lb.setRowCount(len(self.repoList)) if self.lb.rowCount() > 0: for x in range(len(self.repoList)): rep = QTableWidgetItem(self.repoList[x]) checkbox = QCheckBox(self.lb) checkbox.setStyleSheet("margin-left:20%; margin-right:10%;") checkbox.setCheckState(0) self.lb.setCellWidget(x, 0, checkbox) self.lb.setItem(x, 1, rep) ### table context menu def contextMenuEvent(self, event): self.menu = QMenu(self) if self.lb.selectionModel().hasSelection(): # copy downloadAction = QAction(QIcon.fromTheme("download"), 'download Repo', self) downloadAction.triggered.connect( lambda: self.downloadRepoFromList()) ### self.menu.addAction(downloadAction) self.menu.popup(QCursor.pos()) def listChanged(self): self.create_dl_list() ### get download list from selected repos def create_dl_list(self): r = "" self.gitList = [] for x in range(self.lb.rowCount()): if self.lb.cellWidget(x, 0).checkState() == 2: r = self.lb.item(x, 1).text() self.gitList.append(r) print("%s %s" % (r, "is selected")) self.downloadRepo(r) ### download repo def downloadRepo(self, gitrepo): merror = "" cmd = "git clone --progress --verbose https://github.com/" + str( self.username) + "/" + str(gitrepo) + " " + str( self.dlFolder) + "/" + str(gitrepo) print("%s %s" % ("username is:", self.username)) print(cmd) try: self.process.execute(cmd) except Exception as e: s = str(e) self.errorBox(s) ### download selected repo (context menu) def downloadRepoFromList(self): row = self.lb.selectionModel().selectedIndexes()[0].row() gitrepo = self.lb.item(row, 1).text() cmd = "git clone --progress --verbose https://github.com/" + str( self.username) + "/" + str(gitrepo) + " " + str( self.dlFolder) + "/" + str(gitrepo) print(cmd) self.process.execute(cmd) ### preferences def appSettings(self): if self.dlFolder == "": self.dlFolder = QDir.homePath() self.msg("settings called") path = QFileDialog.getExistingDirectory(self, "select Folder", self.dlFolder) if path: self.dlFolder = path print("%s %s" % ("download folder changed to", self.dlFolder)) def closeEvent(self, e): self.writeSettings() e.accept() ### read settings from config file def readSettings(self): print("reading settings") if self.settings.contains('geometry'): self.setGeometry(self.settings.value('geometry')) if self.settings.contains('downloadFolder'): self.dlFolder = self.settings.value('downloadFolder') self.msg(self.dlFolder) print("%s %s" % ("download folder:", self.dlFolder)) ### write settings to config file def writeSettings(self): print("writing settings") self.settings.setValue('geometry', self.geometry()) self.settings.setValue('downloadFolder', self.dlFolder) ### about window def aboutApp(self): title = "about myGitClone" message = """ <span style='color: #3465a4; font-size: 18pt;font-weight: bold;' >myGitClone</strong></span></p> <h3>based on <a title='git_clones' href='https://github.com/rootVIII/git_clones' target='_blank'> git_clones</a> by James</h3> <h4>created by <a title='Axel Schneider' href='http://goodoldsongs.jimdo.com' target='_blank'>Axel Schneider</a> with PyQt5</h3> <br> <span style='color: #555753; font-size: 9pt;'>©2019 Axel Schneider, James</strong></span></p> """ self.infobox(title, message) ### error messagebox def errorBox(self, message): mwin = QMessageBox.warning(self, "Error", message) ### messagebox def infobox(self, title, message): QMessageBox.about(self, title, message).show() ### set statusbar text def msg(self, message): self.statusbar.showMessage(message) def msgBox(self, message): msg = QMessageBox.warning(self, "Information", message) ### begin from git_clones ### def http_get(self): if version_info[0] != 2: req = urlopen(self.url) return req.read().decode('utf-8') req = Request(self.url) request = urlopen(req) return request.read() def get_repo_data(self): try: response = self.http_get() except Exception as e: s = str(e) self.errorBox(s) print("Unable to make request to %s's Github page" % self.username) exit(1) else: pattern = r"<a\s?href\W+%s/(.*)\"\s+" % self.username for line in findall(pattern, response): yield line.split('\"')[0] def get_repositories(self): return set([repo for repo in self.get_repo_data()])
class MainWindow(QMainWindow): """ Main UI window Class creates window elements, fills main menu with items. If you need to access to some existing menu items - check action path in the class constructor, than use next code: :: core.actionManager().action("mFile/aOpen").setEnabled(True) core.actionManager().action("mFile/aOpen").triggered.connect(self.myCoolMethod) MainWindow instance is accessible as: :: from enki.core.core import core core.mainwindow() Created by the core """ hideAllWindows = pyqtSignal() """ hideAllWindows() **Signal** emitted, when user toggled "Hide all" . Dock widgets are closed automatically, but other widgets, i.e. search widget, must catch this signal and close themselves. """ # pylint: disable=W0105 directoryDropt = pyqtSignal(str) """ directoryDropt() **Signal** emitted, when user drag-n-dropt directory to main windowd. FileBrowser shows directory """ # pylint: disable=W0105 _STATE_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_state.bin") _GEOMETRY_FILE = os.path.join(enki.core.defines.CONFIG_DIR, "main_window_geometry.bin") def __init__(self): QMainWindow.__init__(self) self._queuedMessageToolBar = None self._createdMenuPathes = [] self._createdActions = [] self._addedDockWidgets = [] if hasattr(self, 'setUnifiedTitleAndToolBarOnMac' ): # missing on some PyQt5 versions self.setUnifiedTitleAndToolBarOnMac(True) self.setIconSize(QSize(16, 16)) self.setAcceptDrops(True) # Set corner settings for dock widgets self.setCorner(Qt.TopLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea) self.setCorner(Qt.BottomLeftCorner, Qt.LeftDockWidgetArea) self.setCorner(Qt.BottomRightCorner, Qt.RightDockWidgetArea) self.setWindowTitle(self.defaultTitle( )) # overwriten by workspace when file or it's modified state changes self.setWindowIcon(QIcon(':/enkiicons/logo/enki.svg')) # Create top tool bar self._topToolBar = QToolBar("topToolBar") self._topToolBar.setObjectName("topToolBar") self._topToolBar.setMovable(False) self._topToolBar.setIconSize(QSize(16, 16)) self._topToolBar.setContextMenuPolicy( Qt.PreventContextMenu) # to avoid possibility to hide the toolbar # Create menu bar self._menuBar = ActionMenuBar(self, core.actionManager()) self._initMenubarAndStatusBarLayout() # create central layout widget = QWidget(self) self._centralLayout = QVBoxLayout(widget) self._centralLayout.setContentsMargins(0, 0, 0, 0) self.setCentralWidget(widget) self.setStyleSheet('QMainWindow::separator{width: 4px}') def _initActions(self): """ Public method for actionManager. """ self._createMenuStructure() core.actionManager().action('mView/aOpenMainMenu').triggered.connect( self._openMainMenu) core.actionManager().action('mFile/aQuit').triggered.connect( self.onQuit) def terminate(self): """Explicitly called destructor """ if self._queuedMessageToolBar: self.removeToolBar(self._queuedMessageToolBar) del self._queuedMessageToolBar for act in self._createdActions: core.actionManager().removeAction(act) for menuPath in self._createdMenuPathes[::-1]: core.actionManager().removeMenu(menuPath) @staticmethod def _isMenuEmbeddedToTaskBar(): """On Unity (Ubuntu) and MacOS menu bar is embedded to task bar """ return 'UBUNTU_MENUPROXY' in os.environ or \ os.environ.get('XDG_CURRENT_DESKTOP') == 'Unity' or \ 'darwin' == sys.platform def _initMenubarAndStatusBarLayout(self): """Create top widget and put it on its place """ if not 'darwin' == sys.platform: # on Ubuntu toolbar, docs and editor area look as one widget. Ugly # Therefore it is separated with line. On Mac seems OK # I can't predict, how it will look on other platforms, therefore line is used for all, except Mac toolBarStyleSheet = "QToolBar {border: 0; border-bottom-width: 1; border-bottom-style: solid}" "" self._topToolBar.setStyleSheet(toolBarStyleSheet) area = Qt.BottomToolBarArea if self._isMenuEmbeddedToTaskBar( ) else Qt.TopToolBarArea self.addToolBar(area, self._topToolBar) if self._isMenuEmbeddedToTaskBar(): # separate menu bar self.setMenuBar(self._menuBar) else: # menubar, statusbar and editor tool bar on one line self._menuBar.setAutoFillBackground(False) menuBarStyleSheet = """ QMenuBar {background-color: transparent; color: %s} QMenuBar::item:!selected {background: transparent;} """ % self.palette().color(QPalette.WindowText).name() self._menuBar.setStyleSheet(menuBarStyleSheet) self._menuBar.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self._topToolBar.addWidget(self._menuBar) # Create status bar self._statusBar = _StatusBar(self) self._topToolBar.addWidget(self._statusBar) def _initQueuedMessageToolBar(self): from enki.core.queued_msg_tool_bar import QueuedMessageToolBar self._queuedMessageToolBar = QueuedMessageToolBar(self) area = Qt.TopToolBarArea if self._isMenuEmbeddedToTaskBar( ) else Qt.BottomToolBarArea self.addToolBar(area, self._queuedMessageToolBar) self._queuedMessageToolBar.setVisible(False) def _createMenuStructure(self): """Fill menu bar with items. The majority of items are not connected to the slots, Connections made by module, which implements menu item functionality, but, all items are in one place, because it's easier to create clear menu layout """ # create menubar menus and actions def menu(path, name, icon, visible=True): """Subfunction for create a menu in the main menu""" menuObject = core.actionManager().addMenu(path, name) if icon: menuObject.setIcon(QIcon(':/enkiicons/' + icon)) self._createdMenuPathes.append(path) if not visible: menuObject.setVisible(False) def action(path, name, icon, shortcut, tooltip, enabled, checkable=False): # pylint: disable=R0913 """Subfunction for create an action in the main menu""" if icon: # has icon actObject = core.actionManager().addAction( path, name, QIcon(':/enkiicons/' + icon), shortcut) else: actObject = core.actionManager().addAction(path, name, shortcut=shortcut) if tooltip: actObject.setStatusTip(tooltip) actObject.setEnabled(enabled) actObject.setCheckable(checkable) self._createdActions.append(actObject) def separator(menu): """Subfunction for insert separator to the menu""" core.actionManager().action(menu).menu().addSeparator() # pylint: disable=C0301 # enable long lines for menu items # Menu or action path Name Icon Shortcut Hint enabled checkable menu("mFile", "File", "") action("mFile/aOpenProject", "Open Pro&ject...", "open.png", "Shift+Ctrl+O", "Open a project", True) separator("mFile") menu("mFile/mUndoClose", "Undo Close", "recents.png") separator("mFile") action("mFile/aNew", "&New file...", "new.png", 'Ctrl+N', "New file", True) action("mFile/aOpen", "&Open...", "open.png", "Ctrl+O", "Open a file", True) menu("mFile/mSave", "&Save", "save.png") action("mFile/mSave/aCurrent", "&Save", "save.png", "Ctrl+S", "Save the current file", False) action("mFile/mSave/aSaveAs", "Save As...", "save.png", "Ctrl+Alt+S", "", False) action("mFile/mSave/aAll", "Save &All", "saveall.png", 'Shift+Ctrl+S', "Save all files", False) menu("mFile/mReload", "&Reload", "reload.png") action("mFile/mReload/aCurrent", "Reload", "reload.png", 'F5', "Reload the current file", False) action("mFile/mReload/aAll", "Reload All", "reload.png", 'Shift+F5', "Reload all files", True) menu("mFile/mClose", "&Close", "close.png") action("mFile/mClose/aCurrent", "&Close", "close.png", "Ctrl+W", "Close the current file", False) action("mFile/mClose/aAll", "Close &All", "closeall.png", 'Shift+Ctrl+W', "Close all files", False) menu("mFile/mFileSystem", "File System", "filesystem.png") action("mFile/mFileSystem/aRename", "Rename", "edit.png", '', "Rename current file", False) if platform.system() != 'Windows': action("mFile/mFileSystem/aToggleExecutable", "Make executable", "", '', "Toggle executable mode", False) separator("mFile") action("mFile/aQuit", "Quit", "quit.png", 'Ctrl+Q', "Quit", True) separator("mFile") menu("mView", "View", "") action("mView/aShowIncorrectIndentation", "Show incorrect indentation", "", "", "", False, True) action("mView/aShowAnyWhitespaces", "Show any whitespace", "", "", "", False, True) separator("mView") action("mView/aHideAll", "Hide all / Restore", "", "Shift+Esc", "Hide all widgets", True) action("mView/aOpenMainMenu", "Open main menu", "", "F10", "", True) separator("mView") menu("mEdit", "Edit", "") action("mEdit/aStripTrailingWhitespace", "Strip trailing whitespace when saving", "", "", "", True, True) separator("mEdit") menu("mEdit/mCopyPasteLines", "Copy-paste lines", "") menu("mEdit/mIndentation", "Indentation", "") separator("mEdit") action("mEdit/aEnableVimMode", "Enable Vim mode", "", "", "", False, True) menu("mNavigation", "Navigation", "") action("mNavigation/aFocusCurrentDocument", "Focus to editor", "text.png", "Ctrl+Return", "Focus current document", False) menu("mNavigation/mSearchReplace", "&Search && Replace", "search-replace-directory.png") menu("mNavigation/mBookmarks", "&Bookmarks", "bookmark.png") separator("mNavigation"), action("mNavigation/aNext", "&Next file", "next.png", "Ctrl+PgDown", "Next file", False) action("mNavigation/aPrevious", "&Previous file", "previous.png", "Ctrl+PgUp", "Previous file", False) separator("mNavigation") action("mNavigation/aGoto", "Go go line...", "goto.png", "Ctrl+G", "Go to line...", False) menu("mNavigation/mFileBrowser", "File browser", ':/enkiicons/open.png', visible=False) menu("mNavigation/mScroll", "Scroll file", '') menu("mSettings", "Settings", "") menu("mTools", "Tools", "") menu("mHelp", "Help", "") @pyqtSlot() def _openMainMenu(self): fileMenu = core.actionManager().menu('mFile') self._menuBar.setActiveAction(fileMenu.menuAction()) def menuBar(self): """Reference to menuBar """ return self._menuBar def topToolBar(self): """Top tool bar. Contains main menu, position indicator, etc """ return self._topToolBar def statusBar(self): """Return main window status bar. It is located on the top tool bar """ return self._statusBar def setWorkspace(self, workspace): """Set central widget of the main window. Normally called only by core when initializing system """ self._centralLayout.addWidget(workspace) self.setFocusProxy(workspace) def defaultTitle(self): """Default title. Contains name and version """ return "%s v.%s" % (enki.core.defines.PACKAGE_NAME, enki.core.defines.PACKAGE_VERSION) def centralLayout(self): """Layout of the central widget. Contains Workspace and search widget """ return self._centralLayout def appendMessage(self, text, timeoutMs=10000): """Append message to the queue. It will be shown as non-modal at the bottom of the window. Use such notifications, which are too long or too important for status bar but, not so important, to interrupt an user with QMessageBox """ if self._queuedMessageToolBar is None: self._initQueuedMessageToolBar() self._queuedMessageToolBar.appendMessage(text, timeoutMs) def closeEvent(self, event): """NOT A PUBLIC API Close event handler. Shows save files dialog. Cancels close, if dialog was rejected """ # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): event.ignore() return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QMainWindow.closeEvent(self, event) def onQuit(self): # saving geometry BEFORE closing widgets, because state might be changed, when docks are closed self._saveState() self._saveGeometry() # request close all documents if not core.workspace().askToCloseAll(): return core.aboutToTerminate.emit() self.hide() core.workspace().forceCloseAllDocuments() return QApplication.quit() def _saveByteArray(self, path, title, data): """Load data, show error and return None if failed""" try: with open(path, 'wb') as f: f.write(data) except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical( None, self.tr("Cannot save {}".format(title)), self.tr("Cannot create file '%s'\nError: %s" % (path, error))) return def _loadByteArray(self, path, title): """Load data, show error and return None if failed""" if os.path.exists(path): try: with open(path, 'rb') as f: return f.read() except (OSError, IOError) as ex: error = str(ex) QMessageBox.critical( None, self.tr("Cannot restore {}".format(title)), self.tr("Cannot read file '%s'\nError: %s" % (path, error))) return None def _saveState(self): """Save window state to main_window_state.bin file in the config directory """ self._saveByteArray(self._STATE_FILE, "main window state", self.saveState()) def loadState(self): """Restore window state from main_window_state.bin and config. Called by the core after all plugins had been initialized """ self._restoreGeometry() state = self._loadByteArray(self._STATE_FILE, "main window state") if state is not None: self.restoreState(state) else: # no state, first start self.showMaximized() for dock in self.findChildren(DockWidget): dock.show() def _saveGeometry(self): """Save window geometry to the config file """ self._saveByteArray(self._GEOMETRY_FILE, "main window geometry", self.saveGeometry()) def _restoreGeometry(self): """Restore window geometry to the config file """ geometry = self._loadByteArray(self._GEOMETRY_FILE, "main window geometry") if geometry is not None: self.restoreGeometry(geometry) def sizeHint(self): return QSize(900, 560) def dragEnterEvent(self, event): """QMainWindow method reimplementation. Say, that we are ready to accept dragged urls """ if event.mimeData().hasUrls(): # accept drag event.acceptProposedAction() # default handler QMainWindow.dragEnterEvent(self, event) def dropEvent(self, event): """QMainWindow method reimplementation. Open dropt files """ if event.mimeData().hasUrls(): for url in event.mimeData().urls(): localFile = url.toLocalFile() if os.path.isfile(localFile): core.workspace().openFile(localFile) elif os.path.isdir(localFile): self.directoryDropt.emit(localFile) # default handler QMainWindow.dropEvent(self, event) def addDockWidget(self, area, dock): pass # not a plugin API method """Add dock widget to previous position, if known. Otherwise add to specified area """ assert not dock in self._addedDockWidgets self._addedDockWidgets.append(dock) if not self.restoreDockWidget(dock): QMainWindow.addDockWidget(self, area, dock) """ Scroll view to make the cursor visible. Otherwise cursor can disappear from the viewport. QTimer is used because ensureCursorVisible works only after the dock has been drawn. A bad fix for #319 """ QTimer.singleShot(0, self._ensureCursorVisible) def _ensureCursorVisible(self): # When the timer fires, first check that there's still a workspace/document. if core.workspace() is not None: document = core.workspace().currentDocument() if document is not None: document.qutepart.ensureCursorVisible def removeDockWidget(self, dock): pass # not a plugin API method """Remove dock widget""" assert dock in self._addedDockWidgets self._addedDockWidgets.remove(dock) QMainWindow.removeDockWidget(self, dock) def restoreState(self, state): pass # not a plugin API method """Restore state shows widgets, which exist but shall not be installed on main window """ QMainWindow.restoreState(self, state) for dock in self.findChildren(DockWidget): if not dock in self._addedDockWidgets: dock.hide()
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error( "loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format( available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True") self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True") self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue( "path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue( "path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue( "path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str( self.regime_list.item(self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon( QIcon(get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format( self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage( "loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format( reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error( "apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect( self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data( os.path.join(self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0 / self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue( (self.speedControl.maximum() - self.speedControl.minimum()) / 2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value( ) / self.timeSliderRange * self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, ))) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der))) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get("output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get("output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) self.webView = QWebView(self) self.webView.settings().setAttribute(self.webView.settings().globalSettings().DeveloperExtrasEnabled, True) self.webView.settings().setUserStyleSheetUrl(QUrl.fromUserInput(os.path.join(app_folder, "style.css"))) self.webView.loadFinished.connect(self.jsHack) self.setCentralWidget(self.webView) # Main toolbar self.toolBar = QToolBar(self) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(Qt.LeftToolBarArea, self.toolBar) page = self.webView.page() backAction = page.action(page.Back) backAction.setShortcut("Alt+Left") self.toolBar.addAction(backAction) nextAction = page.action(page.Forward) nextAction.setShortcut("Alt+Right") self.toolBar.addAction(nextAction) reloadAction = page.action(page.Reload) reloadAction.setShortcuts(["Ctrl+R", "F5"]) self.toolBar.addAction(reloadAction) stopAction = page.action(page.Stop) stopAction.setShortcut("Esc") self.toolBar.addAction(stopAction) self.toolBar.addSeparator() style = QApplication.style() self.uploadAction = QAction(self, text="Upload", icon=style.standardIcon(style.SP_ArrowUp)) self.uploadAction.setShortcut("Alt+Up") self.uploadAction.triggered.connect(self.upload) self.toolBar.addAction(self.uploadAction) self.setGitHubSiteAction = QAction(self, text="Set Page", icon=style.standardIcon(style.SP_FileIcon)) self.setGitHubSiteAction.setShortcut("Ctrl+L") self.setGitHubSiteAction.triggered.connect(self.setGitHubSite) self.toolBar.addAction(self.setGitHubSiteAction) self.setGitDirectoryAction = QAction(self, text="Set Directory", icon=style.standardIcon(style.SP_DirIcon)) self.setGitDirectoryAction.setShortcut("Ctrl+O") self.setGitDirectoryAction.triggered.connect(self.setGitDirectory) self.toolBar.addAction(self.setGitDirectoryAction) self.webView.load(QUrl.fromUserInput(settings.value("settings/GitUrl"))) def setGitHubSite(self): url = QInputDialog.getText(self, "Githost", "Enter the URL of your project page here:") if url[1]: settings.setValue("settings/GitUrl", url[0]) self.webView.load(QUrl.fromUserInput(settings.value("settings/GitUrl"))) settings.sync() def setGitDirectory(self): gitRepo = QFileDialog.getExistingDirectory(self, "Select directory...", os.path.expanduser("~")) if not os.path.isdir(gitRepo): pass else: settings.setValue("settings/GitRepo", gitRepo) settings.sync() def upload(self): if not settings.value("settings/GitRepo"): QMessageBox.information(self, "Githost", "Please select the directory of your Git repository.") self.setGitDirectory() repo = settings.value("settings/GitRepo") if repo: fnames = QFileDialog.getOpenFileNames(self, "Select files to upload...", os.path.expanduser("~"), "All files (*)") if type(fnames) is tuple: fnames = fnames[0] if not settings.value("settings/NoPromptForGitRepo") and len(fnames) > 0: confirm = QMessageBox.question(self, "Confirm selection", "Commit files to repository?", QMessageBox.Yes | QMessageBox.YesToAll | QMessageBox.No) else: confirm = QMessageBox.Yes if confirm == QMessageBox.YesToAll: settings.setValue("settings/NoPromptForGitRepo", True) if len(fnames) > 0 and confirm in (QMessageBox.Yes, QMessageBox.YesToAll): for fname in fnames: os.system("cp %s %s" % (fname, repo)) os.system("cd %s && git add . && git commit -m \"Added %s new file%s.\"" % (repo, len(fnames), ("s" if len(fnames) > 1 else ""))) os.system("cd %s && xterm -e \"git push --all\" &" % (repo,)) def jsHack(self): self.webView.page().mainFrame().evaluateJavaScript("""String.prototype.endsWith = function(suffix) { return this.indexOf(suffix, this.length - suffix.length) !== -1; }; contents = document.getElementsByClassName("content"); links = document.getElementsByClassName("js-directory-link"); for(i=0; i<contents.length;i++) { br = document.createElement("br"); contents[i+1].appendChild(br); imgLink = document.location.href.replace("github.com", "raw.githubusercontent.com") + "/master/" + links[i].innerHTML; input = document.createElement("input"); input.setAttribute("style", "width: 100%;"); lowerLink = imgLink.toLowerCase(); if (lowerLink.endsWith(".jpg") || lowerLink.endsWith(".jpeg") || lowerLink.endsWith(".png") || lowerLink.endsWith(".gif") || lowerLink.endsWith(".mng") || lowerLink.endsWith(".apng") || lowerLink.endsWith(".webp")) { input.setAttribute("value", "[IMG]" + imgLink + "[/IMG]"); } else { input.setAttribute("value", "[URL]" + imgLink + "[/URL]"); } contents[i+1].appendChild(input); }""")
class UploadWindowUI(object): title = "DERIVA File Uploader" def __init__(self, MainWin): # Main Window MainWin.setObjectName("UploadWindow") MainWin.setWindowTitle(MainWin.tr(self.title)) MainWin.resize(1024, 768) self.centralWidget = QWidget(MainWin) self.centralWidget.setObjectName("centralWidget") MainWin.setCentralWidget(self.centralWidget) self.verticalLayout = QVBoxLayout(self.centralWidget) self.verticalLayout.setContentsMargins(11, 11, 11, 11) self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") self.horizontalLayout = QHBoxLayout() self.pathLabel = QLabel("Directory:") self.horizontalLayout.addWidget(self.pathLabel) self.pathTextBox = QLineEdit() self.pathTextBox.setReadOnly(True) self.horizontalLayout.addWidget(self.pathTextBox) self.browseButton = QPushButton("Browse", self.centralWidget) self.browseButton.clicked.connect(MainWin.on_actionBrowse_triggered) self.horizontalLayout.addWidget(self.browseButton) self.verticalLayout.addLayout(self.horizontalLayout) # Splitter for Upload list/Log self.splitter = QSplitter(Qt.Vertical) # Table View (Upload list) self.uploadList = TableWidget(self.centralWidget) self.uploadList.setObjectName("uploadList") self.uploadList.setStyleSheet( """ QTableWidget { border: 2px solid grey; border-radius: 5px; } """) self.uploadList.setEditTriggers(QAbstractItemView.NoEditTriggers) # use NoEditTriggers to disable editing self.uploadList.setAlternatingRowColors(True) self.uploadList.setSelectionBehavior(QAbstractItemView.SelectRows) self.uploadList.setSelectionMode(QAbstractItemView.NoSelection) self.uploadList.verticalHeader().setDefaultSectionSize(18) # tighten up the row size self.uploadList.horizontalHeader().setStretchLastSection(True) self.uploadList.setSortingEnabled(True) # allow sorting self.splitter.addWidget(self.uploadList) # Log Widget self.logTextBrowser = QPlainTextEditLogger(self.centralWidget) self.logTextBrowser.widget.setObjectName("logTextBrowser") self.logTextBrowser.widget.setStyleSheet( """ QPlainTextEdit { border: 2px solid grey; border-radius: 5px; background-color: lightgray; } """) self.splitter.addWidget(self.logTextBrowser.widget) # add splitter self.splitter.setSizes([400, 200]) self.verticalLayout.addWidget(self.splitter) # Actions # Browse self.actionBrowse = QAction(MainWin) self.actionBrowse.setObjectName("actionBrowse") self.actionBrowse.setText(MainWin.tr("Browse")) self.actionBrowse.setToolTip(MainWin.tr("Set the upload directory")) self.actionBrowse.setShortcut(MainWin.tr("Ctrl+B")) # Upload self.actionUpload = QAction(MainWin) self.actionUpload.setObjectName("actionUpload") self.actionUpload.setText(MainWin.tr("Upload")) self.actionUpload.setToolTip(MainWin.tr("Upload files")) self.actionUpload.setShortcut(MainWin.tr("Ctrl+L")) self.actionUpload.setEnabled(False) # Rescan self.actionRescan = QAction(MainWin) self.actionRescan.setObjectName("actionRescan") self.actionRescan.setText(MainWin.tr("Rescan")) self.actionRescan.setToolTip(MainWin.tr("Rescan the upload directory")) self.actionRescan.setShortcut(MainWin.tr("Ctrl+R")) self.actionRescan.setEnabled(False) # Cancel self.actionCancel = QAction(MainWin) self.actionCancel.setObjectName("actionCancel") self.actionCancel.setText(MainWin.tr("Cancel")) self.actionCancel.setToolTip(MainWin.tr("Cancel any upload(s) in-progress")) self.actionCancel.setShortcut(MainWin.tr("Ctrl+C")) # Options self.actionOptions = QAction(MainWin) self.actionOptions.setObjectName("actionOptions") self.actionOptions.setText(MainWin.tr("Options")) self.actionOptions.setToolTip(MainWin.tr("Configuration Options")) self.actionOptions.setShortcut(MainWin.tr("Ctrl+P")) # Login self.actionLogin = QAction(MainWin) self.actionLogin.setObjectName("actionLogin") self.actionLogin.setText(MainWin.tr("Login")) self.actionLogin.setToolTip(MainWin.tr("Login to the server")) self.actionLogin.setShortcut(MainWin.tr("Ctrl+G")) self.actionLogin.setEnabled(False) # Logout self.actionLogout = QAction(MainWin) self.actionLogout.setObjectName("actionLogout") self.actionLogout.setText(MainWin.tr("Logout")) self.actionLogout.setToolTip(MainWin.tr("Logout of the server")) self.actionLogout.setShortcut(MainWin.tr("Ctrl+O")) self.actionLogout.setEnabled(False) # Exit self.actionExit = QAction(MainWin) self.actionExit.setObjectName("actionExit") self.actionExit.setText(MainWin.tr("Exit")) self.actionExit.setToolTip(MainWin.tr("Exit the application")) self.actionExit.setShortcut(MainWin.tr("Ctrl+Z")) # Help self.actionHelp = QAction(MainWin) self.actionHelp.setObjectName("actionHelp") self.actionHelp.setText(MainWin.tr("Help")) self.actionHelp.setToolTip(MainWin.tr("Help")) self.actionHelp.setShortcut(MainWin.tr("Ctrl+H")) # Menu Bar """ self.menuBar = QMenuBar(MainWin) self.menuBar.setObjectName("menuBar") MainWin.setMenuBar(self.menuBar) self.menuBar.setStyleSheet( "QMenuBar{font-family: Arial;font-style: normal;font-size: 10pt;font-weight: bold;};") """ # Tool Bar self.mainToolBar = QToolBar(MainWin) self.mainToolBar.setObjectName("mainToolBar") self.mainToolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.mainToolBar.setContextMenuPolicy(Qt.PreventContextMenu) MainWin.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # Upload self.mainToolBar.addAction(self.actionUpload) self.actionUpload.setIcon(qApp.style().standardIcon(QStyle.SP_FileDialogToParent)) # Rescan self.mainToolBar.addAction(self.actionRescan) self.actionRescan.setIcon(qApp.style().standardIcon(QStyle.SP_BrowserReload)) # Cancel self.mainToolBar.addAction(self.actionCancel) self.actionCancel.setIcon(qApp.style().standardIcon(QStyle.SP_BrowserStop)) self.actionCancel.setEnabled(False) # Options self.mainToolBar.addAction(self.actionOptions) self.actionOptions.setIcon(qApp.style().standardIcon(QStyle.SP_FileDialogDetailedView)) # this spacer right justifies everything that comes after it spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mainToolBar.addWidget(spacer) # Login self.mainToolBar.addAction(self.actionLogin) self.actionLogin.setIcon(qApp.style().standardIcon(QStyle.SP_DialogApplyButton)) # Logout self.mainToolBar.addAction(self.actionLogout) self.actionLogout.setIcon(qApp.style().standardIcon(QStyle.SP_DialogOkButton)) # Help #self.mainToolBar.addAction(self.actionHelp) self.actionHelp.setIcon(qApp.style().standardIcon(QStyle.SP_MessageBoxQuestion)) # Exit self.mainToolBar.addAction(self.actionExit) self.actionExit.setIcon(qApp.style().standardIcon(QStyle.SP_DialogCancelButton)) # Status Bar self.statusBar = QStatusBar(MainWin) self.statusBar.setToolTip("") self.statusBar.setStatusTip("") self.statusBar.setObjectName("statusBar") MainWin.setStatusBar(self.statusBar) # configure logging self.logTextBrowser.widget.log_update_signal.connect(MainWin.updateLog) self.logTextBrowser.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logging.getLogger().addHandler(self.logTextBrowser) # finalize UI setup QMetaObject.connectSlotsByName(MainWin)
class AuthWindowUI(object): def __init__(self, MainWin): # Main Window MainWin.setObjectName("AuthWindow") MainWin.setWindowIcon(MainWin.window_icon) MainWin.setWindowTitle(MainWin.tr(MainWin.window_title)) MainWin.resize(1024, 860) self.config = MainWin.config self.centralWidget = QWidget(MainWin) self.centralWidget.setObjectName("centralWidget") MainWin.setCentralWidget(self.centralWidget) self.verticalLayout = QVBoxLayout(self.centralWidget) self.verticalLayout.setContentsMargins(11, 11, 11, 11) self.verticalLayout.setSpacing(6) self.verticalLayout.setObjectName("verticalLayout") self.tabWidget = QTabWidget(MainWin) self.tabWidget.currentChanged.connect(MainWin.onTabChanged) self.tabWidget.tabCloseRequested.connect(MainWin.onTabClosed) self.tabWidget.setTabsClosable(True) # workaround for https://bugreports.qt.io/browse/QTBUG-58267 if "darwin" in sys.platform: self.tabWidget.setDocumentMode(True) # Splitter for log self.splitter = QSplitter(Qt.Vertical) self.splitter.addWidget(self.tabWidget) # Log Widget self.logTextBrowser = QPlainTextEditLogger(self.centralWidget) self.logTextBrowser.widget.setObjectName("logTextBrowser") self.logTextBrowser.widget.setStyleSheet(""" QPlainTextEdit { border: 2px solid grey; border-radius: 5px; background-color: lightgray; } """) self.splitter.addWidget(self.logTextBrowser.widget) # add splitter self.splitter.setSizes([800, 100]) self.verticalLayout.addWidget(self.splitter) # Tool Bar self.mainToolBar = QToolBar(MainWin) self.mainToolBar.setObjectName("mainToolBar") self.mainToolBar.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) self.mainToolBar.setContextMenuPolicy(Qt.PreventContextMenu) MainWin.addToolBar(Qt.TopToolBarArea, self.mainToolBar) # Servers self.serverWidget = QWidget(MainWin) self.serverLayout = QHBoxLayout() self.serverLabel = QLabel("Server:") self.serverLayout.addWidget(self.serverLabel) self.serverComboBox = QComboBox() self.serverComboBox.setEditable(True) self.serverComboBox.setDuplicatesEnabled(False) self.serverComboBox.setMinimumContentsLength(50) self.serverComboBox.currentIndexChanged.connect( MainWin.onServerListChanged) lineEdit = self.serverComboBox.lineEdit() lineEdit.returnPressed.connect(MainWin.on_actionAdd_triggered) self.serverLayout.addWidget(self.serverComboBox) self.serverWidget.setLayout(self.serverLayout) self.mainToolBar.addWidget(self.serverWidget) # Add self.actionAdd = QAction(MainWin) self.actionAdd.setObjectName("actionAdd") self.actionAdd.setText(MainWin.tr("Add")) self.actionAdd.setToolTip(MainWin.tr("Add to server list")) self.actionAdd.setShortcut(MainWin.tr("Ctrl+A")) # Remove self.actionRemove = QAction(MainWin) self.actionRemove.setObjectName("actionRemove") self.actionRemove.setText(MainWin.tr("Remove")) self.actionRemove.setToolTip(MainWin.tr("Remove from server list")) self.actionRemove.setShortcut(MainWin.tr("Ctrl+X")) # Show Token self.actionShowToken = QAction(MainWin) self.actionShowToken.setEnabled(False) self.actionShowToken.setObjectName("actionShowToken") self.actionShowToken.setText(MainWin.tr("Show Token")) self.actionShowToken.setToolTip( MainWin.tr("Display the current authentication token")) self.actionShowToken.setShortcut(MainWin.tr("Ctrl+S")) # Login self.actionLogin = QAction(MainWin) self.actionLogin.setObjectName("actionLogin") self.actionLogin.setText(MainWin.tr("Login")) self.actionLogin.setToolTip( MainWin.tr("Login to the currently selected server")) self.actionLogin.setShortcut(MainWin.tr("Ctrl+L")) # Logout self.actionLogout = QAction(MainWin) self.actionLogout.setObjectName("actionLogout") self.actionLogout.setText(MainWin.tr("Logout")) self.actionLogout.setToolTip( MainWin.tr("Logout of the currently selected server")) self.actionLogout.setShortcut(MainWin.tr("Ctrl+O")) # Add self.mainToolBar.addAction(self.actionAdd) self.actionAdd.setIcon(qApp.style().standardIcon( QStyle.SP_FileDialogNewFolder)) # Remove self.mainToolBar.addAction(self.actionRemove) self.actionRemove.setIcon(qApp.style().standardIcon( QStyle.SP_DialogDiscardButton)) # Show Token self.mainToolBar.addAction(self.actionShowToken) self.actionShowToken.setIcon(qApp.style().standardIcon( QStyle.SP_FileDialogInfoView)) self.mainToolBar.addSeparator() # this spacer right justifies everything that comes after it spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self.mainToolBar.addWidget(spacer) # Login self.mainToolBar.addSeparator() self.mainToolBar.addAction(self.actionLogin) self.actionLogin.setIcon(qApp.style().standardIcon( QStyle.SP_DialogApplyButton)) # Logout self.mainToolBar.addSeparator() self.mainToolBar.addAction(self.actionLogout) self.actionLogout.setIcon(qApp.style().standardIcon( QStyle.SP_DialogOkButton)) # Status Bar self.statusBar = QStatusBar(MainWin) self.statusBar.setToolTip("") self.statusBar.setStatusTip("") self.statusBar.setObjectName("statusBar") MainWin.setStatusBar(self.statusBar) # configure logging self.logTextBrowser.widget.log_update_signal.connect(MainWin.updateLog) self.logTextBrowser.setFormatter( logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")) logging.getLogger().addHandler(self.logTextBrowser) logging.getLogger().setLevel(logging.INFO) # finalize UI setup QMetaObject.connectSlotsByName(MainWin)
class SettingsDialog(QDialog): def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent) # Set layout. _layout = QVBoxLayout(self) _layout.setContentsMargins(0,0,0,0) self.setLayout(_layout) # Set window title. self.setWindowTitle(tr("Settings")) # Tab widget self.tabs = QTabWidget(self) self.layout().addWidget(self.tabs) self.tabs.addTab(GeneralSettingsPanel(self), tr("&General")) self.tabs.addTab(ContentSettingsPanel(self), tr("Con&tent")) self.tabs.addTab(AdremoverSettingsPanel(self), tr("Ad &Remover")) self.tabs.addTab(DataSettingsPanel(self), tr("&Data && Privacy")) self.tabs.addTab(NetworkSettingsPanel(self), tr("N&etwork")) self.tabs.addTab(ExtensionsSettingsPanel(self), tr("E&xtensions")) # Toolbar self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.toolBar.setStyleSheet(common.blank_toolbar) self.layout().addWidget(self.toolBar) # Apply button applyButton = QPushButton(tr("&Apply"), self) applyButton.clicked.connect(self.saveSettings) self.toolBar.addWidget(applyButton) # Reload settings button closeButton = QPushButton(tr("&Close"), self) closeButton.clicked.connect(self.hide) self.toolBar.addWidget(closeButton) # Load settings self.loadSettings() def show(self): super(SettingsDialog, self).show() self.loadSettings() def url(self): return QUrl("") def icon(self): return common.complete_icon("preferences-system") # Method to load all settings. def loadSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).loadSettings() # Method to save all settings. def saveSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).saveSettings() settings.reset_extensions() settings.reload_userscripts() for window in browser.windows: try: window.reloadExtensions() except: pass try: window.applySettings() except: pass
class ClearHistoryDialog(QDialog): def __init__(self, parent=None): super(ClearHistoryDialog, self).__init__(parent) self.setWindowFlags(Qt.Dialog) self.setWindowTitle(tr("Clear Data")) closeWindowAction = QAction(self) closeWindowAction.setShortcuts(["Esc", "Ctrl+W", "Ctrl+Shift+Del"]) closeWindowAction.triggered.connect(self.close) self.addAction(closeWindowAction) self.layout = QVBoxLayout() self.setLayout(self.layout) label = QLabel(tr("What to clear:"), self) self.layout.addWidget(label) self.dataType = QComboBox(self) self.dataType.addItem(tr("History")) self.dataType.addItem(tr("Cookies")) self.dataType.addItem(tr("Memory Caches")) self.dataType.addItem(tr("Persistent Storage")) self.dataType.addItem(tr("Everything")) self.layout.addWidget(self.dataType) self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(common.blank_toolbar) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.layout.addWidget(self.toolBar) self.clearHistoryButton = QPushButton(tr("Clear"), self) self.clearHistoryButton.clicked.connect(self.clearHistory) self.toolBar.addWidget(self.clearHistoryButton) self.closeButton = QPushButton(tr("Close"), self) self.closeButton.clicked.connect(self.close) self.toolBar.addWidget(self.closeButton) def display(self): self.show() self.activateWindow() def clearHistory(self): clear_everything = (self.dataType.currentIndex() == self.dataType.count()-1) if self.dataType.currentIndex() == 0 or clear_everything: data.clearHistory() if self.dataType.currentIndex() == 1 or clear_everything: data.clearCookies() if self.dataType.currentIndex() == 2 or clear_everything: QWebSettings.globalSettings().clearMemoryCaches() if self.dataType.currentIndex() == 3 or clear_everything: QWebSettings.globalSettings().setIconDatabasePath("") QWebSettings.globalSettings().setLocalStoragePath("") QWebSettings.globalSettings().setOfflineStoragePath("") QWebSettings.globalSettings().setOfflineWebApplicationCachePath("") for subpath in ("WebpageIcons.db", "LocalStorage", "Databases",): path = os.path.abspath(os.path.join(settings.settings_folder, subpath)) if os.path.isfile(path): try: os.remove(path) except: pass elif os.path.isdir(path): if sys.platform.startswith("win"): args = ["rmdir", "/s", "/q", "\"" + path + "\""] try: os.system(" ".join(args)) except: pass else: try: subprocess.Popen(["rm", "-rf", path]) except: pass QWebSettings.globalSettings().enablePersistentStorage(settings.settings_folder)
class SimulationGui(QMainWindow): """ class for the graphical user interface """ # TODO enable closing plot docks by right-clicking their name runSimulation = pyqtSignal() stopSimulation = pyqtSignal() playbackTimeChanged = pyqtSignal() regimeFinished = pyqtSignal() finishedRegimeBatch = pyqtSignal(bool) def __init__(self): # constructor of the base class QMainWindow.__init__(self) QCoreApplication.setOrganizationName("RST") QCoreApplication.setOrganizationDomain("https://tu-dresden.de/rst") QCoreApplication.setApplicationVersion( pkg_resources.require("PyMoskito")[0].version) QCoreApplication.setApplicationName(globals()["__package__"]) # load settings self._settings = QSettings() self._read_settings() # initialize logger self._logger = logging.getLogger(self.__class__.__name__) # Create Simulation Backend self.guiProgress = None self.cmdProgress = None self.sim = SimulatorInteractor(self) self.runSimulation.connect(self.sim.run_simulation) self.stopSimulation.connect(self.sim.stop_simulation) self.sim.simulation_finalized.connect(self.new_simulation_data) self.currentDataset = None self.interpolator = None # sim setup viewer self.targetView = SimulatorView(self) self.targetView.setModel(self.sim.target_model) self.targetView.expanded.connect(self.target_view_changed) self.targetView.collapsed.connect(self.target_view_changed) # sim results viewer self.result_view = QTreeView() # the docking area allows to rearrange the user interface at runtime self.area = pg.dockarea.DockArea() # Window properties icon_size = QSize(25, 25) self.setCentralWidget(self.area) self.resize(1000, 700) self.setWindowTitle("PyMoskito") res_path = get_resource("mosquito.png") icon = QIcon(res_path) self.setWindowIcon(icon) # create docks self.propertyDock = pg.dockarea.Dock("Properties") self.animationDock = pg.dockarea.Dock("Animation") self.regimeDock = pg.dockarea.Dock("Regimes") self.dataDock = pg.dockarea.Dock("Data") self.logDock = pg.dockarea.Dock("Log") self.plotDockPlaceholder = pg.dockarea.Dock("Placeholder") # arrange docks self.area.addDock(self.animationDock, "right") self.area.addDock(self.regimeDock, "left", self.animationDock) self.area.addDock(self.propertyDock, "bottom", self.regimeDock) self.area.addDock(self.dataDock, "bottom", self.propertyDock) self.area.addDock(self.plotDockPlaceholder, "bottom", self.animationDock) self.area.addDock(self.logDock, "bottom", self.dataDock) self.non_plotting_docks = list(self.area.findAll()[1].keys()) # add widgets to the docks self.propertyDock.addWidget(self.targetView) if not vtk_available: self._logger.error("loading vtk failed with:{}".format(vtk_error_msg)) # check if there is a registered visualizer available_vis = get_registered_visualizers() self._logger.info("found visualizers: {}".format( [name for cls, name in available_vis])) if available_vis: # instantiate the first visualizer self._logger.info("loading visualizer '{}'".format(available_vis[0][1])) self.animationLayout = QVBoxLayout() if issubclass(available_vis[0][0], MplVisualizer): self.animationWidget = QWidget() self.visualizer = available_vis[0][0](self.animationWidget, self.animationLayout) self.animationDock.addWidget(self.animationWidget) elif issubclass(available_vis[0][0], VtkVisualizer): if vtk_available: # vtk window self.animationFrame = QFrame() self.vtkWidget = QVTKRenderWindowInteractor( self.animationFrame) self.animationLayout.addWidget(self.vtkWidget) self.animationFrame.setLayout(self.animationLayout) self.animationDock.addWidget(self.animationFrame) self.vtk_renderer = vtkRenderer() self.vtkWidget.GetRenderWindow().AddRenderer( self.vtk_renderer) self.visualizer = available_vis[0][0](self.vtk_renderer) self.vtkWidget.Initialize() else: self._logger.warning("visualizer depends on vtk which is " "not available on this system!") elif available_vis: raise NotImplementedError else: self.visualizer = None # regime window self.regime_list = QListWidget(self) self.regime_list.setSelectionMode(QAbstractItemView.ExtendedSelection) self.regimeDock.addWidget(self.regime_list) self.regime_list.itemDoubleClicked.connect(self.regime_dclicked) self._regimes = [] self.regime_file_name = "" self.actDeleteRegimes = QAction(self.regime_list) self.actDeleteRegimes.setText("&Delete Selected Regimes") # TODO shortcut works always, not only with focus on the regime list # self.actDeleteRegimes.setShortcutContext(Qt.WindowShortcut) self.actDeleteRegimes.setShortcut(QKeySequence(Qt.Key_Delete)) self.actDeleteRegimes.triggered.connect(self.remove_regime_items) self.actSave = QAction(self) self.actSave.setText('Save Results As') self.actSave.setIcon(QIcon(get_resource("save.png"))) self.actSave.setDisabled(True) self.actSave.setShortcut(QKeySequence.Save) self.actSave.triggered.connect(self.export_simulation_data) self.actLoadRegimes = QAction(self) self.actLoadRegimes.setText("Load Regimes from File") self.actLoadRegimes.setIcon(QIcon(get_resource("load.png"))) self.actLoadRegimes.setDisabled(False) self.actLoadRegimes.setShortcut(QKeySequence.Open) self.actLoadRegimes.triggered.connect(self.load_regime_dialog) self.actExitOnBatchCompletion = QAction(self) self.actExitOnBatchCompletion.setText("&Exit On Batch Completion") self.actExitOnBatchCompletion.setCheckable(True) self.actExitOnBatchCompletion.setChecked( self._settings.value("control/exit_on_batch_completion") == "True" ) self.actExitOnBatchCompletion.changed.connect( self.update_exit_on_batch_completion_setting) # regime management self.runningBatch = False self._current_regime_index = None self._current_regime_name = None self._regimes = [] self.regimeFinished.connect(self.run_next_regime) self.finishedRegimeBatch.connect(self.regime_batch_finished) # data window self.dataList = QListWidget(self) self.dataDock.addWidget(self.dataList) self.dataList.itemDoubleClicked.connect(self.create_plot) # actions for simulation control self.actSimulateCurrent = QAction(self) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.setShortcut(QKeySequence("F5")) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actSimulateAll = QAction(self) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.setShortcut(QKeySequence("F6")) self.actSimulateAll.setDisabled(True) self.actSimulateAll.triggered.connect(self.start_regime_execution) # actions for animation control self.actAutoPlay = QAction(self) self.actAutoPlay.setText("&Autoplay Simulation") self.actAutoPlay.setCheckable(True) self.actAutoPlay.setChecked( self._settings.value("control/autoplay_animation") == "True" ) self.actAutoPlay.changed.connect(self.update_autoplay_setting) self.actPlayPause = QAction(self) self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.setDisabled(True) self.actPlayPause.setShortcut(QKeySequence(Qt.Key_Space)) self.actPlayPause.triggered.connect(self.play_animation) self.actStop = QAction(self) self.actStop.setText("Stop") self.actStop.setIcon(QIcon(get_resource("stop.png"))) self.actStop.setDisabled(True) self.actStop.triggered.connect(self.stop_animation) self.actSlow = QAction(self) self.actSlow.setText("Slowest") self.actSlow.setIcon(QIcon(get_resource("slow.png"))) self.actSlow.setDisabled(False) self.actSlow.triggered.connect(self.set_slowest_playback_speed) self.actFast = QAction(self) self.actFast.setText("Fastest") self.actFast.setIcon(QIcon(get_resource("fast.png"))) self.actFast.setDisabled(False) self.actFast.triggered.connect(self.set_fastest_playback_speed) self.speedControl = QSlider(Qt.Horizontal, self) self.speedControl.setMaximumSize(200, 25) self.speedControl.setTickPosition(QSlider.TicksBothSides) self.speedControl.setDisabled(False) self.speedControl.setMinimum(0) self.speedControl.setMaximum(12) self.speedControl.setValue(6) self.speedControl.setTickInterval(6) self.speedControl.setSingleStep(2) self.speedControl.setPageStep(3) self.speedControl.valueChanged.connect(self.update_playback_speed) self.timeSlider = QSlider(Qt.Horizontal, self) self.timeSlider.setMinimum(0) self.timeSliderRange = 1000 self.timeSlider.setMaximum(self.timeSliderRange) self.timeSlider.setTickInterval(1) self.timeSlider.setTracking(True) self.timeSlider.setDisabled(True) self.timeSlider.valueChanged.connect(self.update_playback_time) self.playbackTime = .0 self.playbackGain = 1 self.currentStepSize = .0 self.currentEndTime = .0 self.playbackTimer = QTimer() self.playbackTimer.timeout.connect(self.increment_playback_time) self.playbackTimeChanged.connect(self.update_gui) self.playbackTimeout = 33 # in [ms] -> 30 fps self.actResetCamera = QAction(self) self.actResetCamera.setText("Reset Camera") self.actResetCamera.setIcon(QIcon(get_resource("reset_camera.png"))) self.actResetCamera.setDisabled(True) if available_vis: self.actResetCamera.setEnabled(self.visualizer.can_reset_view) self.actResetCamera.triggered.connect(self.reset_camera_clicked) # postprocessing self.actPostprocessing = QAction(self) self.actPostprocessing.setText("Launch Postprocessor") self.actPostprocessing.setIcon(QIcon(get_resource("processing.png"))) self.actPostprocessing.setDisabled(False) self.actPostprocessing.triggered.connect(self.postprocessing_clicked) self.actPostprocessing.setShortcut(QKeySequence("F7")) self.postprocessor = None # toolbar self.toolbarSim = QToolBar("Simulation") self.toolbarSim.setContextMenuPolicy(Qt.PreventContextMenu) self.toolbarSim.setMovable(False) self.toolbarSim.setIconSize(icon_size) self.addToolBar(self.toolbarSim) self.toolbarSim.addAction(self.actLoadRegimes) self.toolbarSim.addAction(self.actSave) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSimulateCurrent) self.toolbarSim.addAction(self.actSimulateAll) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPlayPause) self.toolbarSim.addAction(self.actStop) self.toolbarSim.addWidget(self.timeSlider) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actSlow) self.toolbarSim.addWidget(self.speedControl) self.toolbarSim.addAction(self.actFast) self.toolbarSim.addSeparator() self.toolbarSim.addAction(self.actPostprocessing) self.toolbarSim.addAction(self.actResetCamera) self.postprocessor = None # log dock self.logBox = QPlainTextEdit(self) self.logBox.setReadOnly(True) self.logDock.addWidget(self.logBox) # init logger for logging box self.textLogger = PlainTextLogger(logging.INFO) self.textLogger.set_target_cb(self.logBox.appendPlainText) logging.getLogger().addHandler(self.textLogger) # menu bar fileMenu = self.menuBar().addMenu("&File") fileMenu.addAction(self.actLoadRegimes) fileMenu.addAction(self.actSave) fileMenu.addAction("&Quit", self.close) editMenu = self.menuBar().addMenu("&Edit") editMenu.addAction(self.actDeleteRegimes) simMenu = self.menuBar().addMenu("&Simulation") simMenu.addAction(self.actSimulateCurrent) simMenu.addAction(self.actSimulateAll) simMenu.addAction(self.actExitOnBatchCompletion) simMenu.addAction(self.actPostprocessing) animMenu = self.menuBar().addMenu("&Animation") animMenu.addAction(self.actPlayPause) animMenu.addAction("&Increase Playback Speed", self.increment_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Plus)) animMenu.addAction("&Decrease Playback Speed", self.decrement_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_Minus)) animMenu.addAction("&Reset Playback Speed", self.reset_playback_speed, QKeySequence(Qt.CTRL + Qt.Key_0)) animMenu.addAction(self.actAutoPlay) animMenu.addAction(self.actResetCamera) helpMenu = self.menuBar().addMenu("&Help") helpMenu.addAction("&Online Documentation", self.show_online_docs) helpMenu.addAction("&About", self.show_info) # status bar self.status = QStatusBar(self) self.setStatusBar(self.status) self.statusLabel = QLabel("Ready.") self.statusBar().addPermanentWidget(self.statusLabel) self.timeLabel = QLabel("current time: 0.0") self.statusBar().addPermanentWidget(self.timeLabel) self._logger.info("Simulation GUI is up and running.") def _read_settings(self): # add default settings if none are present if not self._settings.contains("path/simulation_results"): self._settings.setValue("path/simulation_results", os.path.join(os.path.curdir, "results", "simulation")) if not self._settings.contains("path/postprocessing_results"): self._settings.setValue("path/postprocessing_results", os.path.join(os.path.curdir, "results", "postprocessing")) if not self._settings.contains("path/metaprocessing_results"): self._settings.setValue("path/metaprocessing_results", os.path.join(os.path.curdir, "results", "metaprocessing")) if not self._settings.contains("control/autoplay_animation"): self._settings.setValue("control/autoplay_animation", "False") if not self._settings.contains("control/exit_on_batch_completion"): self._settings.setValue("control/exit_on_batch_completion", "False") def _write_settings(self): """ Store the application state. """ pass @pyqtSlot() def update_autoplay_setting(self): self._settings.setValue("control/autoplay_animation", str(self.actAutoPlay.isChecked())) @pyqtSlot() def update_exit_on_batch_completion_setting(self, state=None): if state is None: state = self.actExitOnBatchCompletion.isChecked() self._settings.setValue("control/exit_on_batch_completion", str(state)) def set_visualizer(self, vis): self.visualizer = vis self.vtkWidget.Initialize() @pyqtSlot() def play_animation(self): """ play the animation """ self._logger.debug("Starting Playback") # if we are at the end, start from the beginning if self.playbackTime == self.currentEndTime: self.timeSlider.setValue(0) self.actPlayPause.setText("Pause Animation") self.actPlayPause.setIcon(QIcon(get_resource("pause.png"))) self.actPlayPause.triggered.disconnect(self.play_animation) self.actPlayPause.triggered.connect(self.pause_animation) self.playbackTimer.start(self.playbackTimeout) @pyqtSlot() def pause_animation(self): """ pause the animation """ self._logger.debug("Pausing Playback") self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) def stop_animation(self): """ Stop the animation if it is running and reset the playback time. """ self._logger.debug("Stopping Playback") if self.actPlayPause.text() == "Pause Animation": # animation is playing -> stop it self.playbackTimer.stop() self.actPlayPause.setText("Play Animation") self.actPlayPause.setIcon(QIcon(get_resource("play.png"))) self.actPlayPause.triggered.disconnect(self.pause_animation) self.actPlayPause.triggered.connect(self.play_animation) self.timeSlider.setValue(0) @pyqtSlot() def start_simulation(self): """ start the simulation and disable start button """ if self._current_regime_index is None: regime_name = "" else: regime_name = str(self.regime_list.item( self._current_regime_index).text()) self.statusLabel.setText("simulating {}".format(regime_name)) self._logger.info("Simulating: {}".format(regime_name)) self.actSimulateCurrent.setIcon(QIcon( get_resource("stop_simulation.png"))) self.actSimulateCurrent.setText("Abort &Simulation") self.actSimulateCurrent.triggered.disconnect(self.start_simulation) self.actSimulateCurrent.triggered.connect(self.stop_simulation) if not self.runningBatch: self.actSimulateAll.setDisabled(True) self.guiProgress = QProgressBar(self) self.sim.simulationProgressChanged.connect(self.guiProgress.setValue) self.statusBar().addWidget(self.guiProgress) self.runSimulation.emit() @pyqtSlot() def stop_simulation(self): self.stopSimulation.emit() def export_simulation_data(self, ok): """ Query the user for a custom name and export the current simulation results. :param ok: unused parameter from QAction.triggered() Signal """ self._save_data() def _save_data(self, file_path=None): """ Save the current simulation results. If *fie_name* is given, the result will be saved to the specified location, making automated exporting easier. Args: file_path(str): Absolute path of the target file. If `None` the use will be asked for a storage location. """ regime_name = self._regimes[self._current_regime_index]["Name"] if file_path is None: # get default path path = self._settings.value("path/simulation_results") # create canonic file name suggestion = self._simfile_name(regime_name) else: path = os.path.dirname(file_path) suggestion = os.path.basename(file_path) # check if path exists otherwise create it if not os.path.isdir(path): box = QMessageBox() box.setText("Export Folder does not exist yet.") box.setInformativeText("Do you want to create it? \n" "{}".format(os.path.abspath(path))) box.setStandardButtons(QMessageBox.Ok | QMessageBox.No) box.setDefaultButton(QMessageBox.Ok) ret = box.exec_() if ret == QMessageBox.Ok: os.makedirs(path) else: path = os.path.abspath(os.path.curdir) file_path = None # If no path was given, present the default and let the user choose if file_path is None: dialog = QFileDialog(self) dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setFileMode(QFileDialog.AnyFile) dialog.setDirectory(path) dialog.setNameFilter("PyMoskito Results (*.pmr)") dialog.selectFile(suggestion) if dialog.exec_(): file_path = dialog.selectedFiles()[0] else: self._logger.warning("Export Aborted") return -1 # ask whether this should act as new default path = os.path.abspath(os.path.dirname(file_path)) if path != self._settings.value("path/simulation_results"): box = QMessageBox() box.setText("Use this path as new default?") box.setInformativeText("{}".format(path)) box.setStandardButtons(QMessageBox.Yes | QMessageBox.No) box.setDefaultButton(QMessageBox.Yes) ret = box.exec_() if ret == QMessageBox.Yes: self._settings.setValue("path/simulation_results", path) self.currentDataset.update({"regime name": regime_name}) with open(file_path, "wb") as f: pickle.dump(self.currentDataset, f, protocol=4) self.statusLabel.setText("results saved to {}".format(file_path)) self._logger.info("results saved to {}".format(file_path)) def _simfile_name(self, regime_name): """ Create a canonical name for a simulation result file """ suggestion = (time.strftime("%Y%m%d-%H%M%S") + "_" + regime_name + ".pmr") return suggestion def load_regime_dialog(self): regime_path = os.path.join(os.curdir) dialog = QFileDialog(self) dialog.setFileMode(QFileDialog.ExistingFile) dialog.setDirectory(regime_path) dialog.setNameFilter("Simulation Regime files (*.sreg)") if dialog.exec_(): file = dialog.selectedFiles()[0] self.load_regimes_from_file(file) def load_regimes_from_file(self, file_name): """ load simulation regime from file :param file_name: """ self.regime_file_name = os.path.split(file_name)[-1][:-5] self._logger.info("loading regime file: {0}".format(self.regime_file_name)) with open(file_name.encode(), "r") as f: self._regimes += yaml.load(f) self._update_regime_list() if self._regimes: self.actSimulateAll.setDisabled(False) self._logger.info("loaded {} regimes".format(len(self._regimes))) self.statusBar().showMessage("loaded {} regimes.".format(len(self._regimes)), 1000) return def _update_regime_list(self): self.regime_list.clear() for reg in self._regimes: self._logger.debug("adding '{}' to regime list".format(reg["Name"])) self.regime_list.addItem(reg["Name"]) def remove_regime_items(self): if self.regime_list.currentRow() >= 0: # flag all selected files as invalid items = self.regime_list.selectedItems() for item in items: del self._regimes[self.regime_list.row(item)] self.regime_list.takeItem(self.regime_list.row(item)) @pyqtSlot(QListWidgetItem) def regime_dclicked(self, item): """ Apply the selected regime to the current target. """ self.apply_regime_by_name(str(item.text())) def apply_regime_by_name(self, regime_name): """ Apply the regime given by `regime_name` und update the regime index. Returns: bool: `True` if successful, `False` if errors occurred. """ # get regime idx try: idx = list(map(itemgetter("Name"), self._regimes)).index(regime_name) except ValueError as e: self._logger.error("apply_regime_by_name(): Error no regime called " "'{0}'".format(regime_name)) return False # apply return self._apply_regime_by_idx(idx) def _apply_regime_by_idx(self, index=0): """ Apply the given regime. Args: index(int): Index of the regime in the `RegimeList` . Returns: bool: `True` if successful, `False` if errors occurred. """ if index >= len(self._regimes): self._logger.error("applyRegime: index error! ({})".format(index)) return False reg_name = self._regimes[index]["Name"] self.statusBar().showMessage("regime {} applied.".format(reg_name), 1000) self._logger.info("applying regime '{}'".format(reg_name)) self._current_regime_index = index self._current_regime_name = reg_name return self.sim.set_regime(self._regimes[index]) @pyqtSlot() def start_regime_execution(self): """ Simulate all regimes in the regime list. """ self.actSimulateAll.setText("Stop Simulating &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("stop_batch.png"))) self.actSimulateAll.triggered.disconnect(self.start_regime_execution) self.actSimulateAll.triggered.connect(self.stop_regime_excecution) self.runningBatch = True self._current_regime_index = -1 self.regimeFinished.emit() def run_next_regime(self): """ Execute the next regime in the regime batch. """ # are we finished? if self._current_regime_index == len(self._regimes) - 1: self.finishedRegimeBatch.emit(True) return suc = self._apply_regime_by_idx(self._current_regime_index + 1) if not suc: self.finishedRegimeBatch.emit(False) return self.start_simulation() @pyqtSlot() def stop_regime_excecution(self): """ Stop the batch process. """ self.stopSimulation.emit() self.finishedRegimeBatch.emit(False) def regime_batch_finished(self, status): self.runningBatch = False self.actSimulateAll.setDisabled(False) self.actSave.setDisabled(True) self.actSimulateAll.setText("Simulate &All Regimes") self.actSimulateAll.setIcon(QIcon(get_resource("execute_regimes.png"))) self.actSimulateAll.triggered.disconnect(self.stop_regime_excecution) self.actSimulateAll.triggered.connect(self.start_regime_execution) if status: self.statusLabel.setText("All regimes have been simulated") self._logger.info("All Regimes have been simulated") else: self._logger.error("Batch simulation has been aborted") if self._settings.value("control/exit_on_batch_completion") == "True": self._logger.info("Shutting down SimulationGUI") self.close() @pyqtSlot(str, dict, name="new_simulation_data") def new_simulation_data(self, status, data): """ Slot to be called when the simulation interface has completed the current job and new data is available. Args: status (str): Status of the simulation, either - `finished` : Simulation has been finished successfully or - `failed` : Simulation has failed. data (dict): Dictionary, holding the simulation data. """ self._logger.info("Simulation {}".format(status)) self.statusLabel.setText("Simulation {}".format(status)) self.actSimulateCurrent.setText("&Simulate Current Regime") self.actSimulateCurrent.setIcon(QIcon(get_resource("simulate.png"))) self.actSimulateCurrent.triggered.disconnect(self.stop_simulation) self.actSimulateCurrent.triggered.connect(self.start_simulation) self.actPlayPause.setDisabled(False) self.actStop.setDisabled(False) self.actSave.setDisabled(False) self.speedControl.setDisabled(False) self.timeSlider.setDisabled(False) self.sim.simulationProgressChanged.disconnect(self.guiProgress.setValue) self.statusBar().removeWidget(self.guiProgress) self.stop_animation() self.currentDataset = data if data: self._read_results() self._update_data_list() self._update_plots() if self._settings.value("control/autoplay_animation") == "True": self.actPlayPause.trigger() if self.runningBatch: regime_name = self._regimes[self._current_regime_index]["Name"] file_name = self._simfile_name(regime_name) self._save_data(os.path.join( self._settings.value("path/simulation_results"), file_name)) self.regimeFinished.emit() else: self.actSimulateAll.setDisabled(False) def _read_results(self): state = self.currentDataset["results"]["Solver"] self.interpolator = interp1d(self.currentDataset["results"]["time"], state, axis=0, bounds_error=False, fill_value=(state[0], state[-1])) self.currentStepSize = 1.0/self.currentDataset["simulation"][ "measure rate"] self.currentEndTime = self.currentDataset["simulation"]["end time"] self.validData = True def increment_playback_speed(self): self.speedControl.setValue(self.speedControl.value() + self.speedControl.singleStep()) def decrement_playback_speed(self): self.speedControl.setValue(self.speedControl.value() - self.speedControl.singleStep()) def reset_playback_speed(self): self.speedControl.setValue((self.speedControl.maximum() - self.speedControl.minimum())/2) def set_slowest_playback_speed(self): self.speedControl.setValue(self.speedControl.minimum()) def set_fastest_playback_speed(self): self.speedControl.setValue(self.speedControl.maximum()) def update_playback_speed(self, val): """ adjust playback time to slider value :param val: """ maximum = self.speedControl.maximum() self.playbackGain = 10**(3.0 * (val - maximum / 2) / maximum) @pyqtSlot() def increment_playback_time(self): """ go one time step forward in playback """ if self.playbackTime == self.currentEndTime: self.pause_animation() return increment = self.playbackGain * self.playbackTimeout / 1000 self.playbackTime = min(self.currentEndTime, self.playbackTime + increment) pos = int(self.playbackTime / self.currentEndTime * self.timeSliderRange) self.timeSlider.blockSignals(True) self.timeSlider.setValue(pos) self.timeSlider.blockSignals(False) self.playbackTimeChanged.emit() def update_playback_time(self): """ adjust playback time to slider value """ self.playbackTime = self.timeSlider.value()/self.timeSliderRange*self.currentEndTime self.playbackTimeChanged.emit() return def update_gui(self): """ updates the graphical user interface, including: - timestamp - visualisation - time cursor in diagrams """ if not self.validData: return self.timeLabel.setText("current time: %4f" % self.playbackTime) # update time cursor in plots self._update_time_cursors() # update state of rendering if self.visualizer: state = self.interpolator(self.playbackTime) self.visualizer.update_scene(state) if isinstance(self.visualizer, MplVisualizer): pass elif isinstance(self.visualizer, VtkVisualizer): self.vtkWidget.GetRenderWindow().Render() def _update_data_list(self): self.dataList.clear() for module_name, results in self.currentDataset["results"].items(): if not isinstance(results, np.ndarray): continue if len(results.shape) == 1: self.dataList.insertItem(0, module_name) elif len(results.shape) == 2: for col in range(results.shape[1]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, )) ) elif len(results.shape) == 3: for col in range(results.shape[1]): for der in range(results.shape[2]): self.dataList.insertItem( 0, self._build_entry_name(module_name, (col, der)) ) def _build_entry_name(self, module_name, idx): """ Construct an identifier for a given entry of a module. Args: module_name (str): name of the module the entry belongs to. idx (tuple): Index of the entry. Returns: str: Identifier to use for display. """ # save the user from defining 1d entries via tuples if len(idx) == 1: m_idx = idx[0] else: m_idx = idx mod_settings = self.currentDataset["modules"] info = mod_settings.get(module_name, {}).get("output_info", None) if info: if m_idx in info: return ".".join([module_name, info[m_idx]["Name"]]) return ".".join([module_name] + [str(i) for i in idx]) def _get_index_from_suffix(self, module_name, suffix): info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) idx = next((i for i in info if info[i]["Name"] == suffix), None) return idx def _get_units(self, entry): """ Return the unit that corresponds to a given entry. If no information is available, None is returned. Args: entry (str): Name of the entry. This can either be "Model.a.b" where a and b are numbers or if information is available "Model.Signal" where signal is the name of that part. Returns: """ args = entry.split(".") module_name = args.pop(0) info = self.currentDataset["modules"].get(module_name, {}).get( "output_info", None) if info is None: return None if len(args) == 1: try: idx = int(args[0]) except ValueError: idx = next((i for i in info if info[i]["Name"] == args[0]), None) else: idx = (int(a) for a in args) return info[idx]["Unit"] def create_plot(self, item): """ Creates a plot widget based on the given item. If a plot for this item is already open no new plot is created but the existing one is raised up again. Args: item(Qt.ListItem): Item to plot. """ title = str(item.text()) if title in self.non_plotting_docks: self._logger.error("Title '{}' not allowed for a plot window since" "it would shadow on of the reserved " "names".format(title)) # check if plot has already been opened if title in self.area.findAll()[1]: self.area.docks[title].raiseDock() return # collect data data = self._get_data_by_name(title) t = self.currentDataset["results"]["time"] unit = self._get_units(title) if "." in title: name = title.split(".")[1] else: name = title # create plot widget widget = pg.PlotWidget(title=title) widget.showGrid(True, True) widget.plot(x=t, y=data) widget.getPlotItem().getAxis("bottom").setLabel(text="Time", units="s") widget.getPlotItem().getAxis("left").setLabel(text=name, units=unit) # add a time line time_line = pg.InfiniteLine(self.playbackTime, angle=90, movable=False, pen=pg.mkPen("#FF0000", width=2.0)) widget.getPlotItem().addItem(time_line) # create dock container and add it to dock area dock = pg.dockarea.Dock(title, closable=True) dock.addWidget(widget) self.area.addDock(dock, "above", self.plotDockPlaceholder) def _get_data_by_name(self, name): tmp = name.split(".") module_name = tmp[0] if len(tmp) == 1: data = np.array(self.currentDataset["results"][module_name]) elif len(tmp) == 2: try: idx = int(tmp[1]) except ValueError: idx = self._get_index_from_suffix(module_name, tmp[1]) finally: data = self.currentDataset["results"][module_name][..., idx] elif len(tmp) == 3: idx = int(tmp[1]) der = int(tmp[2]) data = self.currentDataset["results"][module_name][..., idx, der] else: raise ValueError("Format not supported") return data def _update_time_cursors(self): """ Update the time lines of all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.InfiniteLine): item.setValue(self.playbackTime) def _update_plots(self): """ Update the data in all plot windows """ for title, dock in self.area.findAll()[1].items(): if title in self.non_plotting_docks: continue if not self.dataList.findItems(dock.name(), Qt.MatchExactly): # no data for this plot -> remove it dock.close() continue for widget in dock.widgets: for item in widget.getPlotItem().items: if isinstance(item, pg.PlotDataItem): x_data = self.currentDataset["results"]["time"] y_data = self._get_data_by_name(dock.name()) item.setData(x=x_data, y=y_data) @pyqtSlot(QModelIndex) def target_view_changed(self, index): self.targetView.resizeColumnToContents(0) def postprocessing_clicked(self): """ starts the post- and metaprocessing application """ self._logger.info("launching postprocessor") self.statusBar().showMessage("launching postprocessor", 1000) if self.postprocessor is None: self.postprocessor = PostProcessor() self.postprocessor.show() def reset_camera_clicked(self): """ reset camera in vtk window """ self.visualizer.reset_camera() self.vtkWidget.GetRenderWindow().Render() def show_info(self): icon_lic = open(get_resource("license.txt"), "r").read() text = "This application was build using PyMoskito ver. {} .<br />" \ "PyMoskito is free software distributed under GPLv3. <br />" \ "It is developed by members of the " \ "<a href=\'https://tu-dresden.de/ing/elektrotechnik/rst'>" \ "Institute of Control Theory</a>" \ " at the <a href=\'https://tu-dresden.de'>" \ "Dresden University of Technology</a>. <br />" \ "".format(pkg_resources.require("PyMoskito")[0].version) \ + "<br />" + icon_lic box = QMessageBox.about(self, "PyMoskito", text) def show_online_docs(self): webbrowser.open("https://pymoskito.readthedocs.org") def closeEvent(self, QCloseEvent): self._logger.info("Close Event received, shutting down.") logging.getLogger().removeHandler(self.textLogger) super().closeEvent(QCloseEvent)
class SettingsDialog(QDialog): def __init__(self, parent=None): super(SettingsDialog, self).__init__(parent) # Set layout. _layout = QVBoxLayout(self) _layout.setContentsMargins(0, 0, 0, 0) self.setLayout(_layout) # Set window title. self.setWindowTitle(tr("Settings")) # Tab widget self.tabs = QTabWidget(self) self.layout().addWidget(self.tabs) self.tabs.addTab(GeneralSettingsPanel(self), tr("&General")) self.tabs.addTab(ContentSettingsPanel(self), tr("Con&tent")) self.tabs.addTab(AdremoverSettingsPanel(self), tr("Ad &Remover")) self.tabs.addTab(DataSettingsPanel(self), tr("&Data && Privacy")) self.tabs.addTab(NetworkSettingsPanel(self), tr("N&etwork")) self.tabs.addTab(ExtensionsSettingsPanel(self), tr("E&xtensions")) # Toolbar self.toolBar = QToolBar(self) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.toolBar.setStyleSheet(common.blank_toolbar) self.layout().addWidget(self.toolBar) # Apply button applyButton = QPushButton(tr("&Apply"), self) applyButton.clicked.connect(self.saveSettings) self.toolBar.addWidget(applyButton) # Reload settings button closeButton = QPushButton(tr("&Close"), self) closeButton.clicked.connect(self.hide) self.toolBar.addWidget(closeButton) # Load settings self.loadSettings() def show(self): super(SettingsDialog, self).show() self.loadSettings() def url(self): return QUrl("") def icon(self): return common.complete_icon("preferences-system") # Method to load all settings. def loadSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).loadSettings() # Method to save all settings. def saveSettings(self): for index in range(0, self.tabs.count()): self.tabs.widget(index).saveSettings() settings.reset_extensions() settings.reload_userscripts() for window in browser.windows: try: window.reloadExtensions() except: pass try: window.applySettings() except: pass
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__() self.setWindowTitle("RainbowBB") self.setParent(parent) self.initUI() def initUI(self): self.setStyleSheet("QCheckBox { background: palette(window); border-radius: 4px; padding: 2px; margin-right: 2px; }") self.toolBar = QToolBar(self) self.toolBar.setStyleSheet(stylesheet % (create_gradient("pastel"),)) self.toolBar.setMovable(False) self.toolBar.setContextMenuPolicy(Qt.CustomContextMenu) self.addToolBar(self.toolBar) self.reverseBox = QCheckBox("&Reverse", self) self.reverseBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.reverseBox) self.byWordBox = QCheckBox("By &word", self) self.toolBar.addWidget(self.byWordBox) self.bounceBox = QCheckBox("&Bounce", self) self.bounceBox.clicked.connect(lambda: self.updateGradient()) self.toolBar.addWidget(self.bounceBox) self.sizeList = QComboBox(self) self.sizeList.addItem("None") for num in range(1, 8): self.sizeList.addItem(str(num)) self.toolBar.addWidget(self.sizeList) self.cycleList = QComboBox(self) self.toolBar.addWidget(self.cycleList) self.cycleList.currentIndexChanged.connect(self.updateGradient) self.loadCycles() self.convertButton = QPushButton("&Convert", self) self.convertButton.clicked.connect(self.convert) self.toolBar.addWidget(self.convertButton) self.reloadButton = QPushButton("Reload", self) self.reloadButton.setShortcut("Alt+Shift+R") self.reloadButton.clicked.connect(self.loadCycles) self.toolBar.addWidget(self.reloadButton) self.inputDock = QDockWidget("Input", self) self.inputDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.inputDock.setContextMenuPolicy(Qt.CustomContextMenu) self.addDockWidget(Qt.LeftDockWidgetArea, self.inputDock) self.inputField = QTextEdit(self) self.inputField.setAcceptRichText(False) self.inputDock.setWidget(self.inputField) self.outputField = QTextEdit(self) self.outputField.setReadOnly(True) self.setCentralWidget(self.outputField) def updateGradient(self, index=None): if not index: index = self.cycleList.currentIndex() self.toolBar.setStyleSheet(stylesheet % (create_gradient(self.cycleList.itemText(index), self.reverseBox.isChecked(), self.bounceBox.isChecked()),)) def loadCycles(self): rainbowbb.load_cycles() self.cycleList.clear() self.cycleList.addItem("pastel") for cycle in sorted(list(rainbowbb.cycles.keys())): if cycle != "pastel": self.cycleList.addItem(cycle) def show(self): self.setVisible(True) self.inputField.setFocus() def convert(self): self.outputField.setPlainText(rainbowbb.size(rainbowbb.colorize(self.inputField.toPlainText(), self.cycleList.currentText(), self.reverseBox.isChecked(), not self.byWordBox.isChecked(), self.bounceBox.isChecked()), self.sizeList.currentText() if self.sizeList.currentText() != "None" else None))
class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setStyleSheet( """ color: white; background-color: #2c2c2c; selection-background-color: #3b5784 """) path = os.path.abspath(__file__) self.HOME = os.path.dirname(path) + '/' self.setWindowIcon(QIcon(self.HOME + 'images/crosscobra.png')) # change to Home Path os.chdir(Path.home()) self.fileBrowser = None self.initUI() self.centerOnScreen() helpAction = QAction(self) helpAction.setShortcut('F1') helpAction.triggered.connect(self.help) self.addAction(helpAction) def initUI(self): self.setGeometry(300, 300, 1200, 600) self.setWindowTitle('CrossCobra - Python IDE') # splitters splitter1 = QSplitter(Qt.Vertical) splitter2 = QSplitter(Qt.Horizontal) # widgets self.notebook = TabWidget(self) self.codeView = CodeView(self, self.notebook) self.notebook.newTab(codeView=self.codeView) self.textPad = self.notebook.textPad self.fileBrowser = FileBrowser(self, self.textPad, self.notebook, self.codeView) self.textPad.fileBrowser = self.fileBrowser # add widgets to splitters splitter1.addWidget(self.fileBrowser) splitter1.addWidget(self.codeView) w = splitter1.width() splitter1.setSizes([w//2, w//2]) splitter2.addWidget(splitter1) splitter2.addWidget(self.notebook) hbox = QHBoxLayout() hbox.addWidget(splitter2) splitter1.setStretchFactor(1, 1) splitter2.setStretchFactor(1, 10) self.setCentralWidget(splitter2) # actions newAction = QAction(QIcon(self.HOME + 'images/new.png'), 'New', self) newAction.setShortcut('Ctrl+N') newAction.triggered.connect(self.new) openAction = QAction(QIcon(self.HOME + 'images/open.png'), 'Open', self) openAction.setShortcut('Ctrl+O') openAction.triggered.connect(self.open) saveAction = QAction(QIcon(self.HOME + 'images/save.png'), 'Save', self) saveAction.setShortcut('Ctrl+S') saveAction.triggered.connect(self.save) saveAsAction = QAction(QIcon(self.HOME + 'images/saveAs.png'), 'Save As', self) saveAsAction.setShortcut('Ctrl+Shift+S') saveAsAction.triggered.connect(self.saveAs) printAction = QAction(QIcon(self.HOME + 'images/print.png'), 'Print', self) printAction.setShortcut('Ctrl+P') printAction.triggered.connect(self.onPrint) undoAction = QAction(QIcon(self.HOME + 'images/undo.png'), 'Undo', self) undoAction.setShortcut('Ctrl+Z') undoAction.triggered.connect(self.undo) redoAction = QAction(QIcon(self.HOME + 'images/redo.png'), 'Redo', self) redoAction.setShortcut('Ctrl+Shift+Z') redoAction.triggered.connect(self.redo) zoomInAction = QAction(QIcon(self.HOME + 'images/zoomIn.png'), 'ZoomIn', self) zoomInAction.setShortcut('Ctrl++') zoomInAction.triggered.connect(self.zoomIn) zoomOutAction = QAction(QIcon(self.HOME + 'images/zoomOut.png'), 'ZoomOut', self) zoomOutAction.setShortcut('Ctrl+-') zoomOutAction.triggered.connect(self.zoomOut) settingsAction = QAction(QIcon(self.HOME + 'images/settings.png'), 'Settings', self) settingsAction.setShortcut('F9') settingsAction.triggered.connect(self.showSettings) interpreterAction = QAction(QIcon(self.HOME + 'images/interpreter.png'), 'Start Python Interpreter', self) interpreterAction.setShortcut('F10') interpreterAction.triggered.connect(self.interpreter) terminalAction = QAction(QIcon(self.HOME + 'images/terminal.png'), 'Start Terminal', self) terminalAction.setShortcut('F11') terminalAction.triggered.connect(self.terminal) runAction = QAction(QIcon(self.HOME + 'images/run.png'), 'Run File', self) runAction.setShortcut('F12') runAction.triggered.connect(self.run) searchShortcut = QShortcut(self) searchShortcut.setKey('Ctrl+F') searchShortcut.activated.connect(self.onSearch) spacer = QWidget() spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # make toolbar self.toolbar = QToolBar() self.toolbar.setStyleSheet(''' QToolButton::hover { background-color: darkgreen;} ''') self.toolbar.setContextMenuPolicy(Qt.PreventContextMenu) self.addToolBar(Qt.RightToolBarArea, self.toolbar) self.toolbar.addSeparator() self.toolbar.addAction(newAction) self.toolbar.addSeparator() self.toolbar.addAction(openAction) self.toolbar.addSeparator() self.toolbar.addAction(saveAction) self.toolbar.addSeparator() self.toolbar.addAction(saveAsAction) self.toolbar.addSeparator() self.toolbar.addAction(printAction) self.toolbar.addSeparator() self.toolbar.addAction(undoAction) self.toolbar.addSeparator() self.toolbar.addAction(redoAction) self.toolbar.addSeparator() self.toolbar.addAction(zoomInAction) self.toolbar.addSeparator() self.toolbar.addAction(zoomOutAction) self.toolbar.addSeparator() self.toolbar.addAction(settingsAction) self.toolbar.addSeparator() self.toolbar.addWidget(spacer) self.toolbar.addAction(interpreterAction) self.toolbar.addSeparator() self.toolbar.addAction(terminalAction) self.toolbar.addSeparator() self.toolbar.addAction(runAction) # make statusbar self.statusBar = QStatusBar() self.searchEdit = QLineEdit() spacer2 = QWidget() self.searchEdit.setStyleSheet( ''' background-color: white; color: black; ''') self.searchEdit.returnPressed.connect(self.onSearch) self.searchButton = QPushButton(QIcon(self.HOME + 'images/search.png'), 'Search', self) self.searchButton.setStyleSheet( ''' QPushButton::hover { background-color: darkgreen;} ''') self.searchButton.clicked.connect(self.onSearch) self.statusBar.addPermanentWidget(spacer2) self.statusBar.addPermanentWidget(self.searchEdit) self.statusBar.addPermanentWidget(self.searchButton) self.setStatusBar(self.statusBar) # show all self.textPad.setFocus() self.show() def new(self): editor = CodeEditor(parent=self) editor.filename = None self.notebook.newTab(editor) x = self.notebook.count() index = x - 1 self.notebook.setCurrentIndex(index) self.textPad = editor self.notebook.textPad = editor self.mainWindow = self.textPad.mainWindow def open(self): dialog = QFileDialog(self) dialog.setViewMode(QFileDialog.List) dialog.setDirectory(os.getcwd()) filename = dialog.getOpenFileName(self, "Save") if filename[0]: filePath = filename[0] try: with open(filePath, 'r') as f: text = f.read() editor = CodeEditor(self) editor.setText(text) editor.filename = filePath self.notebook.newTab(editor) x = self.notebook.count() # number of tabs index = x - 1 self.notebook.setCurrentIndex(index) tabName = os.path.basename(editor.filename) self.notebook.setTabText(x, tabName) self.textPad = editor except Exception as e: self.statusBar.showMessage(str(e), 3000) def save(self): filename = self.textPad.filename index = self.notebook.currentIndex() tabText = self.notebook.tabText(index) if not filename: self.saveAs() else: text = self.textPad.text() try: with open(filename, 'w') as file: file.write(text) self.statusBar.showMessage(filename + " saved", 3000) # remove '*' in tabText fname = os.path.basename(filename) self.notebook.setTabText(index, fname) except Exception as e: self.statusBar.showMessage(str(e), 3000) self.saveAs() def saveAs(self): ## to do .... dialog = QFileDialog(self) dialog.setViewMode(QFileDialog.List) dialog.setDirectory(os.getcwd()) filename = dialog.getSaveFileName(self, "Save") if filename[0]: fullpath = filename[0] text = self.textPad.text() try: with open(fullpath, 'w') as file: file.write(text) self.statusBar.showMessage(fullpath + " saved", 3000) # update all widgets self.textPad.filename = fullpath self.refresh(self.textPad) self.fileBrowser.refresh() fname = os.path.basename(fullpath) index = self.notebook.currentIndex() self.notebook.setTabText(index, fname) except Exception as e: self.statusBar.showMessage(str(e), 3000) else: self.statusBar.showMessage('File not saved !', 3000) def onPrint(self): doc = QsciPrinter() dialog = QPrintDialog(doc, self) dialog.setWindowTitle('Print') if (dialog.exec_() == QDialog.Accepted): self.textPad.setPythonPrintStyle() try: doc.printRange(self.textPad) except Exception as e: print(str(e)) else: return self.textPad.setPythonStyle() def undo(self): self.textPad.undo() def redo(self): self.textPad.redo() def zoomIn(self): self.textPad.zoomIn() def zoomOut(self): self.textPad.zoomOut() def showSettings(self): dialog = SettingsDialog(self, self.textPad) dialog.setModal(False) dialog.exec_() def interpreter(self): c = Configuration() system = c.getSystem() command = c.getInterpreter(system) thread = RunThread(command) thread.start() def terminal(self): c = Configuration() system = c.getSystem() command = c.getTerminal(system) thread = RunThread(command) thread.start() def run(self): self.save() c = Configuration() system = c.getSystem() command = c.getRun(system).format(self.textPad.filename) if not self.textPad.filename: self.statusBar.showMessage("can't run without filename !", 3000) return thread = RunThread(command) thread.start() def onSearch(self): text = self.searchEdit.text() if text == '': self.statusBar.showMessage("can't start search without word", 3000) return else: x = self.textPad.findFirst(text, False, True, False, True, True) # case sensitive if x == False: l = len(self.searchEdit.text()) self.searchEdit.setSelection(0, l) self.searchEdit.setFocus() self.statusBar.showMessage('<' + text + '> not found', 3000) def refresh(self, textPad=None): if not textPad: return self.textPad = textPad if not self.textPad.filename: self.setWindowTitle('CrossCobra - Python IDE') return dir = os.path.dirname(self.textPad.filename) try: os.chdir(dir) self.setWindowTitle(self.textPad.filename) except Exception as e: self.statusBar.showMessage(str(e), 3000) self.fileBrowser.refresh(dir) self.codeView.refresh() def centerOnScreen(self): res = QDesktopWidget().screenGeometry() self.move((res.width() // 2) - (self.frameSize().width() // 2), (res.height() // 2) - (self.frameSize().height() // 2)) def help(self): helpdialog = HelpDialog(self) helpdialog.exec_()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(QMainWindow, self).__init__(parent) # Initialize UI self.setWindowTitle('Durumari') self.resize(800, 600) self.status_bar = self.statusBar() self.setStatusBar(self.status_bar) self.central_widget = QWidget(self) self.setCentralWidget(self.central_widget) self.layout = QHBoxLayout(self.central_widget) # Editor self.edit = ScriptEdit() self.layout.addWidget(self.edit) ''' self.central_tab = QTabWidget() self.central_tab.addTab(ScriptEdit(), 'File 1') self.layout.addWidget(self.central_tab) ''' # Scene Tree self.scn_tree = SceneTree(self) self.scn_tree.setMaximumSize(10000, 10000) self.addDockWidget(Qt.LeftDockWidgetArea, self.scn_tree) # Tool Bar self.tool_bar = QToolBar() self.tool_bar.setContextMenuPolicy(Qt.PreventContextMenu) self.addToolBar(Qt.LeftToolBarArea, self.tool_bar) self.act_toggle_tree = set_action('Toggle Tree', QIcon('toggle_tree.png'), 'Ctrl+T') self.tool_bar.addAction(self.act_toggle_tree) self.tool_bar.setIconSize(QSize(24, 24)) self.addToolBarBreak() # Menu Bar self.main_menu = self.menuBar() # <-*- File Menu self.file_menu = self.main_menu.addMenu('&File') self.act_new = set_action('&New ..', shortcut='Ctrl+N') self.act_open = set_action('&Open', shortcut='Ctrl+O') self.act_save = set_action('&Save', shortcut='Ctrl+S') self.file_menu.addAction(self.act_new) self.file_menu.addAction(self.act_open) self.file_menu.addAction(self.act_save) self.file_menu.addSeparator() # File Menu -*-> # <-*- Text Menu self.text_menu = self.main_menu.addMenu('&Text') self.act_font = set_action('Font', shortcut='Ctrl+Alt+F') self.text_menu.addAction(self.act_font) self.text_menu.addSeparator()