Exemple #1
0
class ActionSet(QtCore.QObject, PluginMixin):
    __plugin__ = 'workbench'
    
    sig_actionAdded     = QtCore.pyqtSignal(object)
    sig_actionRemoved   = QtCore.pyqtSignal(object)
    
    def __init__(self, parent=None):
        QtCore.QObject.__init__(self, parent)
        self._actions       = ExtendList()
        self.loadExtensions()
        
    def loadExtensions(self):
        def load_action(ext):
            factory, meta       = ext
            action              = factory(self)
            action.name       = '@'.join( (meta.name, meta._provider) )
            action.domain     = meta.domain
            action.location   = meta.location
            action.position   = int(meta.position)
            if hasattr(meta, 'Icon'):
                action.setIcon(QtGui.QIcon(meta.Icon))
            if hasattr(meta, 'Text'):
                action.setText(meta.Text)
            self.addAction(action)
        self.extensions['action'].cook(load_action)
        
    def addAction(self, action):
        exist = self._actions.find(lambda a: a.domain==action.domain and a.name==action.name)
        if not exist:
            self._actions.append(action)
            self.sig_actionAdded.emit(action)

    def removeAction(self, action):
        if action in self._actions:
            self._action.remove(action)
            self.sig_actionRemoved.emit(action)

    def walk(self, root, func):
        children = self._actions.screen(lambda a: a.domain==root.domain and a.location==root.name)
        func(root, children)
        for child in children:
            self.walk(child, func)

    def populate(self, root, context):
        def expand(parent, children):
            if not hasattr(parent, 'clear'): return
            children = children.sorted(lambda a:a.position).screen(lambda a:a.assignContext(context))
            if children:
                cat = children[0].position//100
                for child in children:
                    if child.position//100 > cat:
                        parent.addSeparator()
                        cat = child.position//100
                    parent.addAction(child)
                for holder in children.screen(lambda a: hasattr(a, 'clear')):
                    holder.clear()
        self.walk(root, expand)
Exemple #2
0
class WorkbenchWindow(QtGui.QMainWindow, IWorkbenchPart, PluginMixin):
    __plugin__ = 'workbench'
    
    sig_pageChanged     = QtCore.pyqtSignal(object)
    
    ## methods used by app
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)
        self.wb_actionset   = None
        self.wb_views       = ExtendList()
        self.wb_pages       = ExtendList()
    
    def init(self, actionset):
        self.wb_actionset = actionset
        self.wbInitWindowUI()
        self.wbInitExtensions()
        self.wpInitWorkbench(self) ## init the workbench after actions are loaded
        self.wbLoadState()

    ## check if the workbench is now able to be closed
    def wbClose(self):
        self.wbSaveState()
        self.wb_pages.cook(lambda p: self.wbClosePage(p))
        self.close()

    ## override IWorkbenchPart method
    def wpInitWorkbench(self, workbench):
        self.setObjectName('window@workbench')
        self.toolKit.domain = self.objectName()
        self.toolKit.name   = 'menubar'
        self.pageToolKit.domain     = None
        self.pageToolKit.name       = 'toolbar'
        IWorkbenchPart.wpInitWorkbench(self, workbench)
        
    def wpResetActions(self):
        self.toolKit.clear()
        self.wb_actionset.populate(self.toolKit, self)        
        
    def wpOnActionChanged(self, action):
        if action.domain == self.objectName():
            self.toolKit.clear()
            self.wb_actionset.populate(self.toolKit, self)
        else:
            page = self.pageStack.currentWidget()
            if page and action.domain==page.objectName():
                self.pageToolKit.domain = page.objectName()
                self.wb_actionset.populate(self.pageToolKit, page)

    ## initialize window UI
    def wbInitWindowUI(self):
        self.setWindowTitle("Workbench")
        self.setWindowIcon(QtGui.QIcon(":/workbench/moon.png"))
        
        self.viewBar = QtGui.QToolBar(self)
        self.viewBar.restore_state  = None
        self.viewBar.setToolButtonStyle(QtCore.Qt.ToolButtonIconOnly)
        self.viewBar.setObjectName("View ToolBar")
        self.viewBar.setIconSize(QtCore.QSize(24,24))
        self.viewBar.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
        self.viewBar.layout().setMargin(0)
        self.addToolBar(QtCore.Qt.LeftToolBarArea, self.viewBar)
        
        self.viewMenu = QtGui.QMenu(self)
        self.viewMenuAction = QtGui.QAction(QtGui.QIcon(':/workbench/stack.png'), '', self)
        self.viewMenuAction.setMenu(self.viewMenu)
        self.viewBar.addAction(self.viewMenuAction)
        self.viewBar.addSeparator()
        def remove_view_action(action):
            if action is not self.viewMenuAction:
                self.viewBar.removeAction(action)
        self.viewBar.actionTriggered.connect(remove_view_action)

        self.toolKit   = QtGui.QMenuBar(self)
        self.toolKit.setObjectName("Workbench Menu")
        self.toolKit.setWindowIcon(QtGui.QIcon(''))
        self.toolKit.setWindowTitle("Workbench Menu")
        self.setMenuBar(self.toolKit)
        addToggleAction(self.toolKit)
        
        central     = QtGui.QFrame(self)
        central.setObjectName("Page Stack")
        central.setWindowIcon(QtGui.QIcon(''))
        central.setWindowTitle("Page Stack")
        addToggleAction(central)
        self.tabBar         = TabBar(central)
        self.tabBar.setMovable(True)
