def __calcRunGenerateState(self): """Calculates the enable/disable state of the run/generate menu items""" editorWidget = self.ide.currentEditorWidget defaultGenerateText = 'Open pylintrc file' if editorWidget.getType() != MainWindowTabWidgetBase.PlainTextEditor: return False, (False, defaultGenerateText) if not isPythonMime(editorWidget.getMime()): return False, (False, defaultGenerateText) if self.__pylintDriver.isInProcess(): return False, (False, defaultGenerateText) fileName = editorWidget.getFileName() if not os.path.isabs(fileName): fileName = None rcfile = self.__pylintDriver.getPylintrc(self.ide, fileName) if editorWidget.isModified(): if rcfile: return False, (True, defaultGenerateText) return False, (True, 'Generate and open pylintrc file') # Saved python file and no pylint running if rcfile: return fileName is not None, (True, defaultGenerateText) return fileName is not None, (True, 'Generate and open pylintrc file')
def __onFileTypeChanged(self, _, uuid, newFileType): """Triggered on the changed file type""" if uuid == self.myUUID: if isPythonMime(newFileType): self.setVisible(True) else: self.setVisible(False)
def __onTabChanged(self, index): """Triggered when another tab becomes active""" # If the timer is still active that means the tab was switched before # the handler had a chance to work. Therefore update the previous tab # first if so. if self.__updateTimer.isActive(): self.__updateTimer.stop() self.__updateView() # Now, switch the pyflakes browser to the new tab if index == -1: widget = self.__editorsManager.currentWidget() else: widget = self.__editorsManager.getWidgetByIndex(index) if widget is None: self.__currentUUID = None self.setAnalysisNotAvailable(self.__uiLabel, self.__ccLabel) return if widget.getType() not in [ MainWindowTabWidgetBase.PlainTextEditor, MainWindowTabWidgetBase.VCSAnnotateViewer ]: self.__currentUUID = None self.setAnalysisNotAvailable(self.__uiLabel, self.__ccLabel) return # This is text editor, detect the file type if not isPythonMime(widget.getMime()): self.__currentUUID = None self.setAnalysisNotAvailable(self.__uiLabel, self.__ccLabel) return # This is a python file, check if we already have the parsed info in # the cache uuid = widget.getUUID() self.__currentUUID = uuid if uuid in self.__flakesResults: # We have it, change the icon and the tooltip correspondingly results = self.__flakesResults[uuid].messages ccResults = self.__flakesResults[uuid].ccMessages self.setAnalysisResults(self.__uiLabel, results, self.__ccLabel, ccResults, None) return # It is first time we are here, create a new editor = widget.getEditor() editor.textChanged.connect(self.__onBufferChanged) editor.cursorPositionChanged.connect(self.__cursorPositionChanged) results, ccResults = getBufferErrors(editor.text) attributes = PyflakesAttributes() attributes.messages = results attributes.ccMessages = ccResults attributes.changed = False self.__flakesResults[uuid] = attributes self.__currentUUID = uuid self.setAnalysisResults(self.__uiLabel, results, self.__ccLabel, ccResults, editor)
def onFileUpdated(self, fileName, uuid): """Triggered when the file is updated""" del uuid # unused argument mime, icon, _ = getFileProperties(fileName) if isPythonMime(mime): path = os.path.realpath(fileName) info = GlobalData().briefModinfoCache.get(path) if info.isOK: icon = getIcon('filepython.png') else: icon = getIcon('filepythonbroken.png') # For all root items for treeItem in self.model().sourceModel().rootItem.childItems: self.__walkTreeAndUpdate(treeItem, path, mime, icon, info) elif isCDMProjectMime(mime): path = os.path.realpath(fileName) # For all root items for treeItem in self.model().sourceModel().rootItem.childItems: self.__walkTreeAndUpdate(treeItem, path, mime, None, None) elif fileName.endswith(".cgi"): path = os.path.realpath(fileName) # For all root items for treeItem in self.model().sourceModel().rootItem.childItems: self.__walkTreeAndUpdate(treeItem, path, mime, icon, None)
def __onFileTypeChanged(self, fileName, uuid, newFileType): """Triggered on the changed file type""" del fileName # unused argument if uuid == self.myUUID: if isPythonMime(newFileType): self.setVisible(True) else: self.setVisible(False)
def updateStatus(self): """Updates the toolbar buttons status""" self.__updateRunDebugButtons() isPythonFile = isPythonMime(self.__editor.mime) self.importsDiagramButton.setEnabled( isPythonFile and GlobalData().graphvizAvailable) self.__editor.diagramsMenu.setEnabled( self.importsDiagramButton.isEnabled()) self.__editor.toolsMenu.setEnabled(self.runScriptButton.isEnabled())
def onOpenImport(self): """Triggered when Ctrl+I is received""" if isPythonMime(self.__editor.mime): # Take all the file imports and resolve them fileImports = getImportsList(self.__editor.text) if not fileImports: GlobalData().mainWindow.showStatusBarMessage( "There are no imports") else: self.__onImportList(self.__fileName, fileImports)
def updateSettings(self): """Called when navigation bar settings have been updated""" textMime = self.__parentWidget.getMime() if Settings()['showNavigationBar'] and isPythonMime(textMime): self.setVisible(True) self.updateBar() else: self.__disconnectEditorSignals() self.__updateTimer.stop() self.__currentInfo = None self.setVisible(False)
def __updateRunDebugButtons(self): """Enables/disables the run and debug buttons as required""" enable = isPythonMime(self.__editor.mime) and \ not self.isModified() and \ not self.__debugMode and \ os.path.isabs(self.__fileName) if enable != self.runScriptButton.isEnabled(): self.runScriptButton.setEnabled(enable) self.profileScriptButton.setEnabled(enable) self.debugScriptButton.setEnabled(enable) self.sigTabRunChanged.emit(enable)
def __canRun(self, editorWidget): """Tells if pylint can be run for the given editor widget""" if self.__pylintDriver.isInProcess(): return False, None if editorWidget.getType() != MainWindowTabWidgetBase.PlainTextEditor: return False, None if not isPythonMime(editorWidget.getMime()): return False, None if editorWidget.isModified(): return False, 'Save changes before running pylint' if not os.path.isabs(editorWidget.getFileName()): return False, 'The new file has never been saved yet. ' \ 'Save it before running pylint' return True, None
def __populateModel(self): """Populates the project browser model""" self.clear() project = self.globalData.project cache = self.globalData.briefModinfoCache for fname in project.filesList: mime, _, _ = getFileProperties(fname) if isPythonMime(mime): info = cache.get(fname) for classObj in info.classes: item = TreeViewClassItem(self.rootItem, classObj) item.appendData([basename(fname), classObj.line]) item.setPath(fname) self.rootItem.appendChild(item)
def __onFileTypeChanged(self, _, uuid, newFileType): """Triggered when a buffer content type has changed""" if self.__parentWidget.getUUID() != uuid: return if isPythonMime(newFileType) and Settings()['showNavigationBar']: # Update the bar and show it self.setVisible(True) self.updateBar() else: self.__disconnectEditorSignals() self.__updateTimer.stop() self.__currentInfo = None self.setVisible(False) self.__currentIconState = self.STATE_UNKNOWN
def __onFileTypeChanged(self, fileName, uuid, newFileType): """Triggered when the current buffer file type is changed, e.g. .cgi""" del fileName # unused argument if isPythonMime(newFileType): # The file became a python one if uuid not in self.__flakesResults: self.__onTabChanged(-1) else: if uuid in self.__flakesResults: # It's not a python file any more if uuid == self.__currentUUID: self.__currentUUID = None del self.__flakesResults[uuid] self.setFlakesNotAvailable(self.__uiLabel)
def __onFileTypeChanged(self, fileName, uuid, newFileType): """Triggered when the current buffer file type is changed, e.g. .cgi""" del fileName # unused argument if isPythonMime(newFileType): # The file became a python one if uuid not in self.__outlineBrowsers: self.__onTabChanged(-1) else: if uuid in self.__outlineBrowsers: # It's not a python file any more if uuid == self.__currentUUID: self.__outlineBrowsers[uuid].browser.hide() self.__noneLabel.show() self.__currentUUID = None del self.__outlineBrowsers[uuid] self.showParsingErrorsButton.setEnabled(False) self.findButton.setEnabled(False)
def __walkTreeAndUpdate(self, treeItem, path, mime, icon, info): """Recursively walks the tree items and updates the icon""" if treeItem.itemType in [DirectoryItemType, SysPathItemType]: for i in treeItem.childItems: if i.itemType in [ DirectoryItemType, SysPathItemType, FileItemType ]: self.__walkTreeAndUpdate(i, path, mime, icon, info) if treeItem.itemType == FileItemType: if path == os.path.realpath(treeItem.getPath()): if isPythonMime(mime) and info: # Update icon treeItem.setIcon(icon) if info.docstring is None: treeItem.toolTip = "" else: treeItem.toolTip = info.docstring.text treeItem.parsingErrors = not info.isOK self._signalItemUpdated(treeItem) # Update content if populated self.updateFileItem(treeItem, info) elif isCDMProjectMime(mime): # Tooltip update only treeItem.toolTip = getProjectFileTooltip(path) self._signalItemUpdated(treeItem) elif path.endswith(".cgi"): # It can only happened when python CGI is not a python any # more. So display it as a general file. # The case when a cgi became a python file is covered in # the first branch of this if statement. treeItem.setIcon(icon) treeItem.toolTip = "" self._signalItemUpdated(treeItem) # Remove child items if so while treeItem.childItems: self.__removeTreeItem(treeItem.childItems[0]) else: treeItem.setIcon(icon) treeItem.toolTip = "" self._signalItemUpdated(treeItem)
def __populateFromOpened(self): """Populates the name dialog from the opened files""" mainWindow = GlobalData().mainWindow editorsManager = mainWindow.editorsManagerWidget.editorsManager showTooltips = Settings()['findFileTooltips'] for record in editorsManager.getTextEditors(): # uuid = record[0] fname = record[1] widget = record[2] mime, icon, _ = getFileProperties(fname) tooltip = "" if showTooltips and isPythonMime(mime): content = widget.getEditor().text info = getBriefModuleInfoFromMemory(content) if info.docstring is not None: tooltip = info.docstring.text newItem = FileItem(self.rootItem, icon, fname, tooltip) self.rootItem.appendChild(newItem) self.count += 1
def __populateFromProject(self): """Populates find name dialog from the project files""" mainWindow = GlobalData().mainWindow showTooltips = Settings()['findFileTooltips'] for fname in GlobalData().project.filesList: if fname.endswith(os.path.sep): continue mime, icon, _ = getFileProperties(fname) tooltip = "" if showTooltips and isPythonMime(mime): widget = mainWindow.getWidgetForFileName(fname) if widget is None: info = GlobalData().briefModinfoCache.get(fname) else: content = widget.getEditor().text info = getBriefModuleInfoFromMemory(content) if info.docstring is not None: tooltip = info.docstring.text newItem = FileItem(self.rootItem, icon, fname, tooltip) self.rootItem.appendChild(newItem) self.count += 1
def __onFileTypeChanged(self, fileName, uuid, newFileType): """Triggered when a buffer content type has changed""" if self.__parentWidget.getUUID() != uuid: return if not isPythonMime(newFileType): self.__disconnectEditorSignals() self.__updateTimer.stop() self.__cf = None self.__validGroups = [] self.setVisible(False) self.__navBar.updateInfoIcon(self.__navBar.STATE_UNKNOWN) return # Update the bar and show it self.setVisible(True) self.process() # The buffer type change event comes when the content is loaded first # time. So this is a good point to restore the position _, _, _, cflowHPos, cflowVPos = getFilePosition(fileName) self.setScrollbarPositions(cflowHPos, cflowVPos)
def __init__(self, parent, path): path = str(path) TreeViewItem.__init__(self, parent, os.path.basename(path)) self.itemType = FileItemType self.parsingErrors = False # Used for python files only self.isLink = False self.fileType, self.icon, _ = getFileProperties(path) if self.fileType is None: if self.icon is None: self.icon = getIcon('filemisc.png') return if 'broken-symlink' in self.fileType: self.isLink = True self.toolTip = self.__brokenLinkTooltip(path) return if os.path.islink(path): self.isLink = True self.toolTip = self.__linkTooltip(path) self.icon = getIcon('filelink.png') self.fileType, _, _ = getFileProperties(os.path.realpath(path)) return # Fine corrections for some file types if isPythonMime(self.fileType): self.populated = False self.lazyPopulation = True return if isCDMProjectMime(self.fileType): # Get the project properties try: self.toolTip = getProjectFileTooltip(path) except: # cannot get project properties self.toolTip = 'Broken project file' return
def __markOK(self): """Mark the file as OK""" self.__isValid = True fileName = self.getFilename() mime, icon, _ = getFileProperties(fileName) if isPythonMime(mime): # The tooltip could be the file docstring info = GlobalData().briefModinfoCache.get(fileName) if info.docstring and Settings()['recentTooltips']: self.setToolTip(1, info.docstring.text) else: self.setToolTip(1, "") if info.isOK: self.setIcon(0, getIcon('filepython.png')) else: self.setIcon(0, getIcon('filepythonbroken.png')) self.setToolTip(0, "") elif isCDMProjectMime(mime): # Get the project properties try: self.setToolTip(0, "") tooltip = getProjectFileTooltip(fileName) if Settings()['recentTooltips']: self.setToolTip(1, tooltip) else: self.setToolTip(1, "") self.setText(0, "") except: # cannot get project properties. Mark broken. self.__isValid = False self.setToolTip(0, 'Broken project file') self.setToolTip(1, 'Broken project file') self.setIcon(0, icon) else: # Get the other file type icon self.setIcon(0, icon) self.setToolTip(2, self.getFilename())
def passFocusToFlow(self): """Sets the focus to the graphics part""" if isPythonMime(self.__editor.mime): self.__flowUI.setFocus() return True return False
def __onFileTypeChanged(self, fileName, uuid, newFileType): """Triggered on the changed file type""" del fileName # unused argument if uuid == self.myUUID: MarginBase.setVisible(self, isPythonMime(newFileType))
def __createLayout(self, action, title, files): """Creates the dialog layout""" self.resize(400, 300) self.setSizeGripEnabled(True) # Top level layout layout = QVBoxLayout(self) # Pixmap and the message topLayout = QHBoxLayout() pixmap = QLabel() pixmap.setPixmap(getPixmap('warning.png')) topLayout.addWidget(pixmap) hSpacer = QWidget() hSpacer.setFixedSize(15, 15) topLayout.addWidget(hSpacer) message = QLabel("All the project files must be " "saved before start debugging") message.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) message.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) message.setWordWrap(True) topLayout.addWidget(message) layout.addLayout(topLayout) vSpacer = QWidget() vSpacer.setFixedSize(15, 15) layout.addWidget(vSpacer) layout.addWidget(QLabel(title + ":")) filesList = QTreeWidget() filesList.setRootIsDecorated(False) filesList.setAlternatingRowColors(True) filesList.setUniformRowHeights(True) filesList.setItemsExpandable(False) filesList.setItemDelegate(NoOutlineHeightDelegate(4)) filesList.setSelectionMode(QAbstractItemView.NoSelection) filesList.setHeaderHidden(True) for item in files: fileName = item[0] fileItem = QTreeWidgetItem([fileName]) fileType, icon, _ = getFileProperties(fileName) fileItem.setIcon(0, icon) if isPythonMime(fileType): info = GlobalData().briefModinfoCache.get(fileName) fileItem.setToolTip( 0, info.docstring.text if info.docstring else '') filesList.addTopLevelItem(fileItem) layout.addWidget(filesList) # Buttons at the bottom buttonBox = QDialogButtonBox() buttonBox.setOrientation(Qt.Horizontal) buttonBox.setStandardButtons(QDialogButtonBox.Cancel) continueButton = buttonBox.addButton(action, QDialogButtonBox.ActionRole) continueButton.setDefault(True) layout.addWidget(buttonBox) continueButton.clicked.connect(self.accept) buttonBox.rejected.connect(self.close) continueButton.setFocus()
def isPythonBuffer(self): """True if it is a python buffer""" return isPythonMime(self.mime)
def __onFileTypeChanged(self, _, uuid, newFileType): """Triggered on the changed file type""" if uuid == self.myUUID: MarginBase.setVisible(self, isPythonMime(newFileType))
def __onTabChanged(self, index): """Triggered when another tab becomes active""" # If the timer is still active that means the tab was switched before # the handler had a chance to work. Therefore update the previous tab # first if so. if self.__updateTimer.isActive(): self.__updateTimer.stop() self.__updateView() # Now, switch the outline browser to the new tab if index == -1: widget = self.__editorsManager.currentWidget() else: widget = self.__editorsManager.getWidgetByIndex(index) if widget is None: self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return if widget.getType() not in [MainWindowTabWidgetBase.PlainTextEditor, MainWindowTabWidgetBase.VCSAnnotateViewer]: if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return # This is text editor, detect the file type if not isPythonMime(widget.getMime()): if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None self.__noneLabel.show() self.showParsingErrorsButton.setEnabled(False) return # This is a python file, check if we already have the parsed info in # the cache uuid = widget.getUUID() if uuid in self.__outlineBrowsers: # We have it, hide the current and show the existed if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid self.__outlineBrowsers[self.__currentUUID].browser.show() info = self.__outlineBrowsers[self.__currentUUID].info self.showParsingErrorsButton.setEnabled(info.isOK != True) return # It is first time we are here, create a new editor = widget.getEditor() editor.textChanged.connect(self.__onBufferChanged) editor.cursorPositionChanged.connect(self.__cursorPositionChanged) info = getBriefModuleInfoFromMemory(editor.text) self.showParsingErrorsButton.setEnabled(info.isOK != True) shortFileName = widget.getShortName() browser = OutlineBrowser(uuid, shortFileName, info, self) browser.setHeaderHighlight(info.isOK != True) self.__connectOutlineBrowser(browser) self.__layout.addWidget(browser) if self.__currentUUID is not None: self.__outlineBrowsers[self.__currentUUID].browser.hide() self.__currentUUID = None else: self.__noneLabel.hide() self.__currentUUID = uuid attributes = OutlineAttributes() attributes.browser = browser attributes.contextItem = None attributes.info = info attributes.shortFileName = shortFileName attributes.changed = False self.__outlineBrowsers[self.__currentUUID] = attributes self.__outlineBrowsers[self.__currentUUID].browser.show()
def populateDirectoryItem(self, parentItem, repopulate=False): """Populates a directory item's subtree""" path = parentItem.getPath() if not os.path.exists(path): return QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) try: items = os.listdir(path) except Exception as exc: QApplication.restoreOverrideCursor() logging.error("Cannot populate directory. " + str(exc)) return excludes = ['.svn', '.cvs', '.hg', '.git'] items = [itm for itm in items if itm not in excludes] if parentItem.needVCSStatus: # That's the project browser. Filter out what not needed. excludeFunctor = GlobalData().project.shouldExclude items = [itm for itm in items if not excludeFunctor(itm)] pathsToRequest = [] if items: infoSrc = self.globalData.briefModinfoCache if repopulate: self.beginInsertRows( self.createIndex(parentItem.row(), 0, parentItem), 0, len(items) - 1) path = os.path.realpath(path) + os.path.sep for item in items: fullPath = path + item if os.path.isdir(fullPath): node = TreeViewDirectoryItem(parentItem, fullPath, False) if parentItem.needVCSStatus: pathsToRequest.append(fullPath + os.path.sep) else: node = TreeViewFileItem(parentItem, fullPath) if parentItem.needVCSStatus: pathsToRequest.append(fullPath) if isPythonMime(node.fileType): modInfo = infoSrc.get(fullPath) node.toolTip = "" if modInfo.docstring is not None: node.toolTip = modInfo.docstring.text if modInfo.isOK == False: # Substitute icon and change the tooltip node.icon = getIcon('filepythonbroken.png') if node.toolTip != "": node.toolTip += "\n\n" node.toolTip += "Parsing errors:\n" + \ "\n".join(modInfo.lexerErrors + \ modInfo.errors) node.parsingErrors = True if modInfo.encoding is None and \ not modInfo.imports and \ not modInfo.globals and \ not modInfo.functions and \ not modInfo.classes: node.populated = True node.lazyPopulation = False node.needVCSStatus = parentItem.needVCSStatus self._addItem(node, parentItem) if repopulate: self.endInsertRows() parentItem.populated = True # Request statuses of the populated items. The request must be sent # after the items are added, otherwise the status received by the model # before the items are populated thus not updated properly. for path in pathsToRequest: GlobalData().mainWindow.vcsManager.requestStatus(path) QApplication.restoreOverrideCursor()
def updateBar(self): """Triggered when the timer is fired""" self.__updateTimer.stop() # just in case if not isPythonMime(self.__parentWidget.getMime()): return if not self.__connected: self.__connectEditorSignals() # Parse the buffer content self.__currentInfo = getBriefModuleInfoFromMemory(self.__editor.text) # Decide what icon to use if self.__currentInfo.isOK: self.__updateInfoIcon(self.STATE_OK_UTD) else: self.__updateInfoIcon(self.STATE_BROKEN_UTD) # Calc the cursor context context = getContext(self.__editor, self.__currentInfo, True, False) # Display the context self.__populateGlobalScope() if context.length == 0: self.__globalScopeCombo.setCurrentIndex(-1) else: index = self.__globalScopeCombo.findData( context.levels[0][0].line) self.__globalScopeCombo.setCurrentIndex(index) usedFromStore = 0 index = 1 while index < context.length: if len(self.__path) < index: newPathItem = PathElement(self) self.__path.append(newPathItem) self.__layout.addWidget(newPathItem.icon) self.__layout.addWidget(newPathItem.combo) combo = newPathItem.combo combo.pathIndex = len(self.__path) - 1 combo.jumpToLine.connect(self.__onJumpToLine) else: self.__path[index - 1].icon.setVisible(True) self.__path[index - 1].combo.setVisible(True) combo = self.__path[index - 1].combo combo.clear() # Populate the combo box self.__populateClassesAndFunctions(context.levels[index - 1][0], combo) combo.setCurrentIndex(combo.findData(context.levels[index][0].line)) index += 1 usedFromStore += 1 # it might need to have one more level with nothing selected if context.length > 0: if len(context.levels[context.length - 1][0].functions) > 0 or \ len(context.levels[context.length - 1][0].classes) > 0: # Need to add a combo if len(self.__path) <= usedFromStore: newPathItem = PathElement(self) self.__path.append(newPathItem) self.__layout.addWidget(newPathItem.icon) self.__layout.addWidget(newPathItem.combo) combo = newPathItem.combo combo.pathIndex = len(self.__path) - 1 combo.jumpToLine.connect(self.__onJumpToLine) else: self.__path[index - 1].icon.setVisible(True) self.__path[index - 1].combo.setVisible(True) combo = self.__path[index - 1].combo combo.clear() self.__populateClassesAndFunctions( context.levels[context.length - 1][0], combo) combo.setCurrentIndex(-1) usedFromStore += 1 # Hide extra components if so index = usedFromStore while index < len(self.__path): self.__path[index].icon.setVisible(False) self.__path[index].combo.setVisible(False) index += 1 # Make sure the spacer is the last item self.__layout.removeWidget(self.__spacer) self.__layout.addWidget(self.__spacer)