Example #1
0
class PackageViewWidget(OALabTreeView):
    """
    Widget for Package Manager
    """
    def __init__(self, parent=None):
        super(PackageViewWidget, self).__init__(parent=parent)

        # package tree view
        self.pkg_model = PkgModel(package_manager)
        self.setModel(self.pkg_model)

        self.clicked.connect(self.on_package_manager_focus_change)

    def on_package_manager_focus_change(self, item):
        pkg_id, factory_id, mimetype = NodeFactoryView.get_item_info(item)
        if len(pkg_id) and len(factory_id) and mimetype in [
                NodeFactory.mimetype, CompositeNodeFactory.mimetype
        ]:
            factory = package_manager[pkg_id][factory_id]
            factoryDoc = factory.get_documentation()
            txt = factory.get_tip(asRst=True) + "\n\n"
            if factoryDoc is not None:
                txt += "**Docstring:**\n" + factoryDoc

            applet = get_applet(identifier='HelpWidget')
            if applet:
                applet.setText(txt)

    def reinit_treeview(self):
        """ Reinitialise package and category views """
        self.pkg_model.reset()
Example #2
0
class PackageViewWidget(OALabTreeView):

    """
    Widget for Package Manager
    """

    def __init__(self, parent=None):
        super(PackageViewWidget, self).__init__(parent=parent)

        # package tree view
        self.pkg_model = PkgModel(package_manager)
        self.setModel(self.pkg_model)

        self.clicked.connect(self.on_package_manager_focus_change)

    def on_package_manager_focus_change(self, item):
        pkg_id, factory_id, mimetype = NodeFactoryView.get_item_info(item)
        if len(pkg_id) and len(factory_id) and mimetype in [NodeFactory.mimetype,
                                                            CompositeNodeFactory.mimetype]:
            factory = package_manager[pkg_id][factory_id]
            factoryDoc = factory.get_documentation()
            txt = factory.get_tip(asRst=True) + "\n\n"
            if factoryDoc is not None:
                txt += "**Docstring:**\n" + factoryDoc

            applet = get_applet(identifier='HelpWidget')
            if applet:
                applet.setText(txt)

    def reinit_treeview(self):
        """ Reinitialise package and category views """
        self.pkg_model.reset()
