Source code for openalea.oalab.project.manager
# -*- python -*-
#
#       OpenAlea.OALab: Multi-Paradigm GUI
#
#       Copyright 2013 INRIA - CIRAD - INRA
#
#       File author(s): Julien Coste <julien.coste@inria.fr>
#
#       File contributor(s):
#
#       Distributed under the Cecill-C License.
#       See accompanying file LICENSE.txt or copy at
#           http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
#       OpenAlea WebSite : http://openalea.gforge.inria.fr
#
###############################################################################
__revision__ = "$Id: "
from openalea.vpltk.qt import QtGui, QtCore
from openalea.core.path import path
from openalea.core import settings
from openalea.core import logger
from openalea.vpltk.project.manager import ProjectManager
from openalea.oalab.project.creator import CreateProjectWidget
[docs]class ProjectManagerWidget(QtGui.QWidget):
    """
    Object which permit to manage projects.
    :Warning: this QWidget is not used like a widget but like the interface between GUI (buttons "newProj", "newScript",...)
    and objects (project, control, ...)
    :TODO: Refactor it
    """
    def __init__(self, session, controller, parent=None):
        super(ProjectManagerWidget, self).__init__(parent)
        self._actions = []
        self.paradigms_actions = []
        self.session = session
        self.controller = controller
        self.setAccessibleName("Project Manager")
        self.projectManager = ProjectManager()
        for proj in self.projectManager.projects:
            self.session._project = proj
        self.actionEditFile = QtGui.QAction(QtGui.QIcon(":/images/resources/edit.png"), "Edit file", self)
        self.actionImportFile = QtGui.QAction(QtGui.QIcon(":/images/resources/import.png"), "Import", self)
        self.actionNewProj = QtGui.QAction(QtGui.QIcon(":/images/resources/new.png"), "New", self)
        self.actionNewProj.setShortcut(
            QtGui.QApplication.translate("MainWindow", "Ctrl+N", None, QtGui.QApplication.UnicodeUTF8))
        self.actionOpenProj = QtGui.QAction(QtGui.QIcon(":/images/resources/open.png"), "Open", self)
        self.actionOpenProj.setShortcut(
            QtGui.QApplication.translate("MainWindow", "Ctrl+O", None, QtGui.QApplication.UnicodeUTF8))
        self.actionSaveProj = QtGui.QAction(QtGui.QIcon(":/images/resources/save.png"), "Save", self)
        self.actionSaveProjAs = QtGui.QAction(QtGui.QIcon(":/images/resources/save.png"), "Save As", self)
        # self.actionSaveProj.setShortcut(QtGui.QApplication.translate("MainWindow", "Ctrl+S", None, QtGui.QApplication.UnicodeUTF8))
        self.actionCloseProj = QtGui.QAction(QtGui.QIcon(":/images/resources/closeButton.png"), "Close All", self)
        self.connect(self.actionNewProj, QtCore.SIGNAL('triggered(bool)'), self.new)
        self.connect(self.actionOpenProj, QtCore.SIGNAL('triggered(bool)'), self.open)
        self.connect(self.actionSaveProjAs, QtCore.SIGNAL('triggered(bool)'), self.saveAs)
        self.connect(self.actionSaveProj, QtCore.SIGNAL('triggered(bool)'), self.saveCurrent)
        self.connect(self.actionCloseProj, QtCore.SIGNAL('triggered(bool)'), self.closeCurrent)
        self.connect(self.actionImportFile, QtCore.SIGNAL('triggered(bool)'), self.importFile)
        self.connect(self.actionEditFile, QtCore.SIGNAL('triggered(bool)'), self.editFile)
        self._actions = [["Project", "Manage Project", self.actionNewProj, 1],
                         ["Project", "Manage Project", self.actionOpenProj, 0],
                         ["Project", "Manage Project", self.actionSaveProj, 0],
                         ["Project", "Manage Project", self.actionSaveProjAs, 1],
                         ["Project", "Manage Project", self.actionCloseProj, 1],
                         # ["Model", "New Model", self.actionEditFile, 0],
                         ["Model", "New Model", self.actionImportFile, 0]]
        self.extensions = ""
        self.connectParadigmContainer()
        self.defaultProj()
[docs]    def connectParadigmContainer(self):
        # Connect actions from applet_container.paradigms to menu (newPython, newLpy,...)
        for applet in self.controller.applet_container.paradigms.values():
            action = QtGui.QAction(QtGui.QIcon(applet.icon), applet.default_name, self)
            action.triggered.connect(self.newModel)
            self._actions.append(["Model", "New Model", action, 0],)
            self.paradigms_actions.append(action)
            self.extensions = self.extensions + applet.pattern + " "
