예제 #1
0
    def exportHtml(self, fileName=False):
        #self.makeError1()
        if fileName is False:
            self.fileDialog = FileDialog(self, "Save HTML as...",
                                         self.manager.getCurrentDir().name())
            #self.fileDialog.setFileMode(Qt.QFileDialog.AnyFile)
            self.fileDialog.setAcceptMode(Qt.QFileDialog.AcceptSave)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.exportHtml)
            return
        if fileName[-5:] != '.html':
            fileName += '.html'

        #doc = self.ui.output.document().toHtml('utf-8')
        #for e in self.displayedEntries:
        #if e.has_key('tracebackHtml'):
        #doc = re.sub(r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'%(str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        global pageTemplate
        doc = pageTemplate
        for e in self.displayedEntries:
            doc += self.cache[id(e)]
        for e in self.displayedEntries:
            if 'tracebackHtml' in e:
                doc = re.sub(
                    r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'
                    % (str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        #doc = self.ui.logView.page().currentFrame().toHtml()
        f = open(fileName, 'w')
        f.write(doc.encode('utf-8'))
        f.close()
예제 #2
0
 def showFileDialog(self):
     bd = self.manager.getBaseDir()
     if self.dialog is None:
         self.dialog = FileDialog()
         self.dialog.setFileMode(Qt.QFileDialog.DirectoryOnly)
         self.dialog.filesSelected.connect(self.setBaseDir)
     if bd is not None:
         self.dialog.setDirectory(bd.name())
     self.dialog.show()
예제 #3
0
 def openDbClicked(self):
     bd = self.man.getBaseDir()
     if bd is None:
         bd = ""
     else:
         bd = bd.name()
     self.fileDialog = FileDialog(self, "Select Database File", bd, "SQLite Database (*.sqlite *.sql);;All Files (*.*)")
     #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
     self.fileDialog.show()
     self.fileDialog.fileSelected.connect(self.openDb)
예제 #4
0
 def createDbClicked(self):
     bd = self.man.getBaseDir()
     if bd is None:
         raise Exception("Must select a base directory before creating database.")
     self.fileDialog = FileDialog(self, "Create Database File", bd.name(), "SQLite Database (*.sqlite *.sql);;All Files (*.*)")
     #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
     self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) 
     self.fileDialog.setOption(QtGui.QFileDialog.DontConfirmOverwrite)
     self.fileDialog.show()
     self.fileDialog.fileSelected.connect(self.createDb)
예제 #5
0
    def __init__(self, manager, name, config):
        Module.__init__(self, manager, name, config)
        #self.dm = self.manager.dataManager
        self.dm = getDataManager()
        self.win = Window()
        mp = os.path.dirname(__file__)
        self.win.setWindowIcon(QtGui.QIcon(os.path.join(mp, 'icon.png')))
        self.win.dm = self  ## so embedded widgets can find the module easily
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.win)
        self.ui.analysisWidget = FileAnalysisView.FileAnalysisView(
            self.ui.analysisTab, self)
        self.ui.analysisTab.layout().addWidget(self.ui.analysisWidget)
        self.ui.logWidget = FileLogView.FileLogView(self.ui.logTab, self)
        self.ui.logTab.layout().addWidget(self.ui.logWidget)

        self.win.show()
        w = self.ui.splitter.width()
        self.ui.splitter.setSizes([int(w * 0.4), int(w * 0.6)])
        self.ui.logDock.hide()
        self.dialog = FileDialog()
        self.dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly)
        self.ui.fileTreeWidget.setSelectionMode(
            QtGui.QAbstractItemView.ExtendedSelection)
        ## Load values into GUI
        #self.model = DMModel(self.manager.getBaseDir())
        #self.ui.fileTreeView.setModel(self.model)
        self.baseDirChanged()
        self.currentDirChanged()
        self.selFile = None
        self.updateNewFolderList()

        ## Make all connections needed
        self.ui.selectDirBtn.clicked.connect(self.showFileDialog)
        self.ui.setCurrentDirBtn.clicked.connect(self.setCurrentClicked)
        self.dialog.filesSelected.connect(self.setBaseDir)
        self.manager.sigBaseDirChanged.connect(self.baseDirChanged)
        self.manager.sigCurrentDirChanged.connect(self.currentDirChanged)
        self.manager.sigConfigChanged.connect(self.updateNewFolderList)
        self.manager.sigLogDirChanged.connect(self.updateLogDir)
        self.ui.setLogDirBtn.clicked.connect(self.setLogDir)
        self.ui.newFolderList.currentIndexChanged.connect(self.newFolder)
        self.ui.fileTreeWidget.itemSelectionChanged.connect(
            self.fileSelectionChanged)
        #self.ui.logEntryText.returnPressed.connect(self.logEntry)
        self.ui.fileDisplayTabs.currentChanged.connect(self.tabChanged)
        self.win.sigClosed.connect(self.quit)
        self.ui.analysisWidget.sigDbChanged.connect(self.analysisDbChanged)

        #self.logBtn = LogButton('Log')
        self.win.setStatusBar(StatusBar())
예제 #6
0
파일: LogWindow.py 프로젝트: hiuwo/acq4
    def exportHtml(self, fileName=False):
        # self.makeError1()
        if fileName is False:
            self.fileDialog = FileDialog(self, "Save HTML as...", self.manager.getCurrentDir().name())
            # self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
            self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.exportHtml)
            return
        if fileName[-5:] != ".html":
            fileName += ".html"

        # doc = self.ui.output.document().toHtml('utf-8')
        # for e in self.displayedEntries:
        # if e.has_key('tracebackHtml'):
        # doc = re.sub(r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'%(str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        global pageTemplate
        doc = pageTemplate
        for e in self.displayedEntries:
            doc += self.cache[id(e)]
        for e in self.displayedEntries:
            if e.has_key("tracebackHtml"):
                doc = re.sub(
                    r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>' % (str(e["id"]), str(e["id"])),
                    e["tracebackHtml"],
                    doc,
                )

        # doc = self.ui.logView.page().currentFrame().toHtml()
        f = open(fileName, "w")
        f.write(doc.encode("utf-8"))
        f.close()
예제 #7
0
 def __init__(self, manager, name, config):
     Module.__init__(self, manager, name, config)
     #self.dm = self.manager.dataManager
     self.dm = getDataManager()
     self.win = Window()
     mp = os.path.dirname(__file__)
     self.win.setWindowIcon(QtGui.QIcon(os.path.join(mp, 'icon.png')))
     self.win.dm = self  ## so embedded widgets can find the module easily
     self.ui = Ui_MainWindow()
     self.ui.setupUi(self.win)
     self.ui.analysisWidget = FileAnalysisView.FileAnalysisView(self.ui.analysisTab, self)
     self.ui.analysisTab.layout().addWidget(self.ui.analysisWidget)
     self.ui.logWidget = FileLogView.FileLogView(self.ui.logTab, self)
     self.ui.logTab.layout().addWidget(self.ui.logWidget)
     
     self.win.show()
     w = self.ui.splitter.width()
     self.ui.splitter.setSizes([int(w*0.4), int(w*0.6)])
     self.ui.logDock.hide()
     self.dialog = FileDialog()
     self.dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly)
     self.ui.fileTreeWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
     ## Load values into GUI
     #self.model = DMModel(self.manager.getBaseDir())
     #self.ui.fileTreeView.setModel(self.model)
     self.baseDirChanged()
     self.currentDirChanged()
     self.selFile = None
     self.updateNewFolderList()
     
     
     ## Make all connections needed
     self.ui.selectDirBtn.clicked.connect(self.showFileDialog)
     self.ui.setCurrentDirBtn.clicked.connect(self.setCurrentClicked)
     self.dialog.filesSelected.connect(self.setBaseDir)
     self.manager.sigBaseDirChanged.connect(self.baseDirChanged)
     self.manager.sigCurrentDirChanged.connect(self.currentDirChanged)
     self.manager.sigConfigChanged.connect(self.updateNewFolderList)
     self.manager.sigLogDirChanged.connect(self.updateLogDir)
     self.ui.setLogDirBtn.clicked.connect(self.setLogDir)
     self.ui.newFolderList.currentIndexChanged.connect(self.newFolder)
     self.ui.fileTreeWidget.itemSelectionChanged.connect(self.fileSelectionChanged)
     #self.ui.logEntryText.returnPressed.connect(self.logEntry)
     self.ui.fileDisplayTabs.currentChanged.connect(self.tabChanged)
     self.win.sigClosed.connect(self.quit)
     self.ui.analysisWidget.sigDbChanged.connect(self.analysisDbChanged)
     
     #self.logBtn = LogButton('Log')
     self.win.setStatusBar(StatusBar())
