Exemple #1
0
class Gui(MultiWorkspaceWindow):
    '''
    The main class to be called to create an instance of dataArtist
    '''
    def __init__(self, title='dataArtist'):
        MultiWorkspaceWindow.__init__(self, Workspace, title)

        s = self.app.session
        self.resize(600,550)
        #ALLOW DRAGnDROP        
        self.setAcceptDrops(True)
        #INIT CHILD PARTS:
        self.dialogs = s.dialogs
        self.pref_import = PreferencesImport(self)
        
        self._appendMenubarAndPreferences()
        #CONNECT OWN SAVE/RESTORE FUNCTIONS TO THE SESSION:
        s.sigSave.connect(self._save)
        s.sigRestore.connect(self._restore)
        st = StatusBar()
        self.setStatusBar(st)
    
        def showOut(msg):
            if msg != '\n':
                # after every message this new line character is emitted 
                # showing this hides the real message
                st.showMessage(msg,3000)
        def showErr(msg):
            if msg != '\n':
                st.showError(msg, 3000)
        s.streamOut.message.connect(showOut)
        s.streamErr.message.connect(showErr)

        self.framelessDisplays = {} # contain all displays that are unbounded 
                                    # from the main window and showed frameless

        self.addWorkspace()
        #PROGRESS BAR:
        self.progressBar = ProgressBar(st)
        st.setSizeGripEnabled(False)


    def isEmpty(self):
        return ( self.centralWidget().count()==1 
                and not self.currentWorkspace().displays() )


    def _save(self, state):
        state['gui'] = self.saveState()
    
    
    def _restore(self, state):
        return self.restoreState(state['gui'])

    def saveState(self):
        l = {}
        i = self.size()
        l['size'] = (i.width(),i.height()) 
        c = self.centralWidget()
        l['nWorkspaces'] = c.count()
        l['currentWorkspace'] = c.indexOf(self.currentWorkspace())
        l['maximized'] = self.isMaximized()
        l['fullscreen'] = self.menuBar().ckBox_fullscreen.isChecked()
        l['showTools'] = self.menu_toolbars.a_show.isChecked()
#         session.addContentToSave(l, 'gui.txt')
        #WORKSPACES
        sw = l['workspaces'] = {}
        for w in self.workspaces():
            sw[w.number()] = w.saveState()
        
        l['undoRedo'] = self.undoRedo.saveState()
        return l
    
                    
    def restoreState(self, l):
        self.resize(*l['size'])  
        self.menuBar().setFullscreen(l['fullscreen'])
        if l['maximized']:
            self.showMaximized()
        else:
            self.showNormal()
        #WORKSPACES
        c = self.centralWidget()
        n = l['nWorkspaces']
            #CLOSE OLD:
        for w in self.workspaces():
            self.closeWorkspace(w)
            #ADD NEW:
        for _ in range(n - c.count()):
            self.addWorkspace()
            #RESTORE:
        self.showWorkspace(l['currentWorkspace'])  
        lw = l['workspaces']
        for number, w in zip(lw, self.workspaces()):
            w.restoreState(lw[number])
            
        self.menu_toolbars.a_show.setChecked(l['showTools'])
        self._toggleShowSelectedToolbars(l['showTools'])
        
        self.undoRedo.restoreState(l['undoRedo'])

    
    def addFilePath(self, filepath):
        '''
        create a new display for one ore more given file paths
        '''
        if filepath:
            return self.currentWorkspace().addFiles([PathStr(filepath)])


    def openFile(self):
        '''
        create a new display for one ore more  files 
        '''
        filepaths = self.dialogs.getOpenFileNames()
        if filepaths:
            return self.currentWorkspace().addFiles(filepaths)

  
    def showDisplay(self, arg):
        '''
        show display as frame-less window 
        '[displaynumber], [area]' = (x,y,width,height)
        '''
        displaynumber,pos = eval(arg)
        if not pos:
            return self.hideDisplay(displaynumber)
        else:
            (x,y,width,height) = pos
        d = self.framelessDisplays.get(displaynumber,None)
        if not d:
            try:
                d = self.currentWorkspace().displaydict()[displaynumber]
                d.release()
                d.hideTitleBar()
                d.setWindowFlags(QtCore.Qt.FramelessWindowHint | 
                                 QtCore.Qt.WindowStaysOnTopHint)
                self.framelessDisplays[displaynumber] = d
            except KeyError:
                print('displaynumber [%s] not known' %displaynumber)
                return
        d.move(QtCore.QPoint(x,y))
        d.resize(width, height)
        d.show()


    def hideDisplay(self, displaynumber):
        '''
        close frame-less display
        [displaynumber]
        '''
        d = self.framelessDisplays.pop(displaynumber,None)
        if d:
            d.showTitleBar()
            d.embedd()
        else:
            print('displaynumber [%s] not known' %displaynumber) 
 
 
    def runScriptFromName(self, name):
        '''
        run a console script identified by name
        in the current display 
        '''
        w = self.currentWorkspace().getCurrentDisplay(
                                ).tab.automation.tabs.widgetByName(name)
        if not w:
            raise Exception('couldnt find script [%s] in the current display' %name)
        w.thread.start()
    
    
    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.setMaximumHeight(25)

        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(MEDIA_FOLDER.join('institution_logo.svg'))
        
        #hide the menu so toolbars can only be show/hidden via gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)
        
        #APPEND PREFERENCES
        t = m.file_preferences.tabs        
        t.addTab(PreferencesView(self), 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        #APPEND MENUBAR
            #MENU - FILE
        f = m.menu_file
        p = f.action_preferences 
        action_file = QtGui.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcuts(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_I))
        f.insertAction(p,action_file)
        f.insertSeparator(p)
            #MENU VIEW
        v = m.menu_view
            #ACTION PRINT VIEW
        aPrintView = QtGui.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
                lambda checked: self.currentWorkspace().setPrintView(checked) ) 
        v.addAction(aPrintView)
            #ACTION VIEW2CLIPBOARD
        aClipboard = QtGui.QAction('Copy view to clipboard', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyViewToClipboard() ) 
        v.addAction(aClipboard)
            #ACTION Display2CLIPBOARD
        aClipboard = QtGui.QAction('Copy active display to clipboard', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyCurrentDisplayToClipboard() ) 
        v.addAction(aClipboard)
            #MENU - TOOLS   
        t = m.menu_tools = QtGui.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
            #ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name in ( #(1, 'Dot'), 
                         (2, 'Graph'), 
                         (3, 'Image/Video'),
                         #(4, 'Surface')
                         #TODO: 
                         #(4, 'TODO: Surface'), 
                         #(5, 'TODO: Volume') 
                         ):
                mDisplay.addAction('%sD - %s' %(i-1, name)).triggered.connect(
                        lambda checked, i=i: self.currentWorkspace().addDisplay(axes=i) )
            #ADD TABLE
        t.addAction('Add Table').triggered.connect(
                lambda: self.currentWorkspace().addTableDock() ) 
            #ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
                lambda: self.currentWorkspace().addTextDock() ) 
        t.addSeparator()
        #DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
                self._duplicateCurrentDiplay ) 
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS ) 
            #MENU - TOOLBARS                
        self.menu_toolbars = QtGui.QMenu('Toolbars', m)
            #SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtGui.QAction('show', m)
        f = a.font()
        f.setBold(True)   
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore( m.menu_workspace, self.menu_toolbars)       
            #MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
            lambda checked: os.startfile(HELP_FILE))
            #TUTORIALS
        ####not used at the moment