#        self.tabBar.setElideMode(QtCore.Qt.ElideMiddle)
        self.tabBar.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
        self.tabActions = QtGui.QActionGroup(self.tabBar)
        self.tabActions.addAction('Close')
        self.tabActions.addAction('Close Others')
        self.tabActions.addAction('Close All')
        self.tabBar.addActions(self.tabActions.actions())
        self.tabActions.triggered.connect(self.wbOnTabAction)
        
        self.pageToolKit   = ToolBar(central)
        self.pageToolKit.setObjectName("Page ToolBar")
        self.pageToolKit.setWindowTitle('Page ToolBar')

        self.pageStack      = QtGui.QStackedWidget(central)
        
        hbox = QtGui.QHBoxLayout()
        hbox.addWidget(self.tabBar)
        hbox.addStretch()
#        hbox.addSpacing(2)
        vbox = QtGui.QVBoxLayout()
        vbox.setSpacing(0)
        vbox.setMargin(0)
        vbox.addLayout(hbox)
        vbox.addWidget(self.pageToolKit)
        vbox.addWidget(self.pageStack)
        central.setLayout(vbox)
        self.setCentralWidget(central)

        self.tabBar.mouseDoubleClickEvent = lambda evt: self.wbChangeViewState('togglemax', central)
        central.mouseDoubleClickEvent = lambda evt: self.wbChangeViewState('togglemax', central)
        
        def page_child_evt(evt):
            if evt.removed()and QtCore.QObject.isWidgetType(evt.child()):
                self.wbCheckPageCoherence()
            QtGui.QStackedWidget.childEvent(self.pageStack, evt)
        self.pageStack.childEvent = page_child_evt
                
        self.currentPage    = self.pageStack.currentWidget
        self.tabBar.currentChanged.connect(self.wbOnTabChanged)
        self.tabBar.tabCloseRequested.connect(self.wbOnTabToClose)
        self.pageStack.currentChanged.connect(self.wbOnPageChanged)
    
    ## initialize extensions
    def wbInitExtensions(self):
        def assign_ext_name(ext):
            meta = ext[1]
            meta._ext_name = '@'.join((meta.name, meta._provider))
        self.extensions['opener'].cook(assign_ext_name)
        self.extensions['view'].cook(assign_ext_name)

    ## manipulate views
    def wbShowView(self, extname):
        view = self.wb_views.find(lambda v:v.objectName()==extname)
        if view is None:
            ext = self.extensions['view'].find(lambda e:e[1]._ext_name ==extname)
            if ext is not None:
                factory, meta = ext
                view = factory()
                view.wpInitWorkbench(self)
                if hasattr(meta, 'Icon'):
                    view.setWindowIcon(QtGui.QIcon(meta.Icon))
                view.setWindowTitle(meta.name)
                view.setObjectName(meta._ext_name )
                self.addDockWidget(8, view)
                self.wb_views.append(view)
                view.sig_requestViewStateChange.connect(self.wbChangeViewState)
        if view:
            view.show()
            view.raise_()
            self.viewBar.removeAction(view.toggleViewAction())
        else:
            QtGui.QMessageBox.information(self, "Error", "View {} is not found.".format(extname))
    
    def wbChangeViewState(self, cmd, view):
        def restore():
            self.viewBar.show()
            self.centralWidget().show()
            if self.viewBar.restore_state:
                self.restoreState(self.viewBar.restore_state)
                self.viewBar.restore_state = None
                self.wb_views.cook(lambda v:v.winBar.setRestoreState(False))
        def backup():
            self.viewBar.restore_state = self.saveState()
            
        if view in self.wb_views:
            if cmd == 'close':
                restore()
                view.hide()
            elif cmd == 'min':
                restore()
                view.hide()
                self.viewBar.addAction(view.toggleViewAction())
            elif cmd == 'max':
                backup()
                self.viewBar.hide()
                self.centralWidget().hide()
                self.wb_views.screen(lambda v: v is not view).cook(lambda v:v.hide())
            elif cmd == 'restore':
                restore()
        elif view is self.centralWidget():
            if cmd == 'togglemax':
                if self.viewBar.restore_state:
                    restore()
                else:
                    backup()
                    self.viewBar.hide()
                    self.wb_views.cook(lambda v:v.hide())
    
    ## manipulate pages
    def wbShowPage(self, page):
        self.tabBar.setCurrentIndexByData(page._pageid)

    def wbAddPage(self, page):
        if page not in self.wb_pages:
            self.pageStack.addWidget(page)
            self.wb_pages.append(page)
            page._pageid = self.tabBar.addTab(page.pgTabTitle(), page.windowIcon())
            
    def wbClosePage(self, page):
        if page in self.wb_pages:
            if page._dirty:
                decision = QtGui.QMessageBox.question(self, 
                                                      'Save or not?', 
                                                      '{} has been changed. Do you want to save it befor close?'.format(page.pgWinTitle()),
                                                      QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard)
                if decision == QtGui.QMessageBox.Save: page.pgSave()
            self.tabBar.removeTabByData(page._pageid)
            self.pageStack.removeWidget(page)
            self.wb_pages.remove(page)
            page.close()
                
    def wbOnTabChanged(self, tabind):
        page = self.wb_pages.find(lambda p: p._pageid==self.tabBar.tabData(tabind))
        if page:
            self.pageStack.setCurrentWidget(page)
        
    def wbOnTabToClose(self, tabind):
        page = self.wb_pages.find(lambda p: p._pageid==self.tabBar.tabData(tabind))
        self.wbClosePage(page)
        
    def wbOnPageChanged(self, pageind):
        page = self.pageStack.widget(pageind)
        self.pageToolKit.clear()
        if page:
            self.setWindowTitle(page.pgWinTitle())
            self.pageToolKit.domain = page.objectName()
            self.wb_actionset.populate(self.pageToolKit, page)
        else:
            self.setWindowTitle('Workbench')
        self.sig_pageChanged.emit(page)
    
    def wbOnTabAction(self, action):
        txt = action.text()
        if txt == 'Close':
            page = self.pageStack.currentWidget()
            if page: self.wbClosePage(page)
        elif txt == 'Close Others':
            page = self.pageStack.currentWidget()
            self.wb_pages.screen(lambda p: p is not page).cook(lambda p: self.wbClosePage(p))
        elif txt == 'Close All':
            self.wb_pages.cook(lambda p: self.wbClosePage(p))
            
    def wbOnPageDirtyChanged(self, page):
        txt = ('*' if page._dirty else '') + page.pgTabTitle()
        self.tabBar.setTextByData(page._pageid, txt)
            
    def wbCheckPageCoherence(self):
        for page in self.wb_pages.screen(lambda p: not isAlive(p)):
            self.wb_pages.remove(page)
            self.tabBar.removeTabByData(page._pageid)
    
    ## "open" service
    def wbMatchOpener(self, scheme):
        opener = (None, None)
        settingid       = 'default.opener.'+scheme
        defaultopener   = self.setting.get(settingid, None)
        if defaultopener:
            opener = self.extensions['opener'].find(lambda o: defaultopener==o[1]._ext_name )
        if opener[0] is None:
            openers = self.extensions['opener'].screen(lambda o: scheme == o[1].scheme)
            if len(openers) == 1:
                opener = openers[0]
            elif len(openers) > 1:
                ind, bedefault = ChooseDlg.choose("Select a opener for scheme: "+scheme,
                                                [o[1]._ext_name  for o in openers],
                                                self, "Choose Opener", None)
                if ind is not None:
                    opener = openers[ind]
                    if bedefault: self.setting.set(settingid, opener[1]._ext_name )
        
        return opener
    
    def wbOpen(self, scheme, uri):
        opener, meta = self.wbMatchOpener(scheme)
        if opener:
            self.wbOpenBy(uri, opener, meta)
        else:
            QtGui.QMessageBox.information(self, "Information", "No proper opener is found for: "+scheme)
                    
    def wbOpenBy(self, uri, opener, meta):
        page = opener.openPage(uri)
        if page:
            if page not in self.wb_pages:
                page.setObjectName(meta._ext_name )
                page.wpInitWorkbench(self)
                self.wbAddPage(page)
            self.wbShowPage(page)
       
    def wbSaveState(self):
        self.setting['state.close.qstate']._bytearray = self.viewBar.restore_state or self.saveState()
        self.setting['state.close.pages']._val = SettingTuple(['@@'.join([p.pgRepr(), p.objectName()]) for p in self.wb_pages if p.objectName() and p.pgRepr() is not None])
        self.setting['state.close.views']._val = SettingTuple([v.objectName() for v in self.wb_views if v.isVisible()])
        self.setting['state.close.size']._int = SettingTuple([self.size().width(), self.size().height()])
    
    def wbLoadState(self):
        ## size
        if self.setting.has('state.close.size'):
            self.resize(QtCore.QSize(*self.setting['state.close.size']._int))
        ## views
        for v in self.setting.get('state.close.views', ()):
            self.wbShowView(v)
        for v in self.setting.get('workbench.onstart.views', ()):
            self.wbShowView(v)
        
        ## load style sheet