[docs]    def defaultProj(self):
        proj = self.projectManager.load_default()
        self.session._project = proj
        self.session._is_proj = True
        if not self.session.project.src:
            txt = '''# -*- coding: utf-8 -*-
"""
OpenAlea Lab editor
This temporary script is saved in temporary project in
%s
You can rename/move this project thanks to the button "Save As" in menu.
"""''' % str(self.session.project.path / self.session.project.name)
            self.session.project.add(category="src", name=".temp.py", value=txt)
        self._project_changed()
        self._load_control()
[docs]    def open(self, name=False, project_path=None):
        """
        If name==false, display a widget to choose project to open.
        Then open project.
        """
        self.closeCurrent()
        # self.controller.applet_container.closeAll()
        """
        if name is False:
            name = showOpenProjectDialog()
        if name:
            proj_path = path(name).abspath()
            proj_name = proj_path.basename()
            proj_path = proj_path.dirname()
            if project_path:
                proj_path = path(project_path)
            logger.debug("Open Project named " + proj_name)
            if self.session.project is not None:
                if self.session.current_is_project():
                    logger.debug("Close Project named " + self.session.project.name)
                    self.projectManager.close(self.session.project.name)
                    logger.debug("Project named " + self.session.project.name + " closed.")
            proj = self.projectManager.load(proj_name, proj_path)
            logger.debug("Project " + str(proj) + " loaded")
            if proj == -1:
                logger.warning("Project was not loaded...")
                return -1
            else:
                return self.openProject(proj)"""
[docs]    def openProject(self, project):
        """
        Open a project in application from project
        :param project: project from vpltk
        """
        project.start()
        logger.debug("Project " + str(project) + " opened")
        project.scene = self.session.scene
        self.session._project = project
        self.session._is_proj = True
        self._project_changed()
        self._load_control()
        logger.debug("Project opened: " + str(self.session._project))
        return self.session._project
[docs]    def editFile(self, filename=None, extension=None):
        """
        Permit to edit a file which is outside project.
        And add link to file in project.
        """
        if extension is None:
            extension = self.extensions
        if self.session.current_is_project():
            project = self.session.project
            if not filename:
                if extension:
                    filename = showOpenFileDialog(extension)
                else:
                    filename = showOpenFileDialog()
            if filename:
                filename = path(filename).abspath()
                f = open(filename, "r")
                txt = f.read()
                f.close()
                tab_name = str(path(filename).splitpath()[-1])
                ext = str(path(filename).splitext()[-1])
                ext = ext.split(".")[-1]
                logger.debug("Try to import file named " + tab_name + " . With applet_type " + ext)
                try:
                    self.controller.applet_container.newTab(applet_type=ext, tab_name=tab_name, script=txt)
                    project.add("src", filename, txt)
                    self.controller._update_locals()
                    self._tree_view_change()
                    logger.debug("Import file named " + tab_name)
                except:
                    print "File extension " + ext + " not recognised"
                    logger.warning("Can't import file named %s in current project. Unknow extension." % filename)
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def importFile(self, filename=None, extension=None):
        """
        Import a file and add it to the project.
        :param filename: name of file to add in project
        :param extension:
        """
        if extension is None:
            extension = self.extensions
        if self.session.current_is_project():
            project = self.session.project
            if not filename:
                filename = showOpenFileDialog(extension)
            if filename:
                filename = path(filename)
                f = open(filename, "r")
                txt = f.read()
                f.close()
                name = filename.splitpath()[1]
                project.add("src", name, txt)
                self.controller._update_locals()
                self._project_changed()
                logger.debug("Import file named " + name)
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def new(self):
        """
        Create an default empty project with a default name.
        Open a window to define metadata of the project.
        """
        self.project_creator = CreateProjectWidget()
        self.project_creator.show()
        self.connect(self.project_creator, QtCore.SIGNAL('ProjectOpened(PyQt_PyObject)'), self.openProject)