#         self.m_tutorials = TutorialMenu(
#                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'), 
#                         openFunction=self._openFnForTutorial,
#                         saveFunction=self.app.session.blockingSave)
#         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
            lambda checked: os.startfile(
            'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
            lambda checked: os.startfile(
            'https://github.com/radjkarl/dataArtist/issues'))        
        
        
    def _duplicateCurrentDiplay(self):
        d = self.currentWorkspace().getCurrentDisplay()
        if not d:
            raise Exception('no display chosen')
        d.duplicate()


    def _moveCurrentDiplayToWorkspace(self, i):
        w = self.currentWorkspace()
        ws = self.workspaces(i)
        w.moveCurrentDisplayToOtherWorkspace(ws)


    def _fillMenuDuplicateToOtherWS(self):
        c = self.centralWidget()
        self._m_duplDisp.clear()
        for i in range(c.count()):
            if i != c.currentIndex():
                t = '[%s]'%str(i+1)
                a = QtGui.QAction(t, self._m_duplDisp)
                a.triggered.connect(lambda clicked, i=i, self=self: 
                        self._moveCurrentDiplayToWorkspace(i))
                self._m_duplDisp.addAction(a)


    def _openFnForTutorial(self, path):
        self.app.session.setSessionPath(path) 
        self.app.session.restoreCurrentState()
  

    def _listToolbarsInMenu(self):        
        m = self.menu_toolbars
        #REMOVE OLD:
        m.clear()
        m.addAction(m.a_show)
        self.currentWorkspace().addShowToolBarAction(m)

        
    def _toggleShowSelectedToolbars(self, checked):
        self.currentWorkspace().toggleShowSelectedToolbars(checked)

                
    #DRAG AND GROP
    #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    def keyPressEvent(self, event):
        if event.matches(QtGui.QKeySequence.Paste):
            self.dropEvent(QtGui.QApplication.clipboard())
            
            
    def dragEnterEvent(self, event):
        m = event.mimeData()
        if (m.hasUrls() or 
            m.hasImage() or
            m.hasText() ):
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()


    def _getFilePathsFromUrls(self, urls):
        '''
        return a list of all file paths in event.mimeData.urls()
        '''
        l = []
        def _appendRecursive(path):
            if path.isfile():
                #file
                l.append(path)
            else:
                for f in path:
                    #for all files in folder
                    _appendRecursive(path.join(f))
        # one or more files/folders are dropped
        for url in urls:
            if url.isLocalFile():
                path = PathStr(url.toLocalFile().toLocal8Bit().data())
                if path.exists():
                    _appendRecursive(path)
        return l


    def dropEvent(self, event):
        '''what to do is sth. if dropped'''
        m = event.mimeData()
        if m.hasHtml():
            #HTML CONTENT
            (paths, data) = html2data(unicode(m.html()))
            self.currentWorkspace().addFiles(paths)
            #raise NotImplementedError('direct import of data (like tables) from a browser is not implemented at the moment')
        elif m.hasUrls():
            
            paths =  self._getFilePathsFromUrls(m.urls())
            #filter from session files and open those:
            i  = 0
            while i < len(paths):
                p = paths[i]
                if p.endswith(self.app.session.FTYPE):
                    if self.isEmpty():
                        self.app.session.replace(p)
                    else:
                        self.app.session.new(p)
                    paths.pop(i)
                else:
                    i += 1
            
            self.currentWorkspace().addFiles(paths)
        if m.hasText():
            txt = unicode(m.text())
            if self.txtIsTable(txt):
                self.currentWorkspace().addTableDock(text=txt)
            else:
                self.currentWorkspace().addTextDock(text=txt)
    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>


    @staticmethod
    def txtIsTable(txt):
        # for now just a simple check whether there is at least 
        # on tab sign in the first line
        return txt[:txt.find('\n')].count('\t') > 0
Exemple #2
0
    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.setMaximumHeight(25)

        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(MEDIA_FOLDER.join('institution_logo.svg'))
        
        #hide the menu so toolbars can only be show/hidden via gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)
        
        #APPEND PREFERENCES
        t = m.file_preferences.tabs        
        t.addTab(PreferencesView(self), 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        #APPEND MENUBAR
            #MENU - FILE
        f = m.menu_file
        p = f.action_preferences 
        action_file = QtGui.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcuts(QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_I))
        f.insertAction(p,action_file)
        f.insertSeparator(p)
            #MENU VIEW
        v = m.menu_view
            #ACTION PRINT VIEW
        aPrintView = QtGui.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
                lambda checked: self.currentWorkspace().setPrintView(checked) ) 
        v.addAction(aPrintView)
            #ACTION VIEW2CLIPBOARD
        aClipboard = QtGui.QAction('Copy view to clipboard', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyViewToClipboard() ) 
        v.addAction(aClipboard)
            #ACTION Display2CLIPBOARD
        aClipboard = QtGui.QAction('Copy active display to clipboard', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyCurrentDisplayToClipboard() ) 
        v.addAction(aClipboard)
            #MENU - TOOLS   
        t = m.menu_tools = QtGui.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
            #ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name in ( #(1, 'Dot'), 
                         (2, 'Graph'), 
                         (3, 'Image/Video'),
                         #(4, 'Surface')
                         #TODO: 
                         #(4, 'TODO: Surface'), 
                         #(5, 'TODO: Volume') 
                         ):
                mDisplay.addAction('%sD - %s' %(i-1, name)).triggered.connect(
                        lambda checked, i=i: self.currentWorkspace().addDisplay(axes=i) )
            #ADD TABLE
        t.addAction('Add Table').triggered.connect(
                lambda: self.currentWorkspace().addTableDock() ) 
            #ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
                lambda: self.currentWorkspace().addTextDock() ) 
        t.addSeparator()
        #DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
                self._duplicateCurrentDiplay ) 
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS ) 
            #MENU - TOOLBARS                
        self.menu_toolbars = QtGui.QMenu('Toolbars', m)
            #SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtGui.QAction('show', m)
        f = a.font()
        f.setBold(True)   
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore( m.menu_workspace, self.menu_toolbars)       
            #MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
            lambda checked: os.startfile(HELP_FILE))
            #TUTORIALS
        ####not used at the moment
