--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProjectPyramid/Project.py Tue Aug 28 17:15:21 2012 +0200 @@ -0,0 +1,796 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2012 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the Pyramid project support. +""" + +import os +import configparser + +from PyQt4.QtCore import QObject, QFileInfo, QProcess, QTimer, QUrl +from PyQt4.QtGui import QMenu, QDialog, QInputDialog, QDesktopServices + +from E5Gui.E5Application import e5App +from E5Gui import E5MessageBox, E5FileDialog +from E5Gui.E5Action import E5Action + +from .FormSelectionDialog import FormSelectionDialog +from .CreateParametersDialog import CreateParametersDialog +from .PyramidDialog import PyramidDialog +from .DistributionTypeSelectionDialog import DistributionTypeSelectionDialog + +import Utilities +from Globals import isWindowsPlatform +import UI.PixmapCache + + +class PyramidNoProjectSelectedException(Exception): + """ + Exception thrown to signal, that there is no current Pyramid project. + """ + pass + + +class Project(QObject): + """ + Class implementing the Pyramid project support. + """ + def __init__(self, plugin, parent = None): + """ + Constructor + + @param plugin reference to the plugin object + @param parent parent (QObject) + """ + super().__init__(parent) + + self.__plugin = plugin + self.__ui = parent + self.__e5project = e5App().getObject("Project") + self.__hooksInstalled = False + + self.__mainMenu = None + + self.__serverProc = None + + def initActions(self): + """ + Public method to define the Pyramid actions. + """ + self.actions = [] + + self.selectProjectAct = E5Action(self.trUtf8('Current Pyramid Project'), + "", + 0, 0, + self,'pyramid_current_project') + self.selectProjectAct.setStatusTip(self.trUtf8( + 'Selects the current Pyramid project')) + self.selectProjectAct.setWhatsThis(self.trUtf8( + """<b>Current Pyramid Project</b>""" + """<p>Selects the Pyramid project. Used for multi-project """ + """Pyramid projects to switch between the projects.</p>""" + )) + self.selectProjectAct.triggered[()].connect(self.__selectProject) + self.actions.append(self.selectProjectAct) + self.__setCurrentProject(None) + + ############################### + ## create actions below ## + ############################### + + self.createProjectAct = E5Action(self.trUtf8('Create Pyramid Project'), + self.trUtf8('Create Pyramid &Project'), + 0, 0, + self,'pyramid_create_project') + self.createProjectAct.setStatusTip(self.trUtf8( + 'Creates a new Pyramid project')) + self.createProjectAct.setWhatsThis(self.trUtf8( + """<b>Create Pyramid Project</b>""" + """<p>Creates a new Pyramid project using "pcreate".</p>""" + )) + self.createProjectAct.triggered[()].connect(self.__createProject) + self.actions.append(self.createProjectAct) + + ############################## + ## run actions below ## + ############################## + + self.runServerAct = E5Action(self.trUtf8('Run Server'), + self.trUtf8('Run &Server'), + 0, 0, + self,'pyramid_run_server') + self.runServerAct.setStatusTip(self.trUtf8( + 'Starts the Pyramid Web server')) + self.runServerAct.setWhatsThis(self.trUtf8( + """<b>Run Server</b>""" + """<p>Starts the Pyramid Web server using""" + """ "pserve --reload development.ini".</p>""" + )) + self.runServerAct.triggered[()].connect(self.__runServer) + self.actions.append(self.runServerAct) + + self.runLoggingServerAct = E5Action(self.trUtf8('Run Server with Logging'), + self.trUtf8('Run Server with &Logging'), + 0, 0, + self,'pyramid_run_logging_server') + self.runLoggingServerAct.setStatusTip(self.trUtf8( + 'Starts the Pyramid Web server with logging')) + self.runLoggingServerAct.setWhatsThis(self.trUtf8( + """<b>Run Server with Logging</b>""" + """<p>Starts the Pyramid Web server with logging using""" + """ "pserve --log-file=server.log --reload development.ini".</p>""" + )) + self.runLoggingServerAct.triggered[()].connect(self.__runLoggingServer) + self.actions.append(self.runLoggingServerAct) + + self.runBrowserAct = E5Action(self.trUtf8('Run Web-Browser'), + self.trUtf8('Run &Web-Browser'), + 0, 0, + self,'pyramid_run_browser') + self.runBrowserAct.setStatusTip(self.trUtf8( + 'Starts the default Web-Browser with the URL of the Pyramid Web server')) + self.runBrowserAct.setWhatsThis(self.trUtf8( + """<b>Run Web-Browser</b>""" + """<p>Starts the default Web-Browser with the URL of the """ + """Pyramid Web server.</p>""" + )) + self.runBrowserAct.triggered[()].connect(self.__runBrowser) + self.actions.append(self.runBrowserAct) + + self.runPythonShellAct = E5Action(self.trUtf8('Start Pyramid Python Console'), + self.trUtf8('Start Pyramid &Python Console'), + 0, 0, + self,'pyramid_python_console') + self.runPythonShellAct.setStatusTip(self.trUtf8( + 'Starts an interactive Python interpreter')) + self.runPythonShellAct.setWhatsThis(self.trUtf8( + """<b>Start Pyramid Python Console</b>""" + """<p>Starts an interactive Python interpreter.</p>""" + )) + self.runPythonShellAct.triggered[()].connect(self.__runPythonShell) + self.actions.append(self.runPythonShellAct) + + ############################## + ## setup actions below ## + ############################## + + self.setupDevelopAct = E5Action(self.trUtf8('Setup Development Environment'), + self.trUtf8('Setup &Development Environment'), + 0, 0, + self,'pyramid_setup_development') + self.setupDevelopAct.setStatusTip(self.trUtf8( + 'Setup the Pyramid project in development mode')) + self.setupDevelopAct.setWhatsThis(self.trUtf8( + """<b>Setup Development Environment</b>""" + """<p>Setup the Pyramid project in development mode using""" + """ "python setup.py develop".</p>""" + )) + self.setupDevelopAct.triggered[()].connect(self.__setupDevelop) + self.actions.append(self.setupDevelopAct) + + ################################## + ## distribution actions below ## + ################################## + + self.buildDistroAct = E5Action(self.trUtf8('Build Distribution'), + self.trUtf8('Build &Distribution'), + 0, 0, + self,'pyramid_build_distribution') + self.buildDistroAct.setStatusTip(self.trUtf8( + 'Builds a distribution file for the Pyramid project')) + self.buildDistroAct.setWhatsThis(self.trUtf8( + """<b>Build Distribution</b>""" + """<p>Builds a distribution file for the Pyramid project using""" + """ "python setup.py sdist".</p>""" + )) + self.buildDistroAct.triggered[()].connect(self.__buildDistribution) + self.actions.append(self.buildDistroAct) + + ################################## + ## documentation action below ## + ################################## + + self.documentationAct = E5Action(self.trUtf8('Documentation'), + self.trUtf8('D&ocumentation'), + 0, 0, + self,'pyramid_documentation') + self.documentationAct.setStatusTip(self.trUtf8( + 'Shows the help viewer with the Pyramid documentation')) + self.documentationAct.setWhatsThis(self.trUtf8( + """<b>Documentation</b>""" + """<p>Shows the help viewer with the Pyramid documentation.</p>""" + )) + self.documentationAct.triggered[()].connect(self.__showDocumentation) + self.actions.append(self.documentationAct) + + ############################## + ## about action below ## + ############################## + + self.aboutPyramidAct = E5Action(self.trUtf8('About Pyramid'), + self.trUtf8('About P&yramid'), + 0, 0, + self,'pyramid_about') + self.aboutPyramidAct.setStatusTip(self.trUtf8( + 'Shows some information about Pyramid')) + self.aboutPyramidAct.setWhatsThis(self.trUtf8( + """<b>About Pyramid</b>""" + """<p>Shows some information about Pyramid.</p>""" + )) + self.aboutPyramidAct.triggered[()].connect(self.__pyramidInfo) + self.actions.append(self.aboutPyramidAct) + + def initMenu(self): + """ + Public slot to initialize the Pyramid menu. + + @return the menu generated (QMenu) + """ + menu = QMenu(self.trUtf8('P&yramid'), self.__ui) + menu.setTearOffEnabled(True) + + menu.addAction(self.selectProjectAct) + menu.addSeparator() + menu.addAction(self.runServerAct) + menu.addAction(self.runLoggingServerAct) + menu.addAction(self.runBrowserAct) + menu.addSeparator() + menu.addAction(self.createProjectAct) + menu.addSeparator() + menu.addAction(self.setupDevelopAct) + menu.addSeparator() + menu.addAction(self.runPythonShellAct) + menu.addSeparator() + menu.addAction(self.buildDistroAct) + menu.addSeparator() + menu.addAction(self.documentationAct) + menu.addSeparator() + menu.addAction(self.aboutPyramidAct) + + self.__mainMenu = menu + return menu + + def projectOpenedHooks(self): + """ + Public method to add our hook methods. + """ + if self.__e5project.getProjectType() == "Pyramid": + self.__formsBrowser = \ + e5App().getObject("ProjectBrowser").getProjectBrowser("forms") + self.__formsBrowser.addHookMethodAndMenuEntry("newForm", + self.newForm, self.trUtf8("New template...")) + +## self.__e5project.projectLanguageAddedByCode.connect( +## self.__projectLanguageAdded) +## self.__translationsBrowser = \ +## e5App().getObject("ProjectBrowser").getProjectBrowser("translations") +## self.__translationsBrowser.addHookMethodAndMenuEntry("extractMessages", +## self.extractMessages, self.trUtf8("Extract messages")) +## self.__translationsBrowser.addHookMethodAndMenuEntry("releaseAll", +## self.compileCatalogs, self.trUtf8("Compile all catalogs")) +## self.__translationsBrowser.addHookMethodAndMenuEntry("releaseSelected", +## self.compileSelectedCatalogs, +## self.trUtf8("Compile selected catalogs")) +## self.__translationsBrowser.addHookMethodAndMenuEntry("generateAll", +## self.updateCatalogs, self.trUtf8("Update all catalogs")) +## self.__translationsBrowser.addHookMethodAndMenuEntry("generateSelected", +## self.updateSelectedCatalogs, self.trUtf8("Update selected catalogs")) + + self.__hooksInstalled = True + + def projectClosedHooks(self): + """ + Public method to remove our hook methods. + """ + if self.__hooksInstalled: + self.__formsBrowser.removeHookMethod("newForm") + self.__formsBrowser = None + +## self.__e5project.projectLanguageAddedByCode.disconnect( +## self.__projectLanguageAdded) +## self.__translationsBrowser.removeHookMethod("extractMessages") +## self.__translationsBrowser.removeHookMethod("releaseAll") +## self.__translationsBrowser.removeHookMethod("releaseSelected") +## self.__translationsBrowser.removeHookMethod("generateAll") +## self.__translationsBrowser.removeHookMethod("generateSelected") +## self.__translationsBrowser = None + + self.__hooksInstalled = False + + def newForm(self, path): + """ + Public method to create a new form. + + @param path full directory path for the new form file (string) + """ + dlg = FormSelectionDialog() + if dlg.exec_() == QDialog.Accepted: + template = dlg.getTemplateText() + + filter = self.trUtf8( + "Chameleon Templates (*.pt);;" + "Chameleon Text Templates (*.txt);;" + "Mako Templates (*.mako);;" + "Mako Templates (*.mak);;" + "HTML Files (*.html);;" + "HTML Files (*.htm);;" + "All Files (*)") + fname, selectedFilter = E5FileDialog.getSaveFileNameAndFilter( + self.__ui, + self.trUtf8("New Form"), + path, + filter, + None, + E5FileDialog.Options(E5FileDialog.DontConfirmOverwrite)) + if fname: + ext = QFileInfo(fname).suffix() + if not ext: + ex = selectedFilter.split("(*")[1].split(")")[0] + if ex: + fname += ex + + if os.path.exists(fname): + res = E5MessageBox.yesNo(self.__ui, + self.trUtf8("New Form"), + self.trUtf8("""The file already exists! Overwrite it?"""), + icon = E5MessageBox.Warning) + if not res: + # user selected to not overwrite + return + + try: + f = open(fname, "w", encoding="utf-8") + f.write(template) + f.close() + except IOError as e: + E5MessageBox.critical(self, + self.trUtf8("New Form"), + self.trUtf8("<p>The new form file <b>{0}</b> could not be" + " created.<br/>" + "Problem: {1}</p>").format(fname, e)) + return + + self.__e5project.appendFile(fname) + self.__formsBrowser.sourceFile.emit(fname) + + ################################################################## + ## methods below implement general functionality + ################################################################## + + def projectClosed(self): + """ + Public method to handle the closing of a project. + """ + if self.__serverProc is not None: + self.__serverProcFinished() + self.__setCurrentProject(None) + + def __getVirtualEnvironment(self): + """ + Private method to get the path of the virtual environment. + + @return path of the virtual environment (string) + """ + language = self.__e5project.getProjectLanguage() + if language == "Python3": + virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy3") + elif language == "Python2": + virtEnv = self.__plugin.getPreferences("VirtualEnvironmentPy2") + else: + virtEnv = "" + if virtEnv and not os.path.exists(virtEnv): + virtEnv = "" + return virtEnv + + def getPyramidCommand(self, cmd): + """ + Public method to build a Pyramid command. + + @param cmd command (string) + @return full pyramid command (string) + """ + virtualEnv = self.__getVirtualEnvironment() + if virtualEnv: + if isWindowsPlatform(): + cmd = os.path.join(virtualEnv, "Scripts", cmd) + else: + cmd = os.path.join(virtualEnv, "bin", cmd) + return cmd + + def getPythonCommand(self): + """ + Public method to build the Python command. + + @return python command (string) + """ + python = "python" + if isWindowsPlatform(): + python += ".exe" + virtualEnv = self.__getVirtualEnvironment() + if virtualEnv: + if isWindowsPlatform(): + python = os.path.join(virtualEnv, "Scripts", python) + else: + python = os.path.join(virtualEnv, "bin", python) + return python + + def __pyramidInfo(self): + """ + Private slot to show some info about Pyramid. + """ + url = "http://www.pylonsproject.org/projects/pyramid/about" + msgBox = E5MessageBox.E5MessageBox(E5MessageBox.Question, + self.trUtf8("About Pyramid"), + self.trUtf8( + "<p>Pyramid is a high-level Python Web framework that encourages rapid " + "development and clean, pragmatic design.</p>" + "<p>URL: <a href=\"{0}\">{0}</a></p>" + ).format(url), + modal=True, + buttons=E5MessageBox.Ok) + msgBox.setIconPixmap(UI.PixmapCache.getPixmap( + os.path.join("ProjectPyramid", "icons", "pyramid64.png"))) + msgBox.exec_() + + def isSpawningConsole(self, consoleCmd): + """ + Public method to check, if the given console is a spawning console. + + @param consoleCmd console command (string) + @return tuple of two entries giving an indication, if the console + is spawning (boolean) and the (possibly) cleaned console command + (string) + """ + if consoleCmd and consoleCmd[0] == '@': + return (True, consoleCmd[1:]) + else: + return (False, consoleCmd) + + ################################################################## + ## slots below implement creation functions + ################################################################## + + def __createProject(self): + """ + Private slot to create a new Pyramid project. + """ + dlg = CreateParametersDialog(self) + if dlg.exec_() == QDialog.Accepted: + scaffold, project, overwrite, simulate = dlg.getData() + + cmd = self.getPyramidCommand("pcreate") + args = [] + if overwrite: + args.append("--overwrite") + else: + args.append("--interactive") + if simulate: + args.append("--simulate") + args.append("--scaffold={0}".format(scaffold)) + args.append(project) + dlg = PyramidDialog(self.trUtf8("Create Pyramid Project"), + linewrap=False, parent=self.__ui) + if dlg.startProcess(cmd, args, self.__e5project.getProjectPath()): + dlg.exec_() + if dlg.normalExit() and not simulate: + # search for files created by pcreate and add them to the project + projectPath = os.path.join(self.__e5project.getProjectPath(), project) + for entry in os.walk(projectPath): + for fileName in entry[2]: + fullName = os.path.join(entry[0], fileName) + self.__e5project.appendFile(fullName) + + # create the base directory for translations + i18nPath = os.path.join(projectPath, project.lower(), "i18n") + if not os.path.exists(i18nPath): + os.makedirs(i18nPath) + self.__e5project.setDirty(True) + + self.__setCurrentProject(project) + + ################################################################## + ## methods below implement site related functions + ################################################################## + + def __findProjects(self): + """ + Private method to determine the relative path of all Pyramid + projects (= top level dirs). + + @return list of projects (list of string) + """ + projects = [] + ppath = self.__e5project.getProjectPath() + for entry in os.listdir(ppath): + if entry[0] not in "._" and \ + os.path.isdir(os.path.join(ppath, entry)): + projects.append(entry) + return projects + + def __selectProject(self): + """ + Private method to select a Pyramid project to work with. + + @return selected project (string) + """ + projects = self.__findProjects() + if len(projects) == 0: + project = None + elif len(projects) == 1: + project = projects[0] + else: + if self.__currentProject is not None: + try: + cur = projects.index(self.__currentProject) + except ValueError: + cur = 0 + else: + cur = 0 + project, ok = QInputDialog.getItem( + self.__ui, + self.trUtf8("Select Pyramid Project"), + self.trUtf8("Select the Pyramid project to work with."), + projects, + cur, False) + if not ok: + projects = None + self.__setCurrentProject(project) + + def __projectPath(self): + """ + Private method to calculate the full path of the Pyramid project. + + @exception PyramidNoProjectSelectedException raised, if no project is selected + @return path of the project (string) + """ + if self.__currentProject is None: + self.__selectProject() + + if self.__currentProject is None: + raise PyramidNoProjectSelectedException + else: + return os.path.join(self.__e5project.getProjectPath(), + self.__currentProject) + + def __setCurrentProject(self, project): + """ + Private slot to set the current project. + + @param project name of the project (string) + """ + if project is not None and len(project) == 0: + self.__currentProject = None + else: + self.__currentProject = project + + if self.__currentProject is None: + curProject = self.trUtf8("None") + else: + curProject = self.__currentProject + self.selectProjectAct.setText( + self.trUtf8('&Current Pyramid Project ({0})').format(curProject)) + + if self.__currentProject is None: + self.__e5project.pdata["TRANSLATIONPATTERN"] = [] + else: + # TODO: adjust this to the Pyramid docu + self.__e5project.pdata["TRANSLATIONPATTERN"] = [ + os.path.join(project, project.lower(), "i18n", "%language%", + "LC_MESSAGES", "%s.po" % project.lower()) + ] + + def __project(self): + """ + Private method to get the name of the current Pyramid project. + + @exception PyramidNoProjectSelectedException raised, if no project is selected + @return name of the project (string) + """ + if self.__currentProject is None: + self.__selectProject() + + if self.__currentProject is None: + raise PyramidNoProjectSelectedException + else: + return self.__currentProject + + ################################################################## + ## slots below implement run functions + ################################################################## + + def __runServer(self, logging = False): + """ + Private slot to start the Pyramid Web server. + + @param logging flag indicating to enable logging (boolean) + """ + consoleCmd = self.isSpawningConsole( + self.__plugin.getPreferences("ConsoleCommand"))[1] + if consoleCmd: + try: + projectPath = self.__projectPath() + except PyramidNoProjectSelectedException: + E5MessageBox.warning(self.__ui, + self.trUtf8('Run Server'), + self.trUtf8('No current Pyramid project selected or no Pyramid ' + 'project created yet. Aborting...')) + return + + args = Utilities.parseOptionString(consoleCmd) + args[0] = Utilities.getExecutablePath(args[0]) + args.append(self.getPyramidCommand("pserve")) + if logging: + args.append("--log-file=server.log") + args.append("--reload") + args.append(os.path.join(projectPath, "development.ini")) + + if isWindowsPlatform(): + serverProcStarted, pid = \ + QProcess.startDetached(args[0], args[1:], projectPath) + else: + if self.__serverProc is not None: + self.__serverProcFinished() + + self.__serverProc = QProcess() + self.__serverProc.finished.connect(self.__serverProcFinished) + self.__serverProc.setWorkingDirectory(projectPath) + self.__serverProc.start(args[0], args[1:]) + serverProcStarted = self.__serverProc.waitForStarted() + if not serverProcStarted: + E5MessageBox.critical(self.__ui, + self.trUtf8('Process Generation Error'), + self.trUtf8('The Pyramid server could not be started.')) + + def __runLoggingServer(self): + """ + Private slot to start the Pyramid Web server with logging. + """ + self.__runServer(True) + + def __serverProcFinished(self): + """ + Private slot connected to the finished signal. + """ + if self.__serverProc is not None and \ + self.__serverProc.state() != QProcess.NotRunning: + self.__serverProc.terminate() + QTimer.singleShot(2000, self.__serverProc.kill) + self.__serverProc.waitForFinished(3000) + self.__serverProc = None + + def __runBrowser(self): + """ + Private slot to start the default web browser with the server URL. + """ + try: + projectPath = self.__projectPath() + except PyramidNoProjectSelectedException: + E5MessageBox.warning(self.__ui, + self.trUtf8('Run Web-Browser'), + self.trUtf8('No current Pyramid project selected or no Pyramid project' + ' created yet. Aborting...')) + return + + config = configparser.ConfigParser() + config.read(os.path.join(projectPath, "development.ini")) + port = config.get("server:main", "port", fallback="6543") + url = QUrl("http://localhost:{0}".format(port)) + res = QDesktopServices.openUrl(url) + if not res: + E5MessageBox.critical(self.__ui, + self.trUtf8('Run Web-Browser'), + self.trUtf8('Could not start the web-browser for the URL "{0}".')\ + .format(url.toString())) + + def __runPythonShell(self): + """ + Private slot to start a Python console for a Pyramid project. + """ + consoleCmd = self.isSpawningConsole( + self.__plugin.getPreferences("ConsoleCommand"))[1] + if consoleCmd: + try: + projectPath = self.__projectPath() + except PyramidNoProjectSelectedException: + E5MessageBox.warning(self.__ui, + self.trUtf8('Start Pyramid Python Console'), + self.trUtf8('No current Pyramid project selected or no Pyramid ' + 'project created yet. Aborting...')) + return + + args = Utilities.parseOptionString(consoleCmd) + args[0] = Utilities.getExecutablePath(args[0]) + args.append(self.getPyramidCommand("pshell")) + language = self.__e5project.getProjectLanguage() + if language == "Python2": + consoleType = self.__plugin.getPreferences("Python2ConsoleType") + else: + consoleType = self.__plugin.getPreferences("Python3ConsoleType") + args.append("--python-shell={0}".format(consoleType)) + args.append(os.path.join(projectPath, "development.ini")) + + started, pid = QProcess.startDetached(args[0], args[1:], projectPath) + if not started: + E5MessageBox.critical(self.__ui, + self.trUtf8('Process Generation Error'), + self.trUtf8('The Pyramid Shell process could not be started.')) + + ################################################################## + ## slots below implement setup functions + ################################################################## + + def __setupDevelop(self): + """ + Private slot to set up the development environment for the current project. + """ + title = self.trUtf8("Setup Development Environment") + + cmd = self.getPythonCommand() + args = [] + args.append("setup.py") + args.append("develop") + + try: + wd = self.__projectPath() + except PyramidNoProjectSelectedException: + E5MessageBox.warning(self.__ui, + title, + self.trUtf8('No current Pyramid project selected or no Pyramid project' + ' created yet. Aborting...')) + return + + dia = PyramidDialog(title, + msgSuccess = \ + self.trUtf8("Pyramid development environment setup successfully.")) + res = dia.startProcess(cmd, args, wd) + if res: + dia.exec_() + + ################################################################## + ## slots below implement distribution functions + ################################################################## + + def __buildDistribution(self): + """ + Private slot to build a distribution file for the current Pyramid project. + """ + title = self.trUtf8("Build Distribution File") + try: + projectPath = self.__projectPath() + except PyramidNoProjectSelectedException: + E5MessageBox.warning(self.__ui, + title, + self.trUtf8('No current Pyramid project selected or no Pyramid project' + ' created yet. Aborting...')) + return + + dlg = DistributionTypeSelectionDialog(self, projectPath, self.__ui) + if dlg.exec_() == QDialog.Accepted: + formats = dlg.getFormats() + cmd = self.getPythonCommand() + args = [] + args.append("setup.py") + args.append("sdist") + if formats: + args.append("--formats={0}".format(','.join(formats))) + + dia = PyramidDialog(title, + msgSuccess = \ + self.trUtf8("Python distribution file built successfully.")) + res = dia.startProcess(cmd, args, projectPath) + if res: + dia.exec_() + + ################################################################## + ## slots below implement documentation functions + ################################################################## + + def __showDocumentation(self): + """ + Private slot to show the helpviewer with the Pylons documentation. + """ + page = self.__plugin.getPreferences("PyramidDocUrl") + self.__ui.launchHelpViewer(page)