Пример #1
0
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
Пример #2
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
Пример #3
0
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)
Пример #4
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
Пример #5
0
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)