Open matthiaskoenig opened 1 month ago
Here the solution. The trick is to first set a bypass for the positions and then clear the bypass afterwards. The positions remain.
p4c.set_node_position_bypass("Gd-EOB-DTPA (liver)", new_x_locations=200, new_y_locations=200, network=1017)
p4c.clear_node_property_bypass(["Gd-EOB-DTPA (liver)"], 'NODE_Y_LOCATION')
p4c.clear_node_property_bypass(["Gd-EOB-DTPA (liver)"], 'NODE_X_LOCATION')
This only works when be executed separately. But not all together in a script. I.e. first the nodes bypass has to be set afterwards in a separate call the bypass must be removed.
Is there any way to set the node positions? E.g. to force an update or letting p4c know to apply everything before executing the next command?
p4c.set_node_position_bypass("Gd-EOB-DTPA (liver)", new_x_locations=200, new_y_locations=200, network=1017)
SOME_MAGIC_COMMAND
p4c.clear_node_property_bypass(["Gd-EOB-DTPA (liver)"], 'NODE_Y_LOCATION')
p4c.clear_node_property_bypass(["Gd-EOB-DTPA (liver)"], 'NODE_X_LOCATION')
Hi, Matthias ... I saw this but don't have time to give a proper answer. I'll be back later today.
Meanwhile, there's an example of what I think you're trying to do in the set_node_position_bypass() test suite in the test_style_bypasses.py file.
Does this help??
Thanks. Unfortunately, not helping. I can set the positions via the bypass, the problem is that
For now I just manually remove the bypass in the UI after setting the positions programmatically. This works compared to trying to do it programmatically.
Feels just strange that there is no simple way to set the positions of nodes.
This is interesting. I often use positional bypasses, but in my use case I want them to be fixed, so it works fine.
If I understand correctly, you can set the bypasses and then later remove them to get the effect you want. HOWEVER, if you run the "remove" commands immediately after the "set" commands in a script, then the positions you wanted are lost.
Sounds like one of the scenarios where a "delay" is needed... we'll look into this further. Thanks for reporting.
It sounds like @AlexanderPico is on to something ...
Note that py4cytoscape is a facade for CyREST, which then passes requests into Cytoscape itself. This is the same for RCy3.
So, what you're experiencing is Cytoscape itself, which means the approach to this would be to influence Cytoscape's internal command execution queuing. Alex' idea about a delay would be to approximate what Cytoscape would be doing when serving a human user, which operates slowly enough for Cytoscape's queues to easily keep up.
An interesting experiment would be to put in a 10 second delay after setting the bypass, and then see the behavior. If that's right, the delay could reasonably be pared down to 1 second.
Thanks for the input. I will try that.
I also have the feeling that things behave strange via py4cytoscape probably due to the queue. E.g.
This is all a bit problematic, because I want to render images for videos programatically. If sometimes things are not applied/redrawn via the API the video frame is broken. I look for something like a "redrawView" which rerenders everything currently in the view. This would allow me to flush all the view changes somehow (annotations, layout, style changes, ...).
Here an example for the annotations not being shown correctly.
Before manual zoom
After manual zoom
Hi, Matthias --
I'd start by assuming @AlexanderPico advise is along the right track ... this sounds like too many requests being pushed to Cytoscape too quickly. To be clear, this would not be the fault of the code, but more like a Cytoscape issue. Here's the history.
While I was developing py4cytoscape, I noticed bizarre problems that disappeared if I would single step through my test code and/or py4cytoscape code. The Cytoscape developers advised that some operations would return CyREST results even while the operations executed in Swing and AWT queues ... and that these queues empty at their own pace and can't be waited on.
(This has never been a Cytoscape problem previous to Cytoscape automation (py4cytoscape and RCy3) because users with mice/keyboards are slow anyway. The Cytoscape developers made numerous changes to minimize Cytoscape/CyREST returning before an operation is completely executed, but there were limits to what they could guarantee.)
If that's so, there isn't a good way to guarantee that when a py4cytoscape operation completes, the Cytoscape operations would be complete, too. Piling one operation after the other could have obvious consistency issues.
So, we created tuning parameters within py4cytoscape and RCy3 (e.g., py4cytoscape_tuning.py) that would delay common functions so that reasonable-sized networks on reasonably quick hardware would finish before py4cytoscape/RCy3 returned to its caller. An example of this is in set_visual_property_default() in style_defaults.py ... at the end of the function, there's a sleep()
that shows this.
Functions that set these delays (set_catchup_filter_secs()
, set_catchup_network_secs()
, and set_model_propagation_secs()
) are exposed in the py4cytoscape interface, but aren't documented.
So for large networks or slow Cytoscape servers, it's possible to adjust these delays. Looking at py4cytoscape_tuning.py
, you can see undocumented functions that do this:
set_catchup_filter_secs()
# How long to sleep during node/edge filter executions
set_catchup_network_secs()
# How long to sleep between network operation retries
set_model_propagation_secs()
# How long to sleep between changing table values or styles
You can see that it's also possible to set the delays in the system environment:
PY4CYTOSCAPE_CATCHUP_FILTER_SECS
default '0'
PY4CYTOSCAPE_MODEL_PROPAGATION_SECS
default '2'
PY4CYTOSCAPE_CATCHUP_NETWORK_SECS
default '4'
PY4CYTOSCAPE_CATCHUP_NETWORK_TIMEOUT_SECS
default '60'
PY4CYTOSCAPE_CATCHUP_NETWORK_MERGE_SECS
default '1'
You can see how these delays work in py4cytoscape by searching the py4cytoscape source (manually) for sleep()
calls using:
CATCHUP_FILTER_SECS
MODEL_PROPAGATION_SECS
CATCHUP_NETWORK_SECS
# How long to sleep between network operation retries
CATCHUP_NETWORK_TIMEOUT_SECS
# How long to keep retrying network operation
CATCHUP_NETWORK_MERGE_SECS
# How long to sleep waiting for merge to complete Network table
You can see whether delays would be helpful by stepping through your python code manually (thereby creating long delays yourself) and seeing whether you get the results you're hoping for. If so, you have a choice of adjusting the above delays or inserting your own delays in your own code.
Note that sometimes a minimal delay (e.g., 0 seconds) is enough to allow Cytoscape to catch its breath. It all depends on the size of the network and the speed of the Cytoscape server.
Does this help??
Thanks. This is great advice but bad news at the same time.
The single steps work in my workflows, I just have to set the right sleeping times. Unfortunately, the code will be applied from networks of a few nodes to up to a thousand nodes. Finding the right delays will be challenging without having to wait for operations to long.
I know there is nothing to do (but is a design flaw in the automation). But basically the task execution engine should just fire an event when it is finished which can be picked up by cyrest and send back so clients know when it is save to send the next command. An automation engine which only reliably executes a single command is not very useful, and having to worry about raise conditions all the time puts unnecessary burden on developers.
Hi, Mattias ...
I'm cc'ing @AlexanderPico on this, as it affects RCy3, too.
It sounds like you're most of the way there. The points you raise about a deterministic API being important for scaling to different networks are completely valid. I'm frustrated by this, too.
In your position, I would expect success by finding the exact point at which the delay is necessary, and then inserting a delay there. When I have had to do that during test development, I set a breakpoint somewhere in the workflow and then see if execution to that point is reliable. If not, set a breakpoint earlier and try again. At some point, it becomes clear where the problem is occurring (and where it's not), and then go ahead and set a significant (but not detrimental) delay (maybe a few seconds) there. If the problem is occurring with code already addressed by the py4cytoscape_tuning delays, maybe just call one of the py4cytoscape_tuning functions to increase the delay. You can even choose a delay that matches your network size (i.e., node count, edge count, and/or table columns), but if the problem exists in only one (or a few) areas, a fixed delay should solve the problem without slowing down the whole workflow.
While this seems like a very sad state for an automation API, believe it or not, this is the first time it's been a problem. Still, I'm sorry for the extra trouble.
Meanwhile, if you can find where the problem is occurring, there's a chance we can direct this to the Cytoscape developers to solve the problem as you're suggesting. They've been very responsive, but they can't anticipate problems because Cytoscape code wasn't designed for this and there's a lot of legacy code.
And if the problem can be solved by inserting a delay into py4cytoscape code, that fix can happen much more quickly.
This is indeed the only solution I can conceive of as well. Layering automation onto a Java application designed over 20 years ago comes with flaws like this. The introduction of delays resolved all the cases we've come across so far. You could also scale the sleep time based on node count if that is a concern.
How can I set the x and y position of nodes? Unfortunately, I cannot find a solution for this basic operation.
I saw the discussion in https://github.com/cytoscape/py4cytoscape/issues/109, but the solutions via the bypass fix the respective node positions. I.e. the following functions set the positions, but have the side effect of freezing the positions.
I have to be able to change the positions of the nodes afterwards.