URCap works as a single Node but not as a Child Node

Hi,

I am writing a Main-URCap that adds a Sub-URCap as a Child Node automatically when a button is pressed.
When I add the Main-URCap to the Program Tree and press the button the Sub-URCap gets added but when I click on it to configure it I get an error screen saying:

                                                          java.lang.IllegalStateException: No node currently selected.

This is interesting as the Sub-URCap works fine on its own when inserted directly into the Program Tree.
And if I first insert the Sub-URCap and configure it and then insert the Main-URCap as the next node in the Program Tree everything works perfectly. Once I configured a node of the Sub-URCap and even if I delete it without having run the program the Main-URCap works afterwards.

Can somebody help me fix this issue and explain why it doesn’t work when I do not configure a Sub-URCap node first but insert the Main-URCap only? The thing is that I only need the Sub-URCaps inside the Main-URCap.

Thanks,
MPonya

I’m also also adding Sub-URCaps and also Sub-Sub-URCaps but never faced this error. Can you show me your code ? :slight_smile:

These are the lines where I insert the Sub-URCap into the Main-URCap.

The Sub-URCap is called CustomScript.

The function createNodes() is called when the button is pressed to insert the Child Node (this is a snippet from the Main-URCap Contribution):

private void createNodes() {
	ProgramAPI programAPI = apiProvider.getProgramAPI();
	ProgramModel programModel = programAPI.getProgramModel();
	final ProgramNodeFactory programNodeFactory = programModel.getProgramNodeFactory();
	
	final TreeNode rootTreeNode = programModel.getRootTreeNode(this); 
	
	undoRedoManager.recordChanges(new UndoableChanges() {
		@Override
		public void executeChanges() {
			try {
				
				scriptTreeNode = rootTreeNode.addChild(programNodeFactory.createURCapProgramNode(CustomScriptProgramNodeService.class));
				
				setDefined(true);
				
			} catch (TreeStructureException e) {
				e.printStackTrace();
			}
		}
	});
	
}

If you wish I can also send the whole program for the Sub- and Main-URCaps.

Thank you for your help :slight_smile:

did you try to initialize

ProgramAPI programAPI = apiProvider.getProgramAPI();
ProgramModel programModel = programAPI.getProgramModel();
final ProgramNodeFactory programNodeFactory = programModel.getProgramNodeFactory();

in the Constructor?
I’m using HTML-based UI URCaps so i’m not yet familiar with the new swing-based Programming

I did, indeed. The issue must lie deeper down as it is actually working once the Sub-URCap has been called. But unfortunately I have no clue what causes this. :confused:

Is the Sub-URCap registered in the Activator?
And e.g. flagged with non-user insertable in the service.

Yes, it is registered in the activator and now I flagged it with the non-user insertable but it still keeps throwing the
IllegalStateException: No node currently selected.

I am having the same problem. The exception seems to be thrown when contributionProvider.get() is called in the view class. This only happens when this particular node is inserted automatically. If I expose it to the URCaps tab and insert it from there things go back to normal. Even automatically inserted nodes start functioning properly.

I think the problem is that you can’t call contributionProvider.get() during buildUI() or openView(). It’s too early in the node’s lifetime. You can use this method only after the node is created and the user starts interacting with it. During the node creation, you have to avoid calling contributionProvider.get().

@andrew.kittross
That is indeed correct.

This restriction is confusing because a lot of the samples provided by UR show calling contributionProvider.get() during buildUI. I have also run into problems calling contributionProvider.get() at later times, after the contribution class has been created. UR often throws the exception when get() is called:

java.lang.IllegalStateException: No node currently selected.
at com.ur.urcap.contribution.program.SwingProgramNodeViewProviderImpl$1.get(SwingProgramNodeViewProviderImpl.java:100)

It’s not clear how UR would think that no node is selected. I’ve selected a node with the GUI and the openView method has been called. I wonder if I’m doing something wrong or if there is a bug or change in behvior, maybe only introduced in 3.6.

I think I’ve finally fixed this issue. It seems like the problem has to do with the lifetime and multiplicity of the Contribution class relative to the View class. There is one View class instance and buildUI is only called once. There are multiple Contribution class instances (one for every node in the program of that type). Because of this, any caching of the Contribution class inside the View class is dangerous. In particular, the “provider” variable passed during buildUI cannot be used later. So when you create actionListener methods for GUI controls, you can’t code anything like provider.get().someFunction(). By the time the event handler is called, provider may no longer be valid. To get around this, I made all event handlers call methods in the View class itself. These event handlers don’t call provider, they call methods on a reference to the Contribution class itself. This reference is kept up to date by having the Contribution always pass a reference to itself to the view when openView is called, and clear this reference when closeView is called. Does this make sense? Have others used this pattern?

2 Likes

@andrew.kittross Your solution sounds very good! Could you please also give some code snippets to show how you create the reference on the Contribution class and call functions on it later as well as the way you update it from openView?
Many thanks in advance!

1 Like

This shouldn’t be necessary, and might not be necessary with later versions of Polyscope. Nonetheless, here’s the pattern I used:

In ViewClass:

field:

private ContributionClass contrib;

methods:

public void setContrib(ContributionClass contrib) {
	this.contrib = contrib;	
}

private void someGuiEventHandler(boolean value) {
	contrib.setSomeValue(value);
}

In ContributionClass:

public void openView() {
        view.setContrib(this);
        // call other methods to set up controls based on model
   }
   public void setSomeValue(boolean value) {
	// perform "business logic" and save to model
}
1 Like