예제 #8
0
class FileAnalysisView(QtGui.QWidget):
    
    sigDbChanged = QtCore.Signal()
    
    def __init__(self, parent, mod):
        QtGui.QWidget.__init__(self, parent)
        self.ui = Ui_Form()
        self.ui.setupUi(self)
        self.man = acq4.Manager.getManager()
        self.mod = mod
        self.dbFile = None
        self.db = None
        self.mods = []
        self.currentModel = None
        
        self.populateModuleList()
        self.populateModelList()
        
        stateFile = os.path.join('modules', self.mod.name + '_db_file_list')
        files = self.mod.manager.readConfigFile(stateFile).get('db_file_list', [])
        self.ui.databaseCombo.addItem('')
        for f in files:
            self.ui.databaseCombo.addItem(f)
        
        
        self.ui.openDbBtn.clicked.connect(self.openDbClicked)
        self.ui.createDbBtn.clicked.connect(self.createDbClicked)
        self.ui.loadModuleBtn.clicked.connect(self.loadModule)
        self.ui.refreshDbBtn.clicked.connect(self.refreshDb)
        self.ui.dataModelCombo.currentIndexChanged.connect(self.loadModel)
        self.ui.analysisModuleList.currentItemChanged.connect(self.showModuleDescription)
        self.ui.analysisModuleList.itemDoubleClicked.connect(self.loadModule)
        self.ui.databaseCombo.currentIndexChanged.connect(self.dbComboChanged)
        

    def openDbClicked(self):
        bd = self.man.getBaseDir()
        if bd is None:
            bd = ""
        else:
            bd = bd.name()
        self.fileDialog = FileDialog(self, "Select Database File", bd, "SQLite Database (*.sqlite *.sql);;All Files (*.*)")
        #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.show()
        self.fileDialog.fileSelected.connect(self.openDb)
            
    def openDb(self, fileName):
        #fn = str(QtGui.QFileDialog.getOpenFileName(self, "Select Database File", self.man.getBaseDir().name(), "SQLite Database (*.sqlite)"))
        fileName = str(fileName)
        if fileName == '':
            return
        
        #if not fileName[-7:] == '.sqlite' and '.' not in fileName:
        #    fileName =+ '.sqlite'
        self.ui.databaseCombo.blockSignals(True)
        try:
            ## put fileName at the top of the list, write to disk
            files = [self.ui.databaseCombo.itemText(i) for i in range(self.ui.databaseCombo.count())]
            files.remove('')
            if fileName in files:
                files.remove(fileName)
            files = [fileName] + files
            self.ui.databaseCombo.clear()
            self.ui.databaseCombo.addItem('')
            for f in files:
                self.ui.databaseCombo.addItem(f)
            stateFile = os.path.join('modules', self.mod.name + '_db_file_list')
            self.mod.manager.writeConfigFile({'db_file_list': files}, stateFile)
            self.ui.databaseCombo.setCurrentIndex(1)
        finally:
            self.ui.databaseCombo.blockSignals(False)
            
        
        self.dbFile = fileName
        self.db = database.AnalysisDatabase(self.dbFile, dataModel=self.currentModel)
        self.sigDbChanged.emit()
        
    def dbComboChanged(self):
        fn = self.ui.databaseCombo.currentText()
        if fn == '':
            return
        if not os.path.exists(fn):
            raise Exception("Database file does not exist: %s" % fn)
        self.openDb(fn)
        
    def quit(self):
        if self.db is not None:
            self.db.close()
        
    def createDbClicked(self):
        bd = self.man.getBaseDir()
        if bd is None:
            raise Exception("Must select a base directory before creating database.")
        self.fileDialog = FileDialog(self, "Create Database File", bd.name(), "SQLite Database (*.sqlite *.sql);;All Files (*.*)")
        #self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
        self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave) 
        self.fileDialog.setOption(QtGui.QFileDialog.DontConfirmOverwrite)
        self.fileDialog.show()
        self.fileDialog.fileSelected.connect(self.createDb)
        
    def createDb(self, fileName):
        #fn = str(QtGui.QFileDialog.getSaveFileName(self, "Create Database File", self.man.getBaseDir().name(), "SQLite Database (*.sqlite)", None, QtGui.QFileDialog.DontConfirmOverwrite))
        fileName = str(fileName)
        if fileName is '':
            return
            
        self.dbFile = fileName
        self.db = database.AnalysisDatabase(self.dbFile, dataModel=self.currentModel, baseDir=self.man.getBaseDir())
        self.ui.databaseCombo.blockSignals(True)
        try:
            self.ui.databaseCombo.addItem(fileName)
            self.ui.databaseCombo.setCurrentIndex(self.ui.databaseCombo.count()-1)
        finally:
            self.ui.databaseCombo.blockSignals(False)
        self.sigDbChanged.emit()
        
    def refreshDb(self):
        if self.db is None:
            return
        self.db._readTableList()
        
    def addFileClicked(self):
        cf = self.mod.selectedFile()
        self.db.addDir(cf)
        
    def populateModuleList(self):
        for m in analysis.listModules():
            self.ui.analysisModuleList.addItem(m)
    
    def loadModule(self):
        mod = self.ui.analysisModuleList.currentItem()
        if mod is None:
            return
        modName = str(mod.text())
        #if self.ui.analysisCombo.currentIndex() == 0:
            #return
        #modName = str(self.ui.analysisCombo.currentText())
        #self.ui.analysisCombo.setCurrentIndex(0)
        mod = AnalysisHost.AnalysisHost(dataManager=self.mod, dataModel=self.currentModel, module=modName)
        self.mods.append(mod)
        self.man.modules[modName] = mod

    def populateModelList(self):
        self.ui.dataModelCombo.clear()
        self.ui.dataModelCombo.addItem('Load...')
        mods = models.listModels()
        for m in mods:
            self.ui.dataModelCombo.addItem(m)
        if len(mods) == 1:
            self.ui.dataModelCombo.setCurrentIndex(1)
            self.loadModel()
    
    def loadModel(self):
        if self.ui.dataModelCombo.currentIndex() == 0:
            return
        modName = str(self.ui.dataModelCombo.currentText())
        self.currentModel = models.loadModel(modName)
        acq4.Manager.getManager().dataModel = self.currentModel  ## make model globally available
        if self.db is not None:
            self.db.setDataModel(self.currentModel)
    
    
    def currentDatabase(self):
        return self.db
        
    def currentDataModel(self):
        return self.currentModel

    def showModuleDescription(self):
        mod = self.ui.analysisModuleList.currentItem()
        if mod is None:
            return
        modName = str(mod.text())
        cls = analysis.getModuleClass(modName)
        doc = cls.__doc__
        self.ui.modDescriptionText.setPlainText(doc)
예제 #9
0
class DataManager(Module):
    moduleDisplayName = "Data Manager"
    moduleCategory = "Acquisition"

    sigAnalysisDbChanged = Qt.Signal()

    def __init__(self, manager, name, config):
        Module.__init__(self, manager, name, config)
        self.dm = getDataManager()
        self.win = Window()
        mp = os.path.dirname(__file__)
        self.win.setWindowIcon(Qt.QIcon(os.path.join(mp, 'icon.png')))
        self.win.dm = self  ## so embedded widgets can find the module easily
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.win)
        self.ui.analysisWidget = FileAnalysisView.FileAnalysisView(
            self.ui.analysisTab, self)
        self.ui.analysisTab.layout().addWidget(self.ui.analysisWidget)
        self.ui.logWidget = FileLogView.FileLogView(self.ui.logTab, self)
        self.ui.logTab.layout().addWidget(self.ui.logWidget)

        self.win.show()
        w = self.ui.splitter.width()
        self.ui.splitter.setSizes([int(w * 0.4), int(w * 0.6)])
        self.ui.logDock.hide()
        self.dialog = None
        self.ui.fileTreeWidget.setSelectionMode(
            Qt.QAbstractItemView.ExtendedSelection)
        self.baseDirChanged()
        self.currentDirChanged()
        self.selFile = None
        self.updateNewFolderList()

        ## Make all connections needed
        self.ui.selectDirBtn.clicked.connect(self.showFileDialog)
        self.ui.setCurrentDirBtn.clicked.connect(self.setCurrentClicked)
        self.manager.sigBaseDirChanged.connect(self.baseDirChanged)
        self.manager.sigCurrentDirChanged.connect(self.currentDirChanged)
        self.manager.sigConfigChanged.connect(self.updateNewFolderList)
        self.manager.sigLogDirChanged.connect(self.updateLogDir)
        self.ui.setLogDirBtn.clicked.connect(self.setLogDir)
        self.ui.newFolderList.currentIndexChanged.connect(self.newFolder)
        self.ui.fileTreeWidget.itemSelectionChanged.connect(
            self.fileSelectionChanged)
        self.ui.fileDisplayTabs.currentChanged.connect(self.tabChanged)
        self.win.sigClosed.connect(self.quit)
        self.ui.analysisWidget.sigDbChanged.connect(self.analysisDbChanged)
        self.ui.baseDirText.editingFinished.connect(self.baseDirTextChanged)

        self.win.setStatusBar(StatusBar())

    def updateNewFolderList(self):
        self.ui.newFolderList.clear()
        conf = self.manager.config['folderTypes']
        #print "folderTypes:", self.manager.config['folderTypes'].keys()
        self.ui.newFolderList.clear()
        self.ui.newFolderList.addItems(['New...', 'Folder'] +
                                       list(conf.keys()))

    def baseDirChanged(self):
        dh = self.manager.getBaseDir()
        self.baseDir = dh
        if dh is None:
            self.ui.baseDirText.setText('')
        else:
            self.ui.baseDirText.setText(dh.name())
        self.ui.fileTreeWidget.setBaseDirHandle(dh)

    def loadLog(self, *args, **kwargs):
        pass

    def selectFile(self, path):
        if isinstance(path, six.string_types):
            path = getHandle(path)
        self.ui.fileTreeWidget.select(path)

    def setLogDir(self):
        d = self.selectedFile()
        if not isinstance(d, DirHandle):
            d = d.parent()
        self.manager.setLogDir(d)

    def updateLogDir(self, d):
        self.ui.logDirText.setText(d.name(relativeTo=self.baseDir))

    def setCurrentClicked(self):
        #print "click"
        handle = self.selectedFile()
        if handle is None:
            #print "no selection"
            return
        if not handle.isDir():
            handle = handle.parent()
        self.manager.setCurrentDir(handle)

    def currentDirChanged(self, name=None, change=None, args=()):
        if change in [None, 'moved', 'renamed', 'parent']:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
                dirName = ""
            else:
                dirName = newDir.name(relativeTo=self.baseDir)
            self.ui.currentDirText.setText(str(dirName))
            self.ui.fileTreeWidget.setCurrentDir(newDir)
        elif change == 'log':
            self.updateLogView(*args)
        if change == None:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
            else:
                self.loadLog(newDir, self.ui.logView)

    def showFileDialog(self):
        bd = self.manager.getBaseDir()
        if self.dialog is None:
            self.dialog = FileDialog()
            self.dialog.setFileMode(Qt.QFileDialog.DirectoryOnly)
            self.dialog.filesSelected.connect(self.setBaseDir)
        if bd is not None:
            self.dialog.setDirectory(bd.name())
        self.dialog.show()

    def baseDirTextChanged(self):
        path = str(self.ui.baseDirText.text())
        if not os.path.isdir(path):
            raise ValueError("Path %s does not exist" % path)
        self.setBaseDir(path)

    def setBaseDir(self, dirName):
        if isinstance(dirName, list):
            if len(dirName) == 1:
                dirName = dirName[0]
            else:
                raise Exception("Caught. Please to be examined: %s" %
                                str(dirName))
        if dirName is None:
            return
        if os.path.isdir(dirName):
            self.manager.setBaseDir(dirName)
        else:
            raise Exception("Storage directory is invalid")

    def selectedFile(self):
        """Return the currently selected file"""
        items = self.ui.fileTreeWidget.selectedItems()
        if len(items) > 0:
            return items[0].handle
        else:
            return None

    def newFolder(self):
        if self.ui.newFolderList.currentIndex() < 1:
            return

        ftype = str(self.ui.newFolderList.currentText())
        self.ui.newFolderList.setCurrentIndex(0)

        cdir = self.manager.getCurrentDir()
        if not cdir.isManaged():
            cdir.createIndex()

        if ftype == 'Folder':
            nd = cdir.mkdir('NewFolder', autoIncrement=True)
            #item = self.model.handleIndex(nd)
            self.ui.fileTreeWidget.editItem(nd)
        else:
            spec = self.manager.config['folderTypes'][ftype]
            name = time.strftime(spec['name'])

            ## Determine where to put the new directory
            parent = cdir
            try:
                checkDir = cdir
                for i in range(5):
                    if not checkDir.isManaged():
                        break
                    inf = checkDir.info()
                    if 'dirType' in inf and inf['dirType'] == ftype:
                        parent = checkDir.parent()
                        break
                    #else:
                    #print "dir no match:", spec, inf
                    checkDir = checkDir.parent()
            except:
                printExc(
                    "Error while deciding where to put new folder (using currentDir by default)"
                )

            ## make
            nd = parent.mkdir(name, autoIncrement=True)

            ## Add meta-info
            info = {'dirType': ftype}
            if spec.get('experimentalUnit', False):
                info['expUnit'] = True
            nd.setInfo(info)

            self.ui.fileTreeWidget.refresh(
                parent
            )  ## fileTreeWidget waits a while before updating; force it to refresh immediately.
            self.ui.fileTreeWidget.select(nd)

        logMsg("Created new folder: %s" % nd.name(relativeTo=self.baseDir),
               msgType='status',
               importance=7)
        self.manager.setCurrentDir(nd)

    def fileSelectionChanged(self):
        #print "file selection changed"
        if self.selFile is not None:
            try:
                self.selFile.sigChanged.disconnect(self.selectedFileAltered)
            except TypeError:
                pass

        fh = self.selectedFile()
        self.manager.currentFile = fh  ## Make this really easy to pick up from an interactive prompt.
        self.loadFile(fh)
        self.selFile = fh
        if fh is not None:
            self.selFile.sigChanged.connect(self.selectedFileAltered)

    def loadFile(self, fh):
        if fh is None:
            self.ui.fileInfo.setCurrentFile(None)
            self.ui.dataViewWidget.setCurrentFile(None)
            self.ui.logWidget.selectedFileChanged(None)
            self.ui.fileNameLabel.setText('')
        else:
            self.ui.fileNameLabel.setText(fh.name(relativeTo=self.baseDir))
            self.tabChanged()

    def tabChanged(self, n=None):
        if n is None:
            n = self.ui.fileDisplayTabs.currentIndex()
        fh = self.selectedFile()
        if n == 0:
            self.ui.fileInfo.setCurrentFile(fh)
        elif n == 1:
            self.ui.logWidget.selectedFileChanged(fh)
        elif n == 2:
            self.ui.dataViewWidget.setCurrentFile(fh)

    def selectedFileAltered(self, name, change, args):
        if change in ['parent', 'renamed', 'moved'
                      ] and self.selFile is not None:
            self.ui.fileTreeWidget.select(
                self.selFile)  ## re-select file if it has moved.
            self.ui.fileNameLabel.setText(
                self.selFile.name(relativeTo=self.baseDir))

    def quit(self):
        ## Silly: needed to prevent lockup on some systems.
        #print "      module quitting.."
        self.ui.fileTreeWidget.quit()
        self.ui.analysisWidget.quit()
        #print "      deleted dialog, calling superclass quit.."
        Module.quit(self)
        #print "      module quit done"
        #print backtrace()

    def currentDatabase(self):
        return self.ui.analysisWidget.currentDatabase()

    def dataModel(self):
        return self.ui.analysisWidget.currentDataModel()

    def analysisDbChanged(self):
        self.sigAnalysisDbChanged.emit()