#         self.m_tutorials = TutorialMenu(
#                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'), 
#                         openFunction=self._openFnForTutorial,
#                         saveFunction=self.app.session.blockingSave)
#         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
            lambda checked: os.startfile(
            'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
            lambda checked: os.startfile(
            'https://github.com/radjkarl/dataArtist/issues'))        
Exemple #3
0
    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.setFixedHeight(25)
        #         m.setMaximumHeight(25)

        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(
                MEDIA_FOLDER.join('institution_logo.svg'))

        # hide the menu so toolbars can only be show/hidden via
        # gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)

        # APPEND PREFERENCES
        pView = PreferencesView(self)
        #pView.setColorTheme('bright')
        
        t = m.file_preferences.tabs
        t.addTab(pView, 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        # APPEND MENUBAR
        # MENU - FILE
        f = m.menu_file
        p = f.action_preferences
        action_file = QtWidgets.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcut(
                QtGui.QKeySequence(
                        QtCore.Qt.CTRL +
                        QtCore.Qt.Key_I))
        f.insertAction(p, action_file)
        f.insertSeparator(p)
        # MENU VIEW
        v = m.menu_view
        # ACTION PRINT VIEW
        aPrintView = QtWidgets.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
                lambda checked: self.currentWorkspace().setPrintView(checked))
        v.addAction(aPrintView)

        # SHOW/HIDE history
        aHistory = QtWidgets.QAction('Program history', v)
        aHistory.setShortcut(QtCore.Qt.Key_F4)

        aHistory.setCheckable(True)

        def showhideHistory(checked):
            s = self.currentWorkspace().middle_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 1.5
            return s.moveSplitter(r, 1)

        def isHistoryVisible():
            s = self.currentWorkspace().middle_splitter
            aHistory.setChecked(s.sizes()[1] != 0)

        aHistory.triggered.connect(showhideHistory)
        v.aboutToShow.connect(isHistoryVisible)
        v.addAction(aHistory)

        # SHOW/HIDE preferences
        aPref = QtWidgets.QAction('Dock preferences', v)
        aPref.setShortcut(QtCore.Qt.Key_F3)
        aPref.setCheckable(True)

        def isPrefVisible():
            w = self.currentWorkspace()
            s = w.vert_splitter
            return s.sizes()[0] != 0

        def showhidePref():
            show = not isPrefVisible()
            s = self.currentWorkspace().vert_splitter
            r = s.getRange(1)[1]
            if show:
                r /= 3
            else:
                r = 0
            return s.moveSplitter(r, 1)

        def setupPref():
            w = self.currentWorkspace()
            aPref.setChecked(isPrefVisible())
            aPref.setEnabled(w.displayPrefTabs.isVisible())

        aPref.triggered.connect(showhidePref)
        v.aboutToShow.connect(setupPref)
        v.addAction(aPref)

        mcopy = v.addMenu('Copy to clipboard')
        # ACTION VIEW2CLIPBOARD
        aClipboard = QtWidgets.QAction('All displays', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyViewToClipboard())
        aClipboard.setShortcut(QtGui.QKeySequence('Ctrl+Shift+F12'))
        mcopy.addAction(aClipboard)
        # ACTION Display2CLIPBOARD
        aClipboard = QtWidgets.QAction('Active Display', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyCurrentDisplayToClipboard())
        aClipboard.setShortcut(QtGui.QKeySequence('Ctrl+F12'))
        mcopy.addAction(aClipboard)

        # ACTION Display2CLIPBOARD
        aClipboard = QtWidgets.QAction('Active Display Item', v)
        aClipboard.triggered.connect(
                lambda checked: self.currentWorkspace().copyCurrentDisplayItemToClipboard())
        aClipboard.setShortcut(QtCore.Qt.Key_F12)
        mcopy.addAction(aClipboard)

        # MENU - TOOLS
        t = m.menu_tools = QtWidgets.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
        # ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name in (  # (1, 'Dot'),
                (2, 'Graph'),
                (3, 'Image/Video'),
                # (4, 'Surface')
                # TODO:
                # (4, 'TODO: Surface'),
                # (5, 'TODO: Volume')
        ):
            mDisplay.addAction('%sD - %s' % (i - 1, name)).triggered.connect(
                    lambda checked, i=i: self.currentWorkspace().addDisplay(axes=i))
            # ADD TABLE
        t.addAction('Add Table').triggered.connect(
                lambda: self.currentWorkspace().addTableDock())
        # ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
                lambda: self.currentWorkspace().addTextDock())
        t.addSeparator()
        # DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
                self._duplicateCurrentDiplay)
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS)
        # MENU - TOOLBARS
        self.menu_toolbars = QtWidgets.QMenu('Toolbars', m)
        self.menu_toolbars.hovered[QtWidgets.QAction].connect(
                lambda action, m=self.menu_toolbars: _showActionToolTipInMenu(m, action))
        # self.connect(self.menu_toolbars, QtCore.SIGNAL("hovered(QAction *)"),
        #              lambda action, m=self.menu_toolbars:
        #                 _showActionToolTipInMenu(m, action))

        # SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtWidgets.QAction('show', m)
        f = a.font()
        f.setBold(True)
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore(m.menu_workspace, self.menu_toolbars)
        # MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
                lambda checked: os.startfile(HELP_FILE))
        # TUTORIALS
        # not used at the moment
        #         self.m_tutorials = TutorialMenu(
        #                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'),
        #                         openFunction=self._openFnForTutorial,
        #                         saveFunction=self.app.session.blockingSave)
        #         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
                lambda checked: os.startfile(
                        'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
                lambda checked: os.startfile(
                        'https://github.com/radjkarl/dataArtist/issues'))
