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
class EditorMainWindow(object): def __init__(self): self.seq = 0 self.widget = loadUi(MAIN_UI_PATH) self.init_mdi() self.init_actions() self.init_instance() self.widget.closeEvent = self.close_handler self.widget.showMaximized() def get_seq(self): self.seq += 1 return self.seq def init_mdi(self): self.mdi = QMdiArea(self.widget) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi.setTabsMovable(True) self.mdi.setTabsClosable(True) self.mdi.setTabShape(QTabWidget.Rounded) self.widget.setCentralWidget(self.mdi) def init_actions(self): self.widget.actionNew.triggered.connect(self.action_new_handler) self.widget.actionOpen.triggered.connect(self.action_open_handler) self.widget.actionSave.triggered.connect(self.action_save_handler) self.widget.actionSave_As.triggered.connect(self.action_save_as_handler) self.widget.actionClose.triggered.connect(self.action_close_handler) self.widget.actionClose_All.triggered.connect(self.action_close_all_handler) self.widget.actionExport.triggered.connect(self.action_export_handler) self.widget.actionStates.triggered.connect(self.action_states_handler) self.widget.actionEvents.triggered.connect(self.action_events_handler) def close_handler(self, ev): l = self.mdi.subWindowList() if len(l) != 0: self.mdi.closeAllSubWindows() ev.ignore() def init_instance(self): self.instances = [] def remove_instance(self, ins): self.instances.remove(ins) def find_non_existing_name(self): while True: tmp_path = os.path.join( config.src_path, "NewFsm" + str(self.get_seq()) + FSM_FILE_EXT) if not os.path.exists(tmp_path): return tmp_path def action_new_handler(self): tmp_path = self.find_non_existing_name() m = FsmModel() m.default_init() vm = InstanceVM(m, self, tmp_path) vm.set_modified(True) def file_already_open(self, pth): for i in self.instances: pth = os.path.abspath(pth) if pth == i.file_path: return i return None def action_open_handler(self): p = QFileDialog() p.setViewMode(QFileDialog.List) p.setFileMode(QFileDialog.ExistingFiles) p.setDirectory(config.src_path) p.exec() paths = p.selectedFiles() for pth in paths: i = self.file_already_open(pth) if i: self.mdi.setActiveSubWindow(i.sub_window) else: m = FsmModel.load_file(pth) vm = InstanceVM(m, self, pth) def action_save_handler(self): w = self.mdi.activeSubWindow() if w is None: return model = w.instance.model model.dump_file(w.instance.file_path) w.instance.set_modified(False) def action_save_as_handler(self): w = self.mdi.activeSubWindow() if w is None: return p = QFileDialog() p.setViewMode(QFileDialog.List) p.setDirectory(config.src_path) p.exec() paths = p.selectedFiles() if len(paths) == 0: return w.instance.file_path = os.path.abspath(paths[0]) w.instance.update_title() model = w.instance.model model.dump_file(w.instance.file_path) w.instance.set_modified(False) def action_close_handler(self): w = self.mdi.activeSubWindow() if w is None: return w.close() def action_close_all_handler(self): self.mdi.closeAllSubWindows() def action_export_handler(self): fsm_files = os.listdir(config.src_path) for fn in fsm_files: full_name = os.path.join(config.src_path, fn) b, e = os.path.splitext(full_name) if e == FSM_FILE_EXT: f = open(full_name, "rb") basename, e = os.path.splitext(fn) model_object = pickle.load(f) f.close() exporter = FsmModelPythonExporter(model_object) exporter.export(os.path.join(config.export_path, basename + "_fsm.py")) def action_states_handler(self): cur = self.get_current_instance() if cur is None: return model = cur.model w = loadUi(STATE_LIST_DIALOG_PATH) list_vm = MultiColumnListModel( model.state, LIST_DIALOG_COLUMNS, STATE_DIALOG_HEADERS) add_item = functools.partial(model.add_item, StateItem, "state") remove_item = functools.partial(model.remove_item, "state") dialog = StateListPanelVM( list_vm, model.state, add_item, remove_item, w) dialog.run() cur.table_vm.refresh() def action_events_handler(self): cur = self.get_current_instance() if cur is None: return model = cur.model w = loadUi(EVENT_DIALOG_PATH) list_vm = MultiColumnListModel( model.event, LIST_DIALOG_COLUMNS, EVENT_DIALOG_HEADERS) add_item = functools.partial(model.add_item, EventItem, "event") remove_item = functools.partial(model.remove_item, "event") dialog = ListEditPanelVM( list_vm, model.event, add_item, remove_item, w) dialog.run() cur.table_vm.refresh() def get_current_instance(self): current = self.mdi.activeSubWindow() if not current: return None for i in self.instances: if i.sub_window == current: return i assert(False) # there is an active sub window but there's no matching instance
class DemoMdi(QMainWindow): def __init__(self, parent=None): super(DemoMdi, self).__init__(parent) # 设置窗口标题 self.setWindowTitle( 'MDI with a dockWidget tree and a tab-view mdiArea') # 设置窗口大小 self.resize(800, 640) self.initUi() self.mytimer = QTimer(self) self.mytimer.start(1000) self.mytimer.timeout.connect(self.timerCallback) def initUi(self): self.initMenuBar() self.initToolBar() self.initDockTree() self.initStatusBar() self.mdiArea = QMdiArea(self) self.setCentralWidget(self.mdiArea) # set as tabbedView by default self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setTabShape(QTabWidget.Triangular) self.mdiArea.setTabsClosable(True) self.mdiArea.setTabsMovable(True) # index of document self.newDocIndex = 1 def initDockTree(self): self.dockWind = QDockWidget(self) self.dockWind.setWindowTitle('QProfile Explorer') self.initTree() self.dockWind.setWidget(self.tree) self.dockWind.setFloating(False) # set floating = false self.addDockWidget(Qt.LeftDockWidgetArea, self.dockWind) # set the position at left side # remove all features of DockWidget like Closable, Moveable, Floatable, VerticalTitle etc. self.dockWind.setFeatures(QDockWidget.NoDockWidgetFeatures) def initTree(self): self.tree = QTreeWidget() self.tree.setColumnCount(1) #设置列数 #self.tree.setHeaderLabels(['QProfiler items']) #设置树形控件头部的标题 self.tree.setIndentation(20) # 项目的缩进 self.tree.setHeaderHidden(True) #设置根节点 Perfmon = myQTreeWidgetItem(sin(pi * perfmon_x)) Perfmon.setText(0, 'Perfmon') perfmon_00 = myQTreeWidgetItem(sin(2 * pi * perfmon_x)) perfmon_00.setText(0, 'perfmon_00') Perfmon.addChild(perfmon_00) perfmon_01 = QTreeWidgetItem() perfmon_01.setText(0, 'perfmon_01') Perfmon.addChild(perfmon_01) perfmon_02 = QTreeWidgetItem() perfmon_02.setText(0, 'perfmon_02') Perfmon.addChild(perfmon_02) perfmon_03 = QTreeWidgetItem() perfmon_03.setText(0, 'perfmon_03') Perfmon.addChild(perfmon_03) self.tree.addTopLevelItem(Perfmon) # CPU cpuLoad = QTreeWidgetItem() cpuLoad.setText(0, 'CPU') cpuLoad_1 = QTreeWidgetItem() cpuLoad_1.setText(0, 'core 1') cpuLoad.addChild(cpuLoad_1) cpuLoad_2 = QTreeWidgetItem() cpuLoad_2.setText(0, 'core 2') cpuLoad.addChild(cpuLoad_2) self.tree.addTopLevelItem(cpuLoad) # treeItem signal self.tree.itemClicked[QTreeWidgetItem, int].connect(self.treeItemClicked) def treeItemWindow_open(self, item): title = item.text(0) subWind = QMdiSubWindow(self) subWind.setAttribute(Qt.WA_DeleteOnClose) subWind.setWindowTitle(title) self.newDocIndex += 1 mainWid = QWidget() l = QtWidgets.QVBoxLayout(mainWid) txtWind = QPlainTextEdit(mainWid) txtWind.setPlainText(f"perfmon.x = {item.x}, \n y = {item.y}") figWind = MyCanvas(mainWid, width=5, height=4, dpi=100, treeWidgetItem=item) l.addWidget(figWind) l.addWidget(txtWind) l.setStretch(0, 3) # 设置第一列的伸展比例为 3 l.setStretch(1, 1) # 设置第二列的伸展比例为 1, 这样2列的伸展比为3:1 subWind.setWidget(mainWid) self.mdiArea.addSubWindow(subWind) subWind.show() def treeItemClicked(self, item, column): tab = self.get_treeItem_tab(item.text(column)) if tab is not None: tab.setFocus() else: if item.text(column) == 'Perfmon': self.treeItemWindow_open(item) else: newDoc = QMdiSubWindow(self) newDoc.setAttribute(Qt.WA_DeleteOnClose) newDoc.setWindowTitle(item.text(column)) self.newDocIndex += 1 newDoc.setWidget(QPlainTextEdit( item.text(column) * 10, newDoc)) self.mdiArea.addSubWindow(newDoc) newDoc.show() def get_treeItem_tab(self, title): for wind in self.mdiArea.subWindowList(): if title == wind.windowTitle(): return wind return None def initStatusBar(self): self.statusBar = self.statusBar() self.statusBar.showMessage('Ready to start ...', 0) def initMenuBar(self): menuBar = self.menuBar() style = QApplication.style() #==== 文件 ====# fileMenu = menuBar.addMenu('文件') #新建一个文档 aFileNew = QAction('新建文档', self) aFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) aFileNew.triggered.connect(self.onFileNew) fileMenu.addAction(aFileNew) #打开一个文档 aFileOpen = QAction('打开文档', self) aFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) aFileOpen.triggered.connect(self.onFileOpen) fileMenu.addAction(aFileOpen) #关闭一个文档 aFileCloseAll = QAction('关闭全部', self) aFileCloseAll.setIcon(style.standardIcon(QStyle.SP_DialogCloseButton)) aFileOpen.triggered.connect(self.onFileCloseAll) fileMenu.addAction(aFileCloseAll) #添加分割线 fileMenu.addSeparator() #退出 aFileExit = QAction('退出', self) aFileExit.triggered.connect(self.close) fileMenu.addAction(aFileExit) #==== 编辑 ====# editMenu = menuBar.addMenu('编辑') #剪切 aEditCut = QAction('剪切', self) aEditCut.setIcon(QIcon(':/ico/cut.png')) aEditCut.triggered.connect(self.onEditCut) editMenu.addAction(aEditCut) #复制 aEditCopy = QAction('复制', self) aEditCopy.setIcon(QIcon(':/ico/copy.png')) aEditCopy.triggered.connect(self.onEditCopy) editMenu.addAction(aEditCopy) #粘贴 aEditPaste = QAction('粘贴', self) aEditPaste.setIcon(QIcon(':/ico/paste.png')) aEditPaste.triggered.connect(self.onEditPaste) editMenu.addAction(aEditPaste) #==== 窗口排列方式 ====# windowMenu = menuBar.addMenu('窗口') #子窗口模式 aWndSubView = QAction('子窗口模式', self) aWndSubView.triggered.connect(lambda: self.onWinowdMode(0)) windowMenu.addAction(aWndSubView) #标签页模式 aWndTab = QAction('标签页模式', self) aWndTab.triggered.connect(lambda: self.onWinowdMode(1)) windowMenu.addAction(aWndTab) windowMenu.addSeparator() #平铺模式 aWndTile = QAction('平铺模式', self) aWndTile.triggered.connect(lambda: self.onWinowdMode(2)) windowMenu.addAction(aWndTile) #窗口级联模式 aWndCascade = QAction('窗口级联模式', self) aWndCascade.triggered.connect(lambda: self.onWinowdMode(3)) windowMenu.addAction(aWndCascade) def initToolBar(self): toolBar = self.addToolBar('ToolBar') style = QApplication.style() min_width = 64 btnFileNew = QToolButton(self) btnFileNew.setText('新建文档') btnFileNew.setMinimumWidth(min_width) btnFileNew.setIcon(style.standardIcon(QStyle.SP_FileIcon)) btnFileNew.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileNew.clicked.connect(self.onFileNew) toolBar.addWidget(btnFileNew) btnFileOpen = QToolButton(self) btnFileOpen.setText('打开文档') btnFileOpen.setMinimumWidth(min_width) btnFileOpen.setIcon(style.standardIcon(QStyle.SP_DialogOpenButton)) btnFileOpen.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileOpen.clicked.connect(self.onFileOpen) toolBar.addWidget(btnFileOpen) btnFileCloseAll = QToolButton(self) btnFileCloseAll.setText('关闭全部') btnFileCloseAll.setMinimumWidth(min_width) btnFileCloseAll.setIcon(style.standardIcon( QStyle.SP_DialogCloseButton)) btnFileCloseAll.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnFileCloseAll.clicked.connect(self.onFileCloseAll) toolBar.addWidget(btnFileCloseAll) toolBar.addSeparator() btnEditCut = QToolButton(self) btnEditCut.setText('剪切') btnEditCut.setMinimumWidth(64) btnEditCut.setIcon(QIcon(':/ico/cut.png')) btnEditCut.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCut.clicked.connect(self.onEditCut) toolBar.addWidget(btnEditCut) btnEditCopy = QToolButton(self) btnEditCopy.setText('复制') btnEditCopy.setMinimumWidth(64) btnEditCopy.setIcon(QIcon(':/ico/copy.png')) btnEditCopy.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditCopy.clicked.connect(self.onEditCopy) toolBar.addWidget(btnEditCopy) btnEditPaste = QToolButton(self) btnEditPaste.setText('粘贴') btnEditPaste.setMinimumWidth(64) btnEditPaste.setIcon(QIcon(':/ico/paste.png')) btnEditPaste.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) btnEditPaste.clicked.connect(self.onEditPaste) toolBar.addWidget(btnEditPaste) def msgCritical(self, strInfo): dlg = QMessageBox(self) dlg.setIcon(QMessageBox.Critical) dlg.setText(strInfo) dlg.show() def onFileNew(self): newDoc = QMdiSubWindow(self) newDoc.setAttribute(Qt.WA_DeleteOnClose) newDoc.setWindowTitle('新文档 ' + str(self.newDocIndex)) self.newDocIndex += 1 newDoc.setWidget(QPlainTextEdit(newDoc)) self.mdiArea.addSubWindow(newDoc) newDoc.show() def onFileOpen(self): path, _ = QFileDialog.getOpenFileName(self, '打开文件', '', '文本文件 (*.txt, *.prf)') if path: try: with open(path, 'rU') as f: text = f.read() except Exception as e: self.msgCritical(str(e)) else: openDoc = QMdiSubWindow(self) openDoc.setWindowTitle(path) txtEdit = QPlainTextEdit(openDoc) txtEdit.setPlainText(text) openDoc.setWidget(txtEdit) self.mdiArea.addSubWindow(openDoc) openDoc.show() def onFileCloseAll(self): self.mdiArea.closeAllSubWindows() def onEditCut(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.cut() def onEditCopy(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.copy() def onEditPaste(self): txtEdit = self.mdiArea.activeSubWindow().widget() txtEdit.paste() def onWinowdMode(self, index): if index == 3: self.mdiArea.cascadeSubWindows() elif index == 2: self.mdiArea.tileSubWindows() elif index == 1: self.mdiArea.setViewMode(QMdiArea.TabbedView) else: self.mdiArea.setViewMode(QMdiArea.SubWindowView) def timerCallback(self): self.statusBar.showMessage( f'Document Index = {self.newDocIndex}, subWind num ={len(self.mdiArea.subWindowList())}', 0)
class EditorMainWindow(object): def __init__(self): self.seq = 0 self.widget = loadUi(MAIN_UI_PATH) self.init_mdi() self.init_actions() self.init_instance() self.widget.closeEvent = self.close_handler self.widget.showMaximized() def get_seq(self): self.seq += 1 return self.seq def init_mdi(self): self.mdi = QMdiArea(self.widget) self.mdi.setViewMode(QMdiArea.TabbedView) self.mdi.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdi.setTabsMovable(True) self.mdi.setTabsClosable(True) self.mdi.setTabShape(QTabWidget.Rounded) self.widget.setCentralWidget(self.mdi) def init_actions(self): self.widget.actionNew.triggered.connect(self.action_new_handler) self.widget.actionOpen.triggered.connect(self.action_open_handler) self.widget.actionSave.triggered.connect(self.action_save_handler) self.widget.actionSave_As.triggered.connect( self.action_save_as_handler) self.widget.actionClose.triggered.connect(self.action_close_handler) self.widget.actionClose_All.triggered.connect( self.action_close_all_handler) self.widget.actionExport.triggered.connect(self.action_export_handler) self.widget.actionStates.triggered.connect(self.action_states_handler) self.widget.actionEvents.triggered.connect(self.action_events_handler) def close_handler(self, ev): l = self.mdi.subWindowList() if len(l) != 0: self.mdi.closeAllSubWindows() ev.ignore() def init_instance(self): self.instances = [] def remove_instance(self, ins): self.instances.remove(ins) def find_non_existing_name(self): while True: tmp_path = os.path.join( config.src_path, "NewFsm" + str(self.get_seq()) + FSM_FILE_EXT) if not os.path.exists(tmp_path): return tmp_path def action_new_handler(self): tmp_path = self.find_non_existing_name() m = FsmModel() m.default_init() vm = InstanceVM(m, self, tmp_path) vm.set_modified(True) def file_already_open(self, pth): for i in self.instances: pth = os.path.abspath(pth) if pth == i.file_path: return i return None def action_open_handler(self): p = QFileDialog() p.setViewMode(QFileDialog.List) p.setFileMode(QFileDialog.ExistingFiles) p.setDirectory(config.src_path) p.exec() paths = p.selectedFiles() for pth in paths: i = self.file_already_open(pth) if i: self.mdi.setActiveSubWindow(i.sub_window) else: m = FsmModel.load_file(pth) vm = InstanceVM(m, self, pth) def action_save_handler(self): w = self.mdi.activeSubWindow() if w is None: return model = w.instance.model model.dump_file(w.instance.file_path) w.instance.set_modified(False) def action_save_as_handler(self): w = self.mdi.activeSubWindow() if w is None: return p = QFileDialog() p.setViewMode(QFileDialog.List) p.setDirectory(config.src_path) p.exec() paths = p.selectedFiles() if len(paths) == 0: return w.instance.file_path = os.path.abspath(paths[0]) w.instance.update_title() model = w.instance.model model.dump_file(w.instance.file_path) w.instance.set_modified(False) def action_close_handler(self): w = self.mdi.activeSubWindow() if w is None: return w.close() def action_close_all_handler(self): self.mdi.closeAllSubWindows() def action_export_handler(self): fsm_files = os.listdir(config.src_path) for fn in fsm_files: full_name = os.path.join(config.src_path, fn) b, e = os.path.splitext(full_name) if e == FSM_FILE_EXT: f = open(full_name, "rb") basename, e = os.path.splitext(fn) model_object = pickle.load(f) f.close() exporter = FsmModelPythonExporter(model_object) exporter.export( os.path.join(config.export_path, basename + "_fsm.py")) def action_states_handler(self): cur = self.get_current_instance() if cur is None: return model = cur.model w = loadUi(STATE_LIST_DIALOG_PATH) list_vm = MultiColumnListModel(model.state, LIST_DIALOG_COLUMNS, STATE_DIALOG_HEADERS) add_item = functools.partial(model.add_item, StateItem, "state") remove_item = functools.partial(model.remove_item, "state") dialog = StateListPanelVM(list_vm, model.state, add_item, remove_item, w) dialog.run() cur.table_vm.refresh() def action_events_handler(self): cur = self.get_current_instance() if cur is None: return model = cur.model w = loadUi(EVENT_DIALOG_PATH) list_vm = MultiColumnListModel(model.event, LIST_DIALOG_COLUMNS, EVENT_DIALOG_HEADERS) add_item = functools.partial(model.add_item, EventItem, "event") remove_item = functools.partial(model.remove_item, "event") dialog = ListEditPanelVM(list_vm, model.event, add_item, remove_item, w) dialog.run() cur.table_vm.refresh() def get_current_instance(self): current = self.mdi.activeSubWindow() if not current: return None for i in self.instances: if i.sub_window == current: return i assert ( False ) # there is an active sub window but there's no matching instance
class MainWindow(QMainWindow): def __init__(self): super(MainWindow, self).__init__() self.mdiArea = QMdiArea() self.mdiArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.mdiArea.setViewMode(QMdiArea.TabbedView) self.mdiArea.setDocumentMode(True) self.mdiArea.setTabsClosable(True) self.mdiArea.setTabsMovable(True) self.setCentralWidget(self.mdiArea) self.mdiArea.subWindowActivated.connect(self.updateMenus) self.windowMapper = QSignalMapper(self) self.windowMapper.mapped[QWidget].connect(self.setActiveSubWindow) self.createActions() self.createMenus() self.createToolBars() self.createStatusBar() self.updateMenus() self.readSettings() self.setWindowTitle("MDI") def closeEvent(self, event): self.mdiArea.closeAllSubWindows() if self.mdiArea.currentSubWindow(): event.ignore() else: self.writeSettings() event.accept() def newFile(self): child = self.createMdiChild() child.newFile() child.show() def open(self): fileName, _ = QFileDialog.getOpenFileName(self) if fileName: existing = self.findMdiChild(fileName) if existing: self.mdiArea.setActiveSubWindow(existing) return child = self.createMdiChild() if child.loadFile(fileName): self.statusBar().showMessage("File loaded", 2000) child.show() else: child.close() def save(self): if self.activeMdiChild() and self.activeMdiChild().save(): self.statusBar().showMessage("File saved", 2000) def saveAs(self): if self.activeMdiChild() and self.activeMdiChild().saveAs(): self.statusBar().showMessage("File saved", 2000) def cut(self): if self.activeMdiChild(): self.activeMdiChild().cut() def copy(self): if self.activeMdiChild(): self.activeMdiChild().copy() def paste(self): if self.activeMdiChild(): self.activeMdiChild().paste() def about(self): QMessageBox.about( self, "About MDI", "The <b>MDI</b> example demonstrates how to write multiple " "document interface applications using Qt.") def updateMenus(self): hasMdiChild = (self.activeMdiChild() is not None) self.saveAct.setEnabled(hasMdiChild) self.saveAsAct.setEnabled(hasMdiChild) self.pasteAct.setEnabled(hasMdiChild) self.closeAct.setEnabled(hasMdiChild) self.closeAllAct.setEnabled(hasMdiChild) self.tileAct.setEnabled(hasMdiChild) self.cascadeAct.setEnabled(hasMdiChild) self.nextAct.setEnabled(hasMdiChild) self.previousAct.setEnabled(hasMdiChild) self.separatorAct.setVisible(hasMdiChild) hasSelection = (self.activeMdiChild() is not None and self.activeMdiChild().textCursor().hasSelection()) self.cutAct.setEnabled(hasSelection) self.copyAct.setEnabled(hasSelection) def updateWindowMenu(self): self.windowMenu.clear() self.windowMenu.addAction(self.closeAct) self.windowMenu.addAction(self.closeAllAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.tileAct) self.windowMenu.addAction(self.cascadeAct) self.windowMenu.addSeparator() self.windowMenu.addAction(self.nextAct) self.windowMenu.addAction(self.previousAct) self.windowMenu.addAction(self.separatorAct) windows = self.mdiArea.subWindowList() self.separatorAct.setVisible(len(windows) != 0) for i, window in enumerate(windows): child = window.widget() text = "%d %s" % (i + 1, child.userFriendlyCurrentFile()) if i < 9: text = '&' + text action = self.windowMenu.addAction(text) action.setCheckable(True) action.setChecked(child is self.activeMdiChild()) action.triggered.connect(self.windowMapper.map) self.windowMapper.setMapping(action, window) def createMdiChild(self): child = MdiChild() self.mdiArea.addSubWindow(child) child.copyAvailable.connect(self.cutAct.setEnabled) child.copyAvailable.connect(self.copyAct.setEnabled) return child def createActions(self): self.newAct = QAction(QIcon(':/images/new.png'), "&New", self, shortcut=QKeySequence.New, statusTip="Create a new file", triggered=self.newFile) self.openAct = QAction(QIcon(':/images/open.png'), "&Open...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.open) self.saveAct = QAction(QIcon(':/images/save.png'), "&Save", self, shortcut=QKeySequence.Save, statusTip="Save the document to disk", triggered=self.save) self.saveAsAct = QAction( "Save &As...", self, shortcut=QKeySequence.SaveAs, statusTip="Save the document under a new name", triggered=self.saveAs) self.exitAct = QAction( "E&xit", self, shortcut=QKeySequence.Quit, statusTip="Exit the application", triggered=QApplication.instance().closeAllWindows) self.cutAct = QAction( QIcon(':/images/cut.png'), "Cu&t", self, shortcut=QKeySequence.Cut, statusTip="Cut the current selection's contents to the clipboard", triggered=self.cut) self.copyAct = QAction( QIcon(':/images/copy.png'), "&Copy", self, shortcut=QKeySequence.Copy, statusTip="Copy the current selection's contents to the clipboard", triggered=self.copy) self.pasteAct = QAction( QIcon(':/images/paste.png'), "&Paste", self, shortcut=QKeySequence.Paste, statusTip= "Paste the clipboard's contents into the current selection", triggered=self.paste) self.closeAct = QAction("Cl&ose", self, statusTip="Close the active window", triggered=self.mdiArea.closeActiveSubWindow) self.closeAllAct = QAction("Close &All", self, statusTip="Close all the windows", triggered=self.mdiArea.closeAllSubWindows) self.tileAct = QAction("&Tile", self, statusTip="Tile the windows", triggered=self.mdiArea.tileSubWindows) self.cascadeAct = QAction("&Cascade", self, statusTip="Cascade the windows", triggered=self.mdiArea.cascadeSubWindows) self.nextAct = QAction("Ne&xt", self, shortcut=QKeySequence.NextChild, statusTip="Move the focus to the next window", triggered=self.mdiArea.activateNextSubWindow) self.previousAct = QAction( "Pre&vious", self, shortcut=QKeySequence.PreviousChild, statusTip="Move the focus to the previous window", triggered=self.mdiArea.activatePreviousSubWindow) self.separatorAct = QAction(self) self.separatorAct.setSeparator(True) self.aboutAct = QAction("&About", self, statusTip="Show the application's About box", triggered=self.about) self.aboutQtAct = QAction("About &Qt", self, statusTip="Show the Qt library's About box", triggered=QApplication.instance().aboutQt) def createMenus(self): self.fileMenu = self.menuBar().addMenu("&File") self.fileMenu.addAction(self.newAct) self.fileMenu.addAction(self.openAct) self.fileMenu.addAction(self.saveAct) self.fileMenu.addAction(self.saveAsAct) self.fileMenu.addSeparator() action = self.fileMenu.addAction("Switch layout direction") action.triggered.connect(self.switchLayoutDirection) self.fileMenu.addAction(self.exitAct) self.editMenu = self.menuBar().addMenu("&Edit") self.editMenu.addAction(self.cutAct) self.editMenu.addAction(self.copyAct) self.editMenu.addAction(self.pasteAct) self.windowMenu = self.menuBar().addMenu("&Window") self.updateWindowMenu() self.windowMenu.aboutToShow.connect(self.updateWindowMenu) self.menuBar().addSeparator() self.helpMenu = self.menuBar().addMenu("&Help") self.helpMenu.addAction(self.aboutAct) self.helpMenu.addAction(self.aboutQtAct) def createToolBars(self): self.fileToolBar = self.addToolBar("File") self.fileToolBar.addAction(self.newAct) self.fileToolBar.addAction(self.openAct) self.fileToolBar.addAction(self.saveAct) self.editToolBar = self.addToolBar("Edit") self.editToolBar.addAction(self.cutAct) self.editToolBar.addAction(self.copyAct) self.editToolBar.addAction(self.pasteAct) def createStatusBar(self): self.statusBar().showMessage("Ready") def readSettings(self): settings = QSettings('Trolltech', 'MDI Example') pos = settings.value('pos', QPoint(200, 200)) size = settings.value('size', QSize(400, 400)) self.move(pos) self.resize(size) def writeSettings(self): settings = QSettings('Trolltech', 'MDI Example') settings.setValue('pos', self.pos()) settings.setValue('size', self.size()) def activeMdiChild(self): activeSubWindow = self.mdiArea.activeSubWindow() if activeSubWindow: return activeSubWindow.widget() return None def findMdiChild(self, fileName): canonicalFilePath = QFileInfo(fileName).canonicalFilePath() for window in self.mdiArea.subWindowList(): if window.widget().currentFile() == canonicalFilePath: return window return None def switchLayoutDirection(self): if self.layoutDirection() == Qt.LeftToRight: QApplication.setLayoutDirection(Qt.RightToLeft) else: QApplication.setLayoutDirection(Qt.LeftToRight) def setActiveSubWindow(self, window): if window: self.mdiArea.setActiveSubWindow(window)