예제 #10
0
class DataManager(Module):

    sigAnalysisDbChanged = QtCore.Signal()

    def __init__(self, manager, name, config):
        Module.__init__(self, manager, name, config)
        #self.dm = self.manager.dataManager
        self.dm = getDataManager()
        self.win = Window()
        mp = os.path.dirname(__file__)
        self.win.setWindowIcon(QtGui.QIcon(os.path.join(mp, 'icon.png')))
        self.win.dm = self  ## so embedded widgets can find the module easily
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.win)
        self.ui.analysisWidget = FileAnalysisView.FileAnalysisView(
            self.ui.analysisTab, self)
        self.ui.analysisTab.layout().addWidget(self.ui.analysisWidget)
        self.ui.logWidget = FileLogView.FileLogView(self.ui.logTab, self)
        self.ui.logTab.layout().addWidget(self.ui.logWidget)

        self.win.show()
        w = self.ui.splitter.width()
        self.ui.splitter.setSizes([int(w * 0.4), int(w * 0.6)])
        self.ui.logDock.hide()
        self.dialog = FileDialog()
        self.dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly)
        self.ui.fileTreeWidget.setSelectionMode(
            QtGui.QAbstractItemView.ExtendedSelection)
        ## Load values into GUI
        #self.model = DMModel(self.manager.getBaseDir())
        #self.ui.fileTreeView.setModel(self.model)
        self.baseDirChanged()
        self.currentDirChanged()
        self.selFile = None
        self.updateNewFolderList()

        ## Make all connections needed
        self.ui.selectDirBtn.clicked.connect(self.showFileDialog)
        self.ui.setCurrentDirBtn.clicked.connect(self.setCurrentClicked)
        self.dialog.filesSelected.connect(self.setBaseDir)
        self.manager.sigBaseDirChanged.connect(self.baseDirChanged)
        self.manager.sigCurrentDirChanged.connect(self.currentDirChanged)
        self.manager.sigConfigChanged.connect(self.updateNewFolderList)
        self.manager.sigLogDirChanged.connect(self.updateLogDir)
        self.ui.setLogDirBtn.clicked.connect(self.setLogDir)
        self.ui.newFolderList.currentIndexChanged.connect(self.newFolder)
        self.ui.fileTreeWidget.itemSelectionChanged.connect(
            self.fileSelectionChanged)
        #self.ui.logEntryText.returnPressed.connect(self.logEntry)
        self.ui.fileDisplayTabs.currentChanged.connect(self.tabChanged)
        self.win.sigClosed.connect(self.quit)
        self.ui.analysisWidget.sigDbChanged.connect(self.analysisDbChanged)

        #self.logBtn = LogButton('Log')
        self.win.setStatusBar(StatusBar())
        #self.win.statusBar().addPermanentWidget(self.logBtn)
        #self.win.statusBar().setFixedHeight(25)
        #self.win.statusBar().layout().setSpacing(0)

    #def hasInterface(self, interface):
    #return interface in ['DataSource']

    def updateNewFolderList(self):
        self.ui.newFolderList.clear()
        conf = self.manager.config['folderTypes']
        #print "folderTypes:", self.manager.config['folderTypes'].keys()
        self.ui.newFolderList.clear()
        self.ui.newFolderList.addItems(['New...', 'Folder'] + conf.keys())

    def baseDirChanged(self):
        dh = self.manager.getBaseDir()
        self.baseDir = dh
        if dh is None:
            self.ui.baseDirText.setText('')
        else:
            self.ui.baseDirText.setText(dh.name())
        self.ui.fileTreeWidget.setBaseDirHandle(dh)

    def loadLog(self, *args, **kwargs):
        pass

    def setLogDir(self):
        d = self.selectedFile()
        if not isinstance(d, DirHandle):
            d = d.parent()
        self.manager.setLogDir(d)

    def updateLogDir(self, d):
        self.ui.logDirText.setText(d.name(relativeTo=self.baseDir))

    def setCurrentClicked(self):
        #print "click"
        handle = self.selectedFile()
        if handle is None:
            #print "no selection"
            return
        if not handle.isDir():
            handle = handle.parent()
        #dh = self.manager.dirHandle(newDir)
        self.manager.setCurrentDir(handle)

    def currentDirChanged(self, name=None, change=None, args=()):
        if change in [None, 'moved', 'renamed', 'parent']:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
                dirName = ""
            else:
                dirName = newDir.name(relativeTo=self.baseDir)
            self.ui.currentDirText.setText(str(dirName))
            self.ui.fileTreeWidget.setCurrentDir(newDir)
        elif change == 'log':
            self.updateLogView(*args)
        if change == None:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
            else:
                self.loadLog(newDir, self.ui.logView)

    #def loadLog(self, dirHandle, widget, recursive=0):
    #widget.clear()
    #log = dirHandle.readLog(recursive)
    #for line in self.logRender(log):
    #widget.append(line)

    def showFileDialog(self):
        bd = self.manager.getBaseDir()
        if bd is not None:
            self.dialog.setDirectory(bd.name())
        self.dialog.show()

    def setBaseDir(self, dirName):
        if isinstance(dirName, list):
            if len(dirName) == 1:
                dirName = dirName[0]
            else:
                raise Exception("Caught. Please to be examined: %s" %
                                str(dirName))
        #if dirName is None:
        #dirName = QtGui.QFileDialog.getExistingDirectory()
        #if type(dirName) is QtCore.QStringList:
        #    dirName = str(dirName[0])
        #raise Exception("Caught. Please to be examined.")
        #if type(dirName) is QtCore.QStringList:
        #dirName = str(dirName[0])
        #elif type(dirName) is QtCore.QString:
        #dirName = str(dirName)
        if dirName is None:
            return
        if os.path.isdir(dirName):
            self.manager.setBaseDir(dirName)
        else:
            raise Exception("Storage directory is invalid")

    def selectedFile(self):
        """Return the currently selected file"""
        items = self.ui.fileTreeWidget.selectedItems()
        if len(items) > 0:
            return items[0].handle
        else:
            return None
        #sel = list(self.ui.fileTreeWidget.selectedIndexes())
        #if len(sel) == 0:
        #    return None
        #if len(sel) == 1:
        #    index = sel[0]
        #else:
        #    raise Exception("Error - multiple items selected")
        ##print "index:", index.internalPointer()
        #if index.internalPointer() is None:
        #    return None
        #return self.model.handle(index)

    def newFolder(self):
        if self.ui.newFolderList.currentIndex() < 1:
            return

        ftype = str(self.ui.newFolderList.currentText())
        self.ui.newFolderList.setCurrentIndex(0)

        cdir = self.manager.getCurrentDir()
        if not cdir.isManaged():
            cdir.createIndex()

        if ftype == 'Folder':
            nd = cdir.mkdir('NewFolder', autoIncrement=True)
            #item = self.model.handleIndex(nd)
            self.ui.fileTreeWidget.editItem(nd)
        else:
            spec = self.manager.config['folderTypes'][ftype]
            name = time.strftime(spec['name'])

            ## Determine where to put the new directory
            parent = cdir
            try:
                checkDir = cdir
                for i in range(5):
                    if not checkDir.isManaged():
                        break
                    inf = checkDir.info()
                    if 'dirType' in inf and inf['dirType'] == ftype:
                        parent = checkDir.parent()
                        break
                    #else:
                    #print "dir no match:", spec, inf
                    checkDir = checkDir.parent()
            except:
                printExc(
                    "Error while deciding where to put new folder (using currentDir by default)"
                )

            ## make
            nd = parent.mkdir(name, autoIncrement=True)

            ## Add meta-info
            info = {'dirType': ftype}
            if spec.get('experimentalUnit', False):
                info['expUnit'] = True
            nd.setInfo(info)

            ## set display to info
            #self.showFileInfo(nd)

            #index = self.model.handleIndex(nd)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Clear)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Select)
            self.ui.fileTreeWidget.refresh(
                parent
            )  ## fileTreeWidget waits a while before updating; force it to refresh immediately.
            self.ui.fileTreeWidget.select(nd)
            ##self.ui.fileInfo.setCurrentFile(nd)

        logMsg("Created new folder: %s" % nd.name(relativeTo=self.baseDir),
               msgType='status',
               importance=7)
        self.manager.setCurrentDir(nd)

    def fileSelectionChanged(self):
        #print "file selection changed"
        if self.selFile is not None:
            #QtCore.QObject.disconnect(self.selFile, QtCore.SIGNAL('changed'), self.selectedFileAltered)
            try:
                self.selFile.sigChanged.disconnect(self.selectedFileAltered)
            except TypeError:
                pass

        fh = self.selectedFile()
        self.manager.currentFile = fh  ## Make this really easy to pick up from an interactive prompt.
        self.loadFile(fh)
        self.selFile = fh
        if fh is not None:
            #QtCore.QObject.connect(self.selFile, QtCore.SIGNAL('changed'), self.selectedFileAltered)
            self.selFile.sigChanged.connect(self.selectedFileAltered)

    def loadFile(self, fh):
        #self.ui.selectedLogView.clear()
        if fh is None:
            self.ui.fileInfo.setCurrentFile(None)
            self.ui.dataViewWidget.setCurrentFile(None)
            self.ui.logWidget.selectedFileChanged(None)
            self.ui.fileNameLabel.setText('')
        else:
            #self.ui.fileInfo.setCurrentFile(fh)
            #self.ui.dataViewWidget.setCurrentFile(fh)
            self.ui.fileNameLabel.setText(fh.name(relativeTo=self.baseDir))
            #if fh.isDir():
            #self.loadLog(fh, self.ui.selectedLogView, recursive=3)
            self.tabChanged()

    def tabChanged(self, n=None):
        if n is None:
            n = self.ui.fileDisplayTabs.currentIndex()
        fh = self.selectedFile()
        if n == 0:
            self.ui.fileInfo.setCurrentFile(fh)
        elif n == 1:
            self.ui.logWidget.selectedFileChanged(fh)
            #if fh.isDir():
            #self.loadLog(fh, self.ui.selectedLogView, recursive=3)
        elif n == 2:
            self.ui.dataViewWidget.setCurrentFile(fh)

    def selectedFileAltered(self, name, change, args):
        if change in ['parent', 'renamed', 'moved'
                      ] and self.selFile is not None:
            #index = self.model.handleIndex(self.selFile)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Clear)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Select)
            self.ui.fileTreeWidget.select(
                self.selFile)  ## re-select file if it has moved.
            self.ui.fileNameLabel.setText(
                self.selFile.name(relativeTo=self.baseDir))

        #self.fileSelectionChanged()

    #def logEntry(self):
    #text = str(self.ui.logEntryText.text())
    #cd = self.manager.getCurrentDir()
    #self.ui.logEntryText.setText('')
    #if text == '' or cd is None:
    #return
    #cd.logMsg(text, {'source': 'user'})

    #def updateLogView(self, *args):
    #msg = args[0]
    #self.ui.logView.append(self.logRender(msg))
    ##print "new log msg"

    #def logRender(self, log):
    #returnList = True
    #if type(log) is dict:
    #log = [log]
    #returnList = False
    #elif type(log) is not list:
    #raise Exception('logRender requires dict or list of dicts as argument')

    #lines = []
    #for msg in log:
    #t = time.strftime('%Y.%m.%d %H:%M:%S', time.localtime(msg['__timestamp__']))
    #style = 'color: #000; font-style: normal'
    #sourceStyles = {
    #'user': '******'
    #}
    #if 'source' in msg and msg['source'] in sourceStyles:
    #style = sourceStyles[msg['source']]
    #parts = ["<span style='color: #888'>[%s]</span>" % t]
    #if 'subdir' in msg:
    #parts.append(msg['subdir'])
    #parts.append("<span style='%s'>%s</span>" % (style, msg['__message__']))
    #lines.append('&nbsp;&nbsp;'.join(parts))
    #if returnList:
    #return lines
    #else:
    #return lines[0]

    def quit(self):
        ## Silly: needed to prevent lockup on some systems.
        #print "      module quitting.."
        self.ui.fileTreeWidget.quit()
        self.ui.analysisWidget.quit()
        #sip.delete(self.dialog)
        #print "      deleted dialog, calling superclass quit.."
        Module.quit(self)
        #print "      module quit done"
        #print backtrace()

    def currentDatabase(self):
        return self.ui.analysisWidget.currentDatabase()

    def dataModel(self):
        return self.ui.analysisWidget.currentDataModel()

    def analysisDbChanged(self):
        self.sigAnalysisDbChanged.emit()