Exemple #4
0
class Gui(MultiWorkspaceWindow):
    '''
    The main class to be called to create an instance of dataArtist
    '''
    def __init__(self, title='dataArtist'):
        MultiWorkspaceWindow.__init__(self, Workspace, title)

        s = self.app.session
        self.resize(605, 550)
        #ALLOW DRAGnDROP
        self.setAcceptDrops(True)
        #INIT CHILD PARTS:
        self.dialogs = s.dialogs
        self.pref_import = PreferencesImport(self)

        self._appendMenubarAndPreferences()
        #CONNECT OWN SAVE/RESTORE FUNCTIONS TO THE SESSION:
        s.sigSave.connect(self._save)
        s.sigRestore.connect(self._restore)
        st = StatusBar()
        self.setStatusBar(st)

        #         def showOut(msg):
        #             if msg != '\n':
        #                 # after every message this new line character is emitted
        #                 # showing this hides the real message
        #                 st.showMessage(msg,9000)
        #         def showErr(msg):
        #             if msg != '\n':
        #                 st.showError(msg, 9000)
        s.streamOut.message.connect(st.showMessage)  #showOut)
        s.streamErr.message.connect(st.showError)  #showErr)

        self.framelessDisplays = {}  # contain all displays that are unbounded
        # from the main window and showed frameless

        self.addWorkspace()
        #PROGRESS BAR:
        self.progressBar = ProgressBar(st)
        st.setSizeGripEnabled(False)

    def isEmpty(self):
        return (self.centralWidget().count() == 1
                and not self.currentWorkspace().displays())

    def _save(self, state):
        state['gui'] = self.saveState()

    def _restore(self, state):
        return self.restoreState(state['gui'])

    def saveState(self):
        l = {}
        i = self.size()
        l['size'] = (i.width(), i.height())
        c = self.centralWidget()
        l['nWorkspaces'] = c.count()
        l['currentWorkspace'] = c.indexOf(self.currentWorkspace())
        l['maximized'] = self.isMaximized()
        l['fullscreen'] = self.menuBar().ckBox_fullscreen.isChecked()
        l['showTools'] = self.menu_toolbars.a_show.isChecked()
        #         session.addContentToSave(l, 'gui.txt')
        #WORKSPACES
        sw = l['workspaces'] = {}
        for w in self.workspaces():
            sw[w.number()] = w.saveState()

        l['undoRedo'] = self.undoRedo.saveState()
        return l

    def restoreState(self, l):
        self.resize(*l['size'])
        self.menuBar().setFullscreen(l['fullscreen'])
        if l['maximized']:
            self.showMaximized()
        else:
            self.showNormal()
        #WORKSPACES
        c = self.centralWidget()
        n = l['nWorkspaces']
        #CLOSE OLD:
        for w in self.workspaces():
            self.closeWorkspace(w)
            #ADD NEW:
        for _ in range(n - c.count()):
            self.addWorkspace()
            #RESTORE:
        self.showWorkspace(l['currentWorkspace'])
        lw = l['workspaces']
        for number, w in zip(lw, self.workspaces()):
            w.restoreState(lw[number])

        self.menu_toolbars.a_show.setChecked(l['showTools'])
        self._toggleShowSelectedToolbars(l['showTools'])

        self.undoRedo.restoreState(l['undoRedo'])

    def addFilePath(self, filepath):
        '''
        create a new display for one ore more given file paths
        '''
        if filepath:
            return self.currentWorkspace().addFiles([PathStr(filepath)])

    def openFile(self):
        '''
        create a new display for one ore more  files 
        '''
        filepaths = self.dialogs.getOpenFileNames()
        if filepaths:
            return self.currentWorkspace().addFiles(filepaths)

    def showDisplay(self, arg):
        '''
        show display as frame-less window 
        '[displaynumber], [area]' = (x,y,width,height)
        '''
        displaynumber, pos = eval(arg)
        if not pos:
            return self.hideDisplay(displaynumber)
        else:
            (x, y, width, height) = pos
        d = self.framelessDisplays.get(displaynumber, None)
        if not d:
            try:
                d = self.currentWorkspace().displaydict()[displaynumber]
                d.release()
                d.hideTitleBar()
                d.setWindowFlags(QtCore.Qt.FramelessWindowHint
                                 | QtCore.Qt.WindowStaysOnTopHint)
                self.framelessDisplays[displaynumber] = d
            except KeyError:
                print('displaynumber [%s] not known' % displaynumber)
                return
        d.move(QtCore.QPoint(x, y))
        d.resize(width, height)
        d.show()

    def hideDisplay(self, displaynumber):
        '''
        close frame-less display
        [displaynumber]
        '''
        d = self.framelessDisplays.pop(displaynumber, None)
        if d:
            d.showTitleBar()
            d.embedd()
        else:
            print('displaynumber [%s] not known' % displaynumber)

    def runScriptFromName(self, name):
        '''
        run a console script identified by name
        in the current display 
        '''
        w = self.currentWorkspace().getCurrentDisplay(
        ).tab.automation.tabs.widgetByName(name)
        if not w:
            raise Exception('couldnt find script [%s] in the current display' %
                            name)
        w.thread.start()

    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.setFixedHeight(25)
        #         m.setMaximumHeight(25)

        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(
            MEDIA_FOLDER.join('institution_logo.svg'))

        #hide the menu so toolbars can only be show/hidden via gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)

        #APPEND PREFERENCES
        t = m.file_preferences.tabs
        t.addTab(PreferencesView(self), 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        #APPEND MENUBAR
        #MENU - FILE
        f = m.menu_file
        p = f.action_preferences
        action_file = QtGui.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcut(
            QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_I))
        f.insertAction(p, action_file)
        f.insertSeparator(p)
        #MENU VIEW
        v = m.menu_view
        #ACTION PRINT VIEW
        aPrintView = QtGui.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
            lambda checked: self.currentWorkspace().setPrintView(checked))
        v.addAction(aPrintView)

        #SHOW/HIDE history
        aHistory = QtGui.QAction('Program history', v)
        aHistory.setShortcut(QtCore.Qt.Key_F4)

        aHistory.setCheckable(True)

        def showhideHistory(checked):
            s = self.currentWorkspace().middle_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 1.5
            return s.moveSplitter(r, 1)

        def isHistoryVisible():
            s = self.currentWorkspace().middle_splitter
            aHistory.setChecked(s.sizes()[1] != 0)

        aHistory.triggered.connect(showhideHistory)
        v.aboutToShow.connect(isHistoryVisible)
        v.addAction(aHistory)

        #SHOW/HIDE preferences
        aPref = QtGui.QAction('Dock preferences', v)
        aPref.setShortcut(QtCore.Qt.Key_F3)
        aPref.setCheckable(True)

        def showhidePref(checked):
            s = self.currentWorkspace().vert_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 3
            else:
                r = 0
            return s.moveSplitter(r, 1)

        def isPrefVisible():
            w = self.currentWorkspace()
            s = w.vert_splitter
            aPref.setChecked(s.sizes()[0] != 0)
            aPref.setEnabled(w.displayPrefTabs.isVisible())

        aPref.triggered.connect(showhidePref)
        v.aboutToShow.connect(isPrefVisible)
        v.addAction(aPref)

        #ACTION VIEW2CLIPBOARD
        aClipboard = QtGui.QAction('Copy view to clipboard', v)
        aClipboard.triggered.connect(
            lambda checked: self.currentWorkspace().copyViewToClipboard())
        v.addAction(aClipboard)
        #ACTION Display2CLIPBOARD
        aClipboard = QtGui.QAction('Copy active display to clipboard', v)
        aClipboard.triggered.connect(lambda checked: self.currentWorkspace().
                                     copyCurrentDisplayToClipboard())
        v.addAction(aClipboard)
        #MENU - TOOLS
        t = m.menu_tools = QtGui.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
        #ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name in (  #(1, 'Dot'), 
            (2, 'Graph'),
            (3, 'Image/Video'),
                #(4, 'Surface')
                #TODO:
                #(4, 'TODO: Surface'),
                #(5, 'TODO: Volume')
        ):
            mDisplay.addAction('%sD - %s' % (i - 1, name)).triggered.connect(
                lambda checked, i=i: self.currentWorkspace().addDisplay(axes=i
                                                                        ))
        #ADD TABLE
        t.addAction('Add Table').triggered.connect(
            lambda: self.currentWorkspace().addTableDock())
        #ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
            lambda: self.currentWorkspace().addTextDock())
        t.addSeparator()
        #DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
            self._duplicateCurrentDiplay)
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS)
        #MENU - TOOLBARS
        self.menu_toolbars = QtGui.QMenu('Toolbars', m)
        self.connect(self.menu_toolbars,
                     QtCore.SIGNAL("hovered(QAction *)"),
                     lambda action, m=self.menu_toolbars:
                     _showActionToolTipInMenu(m, action))

        #SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtGui.QAction('show', m)
        f = a.font()
        f.setBold(True)
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore(m.menu_workspace, self.menu_toolbars)
        #MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
            lambda checked: os.startfile(HELP_FILE))
        #TUTORIALS
        ####not used at the moment
        #         self.m_tutorials = TutorialMenu(
        #                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'),
        #                         openFunction=self._openFnForTutorial,
        #                         saveFunction=self.app.session.blockingSave)
        #         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
            lambda checked: os.startfile(
                'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
            lambda checked: os.startfile(
                'https://github.com/radjkarl/dataArtist/issues'))

    def _duplicateCurrentDiplay(self):
        d = self.currentWorkspace().getCurrentDisplay()
        if not d:
            raise Exception('no display chosen')
        d.duplicate()

    def _moveCurrentDiplayToWorkspace(self, i):
        w = self.currentWorkspace()
        ws = self.workspaces(i)
        w.moveCurrentDisplayToOtherWorkspace(ws)

    def _fillMenuDuplicateToOtherWS(self):
        c = self.centralWidget()
        self._m_duplDisp.clear()
        for i in range(c.count()):
            if i != c.currentIndex():
                t = '[%s]' % str(i + 1)
                a = QtGui.QAction(t, self._m_duplDisp)
                a.triggered.connect(lambda clicked, i=i, self=self: self.
                                    _moveCurrentDiplayToWorkspace(i))
                self._m_duplDisp.addAction(a)

    def _openFnForTutorial(self, path):
        self.app.session.setSessionPath(path)
        self.app.session.restoreCurrentState()

    def _listToolbarsInMenu(self):
        m = self.menu_toolbars
        #REMOVE OLD:
        m.clear()
        m.addAction(m.a_show)
        if m.a_show.isChecked():
            self.currentWorkspace().addShowToolBarAction(m)

    def _toggleShowSelectedToolbars(self, checked):
        self.currentWorkspace().toggleShowSelectedToolbars(checked)

    #DRAG AND GROP
    #<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    def keyPressEvent(self, event):
        if event.matches(QtGui.QKeySequence.Paste):
            self.dropEvent(QtGui.QApplication.clipboard())

    def dragEnterEvent(self, event):
        m = event.mimeData()
        if (m.hasUrls() or m.hasImage() or m.hasText()):
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def _getFilePathsFromUrls(self, urls):
        '''
        return a list of all file paths in event.mimeData.urls()
        '''
        l = []

        def _appendRecursive(path):
            if path.isfile():
                #file
                l.append(path)
            else:
                for f in path:
                    #for all files in folder
                    _appendRecursive(path.join(f))

        # one or more files/folders are dropped
        for url in urls:
            if url.isLocalFile():
                path = PathStr(url.toLocalFile().toLocal8Bit().data())
                if path.exists():
                    _appendRecursive(path)
        return l

    def dropEvent(self, event):
        '''what to do is sth. if dropped'''
        m = event.mimeData()

        #HTML CONTENT
        if m.hasHtml():
            (paths, data) = html2data(unicode(m.html()))
            self.currentWorkspace().addFiles(paths)
            #raise NotImplementedError('direct import of data (like tables) from a browser is not implemented at the moment')

        #FILES
        elif m.hasUrls():
            paths = self._getFilePathsFromUrls(m.urls())
            #filter from session files and open those:
            i = 0
            while i < len(paths):
                p = paths[i]
                #DATAARTIST SESSION
                if p.endswith(self.app.session.FTYPE):
                    if self.isEmpty():
                        self.app.session.replace(p)
                    else:
                        self.app.session.new(p)
                    paths.pop(i)
                else:
                    i += 1

            self.currentWorkspace().addFiles(paths)

        #TEXT/TABLES
        if m.hasText():
            txt = unicode(m.text())
            if self.txtIsTable(txt):
                self.currentWorkspace().addTableDock(text=txt)
            else:
                self.currentWorkspace().addTextDock(text=txt)

    #>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    @staticmethod
    def txtIsTable(txt):
        # for now just a simple check whether there is at least
        # on tab sign in the first line
        return txt[:txt.find('\n')].count('\t') > 0
