Esempio n. 1
0
 def invoke_matrix_viewer(self):
     """ Invoke the Conectome Matrix Viewer """
     from cviewer.visualization.matrix.cmatrix_viewer import CMatrixViewer
     self.cmatrix_viewer = CMatrixViewer(self.network)
     self.cmatrix_viewer.edit_traits()
Esempio n. 2
0
class RenderManager(HasTraits):
    """ Handles the multiple views (scenes) on the network and surface
    data, handles picking as well. """
    
    from enthought.mayavi.core.scene import Scene
    from enthought.mayavi.api import Engine
    
    # the scene for the 3D view of the network
    scene3d = Instance(Scene)
    
    # VTK Graph Pipeline in 2D
    scene2d = Instance(Scene)
   
    # the mayavi engine
    engine = Instance(Engine)

    # the network this render manager manages
    network = Instance(INetwork)

    # nodepicker
    nodepicker = Instance(NodePicker)

    # cmatrix viewer
    cmatrix_viewer = Any
    
    # references to the data
    # -----
    # the node glyphs
    nodes = Any
    # the selected node glyphs (actor)
    nodes_selected = Any
    # the node source
    nodesrc = Any
    # a dictionary with references to created text3d labels
    # keyed by the node index
    node_labels = Any
    
    # connectivity source
    vectorsrc = Any
    # connectivity actor
    vectors = Any
    
    # surface source
    surfsrc = Any
    # surface triangular mesh actor
    surf = Any
    
    # threshold
    thres = Any
    # attribute selector
    attract = Any
    
    node_info_view = View(Group(
                            Group(Item(name='nodepicker', style='custom'),
                                    show_border=True, show_labels=False),
                              Group(Item(name='show_gui'),
                                    Item(name='auto_raise'), show_border=True),
                              ),
                        resizable=True,
                        buttons=['OK'])
    
    
    def __init__(self, network, **traits):
        super(RenderManager, self).__init__(**traits)
        
        from enthought.mayavi import mlab
        
        logger.info('Initalize RenderManager...')

        # set the datasource manager
        self.datasourcemanager = network.datasourcemanager
        
        # getting mlab engine
        self.engine = mlab.get_engine()
        
        # store reference to managed network
        self.network = network
        
        # create the scene instances
        self._create_scenes()
        
        # use nice trackball camera interactor
        # self.scene3d.scene.interactor.interactor_style = tvtk.InteractorStyleTrackballCamera()
        # otherwise just use tvtk.InteractorStyleSwitch()
                
        # create my node picker inclusive the NodePickHandler
        self.nodepicker = NodePicker(renwin=self.scene3d.scene)
                
        # add custom picker for the scene3d
        self.scene3d.scene.picker = self.nodepicker
        
        # adding observers to capture key events
        self.scene3d.scene.interactor.add_observer('KeyPressEvent', self.nodepicker.key_pressed)

        # add the callback function after a pick event  
        self.scene3d.scene.picker.pointpicker.add_observer('EndPickEvent', self._nodepicked_callback)
        
        # initialize node_labels
        self.node_labels = {}
        
        logger.info('RenderManager created.')


    ###############
    # Scene2D Handling
    ###############
    
    def _create2DScene(self):
        """ Create the Mayavi Scene2D and adds it to the current engine """
        
        figure = self.engine.new_scene(name="2D View: "+self.network.networkname)
        figure.scene.background = (0, 0, 0)
        return figure
    
    def visualize_scene2d(self):
        """ Creates the VTK Graph Pipeline to render the Graph """
        
        from enthought.tvtk.api import tvtk
        
        # precondition for the graph visualization
        if self.scene2d is None:
            self.scene2d = self._create2DScene()
            
        # alias for currently used scene
        fig = self.scene2d

        # get source object
        srcobj = self.datasourcemanager.get_sourceobject()
        
        # XXX_start: the following section for graph data structure generation
        # might be encapsulated in another function
        
        edges, weights = self.datasourcemanager.get_current_edges_and_weights()

        # create table
        T = tvtk.Table()
        
        col1 = tvtk.IntArray()
        col1.name = "From"
        for ele in edges:
            col1.insert_next_value(ele[0])
        T.add_column(col1)

        col2 = tvtk.IntArray()
        col2.name = "To"
        for ele in edges:
            col2.insert_next_value(ele[1])
        T.add_column(col2)

        col3 = tvtk.FloatArray()
        col3.name = "Weight"
        for ele in weights:
            col3.insert_next_value(ele)
        T.add_column(col3)

        # XXX_end
        
        # build the pipeline (using TVTK)

        graph = tvtk.TableToGraph()
        graph.add_input_connection(T.producer_port)
        #graph.AddLinkVertex("From", "Name", False)
        #graph.AddLinkVertex("To", "Name", False)
        graph.add_link_edge("From", "To")
        #graph.SetVertexTableConnection(vertex_table.GetOutputPort())

        view = tvtk.GraphLayoutView()
        view.add_representation_from_input_connection(graph.output_port)
        #view.vertex_label_array_name = "label"
        #view.vertex_label_visibility = True
        #view.vertex_color_array_name = "Age"
        view.color_vertices = False
        view.color_edges = True
        #view.set_layout_strategy_to_simple2d()
        view.set_layout_strategy_to_force_directed()
        # random, pass_trhough, fast2d, simple2d, community2d, circular, clustering2d

        # Add my new layout strategy
        # myFoo = vtkCircularLayoutStrategy()
        # view.SetLayoutStrategy(myFoo)
        
        theme = tvtk.ViewTheme().create_mellow_theme()
        theme.cell_color = (0.2,0.2,0.6)
        theme.line_width = 5
        theme.point_size = 10
        view.apply_view_theme(theme)
        view.vertex_label_font_size = 20

        # the rendering part is a bit different than
            #window = vtkRenderWindow()
            #window.SetSize(600, 600)
            #view.SetupRenderWindow(window)
            #view.GetRenderer().ResetCamera()
            #window.GetInteractor().Start()
        # for mayavi. maybe this can be simplified
        # XXX: where is the scene interactor set?
        
        # get renderer
        ren = view.renderer
        ren.reset_camera()
        props = ren.view_props
        props0 = props.get_item_as_object(0)
        map0 = props0.mapper
        graph0 = map0.get_input_data_object(0,0)
        
        # export to polydata
        pd = tvtk.GraphToPolyData()
        pd.set_input(graph0)
        pd.update()
        # same as  pd.get_output() or pd.output
        polydata = pd.get_output_data_object(0)
        
        a = tvtk.Actor(mapper=map0)
        
        # finally, add the actor to the scene2d
        fig.scene.add_actor(a)
        # and we are lucky, aren't we?
        
    def update_scene2d(self):
        """ Grabs the graph infromation from the DataSourceManager and
            updates the Scene2D rendering Pipeline """
            
        # XXX: is it at all possible to update the underlying datastructure
        # and then update the pipeline with the approach taken here?
        
        pass

    def _nodepicked_callback(self, picker_obj, evt):
        """ The callback function to determine what to do with a picked node """
        from enthought.tvtk.api import tvtk

        picker_obj = tvtk.to_tvtk(picker_obj)
        picked = picker_obj.actors

        # if picked actor are the glyphs
        if self.nodes.actor.actor._vtk_obj in [o._vtk_obj for o in picked]:

            pos = picker_obj.picked_positions[0]
            
            dist_small = sys.maxint # the biggest integer
            
            for i, points in enumerate(self.nodes.mlab_source.points):
                # if picked positions ON a node
                dist = np.linalg.norm((np.array(pos)-  points))
                # save the smallest distance node
                if dist <= dist_small:
                    j = i
                    dist_small = dist

            # get the id of the picked node
            picknodeid = 'n' + str(j+1)

            if self.nodepicker.show_info:
                
                # generate a nice view for the information
                nodedata = self.network.graph.node[picknodeid]

                nodedata['dn_internal_id'] = picknodeid

                deg = self.network.graph.degree(picknodeid)
                from types import IntType, StringType
                if type(deg) == IntType or type(deg) == StringType:
                    nodedata['degree'] = deg
                    
                # does this node has a self-loop?
                if self.network.graph.has_edge(picknodeid, picknodeid):
                    nodedata['has_selfloop'] = True
                else:
                    nodedata['has_selfloop'] = True
                    
                mynode = Node(nodedata=nodedata)
                mynode.configure_traits()
            

            elif self.nodepicker.toggle_selection:
                # toggles the selection of a node
                if self.datasourcemanager._srcobj.selected_nodes[j] == 1:
                    # unpick
                    self._select_nodes(selection_node_array = np.array([j]), \
                                       first_is_selected = True, change_vector = False)
                else:
                    # pick
                    self._select_nodes(selection_node_array = np.array([j]), activate = True, \
                                       first_is_selected = True, change_vector = False)

            elif self.nodepicker.select_node:
                # toggle the picking state              
                if self.datasourcemanager._srcobj.selected_nodes[j] == 1:
                    # unpick
                    self._select_nodes(selection_node_array = np.array([j]), \
                                       first_is_selected = True)
                else:
                    # pick
                    self._select_nodes(selection_node_array = np.array([j]), activate = True, \
                                       first_is_selected = True)

            elif self.nodepicker.select_node2:
                
                # get the neighbors of the picked node in an array
                nbrs = self.datasourcemanager._srcobj.relabled_graph.neighbors(j)
                # put the two lists together
                cur_nodes = np.append(j, np.array(nbrs))
                
                if self.datasourcemanager._srcobj.selected_nodes[j] == 1:
                    # unpick
                    self._select_nodes(selection_node_array = cur_nodes, \
                                       first_is_selected = True)
                else:
                    # pick
                    self._select_nodes(selection_node_array = cur_nodes, activate = True ,\
                                       first_is_selected = True)
                    
            elif self.nodepicker.toggle_label:
                
                if self.node_labels.has_key(j):
                    # we want to remove the node label from the pipeline first
                    # and then delete the entry in the dictionary
                    a = self.node_labels[j]
                    a.remove()
                    del self.node_labels[j]
                    
                else:
                    srcobj = self.datasourcemanager._srcobj
                    # create the text3d instance and add it to the dictionary
                    from enthought.mayavi import mlab
                    a = mlab.text3d(srcobj.positions[j,0], srcobj.positions[j,1], \
                                                srcobj.positions[j,2], \
                                                '     ' + srcobj.labels[j], # white spaces are hackish
                                                name = 'Node ' + srcobj.labels[j])
                    self.node_labels[j] = a

            else:
                logger.info("No valid pick operation.")
                

    def _select_nodes(self, selection_node_array, activate = False, first_is_selected = False, change_vector = True):
        """ Helper function to update the datasourceobject nodes selection status
        and update the currently rendered objects
        
        Parameters
        ----------
        fist_is_selected : boolean
            if the first node id in the selection_node_array is a clicked node
        change_vector : boolean
            add edges for visualization
        
        """
        logger.debug('_select_nodes invoked')
        import time
        
        # update the edges
        if first_is_selected:
            if change_vector:
                now=time.clock()
                self._select_edges(activate = activate, fis = selection_node_array[0])
                logger.debug('change_vector True: _select_edges used %s s' % str(time.clock()-now))
        else:
            now=time.clock()
            self._select_edges(activate = activate)
            logger.debug('first_is_selected False: _select_edges used %s s' % str(time.clock()-now))
        
        now=time.clock()
        tmparr = self.nodesrc.mlab_source.dataset.point_data.get_array('selected_nodes')
        num_tmparr = tmparr.to_array()
        logger.debug('getting nodesrc mlab array %s s' % str(time.clock()-now))

        now=time.clock()
        # setting the datastructure
        self.datasourcemanager._srcobj.selected_nodes[selection_node_array] = int(activate)
        logger.debug('setting selected_nodes in srcmng %s s' % str(time.clock()-now))
        
        # update rendering information
        num_tmparr[selection_node_array] = int(activate)
        
        now=time.clock()
        # update the nodes
        tmparr = num_tmparr
        self.nodesrc.mlab_source.update()
        logger.debug('update the nodes %s s' % str(time.clock()-now))

    def _select_edges(self, activate, fis = -1):
        """ Helper function to update the selected edges in the datasourceobject
        based on the currently selected nodes and update the currently rendered edges.
        """
        
        # get source object
        srcobj = self.datasourcemanager.get_sourceobject()

        # if there are no edges in this network, return
        if len(srcobj.edges) == 0:
            return

        if not fis == -1:
            # first value is the starting node
            if not srcobj.directed:
                idx, pos = np.where(srcobj.edges == fis)
                # idx stores the index, pos either from or to in tuple
                # for undirected, we can discard pos
                # now, we can (de)activate the edges corresponding to fis
                srcobj.selected_edges[idx] = int(activate)
                self.datasourcemanager._update_vector()
            else:
                # TODO
                # how to select the edges if the graph is directed?
                pass
        else:
            # just select the whole subgraph spanned by the selected_nodes
            if activate:
                tmp_selnodes = np.where(srcobj.selected_nodes == 0)
            else:
                tmp_selnodes = np.where(srcobj.selected_nodes == 1)
            if len(tmp_selnodes) >= 1:
                for i in tmp_selnodes[0]:
                    idx, pos = np.where(srcobj.edges == i)
                    srcobj.selected_edges[idx] = int(activate)
            self.datasourcemanager._update_vector()

        # update vectorsrc
        self.vectorsrc.mlab_source.set(u=srcobj.vectors[0],v=srcobj.vectors[1], w=srcobj.vectors[2])
        self.vectorsrc.mlab_source.update()
        

    def _show_surface(self, forced = False):
        """ Shows all the surfaces patches of the selected nodes depending
        on the show_surface status
        
        Parameters
        ----------
        forced : boolean
            If true, the surfaces have to be displayed
        
        
        """
        # precondition
        # can only be called when there is already a surface rendered
        if self.surfsrc is None: return
        
        # get the intensity value of the selected nodes
        sellist = self.network.get_selectiongraph_list()
        if not len(sellist) == 0:
            iva = np.zeros(len(sellist))
            for i, nodeid in enumerate(sellist):
                if self.network.graph.node[nodeid].has_key('dn_intensityvalue'):
                        iva[i] = int(self.network.graph.node[nodeid]['dn_intensityvalue'])
        else:
            iva = None

        # set the show_surfaces status to True if forced
        if forced:
            self.datasourcemanager._srcobj.show_surface = True

        # updates the surface label array
        self.datasourcemanager._update_labelarr(intensityvalue_array = iva)
        
        # toggle the surface visibility        
        # the shape of the scalars and vertices array have to be consistent at this point (use set)
        assert self.surfsrc.mlab_source.scalars.shape[0] == self.datasourcemanager._srcobj.labelarr.shape[0]
        
        self.surfsrc.mlab_source.set(scalars = self.datasourcemanager._srcobj.labelarr.ravel())
        self.surfsrc.mlab_source.update()

    def _toggle_surface(self):
        """ Toggles visibility of the surface of the selected nodes
        
        """

        # precondition
        # can only be called when there is already a surface rendered
        if self.surfsrc is None: return
        
        iva = None
        if not self.datasourcemanager._srcobj.show_surface:
            # get the intensity value of the selected nodes
            sellist = self.network.get_selectiongraph_list()
            iva = np.zeros(len(sellist))
            for i, nodeid in enumerate(sellist):
                if self.network.graph.node[nodeid].has_key('dn_intensityvalue'):
                        iva[i] = int(self.network.graph.node[nodeid]['dn_intensityvalue'])

        self.datasourcemanager._srcobj.show_surface = not self.datasourcemanager._srcobj.show_surface

        # updates the surface label array
        self.datasourcemanager._update_labelarr(intensityvalue_array = iva)
        
        # toggle the surface visibility
        # the shape of the scalars and vertices array have to be consistent at this point (use set)
        assert self.surfsrc.mlab_source.scalars.shape[0] == self.datasourcemanager._srcobj.labelarr.shape[0]

        self.surfsrc.mlab_source.set(scalars = self.datasourcemanager._srcobj.labelarr.ravel())
        self.surfsrc.mlab_source.update()
        
    def _create3DScene(self):
        """ Creates a 3D Scene """
        figure = self.engine.new_scene(name="3D View: "+self.network.networkname)
        figure.scene.show_axes = True
        figure.scene.background = (0, 0, 0)
        return figure

    def _create_scenes(self):
        """ Reopens the closed scenes """
        self.scene3d = self._create3DScene()
        # Uncomment to activate again
        #self.scene2d = self._create2DScene()
        
    def close_scenes(self):
        """ Closes all scenes for this render manager """
        
        from enthought.mayavi import mlab
        eng = mlab.get_engine()
        try:
            # Uncomment to activate again
            #eng.close_scene(self.scene2d)
            eng.close_scene(self.scene3d)
            
        except Exception:
            pass
        # mlab.close(self.scene3d)

        
    def layout_3dview(self, surfacecontainerid, surfaceid):
        """ Visualizes the 3D view, initial rendering if necessary!
        
        Parameters
        ----------
        surfacecontainerid : int
            The surface container id to use for layouting
        surfaceid : int
            The particular surface to use for layouting
        
        """
        import time
        
        tic = time.time()
        # compute the sourceobject for layouting
        self.datasourcemanager._compute_3DLayout(surfacecontainerid, surfaceid)
        toc = time.time()
        logger.debug("SourceObject has been changed successfully. (Time: %s)" % str(toc-tic))

        # disable rendering for speedup
        self.scene3d.scene.disable_render = True

        # set the correct scene for the update (scene3d)
        self.engine.current_scene = self.scene3d

        # is there already data in the scenes, when not, create.
        if self.nodesrc is None and self.vectorsrc is None and self.surfsrc is None:
            
            tic = time.time()
            self.visualize_graph()
            toc = time.time()
            logger.debug('Graph rendered. Time: %s' % str(toc-tic))
            
            tic = time.time()
            self.visualize_surface()
            toc = time.time()
            logger.debug('Surface rendered. Time: %s' % str(toc-tic))
        else:
            # get the source object
            tso = self.datasourcemanager.get_sourceobject()
            
            tic = time.time()
            # update the positions of the network nodes
            self.nodesrc.mlab_source.set(x=tso.positions[:,0],\
                                                 y=tso.positions[:,1],\
                                                 z=tso.positions[:,2])
            self.nodesrc.mlab_source.update()
            
            toc = time.time()
            logger.debug('Node position updated. Time: %s' % str(toc-tic))
            
            tic = time.time()
            # update start_positions and vectors for the network
            self.vectorsrc.mlab_source.set(x=tso.start_positions[0],\
                y=tso.start_positions[1],z=tso.start_positions[2],\
                u=tso.vectors[0],v=tso.vectors[1],w=tso.vectors[2])
            
            # update the rendering
            self.vectorsrc.mlab_source.update()
            toc = time.time()
            logger.debug('Connectivity source updated. Time: %s' % str(toc-tic))
            
            tic = time.time()
            # update surface coordinates, triangles and labelarray
            # important: use reset because dimensions might have changed
            self.surfsrc.mlab_source.reset(x=tso.daV[:,0],\
                y=tso.daV[:,1], z=tso.daV[:,2],\
                triangles=tso.daF,\
                scalars=tso.labelarr.ravel())
            toc = time.time()
            
            # update the surface visibility
            self._show_surface()
            
            logger.debug('Surface source updated. Time: %s' % str(toc-tic))
            
        # enable rendering again
        self.scene3d.scene.disable_render = False
        
        # update statusbar (with do_later)
        from enthought.pyface.timer.api import do_later
        do_later(self.network._parentcfile._workbenchwin.status_bar_manager.set, message = '')

    def visualize_surface(self):
        """ Visualizes the given surface """
        from enthought.mayavi import mlab
        
        # precondition for surface visualization
        if self.scene3d is None:
            self.scene3d = self._create3DScene()

        # alias for currently used scene
        fig = self.scene3d

        # get source object
        srcobj = self.datasourcemanager.get_sourceobject()

        # dummy scalar array includes zero up to max
        scalars_dummy = np.zeros(srcobj.labelarr_orig.ravel().shape )
        scalars_dummy[-1] = np.max(srcobj.labelarr_orig)

        self.surfsrc = mlab.pipeline.triangular_mesh_source(srcobj.daV[:,0],srcobj.daV[:,1],srcobj.daV[:,2],\
                             srcobj.daF, name='Surface', scalars = scalars_dummy) #srcobj.labelarr_orig.ravel() )
        
        # to ameliorate vtkLookupTable: Bad table range
        outsurf = mlab.pipeline.select_output(self.surfsrc)
        
        thr = mlab.pipeline.threshold(outsurf, low=0.0, up=np.max(srcobj.labelarr_orig) )
        thr.auto_reset_lower = False
        thr.auto_reset_upper = False
        thr.filter_type = 'cells'

        self.surf = mlab.pipeline.surface(thr, colormap='prism', opacity = 1.0)
        self.surf.actor.mapper.interpolate_scalars_before_mapping = True
        
        # setting surfsrc to labelarr
        self.surfsrc.mlab_source.scalars = srcobj.labelarr
        self.surfsrc.mlab_source.update()
        
        # set lower threshold to 1
        thr.lower_threshold = 1.0
        thr.upper_threshold = np.max(srcobj.labelarr_orig)
        
     
    def visualize_graph(self):
        """ Visualize the graph with the 3D view  """
        from enthought.mayavi import mlab
        from enthought.tvtk.api import tvtk
        
        # precondition for the graph visualization
        if self.scene3d is None:
            self.scene3d = self._create3DScene()
            
        # alias for currently used scene
        fig = self.scene3d

        # get source object
        srcobj = self.datasourcemanager.get_sourceobject()

        # create node source
        #####
        self.nodesrc = mlab.pipeline.scalar_scatter(srcobj.positions[:,0], srcobj.positions[:,1], srcobj.positions[:,2],\
                                               figure = fig, name = 'Node Source')

        # adding scalar data arrays
        # create a sequence array to map the node ids to color
        from numpy import array
        ar = array(range(1,len(srcobj.nodeids)+1))
        
        self.nodesrc.mlab_source.dataset.point_data.add_array(ar)
        self.nodesrc.mlab_source.dataset.point_data.get_array(0).name = 'nodeids'

        self.nodesrc.mlab_source.dataset.point_data.add_array(srcobj.selected_nodes)
        self.nodesrc.mlab_source.dataset.point_data.get_array(1).name = 'selected_nodes'
        
        # setting the active attributes
        actsel1 = mlab.pipeline.set_active_attribute(self.nodesrc, point_scalars='nodeids', \
                                                     name="Colored Nodes")
        actsel2 = mlab.pipeline.set_active_attribute(self.nodesrc, point_scalars='selected_nodes', \
                                                     name="Selected Nodes")
                
        # create glyphs
        self.nodes = mlab.pipeline.glyph(actsel1, scale_factor=3.0, scale_mode='none',\
                              name = 'Nodes', mode='cube')
        self.nodes.glyph.color_mode = 'color_by_scalar'
        # FIXME:
        # interpolate setting to true makes the color mapping wrong on linux
        # otherwise on windows, if not set to true, the cubes stay dark (why?)
        from sys import platform
        if 'win32' in platform:
            self.nodes.actor.mapper.interpolate_scalars_before_mapping = True
        
        # if there exists a colormap, use it
        l = self.nodes.glyph.module.module_manager.scalar_lut_manager.lut
        # number of colors has to be greater equal to 2
        if not len(srcobj.nodeids) < 2:
            l.number_of_colors = len(srcobj.nodeids)
            l.build()
        
            # getting colors from source object
            l.table = srcobj.colors
        
        # create glyphs for selected nodes
        self.nodes_selected = mlab.pipeline.glyph(actsel2, scale_mode = 'scalar', \
                                                  opacity = 0.2, scale_factor = 7.0, \
                                                    name = 'Nodes', color = (0.92, 0.98, 0.27))
        self.nodes_selected.glyph.color_mode = 'no_coloring'
        self.nodes_selected.glyph.glyph.clamping = False
        self.nodes_selected.glyph.glyph_source.glyph_source.phi_resolution = 12
        self.nodes_selected.glyph.glyph_source.glyph_source.theta_resolution = 12
        self.nodes_selected.actor.mapper.interpolate_scalars_before_mapping = True
        
        # should I load the node labels?
        # this is too slow for many nodes
        if preference_manager.cviewerui.labelload:
            for index, label in enumerate(srcobj.labels):
                tmplabel = mlab.text(srcobj.positions[index,0], srcobj.positions[index,1], \
                                     label, z=srcobj.positions[index,2], \
                                     width=0.010*len(label), \
                                    color = (1,1,1), name='Node Label ' + label)
                tmplabel.property.shadow = True

        # split up what was achieved before by quiver3d
        # add a vector_scatter data source with scalars    
        self.vectorsrc = mlab.pipeline.vector_scatter(srcobj.start_positions[0], 
                             srcobj.start_positions[1],
                             srcobj.start_positions[2],
                             srcobj.vectors[0],
                             srcobj.vectors[1],
                             srcobj.vectors[2],
                             name = 'Connectivity Source',
                             figure = fig)
        
        lastkey = ''
        # add all the scalars we have (from the srcobj scalarsdict)
        # this should be an ordered dict!
        for key, value in srcobj.scalarsdict.items():
            
            da = tvtk.DoubleArray(name=str(key))
            da.from_array(srcobj.scalarsdict[key])
            
            self.vectorsrc.mlab_source.dataset.point_data.add_array(da)
            self.vectorsrc.mlab_source.dataset.point_data.scalars = da.to_array()
            self.vectorsrc.mlab_source.dataset.point_data.scalars.name = str(key)
            lastkey = str(key)
            
        # this is the winning time-consuming line, and can be ommited in further mayavi releases
        self.vectorsrc.outputs[0].update()
        
        # add a set active attribute filter to select the scalars
        self.attract = mlab.pipeline.set_active_attribute(self.vectorsrc, point_scalars=lastkey, \
                                                     name="Set Edge Attribute")
        
        self.thres = mlab.pipeline.threshold(self.attract, name="Thresholding")
        # to prevent vtkLookupTable: Bad table range
        self.thres.filter_type = 'cells'
        #self.thres.threshold_filter.all_scalars = False

        if srcobj.directed:
            
            self.vectors = mlab.pipeline.vectors(self.thres,\
                                            colormap='OrRd',
                                            figure=fig,
                                            name='Connections',
                                            mode='arrow',
                                            scale_factor=1,
                                            resolution=20,
                                            scale_mode = 'vector')
            
            self.vectors.glyph.glyph_source.glyph_source.shaft_radius = 0.015
            self.vectors.glyph.glyph_source.glyph_source.shaft_resolution = 20
            
            self.vectors.glyph.glyph_source.glyph_source.tip_radius = 0.04
            self.vectors.glyph.glyph_source.glyph_source.tip_length = 0.15
            self.vectors.glyph.glyph_source.glyph_source.tip_resolution = 20
            
            self.vectors.glyph.color_mode = 'color_by_scalar'
            self.vectors.glyph.glyph.clamping = False
            
        else:
            # then create a .vectors filter to display
            self.vectors = mlab.pipeline.vectors(self.thres,\
                                            colormap='OrRd',
                                            #mode='cylinder',
                                            figure=fig,
                                            name='Connections',
                                            #scale_factor=1,
                                            #resolution=20,
                                            # make the opacity of the actor depend on the scalar.
                                            #transparent=True,
                                            scale_mode = 'vector')

            self.vectors.glyph.glyph_source.glyph_source.glyph_type = 'dash'
            # vectors.glyph.glyph_source.glyph_source.radius = 0.01
            self.vectors.glyph.color_mode = 'color_by_scalar'
            self.vectors.glyph.glyph.clamping = False
          
    def update_node_scale_factor(self, scale_factor):
        """ Updates the scale factor for both the colored and the selected nodes """
        self.nodes.glyph.glyph.scale_factor = float(scale_factor)
        self.nodes_selected.glyph.glyph.scale_factor = float(scale_factor) * 2.33

    def update_graph_visualization(self):
        """ Updates the graph visualization, taking the information
        from the source object.  """
        import time
        
        # update the node position

        # disable rendering for speedup
        self.scene3d.scene.disable_render = True

        # set the correct scene for the update (scene3d)
        self.engine.current_scene = self.scene3d

        tso = self.datasourcemanager.get_sourceobject()
        
        # update the positions of the network nodes
        tic = time.time()
        self.nodesrc.mlab_source.set(x=tso.positions[:,0],\
                                             y=tso.positions[:,1],\
                                             z=tso.positions[:,2])
        self.nodesrc.mlab_source.update()
        toc = time.time()
        logger.debug('Node positions updated. Time: %s' % str(toc-tic))
        
        # update start_positions and vectors for the network
        tic = time.time()
        self.vectorsrc.mlab_source.set(x=tso.start_positions[0],\
            y=tso.start_positions[1],z=tso.start_positions[2],\
            u=tso.vectors[0],v=tso.vectors[1],w=tso.vectors[2])
        
        # update the rendering
        self.vectorsrc.mlab_source.update()
        toc = time.time()
        logger.debug('Connectivity source updated. Time: %s' % str(toc-tic))
        tic = time.time()
    
        # enable rendering again
        self.scene3d.scene.disable_render = False
                
        # update the node size and color
        # XXX
            
    def invoke_matrix_viewer(self):
        """ Invoke the Conectome Matrix Viewer """
        from cviewer.visualization.matrix.cmatrix_viewer import CMatrixViewer
        self.cmatrix_viewer = CMatrixViewer(self.network)
        self.cmatrix_viewer.edit_traits()