예제 #11
0
파일: LogWindow.py 프로젝트: hiuwo/acq4
class LogWidget(QtGui.QWidget):

    sigDisplayEntry = QtCore.Signal(object)  ## for thread-safetyness
    sigAddEntry = QtCore.Signal(object)  ## for thread-safetyness
    sigScrollToAnchor = QtCore.Signal(object)  # for internal use.

    def __init__(self, parent, manager):
        QtGui.QWidget.__init__(self, parent)
        self.ui = LogWidgetTemplate.Ui_Form()
        self.manager = manager
        self.ui.setupUi(self)
        # self.ui.input.hide()
        self.ui.filterTree.topLevelItem(1).setExpanded(True)

        self.entries = []  ## stores all log entries in memory
        self.cache = {}  ## for storing html strings of entries that have already been processed
        self.displayedEntries = []
        # self.currentEntries = None ## recordArray that stores currently displayed entries -- so that if filters get more restrictive we can just refilter this list instead of filtering everything
        self.typeFilters = []
        self.importanceFilter = 0
        self.dirFilter = False
        self.entryArrayBuffer = np.zeros(
            1000,
            dtype=[  ### a record array for quick filtering of entries
                ("index", "int32"),
                ("importance", "int32"),
                ("msgType", "|S10"),
                ("directory", "|S100"),
                ("entryId", "int32"),
            ],
        )
        self.entryArray = self.entryArrayBuffer[:0]

        self.filtersChanged()

        self.sigDisplayEntry.connect(self.displayEntry, QtCore.Qt.QueuedConnection)
        self.sigAddEntry.connect(self.addEntry, QtCore.Qt.QueuedConnection)
        self.ui.exportHtmlBtn.clicked.connect(self.exportHtml)
        self.ui.filterTree.itemChanged.connect(self.setCheckStates)
        self.ui.importanceSlider.valueChanged.connect(self.filtersChanged)
        # self.ui.logView.linkClicked.connect(self.linkClicked)
        self.ui.output.anchorClicked.connect(self.linkClicked)
        self.sigScrollToAnchor.connect(self.scrollToAnchor, QtCore.Qt.QueuedConnection)

        # page = self.ui.logView.page()
        # page.setLinkDelegationPolicy(page.DelegateAllLinks)

    def loadFile(self, f):
        """Load the file, f. f must be able to be read by configfile.py"""
        log = configfile.readConfigFile(f)
        self.entries = []
        self.entryArrayBuffer = np.zeros(
            len(log),
            dtype=[
                ("index", "int32"),
                ("importance", "int32"),
                ("msgType", "|S10"),
                ("directory", "|S100"),
                ("entryId", "int32"),
            ],
        )
        self.entryArray = self.entryArrayBuffer[:]

        i = 0
        for k, v in log.iteritems():
            v["id"] = k[9:]  ## record unique ID to facilitate HTML generation (javascript needs this ID)
            self.entries.append(v)
            self.entryArray[i] = np.array(
                [
                    (
                        i,
                        v.get("importance", 5),
                        v.get("msgType", "status"),
                        v.get("currentDir", ""),
                        v.get("entryId", v["id"]),
                    )
                ],
                dtype=[
                    ("index", "int32"),
                    ("importance", "int32"),
                    ("msgType", "|S10"),
                    ("directory", "|S100"),
                    ("entryId", "int32"),
                ],
            )
            i += 1

        self.filterEntries()  ## puts all entries through current filters and displays the ones that pass

    def addEntry(self, entry):
        ## All incoming messages begin here

        ## for thread-safetyness:
        isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread()
        if not isGuiThread:
            self.sigAddEntry.emit(entry)
            return

        self.entries.append(entry)
        i = len(self.entryArray)

        entryDir = entry.get("currentDir", None)
        if entryDir is None:
            entryDir = ""

        arr = np.array(
            [(i, entry["importance"], entry["msgType"], entryDir, entry["id"])],
            dtype=[
                ("index", "int32"),
                ("importance", "int32"),
                ("msgType", "|S10"),
                ("directory", "|S100"),
                ("entryId", "int32"),
            ],
        )

        ## make more room if needed
        if len(self.entryArrayBuffer) == len(self.entryArray):
            newArray = np.empty(len(self.entryArrayBuffer) + 1000, self.entryArrayBuffer.dtype)
            newArray[: len(self.entryArray)] = self.entryArray
            self.entryArrayBuffer = newArray
        self.entryArray = self.entryArrayBuffer[: len(self.entryArray) + 1]
        # self.entryArray[i] = [(i, entry['importance'], entry['msgType'], entry['currentDir'])]
        self.entryArray[i] = arr
        self.checkDisplay(entry)  ## displays the entry if it passes the current filters
        # np.append(self.entryArray, np.array(i, [[i, entry['importance'], entry['msgType'], entry['currentDir']]]), dtype = [('index', int), ('importance', int), ('msgType', str), ('directory', str)])

    def setCheckStates(self, item, column):
        if item == self.ui.filterTree.topLevelItem(1):
            if item.checkState(0):
                for i in range(item.childCount()):
                    item.child(i).setCheckState(0, QtCore.Qt.Checked)
        elif item.parent() == self.ui.filterTree.topLevelItem(1):
            if not item.checkState(0):
                self.ui.filterTree.topLevelItem(1).setCheckState(0, QtCore.Qt.Unchecked)
        self.filtersChanged()

    def filtersChanged(self):
        ### Update self.typeFilters, self.importanceFilter, and self.dirFilter to reflect changes.
        tree = self.ui.filterTree

        self.typeFilters = []
        for i in range(tree.topLevelItem(1).childCount()):
            child = tree.topLevelItem(1).child(i)
            if tree.topLevelItem(1).checkState(0) or child.checkState(0):
                text = child.text(0)
                self.typeFilters.append(unicode(text))

        self.importanceFilter = self.ui.importanceSlider.value()

        self.updateDirFilter()
        # self.dirFilter = self.manager.getDirOfSelectedFile().name()
        # else:
        # self.dirFilter = False

        self.filterEntries()

    def updateDirFilter(self, dh=None):
        if self.ui.filterTree.topLevelItem(0).checkState(0):
            if dh == None:
                self.dirFilter = self.manager.getDirOfSelectedFile().name()
            else:
                self.dirFilter = dh.name()
        else:
            self.dirFilter = False

    def filterEntries(self):
        """Runs each entry in self.entries through the filters and displays if it makes it through."""
        ### make self.entries a record array, then filtering will be much faster (to OR true/false arrays, + them)
        typeMask = self.entryArray["msgType"] == ""
        for t in self.typeFilters:
            typeMask += self.entryArray["msgType"] == t
        mask = (self.entryArray["importance"] > self.importanceFilter) * typeMask
        if self.dirFilter != False:
            d = np.ascontiguousarray(self.entryArray["directory"])
            j = len(self.dirFilter)
            i = len(d)
            d = d.view(np.byte).reshape(i, 100)[:, :j]
            d = d.reshape(i * j).view("|S%d" % j)
            mask *= d == self.dirFilter

        self.ui.output.clear()
        global Stylesheet
        self.ui.output.document().setDefaultStyleSheet(Stylesheet)
        # global pageTemplate
        # self.ui.logView.setHtml(pageTemplate)
        indices = list(self.entryArray[mask]["index"])
        inds = indices

        # if self.dirFilter != False:
        # j = len(self.dirFilter)
        # for i, n in inds:
        # if not self.entries[n]['currentDir'][:j] == self.dirFilter:
        # indices.pop(i)

        self.displayEntry([self.entries[i] for i in indices])

    def checkDisplay(self, entry):
        ### checks whether entry passes the current filters and displays it if it does.
        if entry["msgType"] not in self.typeFilters:
            return
        elif entry["importance"] < self.importanceFilter:
            return
        elif self.dirFilter is not False:
            if entry["currentDir"][: len(self.dirFilter)] != self.dirFilter:
                return
        else:
            self.displayEntry([entry])

    def displayEntry(self, entries):
        ## entries should be a list of log entries

        ## for thread-safetyness:
        isGuiThread = QtCore.QThread.currentThread() == QtCore.QCoreApplication.instance().thread()
        if not isGuiThread:
            self.sigDisplayEntry.emit(entries)
            return

        for entry in entries:
            if not self.cache.has_key(id(entry)):
                self.cache[id(entry)] = self.generateEntryHtml(entry)

                ## determine message color:
                # if entry['msgType'] == 'status':
                # color = 'green'
                # elif entry['msgType'] == 'user':
                # color = 'blue'
                # elif entry['msgType'] == 'error':
                # color = 'red'
                # elif entry['msgType'] == 'warning':
                # color = '#DD4400' ## orange
                # else:
                # color = 'black'

                # if entry.has_key('exception') or entry.has_key('docs') or entry.has_key('reasons'):
                ##self.displayComplexMessage(entry, color)
                # self.displayComplexMessage(entry)
                # else:
                # self.displayText(entry['message'], entry, color, timeStamp=entry['timestamp'])

            # for x in self.cache[id(entry)]:
            # self.ui.output.appendHtml(x)

            html = self.cache[id(entry)]
            # frame = self.ui.logView.page().currentFrame()
            # isMax = frame.scrollBarValue(QtCore.Qt.Vertical) == frame.scrollBarMaximum(QtCore.Qt.Vertical)
            sb = self.ui.output.verticalScrollBar()
            isMax = sb.value() == sb.maximum()

            # frame.findFirstElement('body').appendInside(html)
            self.ui.output.append(html)
            self.displayedEntries.append(entry)

            if isMax:
                ## can't scroll to end until the web frame has processed the html change
                # frame.setScrollBarValue(QtCore.Qt.Vertical, frame.scrollBarMaximum(QtCore.Qt.Vertical))

                ## Calling processEvents anywhere inside an error handler is forbidden
                ## because this can lead to Qt complaining about paint() recursion.
                # QtGui.QApplication.processEvents()
                # self.ui.output.scrollToAnchor(str(entry['id']))

                self.sigScrollToAnchor.emit(str(entry["id"]))  ## queued connection
            # self.ui.logView.update()

    def scrollToAnchor(self, anchor):
        self.ui.output.scrollToAnchor(anchor)

    def generateEntryHtml(self, entry):
        msg = self.cleanText(entry["message"])

        reasons = ""
        docs = ""
        exc = ""
        if entry.has_key("reasons"):
            reasons = self.formatReasonStrForHTML(entry["reasons"])
        if entry.has_key("docs"):
            docs = self.formatDocsStrForHTML(entry["docs"])
        if entry.get("exception", None) is not None:
            exc = self.formatExceptionForHTML(entry, entryId=entry["id"])

        extra = reasons + docs + exc
        if extra != "":
            # extra = "<div class='logExtra'>" + extra + "</div>"
            extra = "<table class='logExtra'><tr><td>" + extra + "</td></tr></table>"

        # return """
        # <div class='entry'>
        # <div class='%s'>
        # <span class='timestamp'>%s</span>
        # <span class='message'>%s</span>
        #%s
        # </div>
        # </div>
        # """ % (entry['msgType'], entry['timestamp'], msg, extra)
        return """
        <a name="%s"/><table class='entry'><tr><td>
            <table class='%s'><tr><td>
                <span class='timestamp'>%s</span>
                <span class='message'>%s</span>
                %s
            </td></tr></table>
        </td></tr></table>
        """ % (
            str(entry["id"]),
            entry["msgType"],
            entry["timestamp"],
            msg,
            extra,
        )

        # if entry.has_key('exception') or entry.has_key('docs') or entry.has_key('reasons'):
        ##self.displayComplexMessage(entry, color)
        # return self.generateComplex(entry)
        # else:
        # return self.generateSimple(entry['message'], entry, color, timeStamp=entry['timestamp'])
        ##self.displayText(entry['message'], entry, color, timeStamp=entry['timestamp'])

    @staticmethod
    def cleanText(text):
        text = re.sub(r"&", "&amp;", text)
        text = re.sub(r">", "&gt;", text)
        text = re.sub(r"<", "&lt;", text)
        text = re.sub(r"\n", "<br/>\n", text)
        return text

    # def displayText(self, msg, entry, colorStr='black', timeStamp=None, clean=True):
    # if clean:
    # msg = self.cleanText(msg)

    # if msg[-1:] == '\n':
    # msg = msg[:-1]
    # msg = '<br />'.join(msg.split('\n'))
    # if timeStamp is not None:
    # strn = '<b style="color:black"> %s </b> <span style="color:%s"> %s </span>' % (timeStamp, colorStr, msg)
    # else:
    # strn = '<span style="color:%s"> %s </span>' % (colorStr, msg)
    ##self.ui.output.appendHtml(strn)
    # self.cache[id(entry)].append(strn)

    # def displayComplexMessage(self, entry, color='black'):
    # self.displayText(entry['message'], entry, color, timeStamp = entry['timestamp'], clean=True)
    # if entry.has_key('reasons'):
    # reasons = self.formatReasonStrForHTML(entry['reasons'])
    # self.displayText(reasons, entry, 'black', clean=False)
    # if entry.has_key('docs'):
    # docs = self.formatDocsStrForHTML(entry['docs'])
    # self.displayText(docs, entry, 'black', clean=False)
    # if entry.get('exception', None) is not None:
    # self.displayException(entry['exception'], entry, 'black', tracebacks=entry.get('traceback', None))

    def formatExceptionForHTML(self, entry, exception=None, count=1, entryId=None):
        ### Here, exception is a dict that holds the message, reasons, docs, traceback and oldExceptions (which are also dicts, with the same entries)
        ## the count and tracebacks keywords are for calling recursively
        if exception is None:
            exception = entry["exception"]
        # if tracebacks is None:
        # tracebacks = []

        indent = 10

        text = self.cleanText(exception["message"])
        text = re.sub(r"^HelpfulException: ", "", text)
        # if exception.has_key('oldExc'):
        # self.displayText("&nbsp;"*indent + str(count)+'. ' + text, entry, color, clean=False)
        # else:
        # self.displayText("&nbsp;"*indent + str(count)+'. Original error: ' + text, entry, color, clean=False)
        messages = [text]
        # print "\n", messages, "\n"

        if exception.has_key("reasons"):
            reasons = self.formatReasonsStrForHTML(exception["reasons"])
            text += reasons
            # self.displayText(reasons, entry, color, clean=False)
        if exception.has_key("docs"):
            docs = self.formatDocsStrForHTML(exception["docs"])
            # self.displayText(docs, entry, color, clean=False)
            text += docs

        traceback = [self.formatTracebackForHTML(exception["traceback"], count)]
        text = [text]

        if exception.has_key("oldExc"):
            exc, tb, msgs = self.formatExceptionForHTML(entry, exception["oldExc"], count=count + 1)
            text.extend(exc)
            messages.extend(msgs)
            traceback.extend(tb)

        # else:
        # if len(tracebacks)==count+1:
        # n=0
        # else:
        # n=1
        # for i, tb in enumerate(tracebacks):
        # self.displayTraceback(tb, entry, number=i+n)
        if count == 1:
            exc = '<div class="exception"><ol>' + "\n".join(["<li>%s</li>" % ex for ex in text]) + "</ol></div>"
            tbStr = "\n".join(
                [
                    "<li><b>%s</b><br/><span class='traceback'>%s</span></li>" % (messages[i], tb)
                    for i, tb in enumerate(traceback)
                ]
            )
            # traceback = "<div class=\"traceback\" id=\"%s\"><ol>"%str(entryId) + tbStr + "</ol></div>"
            entry["tracebackHtml"] = tbStr

            # return exc + '<a href="#" onclick="showDiv(\'%s\')">Show traceback</a>'%str(entryId) + traceback
            return exc + '<a href="exc:%s">Show traceback %s</a>' % (str(entryId), str(entryId))
        else:
            return text, traceback, messages

    def formatTracebackForHTML(self, tb, number):
        try:
            tb = [line for line in tb if not line.startswith("Traceback (most recent call last)")]
        except:
            print "\n" + str(tb) + "\n"
            raise
        return re.sub(" ", "&nbsp;", ("").join(map(self.cleanText, tb)))[:-1]
        # tb = [self.cleanText(strip(x)) for x in tb]
        # lines = []
        # prefix = ''
        # for l in ''.join(tb).split('\n'):
        # if l == '':
        # continue
        # if l[:9] == "Traceback":
        # prefix = ' ' + str(number) + '. '
        # continue
        # spaceCount = 0
        # while l[spaceCount] == ' ':
        # spaceCount += 1
        # if prefix is not '':
        # spaceCount -= 1
        # lines.append("&nbsp;"*(spaceCount*4) + prefix + l)
        # prefix = ''
        # return '<div class="traceback">' + '<br />'.join(lines) + '</div>'
        # self.displayText('<br />'.join(lines), entry, color, clean=False)

    def formatReasonsStrForHTML(self, reasons):
        # indent = 6
        reasonStr = "<table class='reasons'><tr><td>Possible reasons include:\n<ul>\n"
        for r in reasons:
            r = self.cleanText(r)
            reasonStr += "<li>" + r + "</li>\n"
            # reasonStr += "&nbsp;"*22 + chr(97+i) + ". " + r + "<br>"
        reasonStr += "</ul></td></tr></table>\n"
        return reasonStr

    def formatDocsStrForHTML(self, docs):
        # indent = 6
        docStr = "<div class='docRefs'>Relevant documentation:\n<ul>\n"
        for d in docs:
            d = self.cleanText(d)
            docStr += '<li><a href="doc:%s">%s</a></li>\n' % (d, d)
        docStr += "</ul></div>\n"
        return docStr

    def exportHtml(self, fileName=False):
        # self.makeError1()
        if fileName is False:
            self.fileDialog = FileDialog(self, "Save HTML as...", self.manager.getCurrentDir().name())
            # self.fileDialog.setFileMode(QtGui.QFileDialog.AnyFile)
            self.fileDialog.setAcceptMode(QtGui.QFileDialog.AcceptSave)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.exportHtml)
            return
        if fileName[-5:] != ".html":
            fileName += ".html"

        # doc = self.ui.output.document().toHtml('utf-8')
        # for e in self.displayedEntries:
        # if e.has_key('tracebackHtml'):
        # doc = re.sub(r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'%(str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        global pageTemplate
        doc = pageTemplate
        for e in self.displayedEntries:
            doc += self.cache[id(e)]
        for e in self.displayedEntries:
            if e.has_key("tracebackHtml"):
                doc = re.sub(
                    r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>' % (str(e["id"]), str(e["id"])),
                    e["tracebackHtml"],
                    doc,
                )

        # doc = self.ui.logView.page().currentFrame().toHtml()
        f = open(fileName, "w")
        f.write(doc.encode("utf-8"))
        f.close()

    def makeError1(self):
        ### just for testing error logging
        try:
            self.makeError2()
            # print x
        except:
            t, exc, tb = sys.exc_info()
            # logExc(message="This button doesn't work", reasons='reason a, reason b', docs='documentation')
            # if isinstance(exc, HelpfulException):
            # exc.prependErr("Button doesn't work", (t,exc,tb), reasons = ["It's supposed to raise an error for testing purposes", "You're doing it wrong."])
            # raise
            # else:
            printExc("This is the message sent to printExc.")
            # raise HelpfulException(message='This button does not work.', exc=(t, exc, tb), reasons=["It's supposed to raise an error for testing purposes", "You're doing it wrong."])

    def makeError2(self):
        ### just for testing error logging
        try:
            print y
        except:
            t, exc, tb = sys.exc_info()
            raise HelpfulException(
                message="msg from makeError",
                exc=(t, exc, tb),
                reasons=["reason one", "reason 2"],
                docs=["what, you expect documentation?"],
            )

    def linkClicked(self, url):
        url = url.toString()
        if url[:4] == "doc:":
            self.manager.showDocumentation(url[4:])
        elif url[:4] == "exc:":
            cursor = self.ui.output.document().find("Show traceback %s" % url[4:])
            try:
                tb = self.entries[int(url[4:]) - 1]["tracebackHtml"]
            except IndexError:
                try:
                    tb = self.entries[self.entryArray[self.entryArray["entryId"] == (int(url[4:]))]["index"]][
                        "tracebackHtml"
                    ]
                except:
                    print "requested index %d, but only %d entries exist." % (int(url[4:]) - 1, len(self.entries))
                    raise
            cursor.insertHtml(tb)

    def clear(self):
        # self.ui.logView.setHtml("")
        self.ui.output.clear()
        self.displayedEntryies = []