[docs]    def newModel(self, applet_type=None, tab_name=None, script=""):
        """
        Create a new model of type 'applet_type
        :param applet_type: type of applet to add. Can be Workflow, LSystem, Python, R
        """
        if self.session.current_is_project():
            if not applet_type:
                button = self.sender()
                applet_type = button.text() # can be Workflow, LSystem, Python, R
                # TODO: this approach is not reliable. If a developer change action name, it breaks the system
                # a better approach should be to define a "newModel" method in IApplet and call it directly
                # for instance in __init__ : action.triggered.connect(applet.newModel)
            Applet = self.controller.applet_container.paradigms[applet_type]
            if not tab_name:
                tab_name = Applet.default_file_name
            self.controller.applet_container.newTab(applet_type=applet_type, tab_name=tab_name, script=script)
            text = self.controller.applet_container.applets[-1].widget().get_text()
            self.session.project.add("src", tab_name, text)
            self.controller._update_locals()
            self._project_changed()
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def removeModel(self, model_name):
        """
        :param model_name: Name of the model to remove in the current project
        """
        if self.session.current_is_project():
            self.session.project.remove("src", model_name)
            self._project_changed()
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def openModel(self, fname=None, extension="*.*"):
        """"
        Open a (script-type) file named "fname".
        If "fname"==None, display a dialog with filter "extension".
        :param fname: filename to open. Default = None
        :param extension: extension of file to open. Default = "*.*"
        """
        if self.session.current_is_project():
            self.importFile(filename=fname, extension=extension)
            self._project_changed()
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def renameCurrent(self, new_name=None):
        """
        Rename current project.
        """
        if self.session.current_is_project():
            name = self.session.project.name
            if not new_name:
                new_name = showNewProjectDialog(default_name=path(name) / "..",
                                                text='Select new name to save project')
            self.session.project.rename(category="project", old_name=name, new_name=new_name)
            self._project_changed()
        else:
            print(
                "You are not working inside project, so you can't use 'rename project'. Please create or load one first.")
[docs]    def saveAs(self):
        """
        Save current project but permit to rename and move it..
        """
        if self.session.current_is_project():
            name = showNewProjectDialog(default_name=None, text="Select name to save project")
            if name:
                self.session.project.rename(category="project", old_name=self.session.project.name, new_name=name)
                self._tree_view_change()
                self.saveCurrent()
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def saveCurrent(self):
        """
        Save current project.
        """
        if self.session.current_is_project():
            container = self.controller.applet_container
            for i in range(container.count()):
                container.setCurrentIndex(i)
                name = container.tabText(i)
                container.widget(i).save(name)
            self._update_control()
            self.session.project.save()
            self._project_changed()
        else:
            print "You are not working inside project. Please create or load one first."
[docs]    def closeCurrent(self):
        """
        Close current project.
        """
        if self.session.current_is_project():
            logger.debug("Close Project named %s" % self.session.project.name)
            self.projectManager.close(self.session.project.name)
            self.session._project = None
            self.session._is_proj = False
            self._clear_control()
            self.controller.applet_container.closeAll()
            self._project_changed()
        else:
            print "You are not working inside project. Please create or load one first."
    def _project_changed(self):
        """
        Update what is needed when the current project is changed
        """
        logger.debug("Project changed")
        self.controller._update_locals()
        self._scene_change()
        self._control_change() # do nothing
        self._script_change()
        self._tree_view_change()
    def _load_control(self):
        """
        Get control from project and put them into widgets
        """
        if self.controller.applets.has_key('ControlPanel'):
            logger.debug("Load Controls")
            self.controller.applets['ControlPanel'].load()
    def _update_control(self):
        """
        Get control from widget and put them into project
        """
        if self.controller.applets.has_key('ControlPanel'):
            logger.debug("Update Controls")
            self.controller.applets['ControlPanel'].update()
    def _clear_control(self):
        if self.controller.applets.has_key('ControlPanel'):
            logger.debug("Clear Controls")
            self.controller.applets['ControlPanel'].clear()
    def _control_change(self):
        pass
    def _tree_view_change(self):
        logger.debug("Tree View changed")
        self.controller.applets['Project'].update()
    def _script_change(self):
        logger.debug("Script changed")
        if self.session.current_is_project():
            project = self.session.project
            self.controller.applet_container.reset()
            for script in project.src:
                language = str(script).split('.')[-1]
                self.controller.applet_container.openTab(language, script, project.src[script])
    def _scene_change(self):
        logger.debug("Scene changed")
        if self.session.current_is_project():
            self.session.scene.reset()
            project = self.session.project
            for w in project.scene:
                self.session.scene.add(name=w, obj=project.scene[w])
[docs]def showNewProjectDialog(default_name=None, text=None, parent=None):
    my_path = path(settings.get_project_dir())
    if default_name:
        my_path = my_path / default_name
    if not text:
        text = 'Select name to create project'
    fname = QtGui.QFileDialog.getSaveFileName(parent, text,
                                              my_path)
    return fname
[docs]def showOpenProjectDialog(parent=None):
    my_path = path(settings.get_project_dir())
    fname = QtGui.QFileDialog.getExistingDirectory(parent, 'Select Project Directory',
                                                   my_path)
    return fname
[docs]def showOpenFileDialog(extension=None, where=None, parent=None):
    if extension is None:
        extension = self.extensions
    if where is not None:
        my_path = path(str(where)).abspath().splitpath()[0]
    else:
        my_path = path(settings.get_project_dir())
    logger.debug("Search to open file with extension " + extension + " from " + my_path)
    fname = QtGui.QFileDialog.getOpenFileName(parent, 'Select File to open',
                                              my_path, "All (*);;Scripts Files (%s)" % extension)
    return fname