Exemple #5
0
    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.setFixedHeight(25)
        #         m.setMaximumHeight(25)

        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(
            MEDIA_FOLDER.join('institution_logo.svg'))

        #hide the menu so toolbars can only be show/hidden via gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)

        #APPEND PREFERENCES
        t = m.file_preferences.tabs
        t.addTab(PreferencesView(self), 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        #APPEND MENUBAR
        #MENU - FILE
        f = m.menu_file
        p = f.action_preferences
        action_file = QtGui.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcut(
            QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_I))
        f.insertAction(p, action_file)
        f.insertSeparator(p)
        #MENU VIEW
        v = m.menu_view
        #ACTION PRINT VIEW
        aPrintView = QtGui.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
            lambda checked: self.currentWorkspace().setPrintView(checked))
        v.addAction(aPrintView)

        #SHOW/HIDE history
        aHistory = QtGui.QAction('Program history', v)
        aHistory.setShortcut(QtCore.Qt.Key_F4)

        aHistory.setCheckable(True)

        def showhideHistory(checked):
            s = self.currentWorkspace().middle_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 1.5
            return s.moveSplitter(r, 1)

        def isHistoryVisible():
            s = self.currentWorkspace().middle_splitter
            aHistory.setChecked(s.sizes()[1] != 0)

        aHistory.triggered.connect(showhideHistory)
        v.aboutToShow.connect(isHistoryVisible)
        v.addAction(aHistory)

        #SHOW/HIDE preferences
        aPref = QtGui.QAction('Dock preferences', v)
        aPref.setShortcut(QtCore.Qt.Key_F3)
        aPref.setCheckable(True)

        def showhidePref(checked):
            s = self.currentWorkspace().vert_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 3
            else:
                r = 0
            return s.moveSplitter(r, 1)

        def isPrefVisible():
            w = self.currentWorkspace()
            s = w.vert_splitter
            aPref.setChecked(s.sizes()[0] != 0)
            aPref.setEnabled(w.displayPrefTabs.isVisible())

        aPref.triggered.connect(showhidePref)
        v.aboutToShow.connect(isPrefVisible)
        v.addAction(aPref)

        #ACTION VIEW2CLIPBOARD
        aClipboard = QtGui.QAction('Copy view to clipboard', v)
        aClipboard.triggered.connect(
            lambda checked: self.currentWorkspace().copyViewToClipboard())
        v.addAction(aClipboard)
        #ACTION Display2CLIPBOARD
        aClipboard = QtGui.QAction('Copy active display to clipboard', v)
        aClipboard.triggered.connect(lambda checked: self.currentWorkspace().
                                     copyCurrentDisplayToClipboard())
        v.addAction(aClipboard)
        #MENU - TOOLS
        t = m.menu_tools = QtGui.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
        #ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name in (  #(1, 'Dot'), 
            (2, 'Graph'),
            (3, 'Image/Video'),
                #(4, 'Surface')
                #TODO:
                #(4, 'TODO: Surface'),
                #(5, 'TODO: Volume')
        ):
            mDisplay.addAction('%sD - %s' % (i - 1, name)).triggered.connect(
                lambda checked, i=i: self.currentWorkspace().addDisplay(axes=i
                                                                        ))
        #ADD TABLE
        t.addAction('Add Table').triggered.connect(
            lambda: self.currentWorkspace().addTableDock())
        #ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
            lambda: self.currentWorkspace().addTextDock())
        t.addSeparator()
        #DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
            self._duplicateCurrentDiplay)
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS)
        #MENU - TOOLBARS
        self.menu_toolbars = QtGui.QMenu('Toolbars', m)
        self.connect(self.menu_toolbars,
                     QtCore.SIGNAL("hovered(QAction *)"),
                     lambda action, m=self.menu_toolbars:
                     _showActionToolTipInMenu(m, action))

        #SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtGui.QAction('show', m)
        f = a.font()
        f.setBold(True)
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore(m.menu_workspace, self.menu_toolbars)
        #MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
            lambda checked: os.startfile(HELP_FILE))
        #TUTORIALS
        ####not used at the moment
        #         self.m_tutorials = TutorialMenu(
        #                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'),
        #                         openFunction=self._openFnForTutorial,
        #                         saveFunction=self.app.session.blockingSave)
        #         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
            lambda checked: os.startfile(
                'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
            lambda checked: os.startfile(
                'https://github.com/radjkarl/dataArtist/issues'))
