When I add a position property on a node, it is ignored unless I also specify locked: true. I would like to specify initial positions on some nodes, yet still allow the user to move those nodes around.
Related (I think) - is there a way to let Cytoscape use a layout to determine node positions, then do a second pass and change them for any nodes that have a position property specified? It would also work to have the layout ignore or not process any nodes that have positions specified. That's what currently happens when I have locked set to true, but I'd like for it to happen if I have locked set to false on a node.
Perhaps another solution would be, how can I manually alter the positions after the layout is finished rendering?
Don't run a layout on nodes you don't want moved. Note that you have a layout specified in the init options.
Use preset at init, and make your own explicit cy.layout() and/or eles.layout() calls. (The preset layout acts pretty much like a nop if all you specify is { name: 'preset' }.)
Or keep your existing workflow and just unlock the nodes when the layout is done.
Related
I have put nodes in a group. And I have put a func on the scene script to make changes to the nodes in group. In that func,I made the nodes to queue free and stuffs like that. But when I change scene and come back to the previous scene,the queued free is back again,it is not queued free anymore. How do I make it not change even after changing scene?
Why information is lost
When you change the current scene with change_scene or change_scene_to the current scene is unloaded, and the new one is loaded and instanced.
Perhaps it helps to conceptualize that the instance of the scene is not the same as the scene in storage. So what happens is like opening a file in an editor, modifying and not saving it.
Alright, that is one solution if you really want to go that route: you can save a scene.
First create a PackedScene, for example:
var packed_scene := PackedScene.new()
Then tell it to pack the root node of the scene, for example:
packed_scene.pack(self)
Note: any Nodes that don't have their owner property set to the Node you passed here will be ignored. And no, add_child does not set the owner property.
If you have been paying attention, you know you can give a PackedScene to change_scene_to... But how do you keep it around when the scene changes?
There are a few ways. And the thing is: if we can keep information around, we might not need to save the scene.
Keep things around in autoloads
The scripts and scene you add to your autoloads (singleton) in project settings stay there (unless you explicitly remove them) even when you change scene.
Thus, a very simple way to keep information around is to have a script with some variables that you can write and read from anywhere in your project. And you can do that with an autoload.
Keep things around in resources
Godot caches resources. When you load the same resource in multiple places, you actually get the same object.
Well, you know you can create a custom Resource class. To do that, on the context menu of the FileSystem panel select "New Script", and in the script make a class that extends Resource, and give it a class_name.
Then you can create resources of that Resource from the context menu of the FielSystem panel by selecting "New Resource" and picking your Resource class when asked.
Everywhere you load one of those, you are going to get the same object. Yes, even if it is in another scene. So you can add variables to your Resource class, write them in one scene and read them in another.
I explain a more concrete example elsewhere.
Keep things around in storage
You can write to a file and read from a file. For example you can do this:
# save
var file = File.new()
file.open("user://a.sav", File.WRITE)
file.store_pascal_string(var2str(data))
file.close()
# load
file.open("user://a.sav", File.READ)
data = str2var(file.get_pascal_string())
file.close()
Or, if what you want to store is a Resource (be it a PackedScene, or some other Resource class including a custom one), you can use ResourceSaver and ResourceLoader:
# save
ResourceSaver.save("user://a.tres", resource)
# load
resource = ResourceSaver.load("user://a.tres")
Of course, you can also load resources with load, or preload. You may also be interested in Background Loading.
By the way, if you are going to save player progress, having all the player progress data in a single object makes sense. And if you are going to have all the data you are keeping all the player progress data in a single place, it makes sense it would be somewhere it is accesible all the time and stays around even when you change scenes, thus: put the player progress data in an autoload. Bonus: you can put the functions to save and load to a file there too.
Don't change scenes
Since changing scenes brings problems - consider another solution: don't.
You can load an scene:
var packed_scene := load("res://scene.tscn") as PackedScene
Then make an instance of it:
var scene := packed_scene.instance()
Then add it to your current scene tree:
add_child(scene)
Yes, it is a Node! It also means you can…
scene.queue_free()
Or you can simply remove it with remove_child which does not free it, so you can add it back later.
So you would be in control of what gets loaded or unloaded and when. Which is useful to keep stuff around (e.g. the UI, the player character, etc...). A drawback of doing it the way I describe here is get_tree().current_scene would not be useful to you anymore. See also Change scenes manually.
Building UI in Godot 3.2.1. Of course I use anchors so UI elements are arranged within the screen automatically according to specified layout. I have UI scale system - nothing fancy - simply change font size (DynamicFont.size). If font size is large enough then some UI nodes may be pushed out of the screen. However, nodes don't return to normal sizes/positions with font size decreasing. The way to fix a mess is to resize game window which is not always an option and doesn't seem like a correct way to handle the issue. So how can I force Godot to recalculate control nodes size/position?
Changing the parent control's minimum size to Vector2(0, 0) after changing the font size might do the trick:
$Control.rect_min_size = Vector2(0, 0)
If it's already set to Vector2(0, 0), you may have to change it twice using call_deferred() for it to work.
In your scene tree, find the top level container that contains all of the elements that you want to recalculate. This would be the lowest common ancestor in the scene tree, in computer science terminology. Now, hide the container, by setting it's 'visible' property to false. Then, add a deferred call to change it's 'visible' property back to true.
var your_container = $".".find_node("your-container")
your_container.visible = false
your_container.call_deferred("set_visible", true)
This seems to cause Godot to recalculate the layout of 'your_container'.
It looks like only CanvasItem derived classes have a 'visible' property, so you would not be able to simply set the 'visible' property on a CanvasLayer, for example.
Fortunately, Containers and Controls both derive from CannvasItem, so this methodology should work fine if your lowest common ancestor node is either a Container or a Control or other CanvasItem derived class instance.
I got this working by emitting a signal from a parent element, which appears to force a refresh:
canvas_item.emit_signal("item_rect_changed")
The problem child got refreshed, and unlike the visibility method, focus was retained.
Let's say I have a button. In it's constructor I use variables to feed the values(before running I set some default values to these variables), so later they can be changed and within the program the look of the GUI can be modified. What I need is to update the widgets whenever I change these values.
For example I have some options to change a certain color, I press the button it calls a certain command defined in the constructor, changes the color variable and after that, it needs to be updated. Here it says the update() redraws widgets as needed. How do I tell it I need the widgets to be redrawn ?
http://effbot.org/tkinterbook/widget.htm
I might be mistaken on what the redrawing actually means. In any case I need it to update with the new values. I have a quite dumb solution for this, that is destroying everything and rebuilding it. I feel like there is a smarter way of doing things.
All widgets have a configure method which can be called to change any of its attributes. All you have to do is keep a reference to the widget(s), and call the method:
def update_the_widgets():
the_label.configure(background="red")
a_button = tk.Button(..., command=update_the_widgets)
the_label = tk.Label(..., background="green")
This is much easier if you use an object oriented style of coding. Otherwise these references need to be global variables.
When your GUI is properly coded you should almost never need to call update.
I'm developing a custom table component for very large and very custom content. Therefore I decided not to go with TableView, but start from scratch (i.e. Control). I adopted the idea of the VirtualFlow to create and manage only the visible part of the table and reuse cells that have become invisible. However I needed virtual rows and columns.
My CustomVirtualFlow implements layoutChildren(). From the component's size and scrollbar positions I know which cells are visible. If necessary, I add cells to the VirtualFlow. I then update the content and css pseudo class states (selected, focused, ...).
This works almost fine ... currently, my only problem is that the css styles are sometimes lagging: newly created cells are shown with e.g. wrong backgrounds for a moment and then get correcteted with the next pulse.
My explanation for this is that JavaFX performs layout in two passes:
first a css pass and secondly the layout pass that calls layoutChildren().
The css of newly added children (during layoutChildren) is therefore not processes correctly.
I tried to call applyCss() directly, which helps, but seems to do too much because it takes a lot of time.
My question is:
How is the correct way to add nodes during layout, i.e. if the size of the component makes it neccessary to use further nodes?
If it is not during layoutChildren(), where else should I do it?
Background/Context:
I have a HBox as a parent and many child Nodes (Buttons, TextFields, Labels…). The HBox is a child of other container (BorderPane/VBox/Grid)
My questions:
How do I dynamically remove/exclude the parent (HBox) and all its children from layout?
I am looking for some three-state property on Node (like in Microsoft WPF):
Visible – visible and participate in layout
Collapsed – is not visible and do not participate in layout (applies to its children as well)
Hidden – is not visible but participate in layout
http://msdn.microsoft.com/en-us/library/ms590101.aspx
What options does JavaFX offer?
My solutions so far:
hBox.setManaged(false);
this work only for HBox, its children are still present
root.getChildren().remove(hBoxTop);
root.getChildren().add(hBoxTop);
Well, this looks like it could work.., but for example in case of root being BorderPane, once I remove/add and remove the HBox, the space after it remains unused. I already tried requestLayout() but id does not force thr rrot to fill it. Am I missing something? Is it correct approach to this problem?
Edited:
Well, I got this working.
Dynamically removing and adding for this specific case can be achieved by:
Remove:
root.setTop(null);
Add:
root.setTop(hBoxTop);
When I did not call setTop(null) on removal, BorderPane still reserved space for HBox (even after removal from its children).
IMHO: it is not very good model as it is parent-container specific. For example if I change, BorderPane to VBox a I had to change these methods as well. For VBox, remove/add on children collection works but I need to remember index so that HBox appears at same place after calling add.
Using root.setTop(null) is not a good idea because as you say it's specific to BorderPane.
I think the best solution is to use:
root.getChildren().remove(yourPane);
And use layout methods to place your other childrens as you want.
But maybe you should not use a BorderPane in the first place with the behaviors you want in your application.
Notice you can also do root.getChildren().clear() to remove all component and add it again to your layout but differently.