class appWindow(QMainWindow): """ Application entry point, subclasses QMainWindow and implements the main widget, sets necessary window behaviour etc. """ def __init__(self, parent=None): super(appWindow, self).__init__(parent) #create the menu bar self.createMenuBar() self.mdi = QMdiArea(self) #create area for files to be displayed self.mdi.setObjectName('mdi area') #create toolbar and add the toolbar plus mdi to layout self.createToolbar() #set flags so that window doesnt look weird self.mdi.setOption(QMdiArea.DontMaximizeSubWindowOnActivation, True) self.mdi.setTabsClosable(True) self.mdi.setTabsMovable(True) self.mdi.setDocumentMode(False) #declare main window layout self.setCentralWidget(self.mdi) # self.resize(1280, 720) #set collapse dim self.mdi.subWindowActivated.connect(self.tabSwitched) self.readSettings() def createMenuBar(self): # Fetches a reference to the menu bar in the main window, and adds actions to it. titleMenu = self.menuBar() #fetch reference to current menu bar self.menuFile = titleMenu.addMenu('File') #File Menu newAction = self.menuFile.addAction("New", self.newProject) openAction = self.menuFile.addAction("Open", self.openProject) saveAction = self.menuFile.addAction("Save", self.saveProject) newAction.setShortcut(QKeySequence.New) openAction.setShortcut(QKeySequence.Open) saveAction.setShortcut(QKeySequence.Save) self.menuEdit = titleMenu.addMenu('Edit') undoAction = self.undo = self.menuEdit.addAction( "Undo", lambda x=self: x.activeScene.painter.undoAction.trigger()) redoAction = self.redo = self.menuEdit.addAction( "Redo", lambda x=self: x.activeScene.painter.redoAction.trigger()) undoAction.setShortcut(QKeySequence.Undo) redoAction.setShortcut(QKeySequence.Redo) self.menuEdit.addAction( "Show Undo Stack", lambda x=self: x.activeScene.painter.createUndoView(self)) self.menuEdit.addSeparator() self.menuEdit.addAction("Add new symbols", self.addSymbolWindow) self.menuGenerate = titleMenu.addMenu('Generate') #Generate menu imageAction = self.menuGenerate.addAction("Image", self.saveImage) reportAction = self.menuGenerate.addAction("Report", self.generateReport) imageAction.setShortcut(QKeySequence("Ctrl+P")) reportAction.setShortcut(QKeySequence("Ctrl+R")) def createToolbar(self): #place holder for toolbar with fixed width, layout may change self.toolbar = toolbar(self) self.toolbar.setObjectName("Toolbar") # self.addToolBar(Qt.LeftToolBarArea, self.toolbar) self.addDockWidget(Qt.LeftDockWidgetArea, self.toolbar) self.toolbar.toolbuttonClicked.connect(self.toolButtonClicked) self.toolbar.populateToolbar() def toolButtonClicked(self, object): # To add the corresponding symbol for the clicked button to active scene. if self.mdi.currentSubWindow(): currentDiagram = self.mdi.currentSubWindow().tabber.currentWidget( ).painter if currentDiagram: graphic = getattr(shapes, object['object'])(*map( lambda x: int(x) if x.isdigit() else x, object['args'])) graphic.setPos(50, 50) currentDiagram.addItemPlus(graphic) def addSymbolWindow(self): # Opens the add symbol window when requested from utils.custom import ShapeDialog ShapeDialog(self).exec() def newProject(self): #call to create a new file inside mdi area project = FileWindow(self.mdi) project.setObjectName("New Project") self.mdi.addSubWindow(project) if not project.tabList: # important when unpickling a file instead project.newDiagram() #create a new tab in the new file project.fileCloseEvent.connect( self.fileClosed) #closed file signal to switch to sub window view if self.count > 1: #switch to tab view if needed self.mdi.setViewMode(QMdiArea.TabbedView) project.show() def openProject(self): #show the open file dialog to open a saved file, then unpickle it. name = QFileDialog.getOpenFileNames(self, 'Open File(s)', '', 'Process Flow Diagram (*pfd)') if name: for files in name[0]: with open(files, 'r') as file: projectData = load(file) project = FileWindow(self.mdi) self.mdi.addSubWindow(project) #create blank window and set its state project.__setstate__(projectData) project.resizeHandler() project.fileCloseEvent.connect(self.fileClosed) project.show() if self.count > 1: # self.tabSpace.setVisible(True) self.mdi.setViewMode(QMdiArea.TabbedView) def saveProject(self): #serialize all files in mdi area for j, i in enumerate(self.activeFiles ): #get list of all windows with atleast one tab if i.tabCount: name = QFileDialog.getSaveFileName( self, 'Save File', f'New Diagram {j}', 'Process Flow Diagram (*.pfd)') i.saveProject(name) else: return False return True def saveImage(self): #place holder for future implementaion pass def generateReport(self): #place holder for future implementaion pass def tabSwitched(self, window): #handle window switched edge case if window and window.tabCount: window.resizeHandler() def resizeEvent(self, event): #overload resize to also handle resize on file windows inside for i in self.mdi.subWindowList(): i.resizeHandler() self.toolbar.resize() super(appWindow, self).resizeEvent(event) def closeEvent(self, event): #save alert on window close if len(self.activeFiles) and not dialogs.saveEvent(self): event.ignore() else: event.accept() self.writeSettings() def fileClosed(self, index): #checks if the file tab menu needs to be removed if self.count <= 2: self.mdi.setViewMode(QMdiArea.SubWindowView) def writeSettings(self): # write window state on window close settings.beginGroup("MainWindow") settings.setValue("maximized", self.isMaximized()) if not self.isMaximized(): settings.setValue("size", self.size()) settings.setValue("pos", self.pos()) settings.endGroup() def readSettings(self): # read window state when app launches settings.beginGroup("MainWindow") self.resize(settings.value("size", QSize(1280, 720))) self.move(settings.value("pos", QPoint(320, 124))) if settings.value("maximized", False, type=bool): self.showMaximized() settings.endGroup() #useful one liner properties for getting data @property def activeFiles(self): return [i for i in self.mdi.subWindowList() if i.tabCount] @property def count(self): return len(self.mdi.subWindowList()) @property def activeScene(self): return self.mdi.currentSubWindow().tabber.currentWidget() #Key input handler def keyPressEvent(self, event): #overload key press event for custom keyboard shortcuts if event.modifiers() & Qt.ControlModifier: if event.key() == Qt.Key_A: #todo implement selectAll for item in self.mdi.activeSubWindow().tabber.currentWidget( ).items: item.setSelected(True) #todo copy, paste, undo redo else: return event.accept() elif event.key() == Qt.Key_Q: if self.mdi.activeSubWindow() and self.mdi.activeSubWindow( ).tabber.currentWidget(): for item in self.mdi.activeSubWindow().tabber.currentWidget( ).painter.selectedItems(): item.rotation -= 1 elif event.key() == Qt.Key_E: if self.mdi.activeSubWindow() and self.mdi.activeSubWindow( ).tabber.currentWidget(): for item in self.mdi.activeSubWindow().tabber.currentWidget( ).painter.selectedItems(): item.rotation += 1