#        if self.setting.has('workbench.css'):
#            with plat.workspace.file(self.setting('workbench.css')._val).open('r') as fp:
#                self.setStyleSheet(fp.read())

        ## state
        if self.setting.has('state.close.qstate'):     
            self.restoreState(self.setting['state.close.qstate']._bytearray)
        ## pages
        for pdata in self.setting.get('state.close.pages', ()):
            uri, name = pdata.rsplit('@@', 1)
            opener = self.extensions['opener'].find(lambda o: o[1]._ext_name ==name)
            if opener: self.wbOpenBy(uri, *opener)
            
        ## view bar
        self.loadViewMenu()
                
    ## context menu
    def loadViewMenu(self):
        self.viewMenu.addActions([self.toolKit.toggleViewAction(), self.centralWidget().toggleViewAction()])
        self.viewMenu.addSeparator()
        
        def add_view_action(view):
            _, meta = view
            a = QtGui.QAction(self.viewMenu)
            a.setText(meta.name)
            a.setIcon(QtGui.QIcon(getattr(meta, 'Icon', '')))
            a.setProperty('viewname', meta._ext_name )
            self.viewMenu.addAction(a)
        self.extensions['view'].cook(add_view_action)

        def show_view(action):
            vn = action.property('viewname')
            if vn: self.wbShowView(vn)
                   
        self.viewMenu.triggered.connect(show_view)