예제 #12
0
class DataManager(Module):
    
    sigAnalysisDbChanged = QtCore.Signal()
    
    def __init__(self, manager, name, config):
        Module.__init__(self, manager, name, config)
        #self.dm = self.manager.dataManager
        self.dm = getDataManager()
        self.win = Window()
        mp = os.path.dirname(__file__)
        self.win.setWindowIcon(QtGui.QIcon(os.path.join(mp, 'icon.png')))
        self.win.dm = self  ## so embedded widgets can find the module easily
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self.win)
        self.ui.analysisWidget = FileAnalysisView.FileAnalysisView(self.ui.analysisTab, self)
        self.ui.analysisTab.layout().addWidget(self.ui.analysisWidget)
        self.ui.logWidget = FileLogView.FileLogView(self.ui.logTab, self)
        self.ui.logTab.layout().addWidget(self.ui.logWidget)
        
        self.win.show()
        w = self.ui.splitter.width()
        self.ui.splitter.setSizes([int(w*0.4), int(w*0.6)])
        self.ui.logDock.hide()
        self.dialog = FileDialog()
        self.dialog.setFileMode(QtGui.QFileDialog.DirectoryOnly)
        self.ui.fileTreeWidget.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
        ## Load values into GUI
        #self.model = DMModel(self.manager.getBaseDir())
        #self.ui.fileTreeView.setModel(self.model)
        self.baseDirChanged()
        self.currentDirChanged()
        self.selFile = None
        self.updateNewFolderList()
        
        
        ## Make all connections needed
        self.ui.selectDirBtn.clicked.connect(self.showFileDialog)
        self.ui.setCurrentDirBtn.clicked.connect(self.setCurrentClicked)
        self.dialog.filesSelected.connect(self.setBaseDir)
        self.manager.sigBaseDirChanged.connect(self.baseDirChanged)
        self.manager.sigCurrentDirChanged.connect(self.currentDirChanged)
        self.manager.sigConfigChanged.connect(self.updateNewFolderList)
        self.manager.sigLogDirChanged.connect(self.updateLogDir)
        self.ui.setLogDirBtn.clicked.connect(self.setLogDir)
        self.ui.newFolderList.currentIndexChanged.connect(self.newFolder)
        self.ui.fileTreeWidget.itemSelectionChanged.connect(self.fileSelectionChanged)
        #self.ui.logEntryText.returnPressed.connect(self.logEntry)
        self.ui.fileDisplayTabs.currentChanged.connect(self.tabChanged)
        self.win.sigClosed.connect(self.quit)
        self.ui.analysisWidget.sigDbChanged.connect(self.analysisDbChanged)
        
        #self.logBtn = LogButton('Log')
        self.win.setStatusBar(StatusBar())
        #self.win.statusBar().addPermanentWidget(self.logBtn)
        #self.win.statusBar().setFixedHeight(25)
        #self.win.statusBar().layout().setSpacing(0)
        
    #def hasInterface(self, interface):
        #return interface in ['DataSource']

    def updateNewFolderList(self):
        self.ui.newFolderList.clear()
        conf = self.manager.config['folderTypes']
        #print "folderTypes:", self.manager.config['folderTypes'].keys()
        self.ui.newFolderList.clear()
        self.ui.newFolderList.addItems(['New...', 'Folder'] + conf.keys())
        
    def baseDirChanged(self):
        dh = self.manager.getBaseDir()
        self.baseDir = dh
        if dh is None:
            self.ui.baseDirText.setText('')
        else:
            self.ui.baseDirText.setText(dh.name())
        self.ui.fileTreeWidget.setBaseDirHandle(dh)
        
    def loadLog(self, *args, **kwargs):
        pass

    def setLogDir(self):
        d = self.selectedFile()
        if not isinstance(d, DirHandle):
            d = d.parent()
        self.manager.setLogDir(d)
        
    def updateLogDir(self, d):
        self.ui.logDirText.setText(d.name(relativeTo=self.baseDir))
    
    def setCurrentClicked(self):
        #print "click"
        handle = self.selectedFile()
        if handle is None:
            #print "no selection"
            return
        if not handle.isDir():
            handle = handle.parent()
        #dh = self.manager.dirHandle(newDir)
        self.manager.setCurrentDir(handle)

    def currentDirChanged(self, name=None, change=None, args=()):
        if change in [None, 'moved', 'renamed', 'parent']:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
                dirName = ""
            else:
                dirName = newDir.name(relativeTo=self.baseDir)
            self.ui.currentDirText.setText(str(dirName))
            self.ui.fileTreeWidget.setCurrentDir(newDir)
        elif change == 'log':
            self.updateLogView(*args)
        if change == None:
            try:
                newDir = self.manager.getCurrentDir()
            except:
                newDir = None
            else:
                self.loadLog(newDir, self.ui.logView)

    #def loadLog(self, dirHandle, widget, recursive=0):
        #widget.clear()
        #log = dirHandle.readLog(recursive)
        #for line in self.logRender(log):
            #widget.append(line)
            

    def showFileDialog(self):
        bd = self.manager.getBaseDir()
        if bd is not None:
            self.dialog.setDirectory(bd.name())
        self.dialog.show()

    def setBaseDir(self, dirName):
        if isinstance(dirName, list):
            if len(dirName) == 1:
                dirName = dirName[0]
            else:
                raise Exception("Caught. Please to be examined: %s" % str(dirName))
        #if dirName is None:
            #dirName = QtGui.QFileDialog.getExistingDirectory()
        #if type(dirName) is QtCore.QStringList:
        #    dirName = str(dirName[0])
            #raise Exception("Caught. Please to be examined.")
        #if type(dirName) is QtCore.QStringList:
            #dirName = str(dirName[0])
        #elif type(dirName) is QtCore.QString:
            #dirName = str(dirName)
        if dirName is None:
            return
        if os.path.isdir(dirName):
            self.manager.setBaseDir(dirName)
        else:
            raise Exception("Storage directory is invalid")
            
    def selectedFile(self):
        """Return the currently selected file"""
        items = self.ui.fileTreeWidget.selectedItems()
        if len(items) > 0:
            return items[0].handle
        else:
            return None
        #sel = list(self.ui.fileTreeWidget.selectedIndexes())
        #if len(sel) == 0:
        #    return None
        #if len(sel) == 1:
        #    index = sel[0]
        #else:
        #    raise Exception("Error - multiple items selected")
        ##print "index:", index.internalPointer()
        #if index.internalPointer() is None:
        #    return None
        #return self.model.handle(index)

    def newFolder(self):
        if self.ui.newFolderList.currentIndex() < 1:
            return
            
        ftype = str(self.ui.newFolderList.currentText())
        self.ui.newFolderList.setCurrentIndex(0)
        
        cdir = self.manager.getCurrentDir()
        if not cdir.isManaged():
            cdir.createIndex()
        
        if ftype == 'Folder':
            nd = cdir.mkdir('NewFolder', autoIncrement=True)
            #item = self.model.handleIndex(nd)
            self.ui.fileTreeWidget.editItem(nd)
        else:
            spec = self.manager.config['folderTypes'][ftype]
            name = time.strftime(spec['name'])
                
            ## Determine where to put the new directory
            parent = cdir
            try:
                checkDir = cdir
                for i in range(5):
                    if not checkDir.isManaged():
                        break
                    inf = checkDir.info()
                    if 'dirType' in inf and inf['dirType'] == ftype:
                        parent = checkDir.parent()
                        break
                    #else:
                        #print "dir no match:", spec, inf
                    checkDir = checkDir.parent()
            except:
                printExc("Error while deciding where to put new folder (using currentDir by default)")
            
            ## make
            nd = parent.mkdir(name, autoIncrement=True)
            
            ## Add meta-info
            info = {'dirType': ftype}
            if spec.get('experimentalUnit', False):
                info['expUnit'] = True
            nd.setInfo(info)
            
            ## set display to info
            #self.showFileInfo(nd)
            
            #index = self.model.handleIndex(nd)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Clear)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Select)
            self.ui.fileTreeWidget.refresh(parent)  ## fileTreeWidget waits a while before updating; force it to refresh immediately.
            self.ui.fileTreeWidget.select(nd)
            ##self.ui.fileInfo.setCurrentFile(nd)
            
        logMsg("Created new folder: %s" %nd.name(relativeTo=self.baseDir), msgType='status', importance=7)   
        self.manager.setCurrentDir(nd)


    def fileSelectionChanged(self):
        #print "file selection changed"
        if self.selFile is not None:
            #QtCore.QObject.disconnect(self.selFile, QtCore.SIGNAL('changed'), self.selectedFileAltered)
            try:
                self.selFile.sigChanged.disconnect(self.selectedFileAltered)
            except TypeError:
                pass
        
        fh = self.selectedFile()
        self.manager.currentFile = fh  ## Make this really easy to pick up from an interactive prompt.
        self.loadFile(fh)
        self.selFile = fh
        if fh is not None:
            #QtCore.QObject.connect(self.selFile, QtCore.SIGNAL('changed'), self.selectedFileAltered)
            self.selFile.sigChanged.connect(self.selectedFileAltered)
        
    def loadFile(self, fh):
        #self.ui.selectedLogView.clear()
        if fh is None:
            self.ui.fileInfo.setCurrentFile(None)
            self.ui.dataViewWidget.setCurrentFile(None)
            self.ui.logWidget.selectedFileChanged(None)
            self.ui.fileNameLabel.setText('')
        else:
            #self.ui.fileInfo.setCurrentFile(fh)
            #self.ui.dataViewWidget.setCurrentFile(fh)
            self.ui.fileNameLabel.setText(fh.name(relativeTo=self.baseDir))
            #if fh.isDir():
                #self.loadLog(fh, self.ui.selectedLogView, recursive=3)
            self.tabChanged()

    def tabChanged(self, n=None):
        if n is None:
            n = self.ui.fileDisplayTabs.currentIndex()
        fh = self.selectedFile()
        if n == 0:
            self.ui.fileInfo.setCurrentFile(fh)
        elif n == 1:
            self.ui.logWidget.selectedFileChanged(fh)
            #if fh.isDir():
                #self.loadLog(fh, self.ui.selectedLogView, recursive=3)
        elif n == 2:
            self.ui.dataViewWidget.setCurrentFile(fh)
            

    def selectedFileAltered(self, name, change, args):
        if change in ['parent', 'renamed', 'moved'] and self.selFile is not None:
            #index = self.model.handleIndex(self.selFile)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Clear)
            #self.ui.fileTreeView.selectionModel().select(index, QtGui.QItemSelectionModel.Select)
            self.ui.fileTreeWidget.select(self.selFile)  ## re-select file if it has moved.
            self.ui.fileNameLabel.setText(self.selFile.name(relativeTo=self.baseDir))
        
        #self.fileSelectionChanged()
        
    #def logEntry(self):
        #text = str(self.ui.logEntryText.text())
        #cd = self.manager.getCurrentDir()
        #self.ui.logEntryText.setText('')
        #if text == '' or cd is None:
            #return
        #cd.logMsg(text, {'source': 'user'})
        
    #def updateLogView(self, *args):
        #msg = args[0]
        #self.ui.logView.append(self.logRender(msg))
        ##print "new log msg"
        
    #def logRender(self, log):
        #returnList = True
        #if type(log) is dict:
            #log = [log]
            #returnList = False
        #elif type(log) is not list:
            #raise Exception('logRender requires dict or list of dicts as argument')
            
        #lines = []
        #for msg in log:
            #t = time.strftime('%Y.%m.%d %H:%M:%S', time.localtime(msg['__timestamp__']))
            #style = 'color: #000; font-style: normal'
            #sourceStyles = {
                #'user': '******'
            #}
            #if 'source' in msg and msg['source'] in sourceStyles:
                #style = sourceStyles[msg['source']]
            #parts = ["<span style='color: #888'>[%s]</span>" % t]
            #if 'subdir' in msg:
                #parts.append(msg['subdir'])
            #parts.append("<span style='%s'>%s</span>" % (style, msg['__message__']))
            #lines.append('&nbsp;&nbsp;'.join(parts))
        #if returnList:
            #return lines
        #else:
            #return lines[0]
            
    def quit(self):
        ## Silly: needed to prevent lockup on some systems.
        #print "      module quitting.."
        self.ui.fileTreeWidget.quit()
        self.ui.analysisWidget.quit()
        #sip.delete(self.dialog)
        #print "      deleted dialog, calling superclass quit.."
        Module.quit(self)
        #print "      module quit done"
        #print backtrace()
        
    def currentDatabase(self):
        return self.ui.analysisWidget.currentDatabase()
        
    def dataModel(self):
        return self.ui.analysisWidget.currentDataModel()
        
    def analysisDbChanged(self):
        self.sigAnalysisDbChanged.emit()
        
        