Example #3
0
class MainWindow(qt.QtGui.QMainWindow, ui_mainwindow.Ui_MainWindow,
                 SignalSlotListener):
    def __init__(self, session, parent=None):
        """
        @param session : user session
        @param parent : parent window
        """
        qt.QtGui.QMainWindow.__init__(self, parent)
        SignalSlotListener.__init__(self)
        ui_mainwindow.Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.setAcceptDrops(True)
        self.setAttribute(qt.QtCore.Qt.WA_QuitOnClose)

        self.tabWorkspace.removeTab(0)
        self.tabWorkspace.setTabsClosable(True)
        self.ws_cpt = 0

        if hasattr(AbstractEvaluation, "__provenance__"):
            self._prov = AbstractEvaluation.__provenance__
        else:
            self._prov = False

        #last opened nodes
        self._last_opened = []

        #lower tab pane : python shell, logger...
        self.lowerpane = qt.QtGui.QTabWidget()
        self.splitter.addWidget(self.lowerpane)

        # python interpreter
        try:
            interpreter = get_interpreter()
        except NameError:
            InterpreterClass = get_interpreter_class()
            interpreter = InterpreterClass()

        # interpreter init defered after session init
        shellclass = get_shell_class()
        self.interpreterWidget = shellclass(interpreter, cli.get_welcome_msg())

        GraphOperator.globalInterpreter = interpreter
        self.lowerpane.addTab(self.interpreterWidget, "Python Shell")

        if logger.QT_LOGGING_MODEL_AVAILABLE:
            # openalea logger
            model = logger.LoggerOffice().get_handler("qt")
            view = LoggerView(parent=self.lowerpane, model=model)
            self.lowerpane.addTab(view, "Logging")

        # search list view
        self.search_model = SearchModel()
        self.searchListView = \
            SearchListView(self, self.searchview)
        self.searchListView.setModel(self.search_model)
        self.vboxlayout3.addWidget(self.searchListView)
        self.searchListView.clicked.connect(
            self.on_package_manager_focus_change)

        # help widget
        self.helpWidget = helpwidget.HelpWidget()
        css = pj(misc.__path__[0], "..", "..", "..", "share", "_static",
                 "openalea.css")
        self.helpWidget.set_stylesheet_file(css)
        self.poolTabWidget.addTab(self.helpWidget, "Help")

        # Widgets
        self.connect(self.tabWorkspace,
                     qt.QtCore.SIGNAL("contextMenuEvent(QContextMenuEvent)"),
                     self.contextMenuEvent)
        self.tabWorkspace.currentChanged.connect(self.ws_changed)
        self.search_lineEdit.editingFinished.connect(self.search_node)
        self.tabWorkspace.tabCloseRequested.connect(self.close_tab_workspace)

        # Help Menu
        self.action_About.triggered.connect(self.about)
        self.actionOpenAlea_Web.triggered.connect(self.web)
        self.action_Help.triggered.connect(self.help)

        # File Menu
        self.action_New_Session.triggered.connect(self.new_session)
        self.action_Open_Session.triggered.connect(self.open_session)
        self.action_Save_Session.triggered.connect(self.save_session)
        self.actionSave_as.triggered.connect(self.save_as)
        self.action_Quit.triggered.connect(self.quit)

        self.action_Image.triggered.connect(self.export_image)
        self.action_Svg.triggered.connect(self.export_image_svg)

        # Package Manager Menu
        self.action_Auto_Search.triggered.connect(self.reload_all)
        self.action_Add_File.triggered.connect(self.add_pkgdir)
        self.actionFind_Node.triggered.connect(self.find_node)
        self.action_New_Network.triggered.connect(self.new_graph)
        self.actionNew_Python_Node.triggered.connect(self.new_python_node)
        self.actionNew_Package.triggered.connect(self.new_package)
        self.action_Data_File.triggered.connect(self.new_data)

        # DataPool Menu
        self.actionClear_Data_Pool.triggered.connect(self.clear_data_pool)

        # Python Menu
        self.action_Execute_script.triggered.connect(self.exec_python_script)
        self.actionOpen_Console.triggered.connect(self.open_python_console)
        self.actionClea_r_Console.triggered.connect(self.clear_python_console)

        # WorkspaceMenu
        self.__operatorAction = dict([
            (self.action_Run, "graph_run"),
            (self.actionInvalidate, "graph_invalidate"),
            (self.actionReset, "graph_reset"),
            (self.actionConfigure_I_O, "graph_configure_io"),
            (self.actionGroup_Selection, "graph_group_selection"),
            (self.action_Copy, "graph_copy"),
            (self.action_Paste, "graph_paste"), (self.action_Cut, "graph_cut"),
            (self.action_Delete_2, "graph_remove_selection"),
            (self.action_Close_current_workspace, "graph_close"),
            (self.action_Export_to_Factory, "graph_export_to_factory"),
            (self.actionReload_from_Model, "graph_reload_from_factory"),
            (self.actionExport_to_Application, "graph_export_application"),
            (self.actionPreview_Application, "graph_preview_application"),
            (self.actionAlignHorizontally, "graph_align_selection_horizontal"),
            (self.actionAlignLeft, "graph_align_selection_left"),
            (self.actionAlignRight, "graph_align_selection_right"),
            (self.actionAlignMean, "graph_align_selection_mean"),
            (self.actionDistributeHorizontally,
             "graph_distribute_selection_horizontally"),
            (self.actionDistributeVertically,
             "graph_distribute_selection_vertically"),
            (self.actionSetCustomColor, "graph_set_selection_color"),
            (self.actionUseCustomColor, "graph_use_user_color")
        ])

        self._last_open_action_group = qt.QtGui.QActionGroup(self)
        self.connect(self._last_open_action_group,
                     qt.QtCore.SIGNAL("triggered(QAction*)"), self.reopen_last)
        self.action_New_Empty_Workspace.triggered.connect(self.new_workspace)
        self.menu_Workspace.aboutToShow.connect(self.__wsMenuShow)
        self.menu_Workspace.aboutToShow.connect(self.__wsMenuHide)
        for ac, fname in self.__operatorAction.iteritems():
            f = self.__make_operator_action_connector(ac, fname)
            ac.triggered.connect(f)

        self.actionTo_script.triggered.connect(self.to_python_script)

        # Window Mneu
        self.actionPreferences.triggered.connect(self.open_preferences)
        self.actionDisplay_Package_Manager.toggled.connect(
            self.display_leftpanel)
        self.actionDisplay_Workspaces.toggled.connect(self.display_rightpanel)

        #load personnal GUI settings
        self.read_settings()

        #############
        # Provenance
        #############
        if PROVENANCE:
            self.menu_provenance = qt.QtGui.QMenu(self.menubar)
            self.menu_provenance.setObjectName("menu_provenance")
            self.menu_provenance.setTitle(
                qt.QtGui.QApplication.translate(
                    "MainWindow", "&Provenance", None,
                    qt.QtGui.QApplication.UnicodeUTF8))

            self.action_activ_prov = qt.QtGui.QAction(self)
            self.action_activ_prov.setCheckable(True)
            prov = self.get_provenance()
            self.action_activ_prov.setChecked(prov)
            self.action_activ_prov.setObjectName("action_activ_prov")
            self.action_activ_prov.setText(
                qt.QtGui.QApplication.translate(
                    "MainWindow", "Connect/Disconnect Provenance", None,
                    qt.QtGui.QApplication.UnicodeUTF8))

            self.action_show_prov = qt.QtGui.QAction(self)
            self.action_show_prov.setCheckable(False)
            self.action_show_prov.setObjectName("action_show_prov")
            self.action_show_prov.setText(
                qt.QtGui.QApplication.translate(
                    "MainWindow", "Show Provenance", None,
                    qt.QtGui.QApplication.UnicodeUTF8))

            self.menu_provenance.addAction(self.action_activ_prov)
            self.menu_provenance.addAction(self.action_show_prov)

            self.menubar.addAction(self.menu_provenance.menuAction())

            self.action_activ_prov.toggled.connect(self.set_provenance)
            self.action_show_prov.triggered.connect(self.show_provenance)

    def set_provenance(self, provenance):
        """
        Set/Unset the registry of provenance
        
        :param provenance: boolean which is set to True if we want to register provenance. Else, False.
        """
        if hasattr(AbstractEvaluation, "__provenance__"):
            self._prov = provenance
            AbstractEvaluation.__provenance__ = self._prov

    def get_provenance(self):
        """
        :return: boolean which is set to True if we want to register provenance. Else, False.
        """
        return self._prov

    def show_provenance(self):
        """
        Display the provenance
        """
        from openalea.visualea.provenance import ModalDialog, ProvenanceSelectorWidget, search_trace
        prov_widget = ProvenanceSelectorWidget(self)
        dialog = ModalDialog(prov_widget)
        dialog.show()
        dialog.raise_()

        if dialog.exec_():
            cn = prov_widget.c_n.text()
            pkg = prov_widget.pkg.text()
            wk = prov_widget.workspace.text()

            search_trace(cn, pkg, wk, parent=self)

    def on_session_started(self, session):
        self.initialise(session)
        self.session = session

        # -- configure the interpreter --
        cli.init_interpreter(self.interpreterWidget.interpreter, session,
                             {"tabs": self.tabWorkspace})

        # -- now, many package manager related views --
        self.pkgmanager = session.pkgmanager
        self.actionShow_log.triggered.connect(self.pkgmanager.log.print_log)

        # package tree view
        self.pkg_model = PkgModel(self.pkgmanager)
        self.packageTreeView = \
            NodeFactoryTreeView(self, self.packageview)
        self.packageTreeView.setModel(self.pkg_model)
        self.vboxlayout1.addWidget(self.packageTreeView)
        self.packageTreeView.clicked.connect(
            self.on_package_manager_focus_change)

        # category tree view
        self.cat_model = CategoryModel(self.pkgmanager)
        self.categoryTreeView = \
            NodeFactoryTreeView(self, self.categoryview)
        self.categoryTreeView.setModel(self.cat_model)
        self.vboxlayout2.addWidget(self.categoryTreeView)
        self.categoryTreeView.clicked.connect(
            self.on_package_manager_focus_change)

        # data pool list view
        self.datapool_model = DataPoolModel(session.datapool)
        self.datapoolListView = \
            DataPoolListView(self, session.datapool, self.pooltab)
        self.datapoolListView.setModel(self.datapool_model)
        self.vboxlayout4.addWidget(self.datapoolListView)

        self.session.simulate_workspace_addition()

    def debug(self):
        v = self.packageTreeView
        print "items", v.expanded_items
        print "model", v.model()
        print "map", v.model().index_map

    def write_settings(self):
        """Save application settings.
        """
        settings = Settings()

        #main window
        settings.set("MainWindow", "size",
                     "(%d,%d)" % (self.width(), self.height()))
        settings.set("MainWindow", "pos", "(%d,%d)" % (self.x(), self.y()))

        sizes = "[%s]" % ",".join("%d" % val
                                  for val in self.splitter_2.sizes())
        settings.set("MainWindow", "splitter_2", sizes)
        sizes = "[%s]" % ",".join("%d" % val
                                  for val in self.splitter_3.sizes())
        settings.set("MainWindow", "splitter_3", sizes)

        #tree view
        settings.set("TreeView", "open", "[]")

        #workspace
        last_open = "[%s]" % ",".join("'%s'" % item
                                      for item in self._last_opened)
        settings.set("WorkSpace", "last", last_open)

        #provenance
        prov = self.get_provenance()
        settings.set("Provenance", "enable", str(prov))

        settings.write()

    def read_settings(self):
        """Read application settings.
        """
        settings = Settings()

        #main window
        try:
            size = eval(settings.get("MainWindow", "size"))
            self.resize(qt.QtCore.QSize(*size))
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            pos = eval(settings.get("MainWindow", "pos"))
            self.move(qt.QtCore.QPoint(*pos))
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            sizes = eval(settings.get("MainWindow", "splitter_2"))
            self.splitter_2.setSizes(sizes)
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            sizes = eval(settings.get("MainWindow", "splitter_3"))
            self.splitter_3.setSizes(sizes)
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        #workspace
        try:
            last_open = eval(settings.get("WorkSpace", "last"))
            last_open.reverse()
            for item in last_open:
                gr = item.split(".")
                pkgid = ".".join(gr[:-1])
                name = gr[-1]
                self.add_last_open(pkgid, name)
        except NoSectionError:
            pass
        except NoOptionError:
            pass

        try:
            prov = eval(settings.get("Provenance", "enable"))
            self.set_provenance(bool(prov))
        except NoSectionError:
            pass
        except NoOptionError:
            pass

    def redo_last_open_menu(self):
        """Create entries for last opened nodes.
        """
        self.menuLast_open.clear()
        for action in self._last_open_action_group.actions():
            self._last_open_action_group.removeAction(action)

        for i, node_descr in enumerate(self._last_opened):
            action = self.menuLast_open.addAction(node_descr)
            action.setShortcut("Ctrl+%d" % (i + 1))
            self._last_open_action_group.addAction(action)

        self.menuLast_open.setEnabled(len(self._last_opened) > 0)

    def reopen_last(self, action):
        """Reopen a last open node.
        """
        gr = str(action.text()).split(".")
        pkgid = ".".join(gr[:-1])
        name = gr[-1]
        manager = PackageManager()
        factory = manager[pkgid][name]
        self.open_compositenode(factory)

    def add_last_open(self, pkgid, factory_name):
        """Register a new lest opened node.
        """
        key = ".".join([pkgid, factory_name])
        try:
            self._last_opened.remove(key)
        except ValueError:
            pass

        self._last_opened.insert(0, key)
        if len(self._last_opened) > 4:
            del self._last_opened[-1]

        self.redo_last_open_menu()

    def __wsMenuShow(self, abool=False):
        graphview = self.tabWorkspace.currentWidget()
        if not isinstance(graphview, dataflowview.DataflowView):
            return

        items = graphview.scene().get_selected_items(
            dataflowview.vertex.GraphicalVertex)
        self.actionUseCustomColor.setChecked(False)
        for i in items:
            if i.vertex().get_ad_hoc_dict().get_metadata("useUserColor"):
                self.actionUseCustomColor.setChecked(True)
                break

    def __make_operator_action_connector(self, action, name):
        def connector(aBool=None):
            graphview = self.tabWorkspace.currentWidget()
            if not isinstance(graphview, dataflowview.DataflowView):
                return

            # daniel was here: now the menu is built using the graph operator.
            operator = GraphOperator(graph=graphview.scene().get_graph(),
                                     graphScene=graphview.scene(),
                                     clipboard=self.session.clipboard,
                                     siblings=self.session.workspaces)
            operator.register_listener(self)
            operator(fName=name)()

        return connector

    def __wsMenuHide(self):
        pass

    def open_compositenode(self, factory):
        """ open a  composite node editor """
        node = factory.instantiate()

        self.session.add_workspace(node, notify=False)
        self.open_widget_tab(node, factory=factory)

        self.add_last_open(factory.__pkg_id__, factory.name)

    def about(self):
        """ Display About Dialog """

        mess = qt.QtGui.QMessageBox.about(
            self, "About Visualea",
            "Version %s\n\n" % (metainfo.get_version()) +
            "VisuAlea is part of the OpenAlea framework.\n" +
            metainfo.get_copyright() +
            "This Software is distributed under the Cecill-V2 License.\n\n" +
            "Visit " + metainfo.url + "\n\n")

    def help(self):
        """ Display help """
        self.web()

    def web(self):
        """ Open OpenAlea website """
        qt.QtGui.QDesktopServices.openUrl(qt.QtCore.QUrl(metainfo.url))

    def quit(self):
        """ Quit Application """
        if (qt.QtGui.QMessageBox.question(
                self, "Quit?", "Are you sure you want to quit?",
                qt.QtGui.QMessageBox.Ok
                | qt.QtGui.QMessageBox.Cancel) == qt.QtGui.QMessageBox.Ok):
            qt.QtGui.QApplication.exit(0)

    def notify(self, sender, event):
        """ Notification from observed """
        if event and isinstance(sender, GraphOperator):
            index = -1
            for i in range(self.tabWorkspace.count()):
                wid = self.tabWorkspace.widget(i)
                if isinstance(
                        wid,
                        dataflowview.DataflowView) and wid.scene() == event[1]:
                    index = i
            if index <= -1:
                return
            if (event[0] == "graphoperator_graphsaved"):
                self.reinit_treeview()
                caption = "Workspace %i - %s" % (index, event[2].name)
                self.tabWorkspace.setTabText(index, caption)
            elif (event[0] == "graphoperator_graphclosed"):
                self.close_tab_workspace(index)
            elif (event[0] == "graphoperator_graphreloaded"):
                self.session.workspaces[index] = event[2]

        if (type(sender) == type(self.session)):
            if (event and event[0] == "workspace_added"):
                graph = event[1]
                self.open_widget_tab(graph, graph.factory)
            else:
                self.update_tabwidget()
                self.reinit_treeview()

    def closeEvent(self, event):
        """ Close All subwindows """

        #Save personnal settings
        self.write_settings()

        #close windows
        for i in range(self.tabWorkspace.count()):
            w = self.tabWorkspace.widget(i)
            w.close()

        event.accept()

    def reinit_treeview(self):
        """ Reinitialise package and category views """
        self.cat_model.reset()
        self.pkg_model.reset()
        self.datapool_model.reset()
        self.search_model.reset()

    def close_tab_workspace(self, cindex):
        """ Close workspace indexed by cindex cindex is Node"""
        w = self.tabWorkspace.widget(cindex)
        self.tabWorkspace.removeTab(cindex)
        self.session.close_workspace(cindex, False)
        g = w.scene().get_graph()
        g.close()
        #finally we close the dataflowview.
        w.close()
        del w

    def current_view(self):
        """ Return the active widget """
        return self.tabWorkspace.currentWidget()

    def update_tabwidget(self):
        """ open tab widget """
        # open tab widgets
        for (i, node) in enumerate(self.session.workspaces):

            if (i < self.tabWorkspace.count()):
                widget = self.tabWorkspace.widget(i)
                if (node != widget.scene().get_graph()):
                    self.close_tab_workspace(i)
                    self.open_widget_tab(node, factory=node.factory, pos=i)

        # close last tabs
        removelist = range(len(self.session.workspaces),
                           self.tabWorkspace.count())
        removelist.reverse()
        for i in removelist:
            self.close_tab_workspace(i)

    def open_widget_tab(self, graph, factory, caption=None, pos=-1):
        """
        Open a widget in a tab giving an instance and its widget
        caption is append to the tab title
        """
        gwidget = None
        try:
            # Since dataflowview.GraphicalGraph.__adapterType__ is dataflowview.adapter.GraphAdapter
            # graph will automatically be wrapped by that class and gwidget will exclusevily
            # talk to the adapter instead of the original graph. This thing is twisted but works well.
            gwidget = dataflowview.GraphicalGraph.create_view(graph,
                                                              parent=self)
            gwidget.set_clipboard(self.session.clipboard)
            gwidget.set_siblings(self.session.workspaces)
            gwidget.scene().focusedItemChanged.connect(
                self.on_scene_focus_change)
            self.session.add_graph_view(gwidget)
        except Exception, e:
            print "open_widget_tab", e
            traceback.print_exc()
            return

        if (not caption):
            i = self.session.workspaces.index(graph)
            caption = "Workspace %i - %s" % (i, graph.get_caption())

        index = self.tabWorkspace.insertTab(pos, gwidget, caption)
        self.tabWorkspace.setCurrentIndex(index)
        #there is a bug in QGraphicsScene+QTabWidget that makes
        #secondary tabs inactive, so we force them to be active
        #by sending new views the QEvent.WindowActivate event.
        #The bug is present until Qt4.6.2 at least. Bugreport:
        #http://bugreports.qt.nokia.com/browse/QTBUG-11148
        qt.QtCore.QCoreApplication.instance().notify(
            gwidget, qt.QtCore.QEvent(qt.QtCore.QEvent.WindowActivate))
        if gwidget is not None:
            gwidget.show_entire_scene()
        return index