Exemple #6
0
class Gui(MultiWorkspaceWindow):
    '''
    The main class to be called to create an instance of dataArtist
    '''
    def __init__(self, title='dataArtist', workspaceCls=Workspace):
        MultiWorkspaceWindow.__init__(self, workspaceCls, title)

        s = self.app.session

        # cannot resize to px size anymore since there
        # are high dpi screens around, therefore rescale relative:
        PX_FACTOR = QtWidgets.QApplication.instance(
        ).PX_FACTOR = QtGui.QPaintDevice.logicalDpiY(self) / 96
        self.resize(620 * PX_FACTOR, 550 * PX_FACTOR)

        # ALLOW DRAGnDROP
        self.setAcceptDrops(True)
        # INIT CHILD PARTS:
        self.dialogs = s.dialogs
        self.pref_import = PreferencesImport(self)

        self._appendMenubarAndPreferences()
        # CONNECT OWN SAVE/RESTORE FUNCTIONS TO THE SESSION:
        s.sigSave.connect(self._save)
        s.sigRestore.connect(self._restore)
        st = StatusBar()
        self.setStatusBar(st)
        # connect output to status bar:
        s.streamOut.message.connect(st.showMessage)
        s.streamErr.message.connect(st.showError)

        # dict that contains all displays that are unbounded
        # from the main window and showed frameless:
        self.framelessDisplays = {}

        self.addWorkspace()
        # PROGRESS BAR:
        self.progressBar = ProgressBar(st)
        st.setSizeGripEnabled(False)

    def isEmpty(self):
        return (self.centralWidget().count() == 1
                and not self.currentWorkspace().displays())

    def _save(self, state):
        state['gui'] = self.saveState()

    def _restore(self, state):
        return self.restoreState(state['gui'])

    def saveState(self):
        l = {}
        i = self.size()
        p = self.pos()
        l['geometry'] = (p.x(), p.y(), i.width(), i.height())
        #         l['desktop'] = QtGui.QApplication.desktop().screenNumber(self)
        c = self.centralWidget()
        l['nWorkspaces'] = c.count()
        l['currentWorkspace'] = c.indexOf(self.currentWorkspace())
        l['maximized'] = self.isMaximized()
        l['fullscreen'] = self.menuBar().ckBox_fullscreen.isChecked()
        l['showTools'] = self.menu_toolbars.a_show.isChecked()
        # WORKSPACES
        sw = l['workspaces'] = {}
        for w in self.workspaces():
            sw[w.number()] = w.saveState()
        l['undoRedo'] = self.undoRedo.saveState()
        # GLOBAL TOOLS
        tt = []
        for t in self.gTools:
            tt.append(t.saveState())
        l['globalTools'] = tt
        return l

    def restoreState(self, l):
        self.setGeometry(*l['geometry'])
        self.menuBar().setFullscreen(l['fullscreen'])
        if l['maximized']:
            self.showMaximized()
        else:
            self.showNormal()
        # WORKSPACES
        c = self.centralWidget()
        n = l['nWorkspaces']
        # CLOSE OLD:
        for w in self.workspaces():
            self.closeWorkspace(w)
            # ADD NEW:
        for _ in range(n - c.count()):
            self.addWorkspace()
            # RESTORE:
        self.showWorkspace(l['currentWorkspace'])
        lw = l['workspaces']
        for number, w in zip(lw, self.workspaces()):
            w.restoreState(lw[number])

        self.menu_toolbars.a_show.setChecked(l['showTools'])
        self._toggleShowSelectedToolbars(l['showTools'])

        self.undoRedo.restoreState(l['undoRedo'])
        # GLOBAL TOOLS
        for t, tt in zip(self.gTools, l['globalTools']):
            t.restoreState(tt)

    def addFilePath(self, filepath):
        '''
        create a new display for one ore more given file paths
        INPUT: "Path/To/File.txt"
        '''
        if filepath:
            return self.currentWorkspace().addFiles([PathStr(filepath)])

    def openFile(self):
        '''
        create a new display for one ore more  files
        '''
        filepaths = self.dialogs.getOpenFileNames()
        if filepaths:
            return self.currentWorkspace().addFiles(filepaths)

    def changeActiveDisplay(self, arg):
        '''
        change the active display
        INPUT: "[displaynumber]"
            e.g.:
            "4" --> make display 4 active display
        '''
        number = int(arg)
        self.currentWorkspace().changeDisplayNumber(number)

    def showDisplay(self, arg):
        '''
        show display as frame-less window
        INPUT: "[displaynumber], [(x,y,width,height)]'
            e.g.:
            "4, (0,0,100,200)" --> show display 4 at position 0,0 with size 100,200
            "3, False" --> hide display 3
        '''
        displaynumber, pos = eval(arg)
        if not pos:
            return self.hideDisplay(displaynumber)
        else:
            (x, y, width, height) = pos
        d = self.framelessDisplays.get(displaynumber, None)
        if not d:
            try:
                d = self.currentWorkspace().displaydict()[displaynumber]
                d.release()
                d.hideTitleBar()
                d.setWindowFlags(QtCore.Qt.FramelessWindowHint
                                 | QtCore.Qt.WindowStaysOnTopHint)
                self.framelessDisplays[displaynumber] = d
            except KeyError:
                print('displaynumber [%s] not known' % displaynumber)
                return
        d.move(QtCore.QPoint(x, y))
        d.resize(width, height)
        d.show()

    def hideDisplay(self, displaynumber):
        '''
        close frame-less display
        [displaynumber]
        '''
        d = self.framelessDisplays.pop(displaynumber, None)
        if d:
            d.showTitleBar()
            d.embedd()
        else:
            print('displaynumber [%s] not known' % displaynumber)

    def runScriptFromName(self, name):
        '''
        run an open script, identified by name
        in the current display
        INPUT: "[scriptname]"
            e.g.:
            'New' --> run a script, called 'New' in the current active display
        '''
        d = self.currentWorkspace().getCurrentDisplay()
        w = d.tab.automation.tabs.widgetByName(name.decode("utf-8"))
        if not w:
            raise Exception('couldnt find script [%s] in the current display' %
                            name)
        w.thread.start()

    def _appendMenubarAndPreferences(self):
        m = self.menuBar()
        m.aboutWidget.setModule(dataArtist)
        m.aboutWidget.setInstitutionLogo(
            ((MEDIA_FOLDER.join('institution_logo_crest.svg'),
              "http://www.lboro.ac.uk/research/crest/"),
             (MEDIA_FOLDER.join('institution_logo_seris.svg'),
              "http://www.seris.nus.edu.sg/")))

        # hide the menu so toolbars can only be show/hidden via
        # gui->view->toolbars:
        m.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)

        self.undoRedo = UndoRedo(MEDIA_FOLDER)

        self.gTools = GlobalTools()
        self.gTools.addWidget(self.undoRedo)

        m.setCornerWidget(self.gTools)

        # APPEND PREFERENCES
        pView = PreferencesView(self)
        # pView.setColorTheme('bright')

        t = m.file_preferences.tabs
        t.addTab(pView, 'View')
        t.addTab(self.pref_import, 'Import')
        t.addTab(PreferencesCommunication(self), 'Communication')
        # APPEND MENUBAR
        # MENU - FILE
        f = m.menu_file
        p = f.action_preferences
        action_file = QtWidgets.QAction('&Import', f)
        action_file.triggered.connect(self.openFile)
        action_file.setShortcut(
            QtGui.QKeySequence(QtCore.Qt.CTRL + QtCore.Qt.Key_I))
        f.insertAction(p, action_file)
        f.insertSeparator(p)
        # MENU VIEW
        v = m.menu_view
        # ACTION PRINT VIEW
        aPrintView = QtWidgets.QAction('Print view', v)
        aPrintView.setCheckable(True)
        aPrintView.triggered.connect(
            lambda checked: self.currentWorkspace().setPrintView(checked))
        v.addAction(aPrintView)

        # SHOW/HIDE history
        aHistory = QtWidgets.QAction('Program log', v)
        aHistory.setShortcut(QtCore.Qt.Key_F4)
        m.shortcutsWidget.addShortcut('F3', 'Show/Hide Program log')

        aHistory.setCheckable(True)

        def showhideHistory(checked):
            s = self.currentWorkspace().middle_splitter
            r = s.getRange(1)[1]
            if checked:
                r /= 1.5
            return s.moveSplitter(r, 1)

        def isHistoryVisible():
            s = self.currentWorkspace().middle_splitter
            aHistory.setChecked(s.sizes()[1] != 0)

        aHistory.triggered.connect(showhideHistory)
        v.aboutToShow.connect(isHistoryVisible)
        v.addAction(aHistory)

        # SHOW/HIDE preferences
        aPref = QtWidgets.QAction('Dock preferences', v)
        aPref.setShortcut(QtCore.Qt.Key_F3)
        aPref.setCheckable(True)
        m.shortcutsWidget.addShortcut('F3', 'Show/Hide Preferences')

        def isPrefVisible():
            w = self.currentWorkspace()
            s = w.vert_splitter
            return s.sizes()[0] != 0

        def showhidePref():
            s = self.currentWorkspace().vert_splitter
            if len(s) == 1:
                # preference tab is popped out
                return
            show = not isPrefVisible()
            if show:
                # restore last size:
                if hasattr(s, '_last_size'):
                    r = s._last_size
                else:
                    # initial width:
                    r = self.width() // 2.5
            else:
                # save current size:
                s._last_size = s.sizes()[0]
                r = 0
            return s.moveSplitter(r, 1)

        def setupPref():
            w = self.currentWorkspace()
            aPref.setChecked(isPrefVisible())
            aPref.setEnabled(w.displayPrefTabs.isVisible())

        aPref.triggered.connect(showhidePref)
        v.aboutToShow.connect(setupPref)
        v.addAction(aPref)

        mcopy = v.addMenu('Copy to clipboard')
        # ACTION VIEW2CLIPBOARD
        aClipboard = QtWidgets.QAction('All displays', v)
        aClipboard.triggered.connect(
            lambda checked: self.currentWorkspace().copyViewToClipboard())
        aClipboard.setShortcut(QtGui.QKeySequence('Ctrl+Shift+F12'))
        mcopy.addAction(aClipboard)
        # ACTION Display2CLIPBOARD
        aClipboard = QtWidgets.QAction('Active Display', v)
        aClipboard.triggered.connect(lambda checked: self.currentWorkspace().
                                     copyCurrentDisplayToClipboard())
        aClipboard.setShortcut(QtGui.QKeySequence('Ctrl+F12'))
        mcopy.addAction(aClipboard)

        # ACTION Display2CLIPBOARD
        aClipboard = QtWidgets.QAction('Active Display Item', v)
        aClipboard.triggered.connect(lambda checked: self.currentWorkspace().
                                     copyCurrentDisplayItemToClipboard())
        aClipboard.setShortcut(QtCore.Qt.Key_F12)
        mcopy.addAction(aClipboard)

        # MENU - TOOLS
        t = m.menu_tools = QtWidgets.QMenu('Dock')
        m.insertMenuBefore(m.menu_workspace, t)
        # ADD DISPLAY
        mDisplay = t.addMenu('Add Display')
        for i, name, key in (  # (1, 'Dot'),
            (2, 'Graph', QtCore.Qt.Key_F5),
            (3, 'Image/Video', QtCore.Qt.Key_F6),
                # TODO:
                # (4, 'Surface')
                # (5, 'TODO: Volume')
        ):
            a = mDisplay.addAction('%sD - %s' % (i - 1, name))
            a.setShortcut(key)
            a.triggered.connect(lambda checked, i=i: self.currentWorkspace().
                                addDisplay(axes=i))
        # ADD TABLE
        t.addAction('Add Table').triggered.connect(
            lambda: self.currentWorkspace().addTableDock())
        # ADD NOTEPAD
        t.addAction('Add Notepad').triggered.connect(
            lambda: self.currentWorkspace().addTextDock())
        t.addSeparator()
        # DUPLICATE CURRENT DOCK
        t.addAction('Duplicate current display').triggered.connect(
            self._duplicateCurrentDiplay)
        self._m_duplDisp = t.addMenu('Move current display to other workspace')
        self._m_duplDisp.aboutToShow.connect(self._fillMenuDuplicateToOtherWS)
        # MENU - TOOLBARS
        self.menu_toolbars = QtWidgets.QMenu('Toolbars', m)
        self.menu_toolbars.hovered[QtWidgets.QAction].connect(
            lambda action, m=self.menu_toolbars: _showActionToolTipInMenu(
                m, action))

        # SHOW ALL TOOLBARS - ACTION
        a = self.menu_toolbars.a_show = QtWidgets.QAction('show', m)
        f = a.font()
        f.setBold(True)
        a.setFont(f)
        a.setCheckable(True)
        a.setChecked(True)
        a.triggered.connect(self._toggleShowSelectedToolbars)

        self.menu_toolbars.aboutToShow.connect(self._listToolbarsInMenu)
        m.insertMenuBefore(m.menu_workspace, self.menu_toolbars)
        # MENU HELP
        m.menu_help.addAction('User manual').triggered.connect(
            lambda checked: os.startfile(HELP_FILE))
        # TUTORIALS
        # not used at the moment
        #         self.m_tutorials = TutorialMenu(
        #                         tutorialFolder=PathStr.getcwd('dataArtist').join('tutorials'),
        #                         openFunction=self._openFnForTutorial,
        #                         saveFunction=self.app.session.blockingSave)
        #         m.menu_help.addMenu(self.m_tutorials)

        m.menu_help.addAction('Online tutorials').triggered.connect(
            lambda checked: os.startfile(
                'http://www.youtube.com/channel/UCjjngrC3jPdx1HL8zJ8yqLQ'))
        m.menu_help.addAction('Support').triggered.connect(
            lambda checked: os.startfile(
                'https://github.com/radjkarl/dataArtist/issues'))

    def _duplicateCurrentDiplay(self):
        d = self.currentWorkspace().getCurrentDisplay()
        if not d:
            raise Exception('no display chosen')
        d.duplicate()

    def _moveCurrentDiplayToWorkspace(self, i):
        w = self.currentWorkspace()
        ws = self.workspaces(i)
        w.moveCurrentDisplayToOtherWorkspace(ws)

    def _fillMenuDuplicateToOtherWS(self):
        c = self.centralWidget()
        self._m_duplDisp.clear()
        for i in range(c.count()):
            if i != c.currentIndex():
                t = '[%s]' % str(i + 1)
                a = QtWidgets.QAction(t, self._m_duplDisp)
                a.triggered.connect(lambda clicked, i=i, self=self: self.
                                    _moveCurrentDiplayToWorkspace(i))
                self._m_duplDisp.addAction(a)

    def _openFnForTutorial(self, path):
        self.app.session.setSessionPath(path)
        self.app.session.restoreCurrentState()

    def _listToolbarsInMenu(self):
        m = self.menu_toolbars
        # REMOVE OLD:
        m.clear()
        m.addAction(m.a_show)
        if m.a_show.isChecked():
            self.currentWorkspace().addShowToolBarAction(m)

    def _toggleShowSelectedToolbars(self, checked):
        self.currentWorkspace().toggleShowSelectedToolbars(checked)

    # DRAG AND GROP
    # <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    def keyPressEvent(self, event):
        if event.matches(QtGui.QKeySequence.Paste):
            self.dropEvent(QtWidgets.QApplication.clipboard())

    def dragEnterEvent(self, event):
        m = event.mimeData()
        if (m.hasUrls() or m.hasImage() or m.hasText()):
            event.setDropAction(QtCore.Qt.CopyAction)
            event.accept()
        else:
            event.ignore()

    def _getFilePathsFromUrls(self, urls):
        '''
        return a list of all file paths in event.mimeData.urls()
        '''
        l = []

        def _appendRecursive(path):
            if path.isfile():
                # file
                l.append(path)
            else:
                for f in path:
                    # for all files in folder
                    _appendRecursive(path.join(f))

        # one or more files/folders are dropped
        for url in urls:
            if url.isLocalFile():
                path = PathStr(url.toLocalFile())
                if path.exists():
                    _appendRecursive(path)
        return l

    def dropEvent(self, event):
        m = event.mimeData()
        w = self.currentWorkspace()
        # HTML Images
        if m.hasHtml():
            paths = html2data(str(m.html()))
            w.addFiles(paths)
        # FILES
        elif m.hasUrls():
            paths = self._getFilePathsFromUrls(m.urls())
            # filter from session files and open those:
            i = 0
            while i < len(paths):
                p = paths[i]
                # DATAARTIST SESSION
                if p.endswith(self.app.session.FTYPE):
                    if self.isEmpty():
                        self.app.session.replace(p)
                    else:
                        self.app.session.new(p)
                    paths.pop(i)
                else:
                    i += 1
            w.addFiles(paths)
        # TEXT/TABLES
        elif m.hasText():
            txt = str(m.text())
            if self.txtIsTable(txt):
                w.addTableDock(text=txt)
            else:
                w.addTextDock(text=txt)

        elif m.hasImage():
            i = QtGui.QImage(m.imageData())
            w.addDisplay(data=[qImageToArray(i)])

    # >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    @staticmethod
    def txtIsTable(txt):
        # for now just a simple check whether there is at least
        # on tab sign in the first line
        return txt[:txt.find('\n')].count('\t') > 0