How to collapse a folder by code?

Hi everyone!!
Order all new nodes maked by a process is wonderfull !!!.. Every thing in a human comprensive tree of elements… But I don´t find the way to collapse by code all made folders… Is it possible?

Thanks in advance!!!

You can use SetItemExpanded method of the subject hierarchy node.

1 Like

It also works for collapsing segmentations on Data Tree. Some code to copy/paste:

shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)

sceneItemID = shNode.GetSceneItemID()
folderID = shNode.GetItemChildWithName(sceneItemID,"folder_name")
shNode.SetItemExpanded(folderID , False)

SegmentationNode = slicer.mrmlScene.GetFirstNodeByName("segmentationNode_name")
segmentationNodeID = shNode.GetItemByDataNode(SegmentationNode)
shNode.SetItemExpanded(segmentationNodeID , False)

Wonderfull!

Thanks a lot!

1 Like

A bit more complex but in the same line…
Is it possible to reorder items position in the tree? I mean, to put simillar folders one over the other, not following the creation orden…

Thanks in advance!!

You can reorder items using the MoveItem method.

1 Like

I have some problem with MoveItem.
I´ve check both itemIDs and they are right but they don´t move with the function. There is no error message either… I´ve aldo tried to invert items order, but anything happends.
I don´t know what i´m doing wrong.

Thanks

C++ methods cannot log error on the console, so check out the application log for errors or warnings. If there are no messages or you cannot figure out what goes wrong then share your scene (save as .mrb file, upload some, and copy the link here) and the script that does not work as you expect.

There is no error message in slicer log either.

I´ve made a simple example scene with an empty segmentation and 2 folders:

shNode = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
sceneItemID = shNode.GetSceneItemID()
folderItemID = shNode.GetItemChildWithName(sceneItemID,'NewFolder')
otherFolderItemID = shNode.GetItemChildWithName(sceneItemID,'OtherFolder')
segmentationItemID = shNode.GetItemChildWithName(sceneItemID,'Segmentation')

I tried:

shNode.MoveItem(otherFolderItemID,segmentationItemID)
shNode.MoveItem(folderItemID,segmentationItemID)

Both send me a “True” but there is no change in Subject Hierarchy order of items…

I´m sure I´m doing something wrong but I can´t find it out.

Thanks and sorry…

I was able to reproduce this update issue. A workaround is to force an update by temporarily switching to scene import state:

slicer.mrmlScene.StartState(slicer.vtkMRMLScene.ImportState)
slicer.mrmlScene.EndState(slicer.vtkMRMLScene.ImportState)

I’ve filed a bug report here:

1 Like

Any solution in the next future?

Hi!

I reached this post because I want to create a folder, put many nodes inside it, and then collapse it using a pyhton script run by prompt with --python-script.

I’m using the code below (from this discussion) to collapse it

but it doesn’t do anything. However when the script ended, if I type the same code in the python interactor it works. Could it be a problem related to the way I use the script? Otherwise, am I missing any lines?

Thanks in advance!!

Hmm maybe it will work if you connect your code to the slicer.util.mainWindow().initialWindowShown signal? I don’t know if the --python-script option already waits for this signal to execute your script. Or maybe you could connect to a qt.QTimer.singleShot with 0 or very small timeout in order to allow other events to process that might be expanding the folder at startup.

I recently ran into a situation where I wanted my script to make some changes to widgets at startup, but it had to happen after everything was sufficiently loaded that it wouldn’t revert my change. Here are the two lines that ended up working for my project:

I’ve been using .SetItemExpanded() and it works both in script and in the console, except for the Scene folder.
I tested the following code, both in a script and directly in the Python console:

folder_tree = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
scene_id = folder_tree.GetSceneItemID()
folder_tree.SetItemExpanded(scene_id, True)

Runing the code, folder_tree.GetItemExpanded(scene_id) will return True, but no change will occur in the data widget, the Scene folder will remain collapsed. Using a children folder ID instead of scene_id will work, imediatelly expanding or collapsing the correct folder.

I have also tried qt.QTimer.singleShot(0, lambda : folder_tree.SetItemExpanded(scene_id, True)) following Ebrahim’s comment.

Is there any solution for this situation?

The problem is that the scene does not have an item (thus it is not a folder either), so the SH model does not know about the collapsed state of the scene.

My only idea at the moment is trying shTreeView.expandToDepth(0). Or a higher number, depending on what you want. If you need to conserve the expanded states of specific items then it may require some extra work.

Update: Maybe we could make qMRMLSubjectHierarchyModel::subjectHierarchySceneIndex callable from Python (Q_INVOKABLE) and call shTreeView.expand(sceneItemModelIndex).

Unfortunatelly, we do need to conserve the expanded states. We even removed the multiple shTreeView.expandToDepth(4) in the code to avoid losing them. It is much better now, since after manually expanding the Scene, our folder pattern is displayed, so it is just a minor inconvenience currently, but on I believe we must fix. EDIT: I tried to programatically expand and collapsed the scene folder in the official 5.3 Slicer too, with the same results.

Ok, so I haven’t worked much with the Slicer source code, but what I understood is that we should include a function like QModelIndex qMRMLSortFilterSubjectHierarchyProxyModel::subjectHierarchySceneIndex()const in qMRMLSubjectHierarchyModel and then use it in the Python console with a new shTreeView.expand(sceneItemModelIndex) function.

I have two questions:
One: Won’t the subjectHierarchySceneIndex be the same as folder_tree.GetSceneItemID()
Second: shTreeView.expand(sceneItemModelIndex) should be different from SetExpanded ?

No. One is an SH ID, the other a Qt Model Index

These are two different things, yes. One sets it by SH ID (that the scene doesn’t have), the other by QModelIndex (that the scene has). Hence the whole thing I suggested

1 Like

I found a workaround, if anyone else faces the same problem. Using the qMRMLSubjectHierarchyTreeView instance instead of vtkMRMLSubjectHierarchyNode to expand the folder worked, using the same scene Item ID.

This is the code I used:

        folder_tree = slicer.vtkMRMLSubjectHierarchyNode.GetSubjectHierarchyNode(slicer.mrmlScene)
        scene_id = folder_tree.GetSceneItemID()
        dataWidget = slicer.modules.data.widgetRepresentation()
        shTreeView = slicer.util.findChild(dataWidget, name="SubjectHierarchyTreeView")
        shTreeView.expandItem(scene_id)

Can also be used to collapse the folder with shTreeView.collapseItem(scene_id)