Example #4
0
class MainWindow(qt.QtGui.QMainWindow,
                 ui_mainwindow.Ui_MainWindow,
                 SignalSlotListener):

    def __init__(self, session, parent=None):
        """
        @param session : user session
        @param parent : parent window
        """
        qt.QtGui.QMainWindow.__init__(self, parent)
        SignalSlotListener.__init__(self)
        ui_mainwindow.Ui_MainWindow.__init__(self)
        self.setupUi(self)
        self.setAcceptDrops(True)
        self.setAttribute(qt.QtCore.Qt.WA_QuitOnClose)

        self.tabWorkspace.removeTab(0)
        self.tabWorkspace.setTabsClosable(True)
        self.ws_cpt = 0

        if hasattr(AbstractEvaluation, "__provenance__"):
            self._prov = AbstractEvaluation.__provenance__
        else:
            self._prov = False

        #last opened nodes
        self._last_opened = []

        #lower tab pane : python shell, logger...
        self.lowerpane = qt.QtGui.QTabWidget()
        self.splitter.addWidget(self.lowerpane)

        # python interpreter
        try:
            interpreter = get_interpreter()
        except NameError:
            InterpreterClass = get_interpreter_class()
            interpreter = InterpreterClass()

        # interpreter init defered after session init
        shellclass = get_shell_class()
        self.interpreterWidget = shellclass(interpreter,
                                            cli.get_welcome_msg())

        GraphOperator.globalInterpreter = interpreter
        self.lowerpane.addTab(self.interpreterWidget, "Python Shell")

        if logger.QT_LOGGING_MODEL_AVAILABLE:
            # openalea logger
            model = logger.LoggerOffice().get_handler("qt")
            view = LoggerView(parent=self.lowerpane, model=model)
            self.lowerpane.addTab(view, "Logging")

        # search list view
        self.search_model = SearchModel()
        self.searchListView = \
            SearchListView(self, self.searchview)
        self.searchListView.setModel(self.search_model)
        self.vboxlayout3.addWidget(self.searchListView)
        self.searchListView.clicked.connect(self.on_package_manager_focus_change)

        # help widget
        self.helpWidget = helpwidget.HelpWidget()
        css = pj(misc.__path__[0], "..", "..", "..",
                 "share", "_static", "openalea.css")
        self.helpWidget.set_stylesheet_file(css)
        self.poolTabWidget.addTab(self.helpWidget, "Help")

        # Widgets
        self.connect(self.tabWorkspace, qt.QtCore.SIGNAL("contextMenuEvent(QContextMenuEvent)"),
                     self.contextMenuEvent)
        self.tabWorkspace.currentChanged.connect(self.ws_changed)
        self.search_lineEdit.editingFinished.connect(self.search_node)
        self.tabWorkspace.tabCloseRequested.connect(self.close_tab_workspace)

        # Help Menu
        self.action_About.triggered.connect(self.about)
        self.actionOpenAlea_Web.triggered.connect(self.web)
        self.action_Help.triggered.connect(self.help)

        # File Menu
        self.action_New_Session.triggered.connect(self.new_session)
        self.action_Open_Session.triggered.connect(self.open_session)
        self.action_Save_Session.triggered.connect(self.save_session)
        self.actionSave_as.triggered.connect(self.save_as)
        self.action_Quit.triggered.connect(self.quit)

        self.action_Image.triggered.connect(self.export_image)
        self.action_Svg.triggered.connect(self.export_image_svg)

        # Package Manager Menu
        self.action_Auto_Search.triggered.connect(self.reload_all)
        self.action_Add_File.triggered.connect(self.add_pkgdir)
        self.actionFind_Node.triggered.connect(self.find_node)
        self.action_New_Network.triggered.connect(self.new_graph)
        self.actionNew_Python_Node.triggered.connect(self.new_python_node)
        self.actionNew_Package.triggered.connect(self.new_package)
        self.action_Data_File.triggered.connect(self.new_data)

        # DataPool Menu
        self.actionClear_Data_Pool.triggered.connect(self.clear_data_pool)

        # Python Menu
        self.action_Execute_script.triggered.connect(
            self.exec_python_script)
        self.actionOpen_Console.triggered.connect(
            self.open_python_console)
        self.actionClea_r_Console.triggered.connect(
            self.clear_python_console)

        # WorkspaceMenu
        self.__operatorAction = dict([(self.action_Run, "graph_run"),
                                      (self.actionInvalidate, "graph_invalidate"),
                                      (self.actionReset, "graph_reset"),
                                      (self.actionConfigure_I_O, "graph_configure_io"),
                                      (self.actionGroup_Selection, "graph_group_selection"),
                                      (self.action_Copy, "graph_copy"),
                                      (self.action_Paste, "graph_paste"),
                                      (self.action_Cut, "graph_cut"),
                                      (self.action_Delete_2, "graph_remove_selection"),
                                      (self.action_Close_current_workspace, "graph_close"),
                                      (self.action_Export_to_Factory, "graph_export_to_factory"),
                                      (self.actionReload_from_Model, "graph_reload_from_factory"),
                                      (self.actionExport_to_Application, "graph_export_application"),
                                      (self.actionPreview_Application, "graph_preview_application"),
                                      (self.actionAlignHorizontally, "graph_align_selection_horizontal"),
                                      (self.actionAlignLeft, "graph_align_selection_left"),
                                      (self.actionAlignRight, "graph_align_selection_right"),
                                      (self.actionAlignMean, "graph_align_selection_mean"),
                                      (self.actionDistributeHorizontally, "graph_distribute_selection_horizontally"),
                                      (self.actionDistributeVertically, "graph_distribute_selection_vertically"),
                                      (self.actionSetCustomColor, "graph_set_selection_color"),
                                      (self.actionUseCustomColor, "graph_use_user_color")])

        self._last_open_action_group = qt.QtGui.QActionGroup(self)
        self.connect(self._last_open_action_group,
                     qt.QtCore.SIGNAL("triggered(QAction*)"),
                     self.reopen_last)
        self.action_New_Empty_Workspace.triggered.connect(self.new_workspace)
        self.menu_Workspace.aboutToShow.connect(self.__wsMenuShow)
        self.menu_Workspace.aboutToShow.connect(self.__wsMenuHide)
        for ac, fname in self.__operatorAction.iteritems():
            f = self.__make_operator_action_connector(ac, fname)
            ac.triggered.connect(f)

        self.actionTo_script.triggered.connect(self.to_python_script)

        # Window Mneu
        self.actionPreferences.triggered.connect(self.open_preferences)
        self.actionDisplay_Package_Manager.toggled.connect(self.display_leftpanel)
        self.actionDisplay_Workspaces.toggled.connect(self.display_rightpanel)

        #load personnal GUI settings
        self.read_settings()

        #############
        # Provenance
        #############
        if PROVENANCE:
            self.menu_provenance = qt.QtGui.QMenu(self.menubar)
            self.menu_provenance.setObjectName("menu_provenance")
            self.menu_provenance.setTitle(qt.QtGui.QApplication.translate("MainWindow", "&Provenance", None, qt.QtGui.QApplication.UnicodeUTF8))

            self.action_activ_prov = qt.QtGui.QAction(self)
            self.action_activ_prov.setCheckable(True)
            prov = self.get_provenance()
            self.action_activ_prov.setChecked(prov)
            self.action_activ_prov.setObjectName("action_activ_prov")
            self.action_activ_prov.setText(qt.QtGui.QApplication.translate("MainWindow", "Connect/Disconnect Provenance", None, qt.QtGui.QApplication.UnicodeUTF8))

            self.action_show_prov = qt.QtGui.QAction(self)
            self.action_show_prov.setCheckable(False)
            self.action_show_prov.setObjectName("action_show_prov")
            self.action_show_prov.setText(qt.QtGui.QApplication.translate("MainWindow", "Show Provenance", None, qt.QtGui.QApplication.UnicodeUTF8))

            self.menu_provenance.addAction(self.action_activ_prov)
            self.menu_provenance.addAction(self.action_show_prov)

            self.menubar.addAction(self.menu_provenance.menuAction())

            self.action_activ_prov.toggled.connect(self.set_provenance)
            self.action_show_prov.triggered.connect(self.show_provenance)

    def set_provenance(self, provenance):
        """
        Set/Unset the registry of provenance

        :param provenance: boolean which is set to True if we want to register provenance. Else, False.
        """
        if hasattr(AbstractEvaluation, "__provenance__"):
            self._prov = provenance
            AbstractEvaluation.__provenance__ = self._prov

    def get_provenance(self):
        """
        :return: boolean which is set to True if we want to register provenance. Else, False.
        """
        return self._prov

    def show_provenance(self):
        """
        Display the provenance
        """
        from openalea.visualea.provenance import ModalDialog, ProvenanceSelectorWidget, search_trace
        prov_widget = ProvenanceSelectorWidget(self)
        dialog = ModalDialog(prov_widget)
        dialog.show()
        dialog.raise_()

        if dialog.exec_():
            cn = prov_widget.c_n.text()
            pkg = prov_widget.pkg.text()
            wk = prov_widget.workspace.text()

            search_trace(cn, pkg, wk, parent=self)

    def on_session_started(self, session):
        self.initialise(session)
        self.session = session

        # -- configure the interpreter --
        cli.init_interpreter(self.interpreterWidget.interpreter,
                             session,
                             {"tabs": self.tabWorkspace})

        # -- now, many package manager related views --
        self.pkgmanager = session.pkgmanager
        self.actionShow_log.triggered.connect(self.pkgmanager.log.print_log)

        # package tree view
        self.pkg_model = PkgModel(self.pkgmanager)
        self.packageTreeView = \
            NodeFactoryTreeView(self, self.packageview)
        self.packageTreeView.setModel(self.pkg_model)
        self.vboxlayout1.addWidget(self.packageTreeView)
        self.packageTreeView.clicked.connect(self.on_package_manager_focus_change)

        # category tree view
        self.cat_model = CategoryModel(self.pkgmanager)
        self.categoryTreeView = \
            NodeFactoryTreeView(self, self.categoryview)
        self.categoryTreeView.setModel(self.cat_model)
        self.vboxlayout2.addWidget(self.categoryTreeView)
        self.categoryTreeView.clicked.connect(self.on_package_manager_focus_change)

        # data pool list view
        self.datapool_model = DataPoolModel(session.datapool)
        self.datapoolListView = \
            DataPoolListView(self, session.datapool, self.pooltab)
        self.datapoolListView.setModel(self.datapool_model)
        self.vboxlayout4.addWidget(self.datapoolListView)

        self.session.simulate_workspace_addition()

    def debug(self):
        v = self.packageTreeView
        print "items", v.expanded_items
        print "model", v.model()
        print "map", v.model().index_map

    def write_settings(self):
        """Save application settings.
        """
        settings = Settings()

        #main window
        settings.set("MainWindow", "size", "(%d,%d)" % (self.width(), self.height()))
        settings.set("MainWindow", "pos", "(%d,%d)" % (self.x(), self.y()))

        sizes = "[%s]" % ",".join("%d" % val for val in self.splitter_2.sizes())
        settings.set("MainWindow", "splitter_2", sizes)
        sizes = "[%s]" % ",".join("%d" % val for val in self.splitter_3.sizes())
        settings.set("MainWindow", "splitter_3", sizes)

        #tree view
        settings.set("TreeView", "open", "[]")

        #workspace
        last_open = "[%s]" % ",".join("'%s'" % item for item in self._last_opened)
        settings.set("WorkSpace", "last", last_open)

        #provenance
        prov = self.get_provenance()
        settings.set("Provenance", "enable", str(prov))

        settings.write()

    def read_settings(self):
        """Read application settings.
        """
        settings = Settings()

        #main window
        try:
            size = eval(settings.get("MainWindow", "size"))
            self.resize(qt.QtCore.QSize(*size))
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            pos = eval(settings.get("MainWindow", "pos"))
            self.move(qt.QtCore.QPoint(*pos))
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            sizes = eval(settings.get("MainWindow", "splitter_2"))
            self.splitter_2.setSizes(sizes)
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        try:
            sizes = eval(settings.get("MainWindow", "splitter_3"))
            self.splitter_3.setSizes(sizes)
        except NoSectionError:
            pass
        except NoOptionError:
            pass
        #workspace
        try:
            last_open = eval(settings.get("WorkSpace", "last"))
            last_open.reverse()
            for item in last_open:
                gr = item.split(".")
                pkgid = ".".join(gr[:-1])
                name = gr[-1]
                self.add_last_open(pkgid, name)
        except NoSectionError:
            pass
        except NoOptionError:
            pass

        try:
            prov = eval(settings.get("Provenance", "enable"))
            self.set_provenance(bool(prov))
        except NoSectionError:
            pass
        except NoOptionError:
            pass

    def redo_last_open_menu(self):
        """Create entries for last opened nodes.
        """
        self.menuLast_open.clear()
        for action in self._last_open_action_group.actions():
            self._last_open_action_group.removeAction(action)

        for i, node_descr in enumerate(self._last_opened):
            action = self.menuLast_open.addAction(node_descr)
            action.setShortcut("Ctrl+%d" % (i + 1))
            self._last_open_action_group.addAction(action)

        self.menuLast_open.setEnabled(len(self._last_opened) > 0)

    def reopen_last(self, action):
        """Reopen a last open node.
        """
        gr = str(action.text()).split(".")
        pkgid = ".".join(gr[:-1])
        name = gr[-1]
        manager = PackageManager()
        factory = manager[pkgid][name]
        self.open_compositenode(factory)

    def add_last_open(self, pkgid, factory_name):
        """Register a new lest opened node.
        """
        key = ".".join([pkgid, factory_name])
        try:
            self._last_opened.remove(key)
        except ValueError:
            pass

        self._last_opened.insert(0, key)
        if len(self._last_opened) > 4:
            del self._last_opened[-1]

        self.redo_last_open_menu()

    def __wsMenuShow(self, abool=False):
        graphview = self.tabWorkspace.currentWidget()
        if not isinstance(graphview, dataflowview.DataflowView):
            return

        items = graphview.scene().get_selected_items(dataflowview.vertex.GraphicalVertex)
        self.actionUseCustomColor.setChecked(False)
        for i in items:
            if i.vertex().get_ad_hoc_dict().get_metadata("useUserColor"):
                self.actionUseCustomColor.setChecked(True)
                break

    def __make_operator_action_connector(self, action, name):
        def connector(aBool=None):
            graphview = self.tabWorkspace.currentWidget()
            if not isinstance(graphview, dataflowview.DataflowView):
                return

            # daniel was here: now the menu is built using the graph operator.
            operator = GraphOperator(graph=graphview.scene().get_graph(),
                                     graphScene=graphview.scene(),
                                     clipboard=self.session.clipboard,
                                     siblings=self.session.workspaces)
            operator.register_listener(self)
            operator(fName=name)()

        return connector

    def __wsMenuHide(self):
        pass

    def open_compositenode(self, factory):
        """ open a  composite node editor """
        node = factory.instantiate()

        self.session.add_workspace(node, notify=False)
        self.open_widget_tab(node, factory=factory)

        self.add_last_open(factory.__pkg_id__, factory.name)

    def about(self):
        """ Display About Dialog """

        mess = qt.QtGui.QMessageBox.about(self, "About Visualea",
                                          "Version %s\n\n" % (metainfo.get_version()) +
                                          "VisuAlea is part of the OpenAlea framework.\n" +
                                          metainfo.get_copyright() +
                                          "This Software is distributed under the Cecill-V2 License.\n\n" +
                                          "Visit " + metainfo.url + "\n\n"
                                          )

    def help(self):
        """ Display help """
        self.web()

    def web(self):
        """ Open OpenAlea website """
        qt.QtGui.QDesktopServices.openUrl(qt.QtCore.QUrl(metainfo.url))

    def quit(self):
        """ Quit Application """
        if(qt.QtGui.QMessageBox.question(self, "Quit?", "Are you sure you want to quit?",
                                         qt.QtGui.QMessageBox.Ok | qt.QtGui.QMessageBox.Cancel) ==
                qt.QtGui.QMessageBox.Ok):
            qt.QtGui.QApplication.exit(0)

    def notify(self, sender, event):
        """ Notification from observed """
        if event and isinstance(sender, GraphOperator):
            index = -1
            for i in range(self.tabWorkspace.count()):
                wid = self.tabWorkspace.widget(i)
                if isinstance(wid, dataflowview.DataflowView) and wid.scene() == event[1]:
                    index = i
            if index <= -1:
                return
            if(event[0] == "graphoperator_graphsaved"):
                self.reinit_treeview()
                caption = "Workspace %i - %s" % (index, event[2].name)
                self.tabWorkspace.setTabText(index, caption)
            elif(event[0] == "graphoperator_graphclosed"):
                self.close_tab_workspace(index)
            elif(event[0] == "graphoperator_graphreloaded"):
                self.session.workspaces[index] = event[2]

        if(type(sender) == type(self.session)):
            if(event and event[0] == "workspace_added"):
                graph = event[1]
                self.open_widget_tab(graph, graph.factory)
            else:
                self.update_tabwidget()
                self.reinit_treeview()

    def closeEvent(self, event):
        """ Close All subwindows """

        #Save personnal settings
        self.write_settings()

        #close windows
        for i in range(self.tabWorkspace.count()):
            w = self.tabWorkspace.widget(i)
            w.close()

        event.accept()

    def reinit_treeview(self):
        """ Reinitialise package and category views """
        self.cat_model.reset()
        self.pkg_model.reset()
        self.datapool_model.reset()
        self.search_model.reset()

    def close_tab_workspace(self, cindex):
        """ Close workspace indexed by cindex cindex is Node"""
        w = self.tabWorkspace.widget(cindex)
        self.tabWorkspace.removeTab(cindex)
        self.session.close_workspace(cindex, False)
        g = w.scene().get_graph()
        g.close()
        #finally we close the dataflowview.
        w.close()
        del w

    def current_view(self):
        """ Return the active widget """
        return self.tabWorkspace.currentWidget()

    def update_tabwidget(self):
        """ open tab widget """
        # open tab widgets
        for (i, node) in enumerate(self.session.workspaces):

            if(i < self.tabWorkspace.count()):
                widget = self.tabWorkspace.widget(i)
                if(node != widget.scene().get_graph()):
                    self.close_tab_workspace(i)
                    self.open_widget_tab(node, factory=node.factory, pos=i)

        # close last tabs
        removelist = range(len(self.session.workspaces), self.tabWorkspace.count())
        removelist.reverse()
        for i in removelist:
            self.close_tab_workspace(i)

    def open_widget_tab(self, graph, factory, caption=None, pos=-1):
        """
        Open a widget in a tab giving an instance and its widget
        caption is append to the tab title
        """
        gwidget = None
        try:
            # Since dataflowview.GraphicalGraph.__adapterType__ is dataflowview.adapter.GraphAdapter
            # graph will automatically be wrapped by that class and gwidget will exclusevily
            # talk to the adapter instead of the original graph. This thing is twisted but works well.
            gwidget = dataflowview.GraphicalGraph.create_view(graph, parent=self)
            gwidget.set_clipboard(self.session.clipboard)
            gwidget.set_siblings(self.session.workspaces)
            gwidget.scene().focusedItemChanged.connect(self.on_scene_focus_change)
            self.session.add_graph_view(gwidget)
        except Exception, e:
            print "open_widget_tab", e
            traceback.print_exc()
            return

        if(not caption):
            i = self.session.workspaces.index(graph)
            caption = "Workspace %i - %s" % (i, graph.get_caption())

        index = self.tabWorkspace.insertTab(pos, gwidget, caption)
        self.tabWorkspace.setCurrentIndex(index)
        #there is a bug in QGraphicsScene+QTabWidget that makes
        #secondary tabs inactive, so we force them to be active
        #by sending new views the QEvent.WindowActivate event.
        #The bug is present until Qt4.6.2 at least. Bugreport:
        #http://bugreports.qt.nokia.com/browse/QTBUG-11148
        qt.QtCore.QCoreApplication.instance().notify(gwidget, qt.QtCore.QEvent(qt.QtCore.QEvent.WindowActivate))
        if gwidget is not None:
            gwidget.show_entire_scene()
        return index