class SupportManager(QtCore.QObject, PMXSupportBaseManager): #Signals for bundle bundleAdded = QtCore.pyqtSignal(object) bundleRemoved = QtCore.pyqtSignal(object) bundleChanged = QtCore.pyqtSignal(object) bundlePopulated = QtCore.pyqtSignal(object) #Signals for bundle items bundleItemAdded = QtCore.pyqtSignal(object) bundleItemRemoved = QtCore.pyqtSignal(object) bundleItemChanged = QtCore.pyqtSignal(object) bundleItemTriggered = QtCore.pyqtSignal(object) #Signals for themes themeAdded = QtCore.pyqtSignal(object) themeRemoved = QtCore.pyqtSignal(object) themeChanged = QtCore.pyqtSignal(object) #Settings shellVariables = pmxConfigPorperty(default=[], tm_name=u'OakShelVariables') @pmxConfigPorperty(default=[], tm_name=u'OakBundleManagerDeletedBundles') def deleted(self, deleted): self.deletedObjects = map(lambda uuid: uuidmodule.UUID(uuid), deleted) @pmxConfigPorperty(default=[], tm_name=u'OakBundleManagerDeletedBundles') def disabled(self, disabled): self.disabledObjects = map(lambda uuid: uuidmodule.UUID(uuid), disabled) #http://manual.macromates.com/en/expert_preferences.html #When you create a new item in the bundle editor without having selected a bundle first, then the bundle with the UUID held by this defaults key is used as the target defaultBundleForNewBundleItems = pmxConfigPorperty( default=u'B7BC3FFD-6E4B-11D9-91AF-000D93589AF6', tm_name=u'OakDefaultBundleForNewBundleItems') SETTINGS_GROUP = 'SupportManager' def __init__(self, application): QtCore.QObject.__init__(self) PMXSupportBaseManager.__init__(self) self.application = application self.bundleTreeModel = BundleItemTreeModel(self) self.themeListModel = ThemeListModel(self) self.themeStylesTableModel = ThemeStylesTableModel(self) self.processTableModel = ExternalProcessTableModel(self) #STYLE PROXY self.themeStyleProxyModel = ThemeStyleProxyTableModel(self) self.themeStyleProxyModel.setSourceModel(self.themeStylesTableModel) #TREE PROXY self.bundleProxyTreeModel = BundleItemProxyTreeModel(self) self.bundleProxyTreeModel.setSourceModel(self.bundleTreeModel) #BUNDLES self.bundleProxyModel = BundleListModel(self) self.bundleProxyModel.setSourceModel(self.bundleTreeModel) #TEMPLATES self.templateProxyModel = TemplateListModel(self) self.templateProxyModel.setSourceModel(self.bundleTreeModel) #PROJECTS self.projectProxyModel = ProjectListModel(self) self.projectProxyModel.setSourceModel(self.bundleTreeModel) #SYNTAX self.syntaxProxyModel = SyntaxListModel(self) self.syntaxProxyModel.setSourceModel(self.bundleTreeModel) #INTERACTIVEITEMS self.actionItemsProxyModel = BundleItemTypeProxyModel( ["command", "snippet", "macro"], self) self.actionItemsProxyModel.setSourceModel(self.bundleTreeModel) #PREFERENCES self.preferenceProxyModel = BundleItemTypeProxyModel( "preference", self) self.preferenceProxyModel.setSourceModel(self.bundleTreeModel) #DRAGCOMMANDS self.dragcommandProxyModel = BundleItemTypeProxyModel( "dragcommand", self) self.dragcommandProxyModel.setSourceModel(self.bundleTreeModel) #BUNDLEMENUGROUP self.bundleMenuGroup = BundleItemMenuGroup(self) @classmethod def contributeToSettings(cls): from prymatex.gui.settings.environment import PMXEnvVariablesWidget return [PMXEnvVariablesWidget] def setEditorAvailable(self, available): self.editorAvailable = available def appendMenuToBundleMenuGroup(self, menu, offset=None): self.bundleMenuGroup.appendMenu(menu, offset) def menuForBundle(self, bundle): return self.bundleMenuGroup.menuForBundle(bundle) #--------------------------------------------------- # Environment #--------------------------------------------------- def environmentVariables(self): environment = PMXSupportBaseManager.buildEnvironment(self) #Extend wiht the user shell variables for var in self.shellVariables: if var['enabled']: environment[var['variable']] = var['value'] return environment def buildEnvironment(self, systemEnvironment=True): env = PMXSupportBaseManager.buildEnvironment(self, systemEnvironment) for var in self.shellVariables: if var['enabled']: env[var['variable']] = var['value'] return env # Override loadSupport for emit signals def loadSupport(self, *largs, **kwargs): PMXSupportBaseManager.loadSupport(self, *largs, **kwargs) self.bundleProxyTreeModel.sort(0, QtCore.Qt.AscendingOrder) self.bundleProxyTreeModel.setDynamicSortFilter(True) def runProcess(self, context, callback): if context.asynchronous: return self.runQProcess(context, callback) else: return PMXSupportBaseManager.runProcess(self, context, callback) #Interface def runQProcess(self, context, callback): process = QtCore.QProcess(self) if context.workingDirectory is not None: process.setWorkingDirectory(context.workingDirectory) self.processTableModel.appendProcess(process, description=context.description()) environment = QtCore.QProcessEnvironment() for key, value in context.environment.iteritems(): environment.insert(key, value) process.setProcessEnvironment(environment) def onQProcessFinished(process, context, callback): def runCallback(exitCode): self.processTableModel.removeProcess(process) context.errorValue = str( process.readAllStandardError()).decode("utf-8") context.outputValue = str( process.readAllStandardOutput()).decode("utf-8") context.outputType = exitCode callback(context) return runCallback process.finished[int].connect( onQProcessFinished(process, context, callback)) if context.inputType is not None: process.start(context.shellCommand, QtCore.QIODevice.ReadWrite) if not process.waitForStarted(): raise Exception("No puedo correr") process.write(unicode(context.inputValue).encode("utf-8")) process.closeWriteChannel() else: process.start(context.shellCommand, QtCore.QIODevice.ReadOnly) def buildAdHocCommand(self, *largs, **kwargs): return BundleItemTreeNode( PMXSupportBaseManager.buildAdHocCommand(self, *largs, **kwargs)) def buildAdHocSnippet(self, *largs, **kwargs): return BundleItemTreeNode( PMXSupportBaseManager.buildAdHocSnippet(self, *largs, **kwargs)) #--------------------------------------------------- # MANAGED OBJECTS OVERRIDE INTERFACE #--------------------------------------------------- def setDeleted(self, uuid): """ Marcar un managed object como eliminado """ self.deletedObjects.append(uuid) deleted = map(lambda uuid: unicode(uuid).upper(), self.deletedObjects) self.settings.setValue('deleted', deleted) def isDeleted(self, uuid): return uuid in self.deletedObjects def isEnabled(self, uuid): return uuid not in self.disabledObjects def setDisabled(self, uuid): self.disabledObjects.append(uuid) disabled = map(lambda uuid: unicode(uuid).upper(), self.disabledObjects) self.settings.setValue('disabled', disabled) def setEnabled(self, uuid): self.disabledObjects.remove(uuid) disabled = map(lambda uuid: unicode(uuid).upper(), self.disabledObjects) self.settings.setValue('disabled', disabled) #--------------------------------------------------- # BUNDLE OVERRIDE INTERFACE #--------------------------------------------------- def addBundle(self, bundle): bundleNode = BundleItemTreeNode(bundle) self.bundleTreeModel.appendBundle(bundleNode) self.bundleAdded.emit(bundleNode) return bundleNode def modifyBundle(self, bundle): self.bundleChanged.emit(bundle) def removeBundle(self, bundle): self.bundleTreeModel.removeBundle(bundle) self.bundleRemoved.emit(bundle) def getAllBundles(self): return self.bundleProxyModel.getAllItems() def getDefaultBundle(self): return self.getBundle(self.defaultBundleForNewBundleItems) def populatedBundle(self, bundle): self.bundlePopulated.emit(bundle) #--------------------------------------------------- # BUNDLEITEM OVERRIDE INTERFACE #--------------------------------------------------- def addBundleItem(self, bundleItem): bundleItemNode = BundleItemTreeNode(bundleItem) self.bundleTreeModel.appendBundleItem(bundleItemNode) self.bundleItemAdded.emit(bundleItemNode) return bundleItemNode def modifyBundleItem(self, bundleItem): self.bundleItemChanged.emit(bundleItem) def removeBundleItem(self, bundleItem): self.bundleTreeModel.removeBundleItem(bundleItem) self.bundleItemRemoved.emit(bundleItem) def getAllBundleItems(self): nodes = [] for bundle in self.getAllBundles(): for node in bundle.childNodes(): nodes.append(node) return nodes #--------------------------------------------------- # TEMPLATEFILE OVERRIDE INTERFACE #--------------------------------------------------- def addTemplateFile(self, file): bundleTemplateFileNode = BundleItemTreeNode(file) self.bundleTreeModel.appendTemplateFile(bundleTemplateFileNode) return bundleTemplateFileNode #--------------------------------------------------- # THEME OVERRIDE INTERFACE #--------------------------------------------------- def addTheme(self, theme): themeRow = ThemeStyleTableRow(theme, self.scores) self.themeListModel.appendTheme(themeRow) self.themeAdded.emit(themeRow) return themeRow def modifyTheme(self, theme): self.themeChanged.emit(theme) def removeTheme(self, theme): self.themeListModel.removeTheme(theme) self.themeRemoved.emit(theme) def getAllThemes(self): return self.themeListModel.getAllItems() #--------------------------------------------------- # THEME STYLE OVERRIDE INTERFACE #--------------------------------------------------- def addThemeStyle(self, style): themeStyle = ThemeStyleTableRow(style) self.themeStylesTableModel.appendStyle(themeStyle) return themeStyle def removeThemeStyle(self, style): self.themeStylesTableModel.removeStyle(style) #--------------------------------------------------- # PREFERENCES OVERRIDE INTERFACE #--------------------------------------------------- @dynamic_memoized def getAllPreferences(self): return self.preferenceProxyModel.getAllItems() #--------------------------------------------------- # TABTRIGGERS OVERRIDE INTERFACE #--------------------------------------------------- @dynamic_memoized def getAllTabTriggerItems(self): tabTriggers = [] for item in self.actionItemsProxyModel.getAllItems(): if item.tabTrigger != None: tabTriggers.append(item) return tabTriggers @dynamic_memoized def getAllBundleItemsByTabTrigger(self, tabTrigger): items = [] for item in self.actionItemsProxyModel.getAllItems(): if item.tabTrigger == tabTrigger: items.append(item) return items #--------------------------------------------------- # KEYEQUIVALENT OVERRIDE INTERFACE #--------------------------------------------------- @dynamic_memoized def getAllKeyEquivalentItems(self): keyEquivalent = [] for item in self.actionItemsProxyModel.getAllItems( ) + self.syntaxProxyModel.getAllItems(): if item.keyEquivalent != None: keyEquivalent.append(item) return keyEquivalent @dynamic_memoized def getAllBundleItemsByKeyEquivalent(self, keyEquivalent): items = [] for item in self.actionItemsProxyModel.getAllItems(): if item.keyEquivalent == keyEquivalent: items.append(item) for syntax in self.syntaxProxyModel.getAllItems(): if syntax.keyEquivalent == keyEquivalent: items.append(syntax) return items #--------------------------------------------------- # FILE EXTENSION OVERRIDE INTERFACE #--------------------------------------------------- def getAllBundleItemsByFileExtension(self, path): items = [] for item in self.dragcommandProxyModel.getAllItems(): if any( map( lambda extension: fnmatch.fnmatch( path, "*.%s" % extension), item.draggedFileExtensions)): items.append(item) return items #--------------------------------------------------- # ACTION ITEMS OVERRIDE INTERFACE #--------------------------------------------------- def getAllActionItems(self): return self.actionItemsProxyModel.getAllItems() #--------------------------------------------------- # SYNTAXES OVERRIDE INTERFACE #--------------------------------------------------- def getAllSyntaxes(self): return self.syntaxProxyModel.getAllItems()
class CommitDialog(QtGui.QDialog, Ui_CommitDialog, PMXBaseDialog): lastCommitSummary = pmxConfigPorperty(default=[]) def __init__(self, parent=None): QtGui.QDialog.__init__(self, parent) PMXBaseDialog.__init__(self) self.setupUi(self) self.filesTableModel = FilesTableModel(self) self.tableViewFiles.setModel(self.filesTableModel) self.tableViewFiles.setContextMenuPolicy(QtCore.Qt.CustomContextMenu) self.tableViewFiles.customContextMenuRequested.connect( self.showTableViewFilesContextMenu) self.tableViewFiles.setSelectionBehavior( QtGui.QAbstractItemView.SelectRows) self.tableViewFiles.horizontalHeader().setResizeMode( 1, QtGui.QHeaderView.Stretch) #Setup Context Menu selectMenu = { "title": "Select Menu", "items": [ { 'text': 'Choose All', 'callback': lambda dialog: dialog.chooseAll(), }, { 'text': 'Choose None', 'callback': lambda dialog: dialog.chooseNone(), }, { 'text': 'Revert to Default Choices', 'callback': lambda dialog: dialog.revertChoices(), }, ] } self.selectMenu, _ = create_menu(self, selectMenu, connectActions=True) self.toolButtonSelect.setMenu(self.selectMenu) def chooseAll(self): print "chooseAll" def chooseNone(self): print "chooseNone" def revertChoices(self): print "revertChoices" def initialize(self, mainWindow): self.lastCommitSummary = self.lastCommitSummary[:10] self.comboBoxSummary.addItem("Previous Summaries") for summary in self.lastCommitSummary: self.comboBoxSummary.addItem("%s ..." % " ".join(summary.split()[:8])) @QtCore.pyqtSlot(int) def on_comboBoxSummary_activated(self, index): index -= 1 if index < len(self.lastCommitSummary): self.textEditSummary.setPlainText(self.lastCommitSummary[index]) self.comboBoxSummary.setCurrentIndex(0) def showTableViewFilesContextMenu(self, point): index = self.tableViewFiles.indexAt(point) if index.isValid(): # TODO Obtener el item y armar el menu menu = QtGui.QMenu(self) for action in self.actions: menu.addAction(action["name"]) menu.popup(self.tableViewFiles.mapToGlobal(point)) def setParameters(self, parameters): filePaths = [] fileStatus = [] self.actions = [] if "title" in parameters: self.setWindowTitle(parameters["title"]) if "files" in parameters: filePaths = parameters["files"].split() if "status" in parameters: fileStatus = parameters["status"].split(":") if "diff-cmd" in parameters: cmd, args = parameters["diff-cmd"].split(',') self.actions.append({ 'name': 'Diff', 'command': cmd, 'args': args, 'status': [] }) if "action-cmd" in parameters: for command in parameters["action-cmd"]: status, action = command.split(':') name, cmd, args = action.split(',') self.actions.append({ 'name': name, 'command': cmd, 'args': args, 'status': status.split(",") }) if len(filePaths) == len(fileStatus): self.filesTableModel.setFiles( map( lambda (f, s): { 'path': f, 'status': s, 'checked': s != "?" }, zip(filePaths, fileStatus))) self.tableViewFiles.resizeColumnsToContents() self.tableViewFiles.resizeRowsToContents() def on_buttonOk_pressed(self): self.accept() def on_buttonCancel_pressed(self): self.close() def execModal(self): code = self.exec_() if code == QtGui.QDialog.Accepted: args = ["dylan"] message = self.textEditSummary.toPlainText() args.append("'%s'" % message) if message not in self.lastCommitSummary: self.lastCommitSummary.insert(0, message) self.settings.setValue("lastCommitSummary", self.lastCommitSummary) args.append(" ".join(self.filesTableModel.selectedFiles())) return " ".join(args) return 'cancel'
class PMXBrowserDock(QtGui.QDockWidget, Ui_BrowserDock, PMXBaseDock): SHORTCUT = "F9" ICON = resources.getIcon("internet-web-browser") PREFERED_AREA = QtCore.Qt.BottomDockWidgetArea SETTINGS_GROUP = "Browser" updateInterval = pmxConfigPorperty(default=3000) homePage = pmxConfigPorperty(default="http://www.prymatex.org") @pmxConfigPorperty(default=os.environ.get('http_proxy', '')) def proxy(self, value): """System wide proxy""" proxy_url = QtCore.QUrl(value) #TODO: Una regexp para filtar basura y quitar el try except if not value: network_proxy = QNetworkProxy(QNetworkProxy.NoProxy) else: try: protocol = QNetworkProxy.NoProxy if proxy_url.scheme().startswith('http'): protocol = QNetworkProxy.HttpProxy else: protocol = QNetworkProxy.Socks5Proxy network_proxy = QNetworkProxy(protocol, proxy_url.host(), proxy_url.port(), proxy_url.userName(), proxy_url.password()) except: network_proxy = QNetworkProxy(QNetworkProxy.NoProxy) QNetworkProxy.setApplicationProxy(network_proxy) @classmethod def contributeToSettings(cls): from prymatex.gui.settings.browser import NetworkSettingsWidget from prymatex.gui.settings.addons import AddonsSettingsWidgetFactory return [NetworkSettingsWidget, AddonsSettingsWidgetFactory("browser")] def __init__(self, parent): QtGui.QDockWidget.__init__(self, parent) PMXBaseDock.__init__(self) self.setupUi(self) self.setupToolBar() #Developers, developers, developers!!! Extras QtWebKit.QWebSettings.globalSettings().setAttribute( QtWebKit.QWebSettings.DeveloperExtrasEnabled, True) QtWebKit.QWebSettings.globalSettings().setAttribute( QtWebKit.QWebSettings.PluginsEnabled, True) #New manager self.networkAccessManager = NetworkAccessManager( self, self.webView.page().networkAccessManager()) self.webView.page().setNetworkAccessManager(self.networkAccessManager) self.networkAccessManager.commandUrlRequested.connect( self.on_manager_commandUrlRequested) # Set the default home page self.lineUrl.setText(self.homePage) self.webView.setUrl(QtCore.QUrl(self.homePage)) self.scrollValues = ( False, 0, 0 ) #(<restore scroll values>, <horizontalValue>, <verticalValue>) # history buttons: self.buttonBack.setEnabled(False) self.buttonNext.setEnabled(False) #Capturar editor, bundleitem self.currentEditor = None self.runningContext = None #Sync Timer self.updateTimer = QtCore.QTimer() self.updateTimer.timeout.connect(self.updateHtmlCurrentEditorContent) def setupToolBar(self): #Setup Context Menu optionsMenu = { "text": "Browser Options", "items": [self.actionSyncEditor, self.actionConnectEditor] } self.browserOptionsMenu, _ = create_menu(self, optionsMenu) self.toolButtonOptions.setMenu(self.browserOptionsMenu) def initialize(self, mainWindow): PMXBaseDock.initialize(self, mainWindow) #TODO: ver el tema de proveer servicios esta instalacion en la main window es pedorra mainWindow.browser = self def showEvent(self, event): self.setFocus() def event(self, event): if event.type() == QtCore.QEvent.KeyPress: if event.key() == QtCore.Qt.Key_Escape: self.close() self.mainWindow.currentEditor().setFocus() return True elif event.key() == QtCore.Qt.Key_L and event.modifiers( ) == QtCore.Qt.ControlModifier: self.lineUrl.setFocus() self.lineUrl.selectAll() return True return QtGui.QDockWidget.event(self, event) #======================================================================= # Browser main methods #======================================================================= def setHtml(self, string, url=None): if not self.isVisible(): self.show() self.raise_() if url is not None: self.lineUrl.setText(url.toString()) self.webView.setHtml(string, url) self.runningContext = None def setRunningContext(self, context): url = QtCore.QUrl.fromUserInput("about:%s" % context.description()) self.setHtml(context.outputValue, url) self.runningContext = context def runCommand(self, command): print command command, environment, tempFile = prepareShellScript( unicode(command), self.runningContext.environment) process = Popen(command, stdout=PIPE, stdin=PIPE, stderr=PIPE, env=environment) self.webView.page().mainFrame().addToJavaScriptWindowObject( "_systemWrapper", SystemWrapper(process, command)) #======================================================================= # Browser Signals handlers #======================================================================= def on_manager_commandUrlRequested(self, url): self.application.handleUrlCommand(url) def on_lineUrl_returnPressed(self): """Url have been changed by user""" page = self.webView.page() history = page.history() self.buttonBack.setEnabled(history.canGoBack()) self.buttonNext.setEnabled(history.canGoForward()) url = QtCore.QUrl.fromUserInput(self.lineUrl.text()) self.webView.setUrl(url) def on_buttonStop_clicked(self): """Stop loading the page""" self.webView.stop() def on_buttonReload_clicked(self): """Reload the web page""" url = QtCore.QUrl.fromUserInput(self.lineUrl.text()) self.webView.setUrl(url) def on_buttonBack_clicked(self): """Back button clicked, go one page back""" page = self.webView.page() history = page.history() history.back() self.buttonBack.setEnabled(history.canGoBack()) def on_buttonNext_clicked(self): """Next button clicked, go to next page""" page = self.webView.page() history = page.history() history.forward() self.buttonNext.setEnabled(history.canGoForward()) #============================= # QWebView Signals handlers #============================= def on_webView_iconChanged(self): # print "iconChanged" pass def on_webView_linkClicked(self, link): #Terminar el timer y navegar hasta esa Url #TODO: validar que el link este bien self.stopTimer() map(lambda action: action.setChecked(False), [self.actionSyncEditor, self.actionConnectEditor]) self.webView.load(link) def on_webView_loadFinished(self, ok): if not ok: return self.webView.page().mainFrame().addToJavaScriptWindowObject( "TextMate", TextMate(self)) environment = "" if self.runningContext is not None: environment = "\n".join( map( lambda (key, value): 'window["{0}"]="{1}";'.format(key, value), self.runningContext.environment.iteritems())) self.webView.page().mainFrame().evaluateJavaScript(BASE_JS % environment) #Restore scroll if self.scrollValues[0]: self.webView.page().mainFrame().setScrollBarValue( QtCore.Qt.Horizontal, self.scrollValues[1]) self.webView.page().mainFrame().setScrollBarValue( QtCore.Qt.Vertical, self.scrollValues[2]) def on_webView_urlChanged(self, url): """Update the URL if a link on a web page is clicked""" #History page = self.webView.page() history = page.history() self.buttonBack.setEnabled(history.canGoBack()) self.buttonNext.setEnabled(history.canGoForward()) #Scroll Values self.scrollValues = (url.toString() == self.webView.url().toString(), self.scrollValues[1], self.scrollValues[2]) #Line Location self.lineUrl.setText(url.toString()) def on_webView_loadStarted(self): self.scrollValues = (False, self.webView.page().mainFrame().scrollBarValue( QtCore.Qt.Horizontal), self.webView.page().mainFrame().scrollBarValue( QtCore.Qt.Vertical)) def on_webView_loadProgress(self, load): """Page load progress""" self.buttonStop.setEnabled(load != 100) def on_webView_selectionChanged(self): # print "selectionChanged" pass def on_webView_statusBarMessage(self, message): # print "statusBarMessage", message pass def on_webView_titleChanged(self, title): """Web page title changed - change the tab name""" #print "titleChanged", title #self.setWindowTitle(title) pass #======================================================================= # Browser Auto update for current Editor #======================================================================= def updateHtmlCurrentEditorContent(self): # TODO Resolver url, asegurar que sea html para no hacer cochinadas editor = self.currentEditor if self.currentEditor is not None else self.mainWindow.currentEditor( ) content = editor.toPlainText() url = QtCore.QUrl.fromUserInput(editor.filePath) self.webView.settings().clearMemoryCaches() self.webView.setHtml(content, url) def stopTimer(self): self.updateTimer.stop() self.webView.page().setLinkDelegationPolicy( QtWebKit.QWebPage.DontDelegateLinks) def startTimer(self): self.updateTimer.start(self.updateInterval) self.webView.page().setLinkDelegationPolicy( QtWebKit.QWebPage.DelegateAllLinks) def connectCurrentEditor(self): self.currentEditor = self.mainWindow.currentEditor() self.connect(self.currentEditor, QtCore.SIGNAL("close()"), self.disconnectCurrentEditor) def disconnectCurrentEditor(self): if self.currentEditor is not None: self.disconnect(self.currentEditor, QtCore.SIGNAL("close()"), self.disconnectCurrentEditor) self.currentEditor = None @QtCore.pyqtSlot(bool) def on_actionSyncEditor_toggled(self, checked): if checked: #Quitar otro check self.actionConnectEditor.setChecked(False) #Desconectar editor self.disconnectCurrentEditor() self.startTimer() else: self.stopTimer() @QtCore.pyqtSlot(bool) def on_actionConnectEditor_toggled(self, checked): # TODO Capturar el current editor y usarlo para el update if checked: #Quitar otro check self.actionSyncEditor.setChecked(False) #Capturar editor self.connectCurrentEditor() self.startTimer() else: self.stopTimer() self.disconnectCurrentEditor()
class ProjectManager(QtCore.QObject, PMXBaseComponent): #Signals projectAdded = QtCore.pyqtSignal(object) projectRemoved = QtCore.pyqtSignal(object) projectClose = QtCore.pyqtSignal(object) projectOpen = QtCore.pyqtSignal(object) #Settings SETTINGS_GROUP = 'ProjectManager' workspaceDirectory = pmxConfigPorperty(default=os.path.join( get_home_dir(), "workspace")) #Eclipse muejejeje knownProjects = pmxConfigPorperty(default=[]) workingSets = pmxConfigPorperty(default={}) VALID_PATH_CARACTERS = "-_.() %s%s" % (string.ascii_letters, string.digits) def __init__(self, application): QtCore.QObject.__init__(self) self.fileManager = application.fileManager self.supportManager = application.supportManager self.projectTreeModel = ProjectTreeModel(self) self.projectTreeProxyModel = ProjectTreeProxyModel(self) self.projectTreeProxyModel.setSourceModel(self.projectTreeModel) self.projectMenuProxyModel = ProjectMenuProxyModel(self) self.projectMenuProxyModel.setSourceModel( self.application.supportManager.bundleProxyModel) self.supportManager.bundleAdded.connect( self.on_supportManager_bundleAdded) self.supportManager.bundleRemoved.connect( self.on_supportManager_bundleRemoved) @classmethod def contributeToSettings(cls): return [] def convertToValidPath(self, name): #TODO: este y el del manager de bundles pasarlos a utils validPath = [] for char in unicodedata.normalize('NFKD', unicode(name)).encode( 'ASCII', 'ignore'): char = char if char in self.VALID_PATH_CARACTERS else '-' validPath.append(char) return ''.join(validPath) def on_supportManager_bundleAdded(self, bundle): for project in self.getAllProjects(): if project.namespace is not None and bundle.hasNamespace( project.namespace) and not project.hasBundleMenu(bundle): self.addProjectBundleMenu(project, bundle) def on_supportManager_bundleRemoved(self, bundle): for project in self.getAllProjects(): if project.namespace is not None and bundle.hasNamespace( project.namespace) and project.hasBundleMenu(bundle): self.removeProjectBundleMenu(project, bundle) def loadProjects(self): for path in self.knownProjects[:]: try: ProjectTreeNode.loadProject(path, self) except exceptions.FileNotExistsException as e: print e self.knownProjects.remove(path) self.settings.setValue('knownProjects', self.knownProjects) def isOpen(self, project): return True def appendToKnowProjects(self, project): self.knownProjects.append(project.path()) self.settings.setValue('knownProjects', self.knownProjects) def removeFromKnowProjects(self, project): self.knownProjects.remove(project.path()) self.settings.setValue('knownProjects', self.knownProjects) #--------------------------------------------------- # Environment #--------------------------------------------------- def environmentVariables(self): return {} def supportProjectEnvironment(self, project): return self.supportManager.projectEnvironment(project) #--------------------------------------------------- # PROJECT CRUD #--------------------------------------------------- def createProject(self, name, directory, description=None, reuseDirectory=True): """ Crea un proyecto nuevo lo agrega en los existentes y lo retorna, """ #TODO: dejar este trabajo al file manager if not os.path.exists(directory): os.makedirs(directory) elif not reuseDirectory: raise Exception() project = ProjectTreeNode(directory, { "name": name, "description": description }) try: project.save() except ProjectExistsException: rslt = QtGui.QMessageBox.information( None, _("Project already created on %s") % name, _("Directory %s already contains .pmxproject directory structure. " "Unless you know what you are doing, Cancel and import project," " if it still fails, choose overwirte. Overwrite?") % directory, QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) if rslt == QtGui.QMessageBox.Cancel: return try: project.save(overwirte=True) except FileException as excp: QtGui.QMessageBox.critical( None, _("Project creation failed"), _("<p>Project %s could not be created<p><pre>%s</pre>") % (name, excp)) self.addProject(project) self.appendToKnowProjects(project) return project def updateProject(self, project, **attrs): """Actualiza un proyecto""" if len(attrs) == 1 and "name" in attrs and attrs["name"] == item.name: #Updates que no son updates return item project.update(attrs) project.save() return project def importProject(self, directory): try: project = ProjectTreeNode.loadProject(directory, self) except exceptions.FileNotExistsException: raise exceptions.LocationIsNotProject() self.appendToKnowProjects(project) def deleteProject(self, project, removeFiles=False): """ Elimina un proyecto """ project.delete(removeFiles) self.removeProject(project) #--------------------------------------------------- # PROJECT INTERFACE #--------------------------------------------------- def addProject(self, project): project.setManager(self) # Todo proyecto define un namespace en el manager de support self.application.supportManager.addProjectNamespace(project) self.projectTreeModel.appendProject(project) self.projectAdded.emit(project) def modifyProject(self, project): pass def removeProject(self, project): self.removeFromKnowProjects(project) self.projectTreeModel.removeProject(project) def getAllProjects(self): #TODO: devolver un copia o no hace falta? return self.projectTreeModel.rootNode.childNodes() def openProject(self, project): # Cuando abro un proyecto agrego su namespace al support para aportar bundles y themes print project.directory def closeProject(self, project): # Cuando cierro un proyecto quito su namespace al support print project.directory def setWorkingSet(self, project, workingSet): projects = self.workingSets.setdefault(workingSet) projects.append(project.filePath) project.setWorkingSet(workingSet) self.projectTreeModel.dataChanged.emit() def addProjectBundleMenu(self, project, bundle): project.addBundleMenu(bundle) project.save() def removeProjectBundleMenu(self, project, bundle): project.removeBundleMenu(bundle) project.save() def findProjectForPath(self, path): for project in self.getAllProjects(): if self.application.fileManager.issubpath(path, project.path()): return project
class PMXApplication(QtGui.QApplication): """The application instance. There can't be two apps running simultaneously, since configuration issues may occur. The application loads the PMX Support.""" #======================================================================= # Settings #======================================================================= SETTINGS_GROUP = "Global" @pmxConfigPorperty(default=resources.APPLICATION_STYLE) def styleSheet(self, style): self.setStyleSheet(style) askAboutExternalActions = pmxConfigPorperty(default=False) RESTART_CODE = 1000 def __init__(self): """Inicialización de la aplicación.""" #TODO: Pasar los argumentos a la QApplication QtGui.QApplication.__init__(self, []) # Some init's self.setApplicationName(prymatex.__name__.title()) self.setApplicationVersion(prymatex.__version__) self.setOrganizationDomain(prymatex.__url__) self.setOrganizationName(prymatex.__author__) self.platform = sys.platform resources.loadPrymatexResources(PMXProfile.PMX_SHARE_PATH) #Connects self.aboutToQuit.connect(self.closePrymatex) def installTranslator(self): pass #slanguage = QtCore.QLocale.system().name() #print language #self.translator = QtCore.QTranslator() #print os.path.join(PMXProfile.PMX_SHARE_PATH, "Languages") #self.translator.load(settings.LANGUAGE) #self.installTranslator(translator) def buildSplashScreen(self): from prymatex.widgets.splash import SplashScreen splash_image = resources.getImage('newsplash') splash = SplashScreen(splash_image) splash.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.SplashScreen) splashFont = QtGui.QFont("Monospace", 11) splashFont.setStyleStrategy(QtGui.QFont.PreferAntialias) splash.setFont(splashFont) splash.setMask(splash_image.mask()) return splash def loadGraphicalUserInterface(self): splash = self.buildSplashScreen() splash.show() try: self.cacheManager = self.setupCacheManager() #Cache system Manager self.pluginManager = self.setupPluginManager( ) #Prepare plugin manager #TODO: Cambiar los setup por build, que retornen los manager # Loads self.supportManager = self.setupSupportManager() #Support Manager self.fileManager = self.setupFileManager() #File Manager self.projectManager = self.setupProjectManager() #Project Manager self.kernelManager = self.setupKernelManager( ) #Console kernel Manager self.setupCoroutines() self.setupZeroMQContext() self.setupMainWindow() self.setupServer() # Setup Dialogs self.setupDialogs() #Connect all loads self.projectManager.loadProjects() self.supportManager.loadSupport(splash.showMessage) self.settingsDialog.loadSettings() # Creates the Main Window self.createMainWindow() splash.finish(self.mainWindow) except KeyboardInterrupt: self.logger.critical( "\nQuit signal catched during application startup. Quiting...") self.quit() def unloadGraphicalUserInterface(self): #TODO: ver como dejar todo lindo y ordenado para terminar correctamente #if self.zmqContext is not None: # self.zmqContext.destroy() self.mainWindow.close() del self.mainWindow def resetSettings(self): self.profile.clear() def switchProfile(self): from prymatex.gui.dialogs.profile import PMXProfileDialog profile = PMXProfileDialog.switchProfile(PMXProfile.PMX_PROFILES_FILE) if profile is not None and profile != self.profile.PMX_PROFILE_NAME: self.restart() def restart(self): self.exit(self.RESTART_CODE) def buildSettings(self, profile): # TODO Cambiar este metodo a buildProfile if profile is None or (profile == "" and not PMXProfile.PMX_PROFILES_DONTASK): #Select profile from prymatex.gui.dialogs.profile import PMXProfileDialog profile = PMXProfileDialog.selectProfile( PMXProfile.PMX_PROFILES_FILE) elif profile == "": #Find default profile in config profile = PMXProfile.PMX_PROFILE_DEFAULT #Settings from prymatex.gui.dialogs.settings import PMXSettingsDialog if not profile: raise ValueError("Invalid Profile") self.profile = PMXProfile(profile) # Configure application self.profile.registerConfigurable(self.__class__) self.profile.configure(self) # TODO Este dialogo no va mas aca self.settingsDialog = PMXSettingsDialog(self) def checkSingleInstance(self): """ Checks if there's another instance using current profile """ self.fileLock = os.path.join(self.profile.PMX_PROFILE_PATH, 'prymatex.pid') if os.path.exists(self.fileLock): #Mejorar esto pass #raise exceptions.AlreadyRunningError('%s seems to be runnig. Please close the instance or run other profile.' % (self.profile.PMX_PROFILE_NAME)) else: f = open(self.fileLock, 'w') f.write('%s' % self.applicationPid()) f.close() #======================================================== # Logging system and loggers #======================================================== def getLogger(self, name): """ return logger, for filter by name in future """ return logging.getLogger(name) def setupLogging(self, verbose, namePattern): from datetime import datetime level = [ logging.CRITICAL, logging.ERROR, logging.WARNING, logging.INFO, logging.DEBUG ][verbose % 5] # File name filename = os.path.join( self.profile.PMX_LOG_PATH, '%s-%s.log' % (logging.getLevelName(level), datetime.now().strftime('%d-%m-%Y'))) logging.basicConfig(filename=filename, level=level) # Console handler ch = logging.StreamHandler() formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s') ch.setFormatter(formatter) ch.setLevel(level) if namePattern: #Solo al de consola ch.addFilter(NameFilter(namePattern)) logging.root.addHandler(ch) logging.root.info("Application startup") logging.root.debug("Application startup debug") self.logger = logging.root # Route Qt output Qt.qInstallMsgHandler(self.qtMessageHandler) def qtMessageHandler(self, msgType, msgString): ''' Route Qt messaging system into Prymatex/Python one''' if msgType == Qt.QtDebugMsg: self.logger.debug(msgString) elif msgType == Qt.QtWarningMsg: self.logger.warn(msgString) elif msgType == Qt.QtCriticalMsg: self.logger.critical(msgString) elif msgType == Qt.QtFatalMsg: self.logger.fatal(msgString) elif msgType == Qt.QtSystemMsg: self.logger.debug("System: %s" % msgString) #======================================================== # Managers #======================================================== @logtime def setupSupportManager(self): from prymatex.managers.support import SupportManager self.populateComponent(SupportManager) manager = SupportManager(self) self.profile.configure(manager) #Prepare prymatex namespace sharePath = self.profile.value('PMX_SHARE_PATH') manager.addNamespace('prymatex', sharePath) manager.updateEnvironment({ #TextMate Compatible :P 'TM_APP_PATH': self.profile.value('PMX_APP_PATH'), 'TM_SUPPORT_PATH': manager.environment['PMX_SUPPORT_PATH'], 'TM_BUNDLES_PATH': manager.environment['PMX_BUNDLES_PATH'], 'TM_THEMES_PATH': manager.environment['PMX_THEMES_PATH'], 'TM_PID': self.applicationPid(), #Prymatex 'PMX_APP_NAME': self.applicationName().title(), 'PMX_APP_PATH': self.profile.value('PMX_APP_PATH'), 'PMX_PREFERENCES_PATH': self.profile.value('PMX_PREFERENCES_PATH'), 'PMX_VERSION': self.applicationVersion(), 'PMX_PID': self.applicationPid() }) #Prepare user namespace homePath = self.profile.value('PMX_HOME_PATH') manager.addNamespace('user', homePath) manager.updateEnvironment({ 'PMX_HOME_PATH': homePath, 'PMX_PROFILE_PATH': self.profile.value('PMX_PROFILE_PATH'), 'PMX_TMP_PATH': self.profile.value('PMX_TMP_PATH'), 'PMX_LOG_PATH': self.profile.value('PMX_LOG_PATH') }) return manager def setupFileManager(self): from prymatex.managers.files import FileManager self.populateComponent(FileManager) manager = FileManager(self) self.profile.configure(manager) manager.filesytemChange.connect(self.on_filesytemChange) return manager def setupProjectManager(self): from prymatex.managers.projects import ProjectManager self.populateComponent(ProjectManager) manager = ProjectManager(self) self.profile.configure(manager) return manager def setupKernelManager(self): kernelManager = None try: from IPython.frontend.qt.kernelmanager import QtKernelManager kernelManager = QtKernelManager() kernelManager.start_kernel() kernelManager.start_channels() if hasattr(kernelManager, "connection_file"): ipconnection = kernelManager.connection_file else: shell_port = kernelManager.shell_address[1] iopub_port = kernelManager.sub_address[1] stdin_port = kernelManager.stdin_address[1] hb_port = kernelManager.hb_address[1] ipconnection = "--shell={0} --iopub={1} --stdin={2} --hb={3}".format( shell_port, iopub_port, stdin_port, hb_port) self.supportManager.updateEnvironment( {"PMX_IPYTHON_CONNECTION": ipconnection}) except ImportError as e: self.logger.warn("Warning: %s" % e) kernelManager = None return kernelManager def setupCacheManager(self): from prymatex.managers.cache import CacheManager return CacheManager() def setupPluginManager(self): from prymatex.managers.plugins import PluginManager self.populateComponent(PluginManager) pluginManager = PluginManager(self) self.profile.configure(pluginManager) # TODO: Ruta de los plugins ver de aprovechar settings quiza esta sea una ruta base solamente pluginManager.addPluginDirectory( self.profile.value('PMX_PLUGINS_PATH')) pluginManager.loadPlugins() return pluginManager def setupCoroutines(self): self.scheduler = coroutines.Scheduler(self) def setupZeroMQContext(self): try: from prymatex.utils import zeromqt self.zmqContext = zeromqt.ZeroMQTContext(parent=self) except ImportError as e: self.logger.warn("Warning: %s" % e) self.zmqContext = None def setupMainWindow(self): from prymatex.gui.mainwindow import PMXMainWindow self.populateComponent(PMXMainWindow) def setupServer(self): #Seria mejor que esto no falle pero bueno tengo que preguntar por none if self.zmqContext is not None: from prymatex.core.server import PrymatexServer self.populateComponent(PrymatexServer) self.server = PrymatexServer(self) #======================================================== # Dialogs #======================================================== def setupDialogs(self): # TODO: Creo que esto del bundle editor global asi no va a caminar muy bien #Bundle Editor from prymatex.gui.support.bundleeditor import PMXBundleEditor self.populateComponent(PMXBundleEditor) self.bundleEditor = PMXBundleEditor(self) #self.bundleEditor.setModal(True) def closePrymatex(self): self.logger.debug("Close") self.profile.saveState(self.mainWindow) os.unlink(self.fileLock) def commitData(self, manager): print "Commit data" def saveState(self, manager): print "saveState" pass #======================================================== # Components #======================================================== def extendComponent(self, componentClass): componentClass.application = self componentClass.logger = self.getLogger('.'.join( [componentClass.__module__, componentClass.__name__])) def populateComponent(self, componentClass): self.extendComponent(componentClass) self.profile.registerConfigurable(componentClass) for settingClass in componentClass.contributeToSettings(): try: self.extendComponent(settingClass) self.settingsDialog.register( settingClass(componentClass.settings)) except (RuntimeError, ImportError): # TODO: Inform user but dont' prevent pmx from starting pass def createWidgetInstance(self, widgetClass, parent): # TODO Que parent sea opcional y pueda ser la mainWindow si no viene seteado return self.pluginManager.createWidgetInstance(widgetClass, parent) #======================================================== # Create Zmq Sockets #======================================================== def zmqSocket(self, socketType, name, interface='tcp://127.0.0.1'): # TODO ver la variable aca socket = self.zmqContext.socket(socketType) port = socket.bind_to_random_port(interface) self.supportManager.addToEnvironment("PMX_" + name.upper() + "_PORT", port) return socket #======================================================== # Editors and mainWindow handle #======================================================== def createEditorInstance(self, filePath=None, parent=None): editorClass = filePath is not None and self.pluginManager.findEditorClassForFile( filePath) or self.pluginManager.defaultEditor() if editorClass is not None: return self.createWidgetInstance(editorClass, parent) def createMainWindow(self): """Creates the windows""" from prymatex.gui.mainwindow import PMXMainWindow #TODO: Testeame con mas de una for _ in range(1): self.mainWindow = PMXMainWindow(self) #Configure and add dockers self.pluginManager.populateMainWindow(self.mainWindow) self.profile.configure(self.mainWindow) self.mainWindow.show() self.profile.restoreState(self.mainWindow) if not self.mainWindow.editors(): self.mainWindow.addEmptyEditor() def showMessage(self, message): #Si tengo mainwindow vamos por este camino, sino hacerlo llegar de otra forma self.mainWindow.showMessage(message) def currentEditor(self): return self.mainWindow.currentEditor() def findEditorForFile(self, filePath): #Para cada mainwindow buscar el editor return self.mainWindow, self.mainWindow.findEditorForFile(filePath) def canBeHandled(self, filePath): #from prymatex.utils.pyqtdebug import ipdb_set_trace #ipdb_set_trace() ext = os.path.splitext(filePath)[1].replace('.', '') for fileTypes in [ syntax.item.fileTypes for syntax in self.supportManager.getAllSyntaxes() if hasattr(syntax.item, 'fileTypes') and syntax.item.fileTypes ]: if ext in fileTypes: return True return False def openFile(self, filePath, cursorPosition=None, focus=True, mainWindow=None, useTasks=True): """Open a editor in current window""" filePath = self.fileManager.normcase(filePath) if self.fileManager.isOpen(filePath): mainWindow, editor = self.findEditorForFile(filePath) if editor is not None: mainWindow.setCurrentEditor(editor) if isinstance(cursorPosition, tuple): editor.setCursorPosition(cursorPosition) elif self.fileManager.exists(filePath): mainWindow = mainWindow or self.mainWindow editor = self.createEditorInstance(filePath, mainWindow) def on_editorReady(mainWindow, editor, cursorPosition, focus): def editorReady(openResult): if isinstance(cursorPosition, tuple): editor.setCursorPosition(cursorPosition) mainWindow.tryCloseEmptyEditor() mainWindow.addEditor(editor, focus) return editorReady if useTasks and inspect.isgeneratorfunction(editor.open): task = self.scheduler.newTask(editor.open(filePath)) task.done.connect( on_editorReady(mainWindow, editor, cursorPosition, focus)) elif inspect.isgeneratorfunction(editor.open): on_editorReady(mainWindow, editor, cursorPosition, focus)(list(editor.open(filePath))) else: on_editorReady(mainWindow, editor, cursorPosition, focus)(editor.open(filePath)) def openDirectory(self, directoryPath): raise NotImplementedError( "Directory contents should be opened as files here") def handleUrlCommand(self, url): if isinstance(url, basestring): url = QtCore.QUrl(url) if url.scheme() == "txmt": #TODO: Controlar que sea un open sourceFile = url.queryItemValue('url') position = (0, 0) line = url.queryItemValue('line') if line: position = (int(line) - 1, position[1]) column = url.queryItemValue('column') if column: position = (position[0], int(column) - 1) if sourceFile: filePath = QtCore.QUrl(sourceFile, QtCore.QUrl.TolerantMode).toLocalFile() self.openFile(filePath, position) else: self.currentEditor().setCursorPosition(position) def openArgumentFiles(self, args): for filePath in filter(lambda f: os.path.exists(f), args): if os.path.isfile(filePath): self.openFile(filePath) else: self.openDirectory(filePath) def checkExternalAction(self, mainWindow, editor): if editor.isExternalChanged(): message = "The file '%s' has been changed on the file system, Do you want to replace the editor contents with these changes?" result = QtGui.QMessageBox.question( editor, _("File changed"), _(message) % editor.filePath, buttons=QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, defaultButton=QtGui.QMessageBox.Yes ) if self.askAboutExternalActions else QtGui.QMessageBox.Yes if result == QtGui.QMessageBox.Yes: if inspect.isgeneratorfunction(editor.reload): task = self.scheduler.newTask(editor.reload()) else: editor.reload() elif result == QtGui.QMessageBox.No: pass elif editor.isExternalDeleted(): message = "The file '%s' has been deleted or is not accessible. Do you want to save your changes or close the editor without saving?" result = QtGui.QMessageBox.question( editor, _("File deleted"), _(message) % editor.filePath, buttons=QtGui.QMessageBox.Save | QtGui.QMessageBox.Close, defaultButton=QtGui.QMessageBox.Close ) if self.askAboutExternalActions else QtGui.QMessageBox.Close if result == QtGui.QMessageBox.Close: mainWindow.closeEditor(editor) elif result == QtGui.QMessageBox.Save: mainWindow.saveEditor(editor) def on_filesytemChange(self, filePath, change): mainWindow, editor = self.findEditorForFile(filePath) editor.setExternalAction(change) if mainWindow.currentEditor() == editor: self.checkExternalAction(mainWindow, editor) #--------------------------------------------------- # Exceptions, Print exceptions in a window #--------------------------------------------------- def replaceSysExceptHook(self): def displayExceptionDialog(exctype, value, traceback): ''' Display a nice dialog showing the python traceback''' from prymatex.gui.emergency.tracedialog import PMXTraceBackDialog sys.__excepthook__(exctype, value, traceback) PMXTraceBackDialog.fromSysExceptHook(exctype, value, traceback).exec_() sys.excepthook = displayExceptionDialog def __str__(self): return '<PMXApplication at {} PID: {}>'.format(hash(self), os.getpid()) __unicode__ = __repr__ = __str__
class FileManager(QtCore.QObject, PMXBaseComponent): """A File Manager""" #========================================================= # Signals #========================================================= fileCreated = QtCore.pyqtSignal(str) fileDeleted = QtCore.pyqtSignal(str) fileChanged = QtCore.pyqtSignal(str) fileRenamed = QtCore.pyqtSignal(str, str) directoryCreated = QtCore.pyqtSignal(str) directoryDeleted = QtCore.pyqtSignal(str) directoryChanged = QtCore.pyqtSignal(str) directoryRenamed = QtCore.pyqtSignal(str, str) # Generic Signal filesytemChange = QtCore.pyqtSignal(str, int) #========================================================= # Settings #========================================================= SETTINGS_GROUP = 'FileManager' fileHistory = pmxConfigPorperty(default=[]) fileHistoryLength = pmxConfigPorperty(default=10) lineEnding = pmxConfigPorperty(default='unix') encoding = pmxConfigPorperty(default='utf-8') #========================================================= # Constants #========================================================= CREATED = 1 << 0 DELETED = 1 << 1 RENAMED = 1 << 2 MOVED = 1 << 3 CHANGED = 1 << 4 ENCODINGS = ['windows-1253', 'iso-8859-7', 'macgreek'] def __init__(self, application): QtCore.QObject.__init__(self) self.last_directory = get_home_dir() self.fileWatcher = QtCore.QFileSystemWatcher() self.fileWatcher.fileChanged.connect(self.on_fileChanged) self.fileWatcher.directoryChanged.connect(self.on_directoryChanged) self.connectGenericSignal() @classmethod def contributeToSettings(cls): return [] #======================================================== # Signals #======================================================== def connectGenericSignal(self): UNARY_SINGAL_CONSTANT_MAP = ( (self.fileCreated, FileManager.CREATED), (self.fileDeleted, FileManager.DELETED), (self.fileChanged, FileManager.CHANGED), (self.directoryCreated, FileManager.CREATED), (self.directoryDeleted, FileManager.DELETED), (self.directoryChanged, FileManager.CHANGED), ) BINARY_SINGAL_CONSTANT_MAP = ( (self.fileRenamed, FileManager.RENAMED), (self.directoryRenamed, FileManager.RENAMED), ) for signal, associatedConstant in UNARY_SINGAL_CONSTANT_MAP: signal.connect(lambda path, constant=associatedConstant: self. filesytemChange.emit(path, constant)) for signal, associatedConstant in BINARY_SINGAL_CONSTANT_MAP: signal.connect(lambda _x, path, constant=associatedConstant: self. filesytemChange.emit(path, constant)) def on_fileChanged(self, filePath): if not os.path.exists(filePath): self.fileDeleted.emit(filePath) else: self.fileChanged.emit(filePath) def on_directoryChanged(self, directoryPath): if not os.path.exists(directoryPath): self.directoryDeleted.emit(directoryPath) else: self.directoryChanged.emit(directoryPath) #======================================================== # History #======================================================== def add_file_history(self, filePath): if filePath in self.fileHistory: self.fileHistory.remove(filePath) self.fileHistory.insert(0, filePath) if len(self.fileHistory) > self.fileHistoryLength: self.fileHistory = self.fileHistory[0:self.fileHistoryLength] self.settings.setValue("fileHistory", self.fileHistory) def clearFileHistory(self): self.fileHistory = [] self.settings.setValue("fileHistory", self.fileHistory) #======================================================== # Path handling, create, move, copy, link, delete #======================================================== def _onerror(func, path, exc_info): #@NoSelf import stat if not os.access(path, os.W_OK): # Is the error an access error ? os.chmod(path, stat.S_IWUSR) func(path) else: raise def createDirectory(self, directory): """ Create a new directory. """ if os.path.exists(directory): raise exceptions.FileExistsException("The directory already exist", directory) os.makedirs(directory) def createFile(self, filePath): """Create a new file.""" if os.path.exists(filePath): raise exceptions.IOException("The file already exist") open(filePath, 'w').close() move = lambda self, src, dst: shutil.move(src, dst) copytree = lambda self, src, dst: shutil.copytree(src, dst) copy = lambda self, src, dst: shutil.copy2(src, dst) link = lambda self, src, dst: dst def deletePath(self, path): if os.path.isfile(path): # Mandar señal para cerrar editores os.unlink(path) else: shutil.rmtree(path, onerror=self._onerror) #================================================================== # Path data #================================================================== exists = lambda self, path: os.path.exists(path) isdir = lambda self, path: os.path.isdir(path) isfile = lambda self, path: os.path.isfile(path) join = lambda self, *path: os.path.join(*path) extension = lambda self, path: os.path.splitext(path.lower())[-1][1:] splitext = lambda self, path: os.path.splitext(path) dirname = lambda self, path: os.path.dirname(path) basename = lambda self, path: os.path.basename(path) mimeType = lambda self, path: mimetypes.guess_type(path)[0] or "" issubpath = lambda self, childPath, parentPath: osextra.path.issubpath( childPath, parentPath) fullsplit = lambda self, path: osextra.path.fullsplit(path) normcase = lambda self, path: os.path.normcase(path) normpath = lambda self, path: os.path.normpath(path) realpath = lambda self, path: os.path.realpath(path) relpath = lambda self, path: os.path.relpath(path) def expandVars(self, text): context = self.application.supportManager.buildEnvironment() path = osextra.path.expand_shell_var(text, context=context) if os.path.exists(path): return path #================================================================== # Handling files for retrieving data. open, read, write, close #================================================================== def isOpen(self, filePath): return filePath in self.fileWatcher.files() def openFile(self, filePath): """ Open and read a file, return the content. """ if not os.path.exists(filePath): raise exceptions.IOException("The file does not exist: %s" % filePath) if not os.path.isfile(filePath): raise exceptions.IOException("%s is not a file" % filePath) self.last_directory = os.path.dirname(filePath) #Update file history self.add_file_history(filePath) self.watchPath(filePath) def readFile(self, filePath): """Read from file""" #TODO: Que se pueda hacer una rutina usando yield for enc in [self.encoding] + self.ENCODINGS: try: fileRead = codecs.open(filePath, "r", encoding=enc) content = fileRead.read() break except Exception, e: print "File: %s, %s" % (filePath, e) fileRead.close() return content