예제 #13
0
class LogWidget(Qt.QWidget):

    sigDisplayEntry = Qt.Signal(object)  ## for thread-safetyness
    sigAddEntry = Qt.Signal(object)  ## for thread-safetyness
    sigScrollToAnchor = Qt.Signal(object)  # for internal use.

    def __init__(self, parent, manager):
        Qt.QWidget.__init__(self, parent)
        self.ui = LogWidgetTemplate.Ui_Form()
        self.manager = manager
        self.ui.setupUi(self)
        #self.ui.input.hide()
        self.ui.filterTree.topLevelItem(1).setExpanded(True)

        self.entries = []  ## stores all log entries in memory
        self.cache = {
        }  ## for storing html strings of entries that have already been processed
        self.displayedEntries = []
        #self.currentEntries = None ## recordArray that stores currently displayed entries -- so that if filters get more restrictive we can just refilter this list instead of filtering everything
        self.typeFilters = []
        self.importanceFilter = 0
        self.dirFilter = False
        self.entryArrayBuffer = np.zeros(
            1000,
            dtype=[  ### a record array for quick filtering of entries
                ('index', 'int32'), ('importance', 'int32'),
                ('msgType', '|S10'), ('directory', '|S100'),
                ('entryId', 'int32')
            ])
        self.entryArray = self.entryArrayBuffer[:0]

        self.filtersChanged()

        self.sigDisplayEntry.connect(self.displayEntry, Qt.Qt.QueuedConnection)
        self.sigAddEntry.connect(self.addEntry, Qt.Qt.QueuedConnection)
        self.ui.exportHtmlBtn.clicked.connect(self.exportHtml)
        self.ui.filterTree.itemChanged.connect(self.setCheckStates)
        self.ui.importanceSlider.valueChanged.connect(self.filtersChanged)
        #self.ui.logView.linkClicked.connect(self.linkClicked)
        self.ui.output.anchorClicked.connect(self.linkClicked)
        self.sigScrollToAnchor.connect(self.scrollToAnchor,
                                       Qt.Qt.QueuedConnection)

        #page = self.ui.logView.page()
        #page.setLinkDelegationPolicy(page.DelegateAllLinks)

    def loadFile(self, f):
        """Load the file, f. f must be able to be read by configfile.py"""
        log = configfile.readConfigFile(f)
        self.entries = []
        self.entryArrayBuffer = np.zeros(len(log),
                                         dtype=[('index', 'int32'),
                                                ('importance', 'int32'),
                                                ('msgType', '|S10'),
                                                ('directory', '|S100'),
                                                ('entryId', 'int32')])
        self.entryArray = self.entryArrayBuffer[:]

        i = 0
        for k, v in log.items():
            v['id'] = k[
                9:]  ## record unique ID to facilitate HTML generation (javascript needs this ID)
            self.entries.append(v)
            self.entryArray[i] = np.array(
                [(i, v.get('importance', 5), v.get('msgType', 'status'),
                  v.get('currentDir', ''), v.get('entryId', v['id']))],
                dtype=[('index', 'int32'), ('importance', 'int32'),
                       ('msgType', '|S10'), ('directory', '|S100'),
                       ('entryId', 'int32')])
            i += 1

        self.filterEntries(
        )  ## puts all entries through current filters and displays the ones that pass

    def addEntry(self, entry):
        ## All incoming messages begin here

        ## for thread-safetyness:
        isGuiThread = Qt.QThread.currentThread(
        ) == Qt.QCoreApplication.instance().thread()
        if not isGuiThread:
            self.sigAddEntry.emit(entry)
            return

        self.entries.append(entry)
        i = len(self.entryArray)

        entryDir = entry.get('currentDir', None)
        if entryDir is None:
            entryDir = ''

        arr = np.array([
            (i, entry['importance'], entry['msgType'], entryDir, entry['id'])
        ],
                       dtype=[('index', 'int32'), ('importance', 'int32'),
                              ('msgType', '|S10'), ('directory', '|S100'),
                              ('entryId', 'int32')])

        ## make more room if needed
        if len(self.entryArrayBuffer) == len(self.entryArray):
            newArray = np.empty(
                len(self.entryArrayBuffer) + 1000, self.entryArrayBuffer.dtype)
            newArray[:len(self.entryArray)] = self.entryArray
            self.entryArrayBuffer = newArray
        self.entryArray = self.entryArrayBuffer[:len(self.entryArray) + 1]
        #self.entryArray[i] = [(i, entry['importance'], entry['msgType'], entry['currentDir'])]
        self.entryArray[i] = arr
        self.checkDisplay(
            entry)  ## displays the entry if it passes the current filters
        #np.append(self.entryArray, np.array(i, [[i, entry['importance'], entry['msgType'], entry['currentDir']]]), dtype = [('index', int), ('importance', int), ('msgType', str), ('directory', str)])

    def setCheckStates(self, item, column):
        if item == self.ui.filterTree.topLevelItem(1):
            if item.checkState(0):
                for i in range(item.childCount()):
                    item.child(i).setCheckState(0, Qt.Qt.Checked)
        elif item.parent() == self.ui.filterTree.topLevelItem(1):
            if not item.checkState(0):
                self.ui.filterTree.topLevelItem(1).setCheckState(
                    0, Qt.Qt.Unchecked)
        self.filtersChanged()

    def filtersChanged(self):
        ### Update self.typeFilters, self.importanceFilter, and self.dirFilter to reflect changes.
        tree = self.ui.filterTree

        self.typeFilters = []
        for i in range(tree.topLevelItem(1).childCount()):
            child = tree.topLevelItem(1).child(i)
            if tree.topLevelItem(1).checkState(0) or child.checkState(0):
                text = child.text(0)
                self.typeFilters.append(six.text_type(text))

        self.importanceFilter = self.ui.importanceSlider.value()

        self.updateDirFilter()
        #self.dirFilter = self.manager.getDirOfSelectedFile().name()
        #else:
        #self.dirFilter = False

        self.filterEntries()

    def updateDirFilter(self, dh=None):
        if self.ui.filterTree.topLevelItem(0).checkState(0):
            if dh == None:
                self.dirFilter = self.manager.getDirOfSelectedFile().name()
            else:
                self.dirFilter = dh.name()
        else:
            self.dirFilter = False

    def filterEntries(self):
        """Runs each entry in self.entries through the filters and displays if it makes it through."""
        ### make self.entries a record array, then filtering will be much faster (to OR true/false arrays, + them)
        typeMask = self.entryArray['msgType'] == ''
        for t in self.typeFilters:
            typeMask += self.entryArray['msgType'] == t
        mask = (self.entryArray['importance'] >
                self.importanceFilter) * typeMask
        if self.dirFilter != False:
            d = np.ascontiguousarray(self.entryArray['directory'])
            j = len(self.dirFilter)
            i = len(d)
            d = d.view(np.byte).reshape(i, 100)[:, :j]
            d = d.reshape(i * j).view('|S%d' % j)
            mask *= (d == self.dirFilter)

        self.ui.output.clear()
        global Stylesheet
        self.ui.output.document().setDefaultStyleSheet(Stylesheet)
        #global pageTemplate
        #self.ui.logView.setHtml(pageTemplate)
        indices = list(self.entryArray[mask]['index'])
        inds = indices

        #if self.dirFilter != False:
        #j = len(self.dirFilter)
        #for i, n in inds:
        #if not self.entries[n]['currentDir'][:j] == self.dirFilter:
        #indices.pop(i)

        self.displayEntry([self.entries[i] for i in indices])

    def checkDisplay(self, entry):
        ### checks whether entry passes the current filters and displays it if it does.
        if entry['msgType'] not in self.typeFilters:
            return
        elif entry['importance'] < self.importanceFilter:
            return
        elif self.dirFilter is not False:
            if entry['currentDir'][:len(self.dirFilter)] != self.dirFilter:
                return
        else:
            self.displayEntry([entry])

    def displayEntry(self, entries):
        ## entries should be a list of log entries

        ## for thread-safetyness:
        isGuiThread = Qt.QThread.currentThread(
        ) == Qt.QCoreApplication.instance().thread()
        if not isGuiThread:
            self.sigDisplayEntry.emit(entries)
            return

        for entry in entries:
            if id(entry) not in self.cache:
                self.cache[id(entry)] = self.generateEntryHtml(entry)

                ## determine message color:
                #if entry['msgType'] == 'status':
                #color = 'green'
                #elif entry['msgType'] == 'user':
                #color = 'blue'
                #elif entry['msgType'] == 'error':
                #color = 'red'
                #elif entry['msgType'] == 'warning':
                #color = '#DD4400' ## orange
                #else:
                #color = 'black'

                #if entry.has_key('exception') or entry.has_key('docs') or entry.has_key('reasons'):
                ##self.displayComplexMessage(entry, color)
                #self.displayComplexMessage(entry)
                #else:
                #self.displayText(entry['message'], entry, color, timeStamp=entry['timestamp'])

            #for x in self.cache[id(entry)]:
            #self.ui.output.appendHtml(x)

            html = self.cache[id(entry)]
            #frame = self.ui.logView.page().currentFrame()
            #isMax = frame.scrollBarValue(Qt.Qt.Vertical) == frame.scrollBarMaximum(Qt.Qt.Vertical)
            sb = self.ui.output.verticalScrollBar()
            isMax = sb.value() == sb.maximum()

            #frame.findFirstElement('body').appendInside(html)
            self.ui.output.append(html)
            self.displayedEntries.append(entry)

            if isMax:
                ## can't scroll to end until the web frame has processed the html change
                #frame.setScrollBarValue(Qt.Qt.Vertical, frame.scrollBarMaximum(Qt.Qt.Vertical))

                ## Calling processEvents anywhere inside an error handler is forbidden
                ## because this can lead to Qt complaining about paint() recursion.
                #Qt.QApplication.processEvents()
                #self.ui.output.scrollToAnchor(str(entry['id']))

                self.sigScrollToAnchor.emit(str(
                    entry['id']))  ## queued connection
            #self.ui.logView.update()

    def scrollToAnchor(self, anchor):
        self.ui.output.scrollToAnchor(anchor)

    def generateEntryHtml(self, entry):
        msg = self.cleanText(entry['message'])

        reasons = ""
        docs = ""
        exc = ""
        if 'reasons' in entry:
            reasons = self.formatReasonStrForHTML(entry['reasons'])
        if 'docs' in entry:
            docs = self.formatDocsStrForHTML(entry['docs'])
        if entry.get('exception', None) is not None:
            exc = self.formatExceptionForHTML(entry, entryId=entry['id'])

        extra = reasons + docs + exc
        if extra != "":
            #extra = "<div class='logExtra'>" + extra + "</div>"
            extra = "<table class='logExtra'><tr><td>" + extra + "</td></tr></table>"

        #return """
        #<div class='entry'>
        #<div class='%s'>
        #<span class='timestamp'>%s</span>
        #<span class='message'>%s</span>
        #%s
        #</div>
        #</div>
        #""" % (entry['msgType'], entry['timestamp'], msg, extra)
        return """
        <a name="%s"/><table class='entry'><tr><td>
            <table class='%s'><tr><td>
                <span class='timestamp'>%s</span>
                <span class='message'>%s</span>
                %s
            </td></tr></table>
        </td></tr></table>
        """ % (str(
            entry['id']), entry['msgType'], entry['timestamp'], msg, extra)

        #if entry.has_key('exception') or entry.has_key('docs') or entry.has_key('reasons'):
        ##self.displayComplexMessage(entry, color)
        #return self.generateComplex(entry)
        #else:
        #return self.generateSimple(entry['message'], entry, color, timeStamp=entry['timestamp'])
        ##self.displayText(entry['message'], entry, color, timeStamp=entry['timestamp'])

    @staticmethod
    def cleanText(text):
        text = re.sub(r'&', '&amp;', text)
        text = re.sub(r'>', '&gt;', text)
        text = re.sub(r'<', '&lt;', text)
        text = re.sub(r'\n', '<br/>\n', text)
        return text

    #def displayText(self, msg, entry, colorStr='black', timeStamp=None, clean=True):
    #if clean:
    #msg = self.cleanText(msg)

    #if msg[-1:] == '\n':
    #msg = msg[:-1]
    #msg = '<br />'.join(msg.split('\n'))
    #if timeStamp is not None:
    #strn = '<b style="color:black"> %s </b> <span style="color:%s"> %s </span>' % (timeStamp, colorStr, msg)
    #else:
    #strn = '<span style="color:%s"> %s </span>' % (colorStr, msg)
    ##self.ui.output.appendHtml(strn)
    #self.cache[id(entry)].append(strn)

    #def displayComplexMessage(self, entry, color='black'):
    #self.displayText(entry['message'], entry, color, timeStamp = entry['timestamp'], clean=True)
    #if entry.has_key('reasons'):
    #reasons = self.formatReasonStrForHTML(entry['reasons'])
    #self.displayText(reasons, entry, 'black', clean=False)
    #if entry.has_key('docs'):
    #docs = self.formatDocsStrForHTML(entry['docs'])
    #self.displayText(docs, entry, 'black', clean=False)
    #if entry.get('exception', None) is not None:
    #self.displayException(entry['exception'], entry, 'black', tracebacks=entry.get('traceback', None))

    def formatExceptionForHTML(self,
                               entry,
                               exception=None,
                               count=1,
                               entryId=None):
        ### Here, exception is a dict that holds the message, reasons, docs, traceback and oldExceptions (which are also dicts, with the same entries)
        ## the count and tracebacks keywords are for calling recursively
        if exception is None:
            exception = entry['exception']
        #if tracebacks is None:
        #tracebacks = []

        indent = 10

        text = self.cleanText(exception['message'])
        text = re.sub(r'^HelpfulException: ', '', text)
        #if exception.has_key('oldExc'):
        #self.displayText("&nbsp;"*indent + str(count)+'. ' + text, entry, color, clean=False)
        #else:
        #self.displayText("&nbsp;"*indent + str(count)+'. Original error: ' + text, entry, color, clean=False)
        messages = [text]
        #print "\n", messages, "\n"

        if 'reasons' in exception:
            reasons = self.formatReasonsStrForHTML(exception['reasons'])
            text += reasons
            #self.displayText(reasons, entry, color, clean=False)
        if 'docs' in exception:
            docs = self.formatDocsStrForHTML(exception['docs'])
            #self.displayText(docs, entry, color, clean=False)
            text += docs

        traceback = [
            self.formatTracebackForHTML(exception['traceback'], count)
        ]
        text = [text]

        if 'oldExc' in exception:
            exc, tb, msgs = self.formatExceptionForHTML(entry,
                                                        exception['oldExc'],
                                                        count=count + 1)
            text.extend(exc)
            messages.extend(msgs)
            traceback.extend(tb)

        #else:
        #if len(tracebacks)==count+1:
        #n=0
        #else:
        #n=1
        #for i, tb in enumerate(tracebacks):
        #self.displayTraceback(tb, entry, number=i+n)
        if count == 1:
            exc = "<div class=\"exception\"><ol>" + "\n".join(
                ["<li>%s</li>" % ex for ex in text]) + "</ol></div>"
            tbStr = "\n".join([
                "<li><b>%s</b><br/><span class='traceback'>%s</span></li>" %
                (messages[i], tb) for i, tb in enumerate(traceback)
            ])
            #traceback = "<div class=\"traceback\" id=\"%s\"><ol>"%str(entryId) + tbStr + "</ol></div>"
            entry['tracebackHtml'] = tbStr

            #return exc + '<a href="#" onclick="showDiv(\'%s\')">Show traceback</a>'%str(entryId) + traceback
            return exc + '<a href="exc:%s">Show traceback %s</a>' % (
                str(entryId), str(entryId))
        else:
            return text, traceback, messages

    def formatTracebackForHTML(self, tb, number):
        try:
            tb = [
                line for line in tb
                if not line.startswith("Traceback (most recent call last)")
            ]
        except:
            print("\n" + str(tb) + "\n")
            raise
        return re.sub(" ", "&nbsp;", ("").join(map(self.cleanText, tb)))[:-1]
        #tb = [self.cleanText(strip(x)) for x in tb]
        #lines = []
        #prefix = ''
        #for l in ''.join(tb).split('\n'):
        #if l == '':
        #continue
        #if l[:9] == "Traceback":
        #prefix = ' ' + str(number) + '. '
        #continue
        #spaceCount = 0
        #while l[spaceCount] == ' ':
        #spaceCount += 1
        #if prefix is not '':
        #spaceCount -= 1
        #lines.append("&nbsp;"*(spaceCount*4) + prefix + l)
        #prefix = ''
        #return '<div class="traceback">' + '<br />'.join(lines) + '</div>'
        #self.displayText('<br />'.join(lines), entry, color, clean=False)

    def formatReasonsStrForHTML(self, reasons):
        #indent = 6
        reasonStr = "<table class='reasons'><tr><td>Possible reasons include:\n<ul>\n"
        for r in reasons:
            r = self.cleanText(r)
            reasonStr += "<li>" + r + "</li>\n"
            #reasonStr += "&nbsp;"*22 + chr(97+i) + ". " + r + "<br>"
        reasonStr += "</ul></td></tr></table>\n"
        return reasonStr

    def formatDocsStrForHTML(self, docs):
        #indent = 6
        docStr = "<div class='docRefs'>Relevant documentation:\n<ul>\n"
        for d in docs:
            d = self.cleanText(d)
            docStr += "<li><a href=\"doc:%s\">%s</a></li>\n" % (d, d)
        docStr += "</ul></div>\n"
        return docStr

    def exportHtml(self, fileName=False):
        #self.makeError1()
        if fileName is False:
            self.fileDialog = FileDialog(self, "Save HTML as...",
                                         self.manager.getCurrentDir().name())
            #self.fileDialog.setFileMode(Qt.QFileDialog.AnyFile)
            self.fileDialog.setAcceptMode(Qt.QFileDialog.AcceptSave)
            self.fileDialog.show()
            self.fileDialog.fileSelected.connect(self.exportHtml)
            return
        if fileName[-5:] != '.html':
            fileName += '.html'

        #doc = self.ui.output.document().toHtml('utf-8')
        #for e in self.displayedEntries:
        #if e.has_key('tracebackHtml'):
        #doc = re.sub(r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'%(str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        global pageTemplate
        doc = pageTemplate
        for e in self.displayedEntries:
            doc += self.cache[id(e)]
        for e in self.displayedEntries:
            if 'tracebackHtml' in e:
                doc = re.sub(
                    r'<a href="exc:%s">(<[^>]+>)*Show traceback %s(<[^>]+>)*</a>'
                    % (str(e['id']), str(e['id'])), e['tracebackHtml'], doc)

        #doc = self.ui.logView.page().currentFrame().toHtml()
        f = open(fileName, 'w')
        f.write(doc.encode('utf-8'))
        f.close()

    def makeError1(self):
        ### just for testing error logging
        try:
            self.makeError2()
            #print x
        except:
            t, exc, tb = sys.exc_info()
            #logExc(message="This button doesn't work", reasons='reason a, reason b', docs='documentation')
            #if isinstance(exc, HelpfulException):
            #exc.prependErr("Button doesn't work", (t,exc,tb), reasons = ["It's supposed to raise an error for testing purposes", "You're doing it wrong."])
            #raise
            #else:
            printExc("This is the message sent to printExc.")
            #raise HelpfulException(message='This button does not work.', exc=(t, exc, tb), reasons=["It's supposed to raise an error for testing purposes", "You're doing it wrong."])

    def makeError2(self):
        ### just for testing error logging
        try:
            print(y)
        except:
            t, exc, tb = sys.exc_info()
            raise HelpfulException(message='msg from makeError',
                                   exc=(t, exc, tb),
                                   reasons=["reason one", "reason 2"],
                                   docs=['what, you expect documentation?'])

    def linkClicked(self, url):
        url = url.toString()
        if url[:4] == 'doc:':
            self.manager.showDocumentation(url[4:])
        elif url[:4] == 'exc:':
            cursor = self.ui.output.document().find('Show traceback %s' %
                                                    url[4:])
            try:
                tb = self.entries[int(url[4:]) - 1]['tracebackHtml']
            except IndexError:
                try:
                    tb = self.entries[self.entryArray[
                        self.entryArray['entryId'] == (
                            int(url[4:]))]['index']]['tracebackHtml']
                except:
                    print("requested index %d, but only %d entries exist." %
                          (int(url[4:]) - 1, len(self.entries)))
                    raise
            cursor.insertHtml(tb)

    def clear(self):
        #self.ui.logView.setHtml("")
        self.ui.output.clear()
        self.displayedEntryies = []