Menu

How to get a node by ID in a script

Alexandre
2021-05-14
2021-08-03
  • Alexandre

    Alexandre - 2021-05-14

    Hi,

    I would like to know how we can get a node in a map by ID in a script (and then use that node found for something).

    I know it is possible to loop all nodes and compare the id but I wanted to know if there is a function that does that already. I know also there is a menu function that could be called to go to the node, but this will display the dialog and would not return the node.

    Thanks.

     
  • abc163

    abc163 - 2021-05-14

    I use this one:

    def locateNode = c.find { it.nodeID == "ID_xxxxxxxxxx" }

     
  • Edo Frohlich

    Edo Frohlich - 2021-05-14

    you can use N('ID_xxxxxxxxxx')

    like for example:

    c.select(N('ID_1049517982'))
    

    (N('ID_1049517982') is the same as: node.map.node('ID_1049517982') )

     
    • Alexandre

      Alexandre - 2021-05-14

      Thanks both. I didn't know these ways of getting nodes. Interesting.

      On Fri, 14 May 2021 at 19:47, Edo Frohlich edo_f@users.sourceforge.net
      wrote:

      you can use N('ID_xxxxxxxxxx')

      like for example:

      c.select(N('ID_1049517982'))

      (N('ID_1049517982') is the same as: node.map.node('ID_1049517982') )

      How to get a node by ID in a script
      https://sourceforge.net/p/freeplane/discussion/758437/thread/59e6b23916/?limit=25#2b31


      Sent from sourceforge.net because you indicated interest in
      https://sourceforge.net/p/freeplane/discussion/758437/

      To unsubscribe from further messages, please visit
      https://sourceforge.net/auth/subscriptions/

       
      • Jochen Kall

        Jochen Kall - 2021-05-15

        This is N('ID') notation is news to me, very cool.

        Btw, if you know the ID of your node while writing your script you can address it directly as a variable like this from within your script:

        ID_1049517982.text="something"
        

        I use that all the time for debugging ^^

         
  • Alexandre

    Alexandre - 2021-05-15

    Thanks all.

    With your help I could do this small script I wanted to do.

    What it does basically is that it allows to save and load a selection of nodes, and then maybe use an action on these selected nodes after, like the "Filter selected nodes" button to see only these nodes in the map, or to do some format on these etc.

    One would select 1 or multiple nodes anywhere in the map, then would do Edit > Copy > Copy node ID. And then he would paste the selected ids anywhere in the map and then do that again if needed for other nodes, and this way creating a branch of node ids. And then would run the script on the root of that branch and it would select all these nodes found in that branch.

    def selectNodesFromNodeIdsInBranch(pNode) {
        // Note: Do ctrl+alt+c to execute the Freeplane command "Edit > Copy > Copy node ID", to copy the node ids from nodes I would like to put in the tree. It is possible to select multiple nodes and hit the hotkey it will copy multiple nodes. 
        // Get the nodes from the current tree
            def selectedNodeIdsTmp = node.findAll().each {}
        // Get only the ids from the node in the current tree
            def selectedNodeIds = []
            selectedNodeIdsTmp.each { n ->
                if (n.plainText.contains(',')) { // This is when multiple nodes where selected and the copy node ids command (ctrl+alt+c) was actioned, then Freeplane copies multiple nodes as a comma separated list. 
                    def idList = n.plainText.split(',')
                    idList.each { id ->
                        selectedNodeIds.add(id.trim())
                    }
                } // if (n.plainText.contains(','))
                else
                    selectedNodeIds.add(n.plainText) // The node ids to select are the plain text (not the node ids of these nodes), they are data like if these node ids to select were in a file, but here they are in a tree. 
            } // selectedNodeIdsTmp.each { n ->
        // Get the nodes from the ids in the current tree
            def nodes = c.find{ it.id in selectedNodeIds }
        // Select these nodes
            c.selectMultipleNodes(nodes)
    } // def loadFromMapTreeAndSelectNodes()
    
    selectNodesFromNodeIdsInBranch(node)
    

    I needed this for a specific task I do.

    Here's a screenshot of my tree of node ids just to show.
    https://ibb.co/pj6Rtdy

    Thanks.

     

    Last edit: Alexandre 2021-05-15
    • Alexandre

      Alexandre - 2021-05-15

      Btw I defined some keyboard mappings to help copying node ids, selecting the nodes, filtering the selected nodes, removing selection, showing ancestors and descendants.

      Use shift+alt+z to copy to clipboard the node ids of the selected nodes.
      Use shift+alt+x on a branch of node ids to select these nodes.

      In combination with these 2 mappings, use also shift+alt+c to filter only the selected nodes and shift+alt+v to remove filtering. This allows to do in steps: Create tree of node ids (with copy), select the nodes, filter them, and then when finished with them, remove filtering.

      There is also shift+alt+b that toggles show ancestors (of selected nodes) and shift+alt+n that toggles show descendants (of selected nodes), these are useful when the nodes are selected to see before them and after.

      NOTE that all these mappings are on the same row on the keyboard, z, x, c, v, b, n, so that it is easy to remember.

       
  • Jochen Kall

    Jochen Kall - 2021-05-15

    You did it, nice!

    Seeing the code I thought, maybe it can be done in a bit more groovy compact fashion, so not to show off or anything, here is a one liner version :)

    c.select(node.findAll().collect{it.plainText.split(',')}.flatten().collect{N(it.trim())}-null)
    

    Happy coding
    Jochen

     
    • Alexandre

      Alexandre - 2021-05-15

      Nice! I don't even know what the flatten() function does.

      Maybe my code seems long because I put a lot of comments. These are the lings without the comments and the function (which is not needed really):

      def selectedNodeIdsTmp = node.findAll().each {}
      def selectedNodeIds = []
      selectedNodeIdsTmp.each { n -> 
          if (n.plainText.contains(',')) { 
              def idList = n.plainText.split(',')
              idList.each { id -> selectedNodeIds.add(id.trim()) }
          }
          else
              selectedNodeIds.add(n.plainText)
      }
      def nodes = c.find{ it.id in selectedNodeIds }
      c.selectMultipleNodes(nodes)
      

      Or this ;)

      def selectedNodeIdsTmp = node.findAll().each {}; def selectedNodeIds = []; selectedNodeIdsTmp.each { n -> if (n.plainText.contains(',')) { def idList = n.plainText.split(','); idList.each { id -> selectedNodeIds.add(id.trim()) } } else selectedNodeIds.add(n.plainText) }; def nodes = c.find{ it.id in selectedNodeIds }; c.selectMultipleNodes(nodes)
      

      I would like to modify the script to be able to select the nodes on another map than the current. The branch or tree of nodes could be on one map, and if its root node would contain the name of a map it would then select the nodes on that map, and not on the current map. But I don't know yet how to do it. If you guys have any idea. I know how loop other maps and select the root node of another map:

      c.getOpenMaps().each { map ->
          ...
           map.rootNode.findAll().each { n ->
          ...
      

      But I don't know how to get the controller "c" (c.something) on another map. How to use for example:
      c.select() or c.selectMultipleNodes(), or do c.find{} on another map than the current one? Is that possible or I would always need to put the tree/branch of node ids on the same map where the nodes to select are located?

      Thanks.

       
      • Alexandre

        Alexandre - 2021-05-16

        With my Temp map being selected, I tried this to select a node on the Temp2 map, thinking it may work but it fails with this error: https://ibb.co/WyL3hVf

        c.getOpenMaps().each { map ->
            if (map.name == 'Temp2') {
                c.select(N('ID_1917842608'))
            }
        }
        

        I tried this too, same error:

        c.mapLoader('c:\\mypath\\Temp2.mm')
        c.select(N('ID_1917842608'))
        

        Any ideas?

         

        Last edit: Alexandre 2021-05-16
        • Alexandre

          Alexandre - 2021-05-16

          I've search the API and the Freeplane source code.. I couldn't find how to get the controller for a map or how to switch the controller to the controller of another map, or to set the current controller to select nodes on another map.

          What I will do then is I will create a branch "NodeSelections" on each map that needs to keep selections, and I will link that section to other maps if needed.

           
          • Dimitry Polivaev

            You can access any map using c.mapLoader(file or url).mindMap.

             
        • Edo Frohlich

          Edo Frohlich - 2021-05-16

          As said before, N() is the shortcut for map.node(). Try to use that. This
          way you indicate which map is the node from.

          I don’t know if you can select a node from a map that is not the active
          one. Maybe you have to activate it first

          make a test changing something to the node in other map, for example the
          details, so you can see if the problem is getting to the node or selecting
          it.

          BR
          edo

           
          • Jochen Kall

            Jochen Kall - 2021-05-16

            Hi Alexandre,

            once you obtained the nodes from the IDs as described above, using c.select() fails with nullpointer exception, if the map the nodes belong to is not active, so Edos hunch was right.
            I don't know how to activate a map via script though.

            Just modifying nodes in non active maps works fine, like this code for instance changes the root node text for all open maps.

            c.getOpenMaps().each{it.root.text='bla'}
            
             
            • Alexandre

              Alexandre - 2021-05-16

              Hi all,

              It may not be possible to select nodes on another map or activate a map
              through a script.

              Maybe this would require code change in Freeplane.

              For now I think I'll do fine with having the branch of node ids in the same
              map.

              Many thanks!

              On Sun, May 16, 2021, 10:28 PM Jochen Kall jok@users.sourceforge.net
              wrote:

              Hi Alexandre,

              once you obtained the nodes from the IDs as described above, using
              c.select() fails with nullpointer exception, if the map the nodes belong to
              is not active, so Edos hunch was right.
              I don't know how to activate a map via script though.

              Just modifying nodes in non active maps works fine, like this code for
              instance changes the root node text for all open maps.

              c.getOpenMaps().each{it.root.text='bla'}


              How to get a node by ID in a script
              https://sourceforge.net/p/freeplane/discussion/758437/thread/59e6b23916/?limit=25#b08e/9d76/639c/b44c/20e4


              Sent from sourceforge.net because you indicated interest in
              https://sourceforge.net/p/freeplane/discussion/758437/

              To unsubscribe from further messages, please visit
              https://sourceforge.net/auth/subscriptions/

               
              • abc163

                abc163 - 2021-05-17

                You can use the following to activate another map:

                Runtime.getRuntime().exec("cmd /c start 'FilePath'")

                In the meantime open a popup to do the selection. In this way it needs two clicks to do the job.

                 

                Last edit: abc163 2021-05-17
                • Alexandre

                  Alexandre - 2021-05-17

                  Hi abc163,

                  So this is like running only the map in the command prompt? When I do
                  it it doesn't active the other map. Also if Freeplane is configure to
                  open another instance it would open it in another instance.

                  Alexandre

                  On 17/05/2021, abc163 abc163@users.sourceforge.net wrote:

                  You can use the following to activate another map:

                  Runtime.getRuntime().exec("cmd /c start 'FilePath'")

                  In the meantime open a popup to do the selection. So it needs two clicks to
                  do the job.


                  How to get a node by ID in a
                  script


                  Sent from sourceforge.net because you indicated interest in
                  https://sourceforge.net/p/freeplane/discussion/758437/

                  To unsubscribe from further messages, please visit
                  https://sourceforge.net/auth/subscriptions/

                   
                  • abc163

                    abc163 - 2021-05-17

                    It is not running in the command prompt. You can also use this one to open the map, both work well under my win 10:

                    "\"C:/Program Files/Freeplane/freeplane.exe\" 'filePath'".execute()

                    You are right. It needs to set freeplane to open files in a running instance. The popup does not act on other instances.

                     
  • peter

    peter - 2021-07-30

    I just have learned how to access mindmap and find nodes, here is my demo:

    def file = "D:\\xxx\\xxx.mm";
    def id = "ID_xxxxxx";
    def mm = c.mapLoader(file).mindMap;
    def n = mm.getRoot().find{it.id == id}.get(0);
    ui.informationMessage(mm.getFile().toString() + ":" + n.text);
    
     

    Last edit: peter 2021-07-30