How do I invoke a function every time the Script is loaded or reloaded?
tool
func _reload():
print("Changes have been made and saved! Script has been reloaded")
func _load():
print("Project was just opened! Script has been loaded")
I'm not sure what you mean by reloaded (or why it is an issue), but it probably is one of these:
When you add a Node to the scene tree (or when you enable a plugin, which is adding the EditorPlugin to scene tree of the editor), or when it is loaded (all plugins are loaded when you load the project), the code in _enter_tree will run. Similarly when it is removed (or when the plugin is disabled), the code in _exit_tree will run. Be aware that for tool scripts that you run manually (EditorScript), these don't work. So make a Node (or an EditorPlugin).
There is a signal that will notify when the script of an object changed. It is appropriately named "script_changed". So if you want to handle the situation when the tool script was modified, and thus reloaded, you could connect to that signal. You may also want to take advantage of the of _init, which is the first virtual method that Godot calls (on Nodes you can also use _enter_tree and _ready). The signal "script_changed" is emitted before _init is called in the new script.
If you want to handle when properties of a Node are modified (I'm including this since you mention "changed has been made"), you would have to use setters (with setget), or you could intercept the properties in _set.
Since you mention "Project was just opened", I think you want to make an EditorPlugin, and then you can use _enter_tree and _exit_tree.
I haven't found a way to get a notification when the currently edited scene on the editor is saved. However, saving the scene does not mean tool script are loaded or reloaded in anyway.
Related
I have a sample.png file which is being changed outside godot
and after it's modified, godot recives a signal and when that signal is received
I want that specific sample.png file to be reimported
I tried this answer but I want to reimport in my script itself not create a plugin for it
(atleast that's what I'm assuming it does)
I also tried this from the documents but I'm not sure how to use it exactly
EditorFileSystem.update_file("res://Assets/sample.png")
so how do I achieve the desired result?
The class EditorFileSystem is intended for plugins.
You would call get_editor_interface() from an EditorPlugin (which would be where you would be writing code if you were making a plugin). And that gives you an EditorInterface object, on which you can call get_resource_filesystem() which gives you an EditorFileSystem object.
So the intended use is something like this:
extends EditorPlugin
func example() -> void:
var editor_file_system := get_editor_interface().get_resource_filesystem()
editor_file_system.scan_sources()
# editor_file_system.update_file("res://icon.png")
By the way, EditorInterface also has a filesystem_changed signal. Although I don't know how reliable it is.
Usually you don't have to do that. When you restore the Godot window, it will scan for changes in the project folder. So you might minimize Godot while you are working on something else and when you bring the Godot window back it will pick on the changes.
In practice, the only situations when I had to use scan or scan_sources was when I had a tool script that write a resource file which should be imported, and I wanted it to reflect right away.
Instead of making a custom plugin, I'll remind you that form a tool script (as long as it is running in the editor) you can simply create an EditorPlugin object. For example:
var ep = EditorPlugin.new()
ep.get_editor_interface().get_resource_filesystem().scan()
ep.free()
I had also shared this example in another answer I wrote for you a while back here, it is under the title "About saving resources from tool scripts".
Node World contains two children- Pe2node and HUD.
Pe2node (Node2D) node has attached pe2.gd script and it has variable - shift.
HUD node (CanvasLayer) has attached HUD.gd script and I want display variable shift from Pe2node. I try some variants, but it don't work at all. I have ready to try autoload but may be there is simple way to get it work.
HUD.gd:
extends CanvasLayer
var fps=0
var shift_hud=0
func _process(delta):
fps = Engine.get_frames_per_second()
$var_fps.text=str(fps)
#shift_hud=get_node("Pe2node").get_variable("shift")
#shift_hud=get_node("Pe2node").shift
#shift_hud=get_node("World/Pe2node").shift
#shift_hud=$World/Pe2node.shift
$var_shift.text=str(shift_hud)
Node World contain 2 children - Pe2node and HUD
This means that Pe2node and HUD are siblings, right?
Let us go over you attempts:
get_node("Pe2node")
This is looking for a "Pe2node" child of the current node, not a sibling.
get_node("World/Pe2node")
And now you are looking for a "Pe2node" child of "World" child of the current node.
$World/Pe2node
Same as above.
You can either go one level up, like this:
get_node("../Pe2node")
Or like this:
$"../Pe2node"
Or you could have an absolute path, that I will not put example of, but if you print the result of calling get_path on the Node you want, it will show the absolute path (it starts with "/root/").
The reason why I often do not advice doing it the way I describe above is because it depends on the relative position of these nodes, which you might not move together, breaking the code.
A better way to do it - without going for the autoload solution - is to export a NodePath:
export var node_path:NodePath
Which you can set to the node you want in the inspector panel. And then we use it to get the node:
get_node(node_path)
This will work as long as the things you are connecting are in the scene tree from the start (you are not trying to get something instanced dynamically). And as long as that is the case, and you only manipulate the scene from the Godot interface, Godot can keep the NodePath updated.
As per the autoload solution, I would suggest to put signals there instead. You can have an autoload with just a signal like this:
extends Node
signal shift_changed(value)
That would be the whole code of the autoload.
You can create that script and then add it as an autoload in the project settings, with some name. I'll be using the name SingalBus.
And then from anywhere emit the signal:
SingalBus.emit_signal("shift_changed", value)
And from anywhere connect to it:
SingalBus.connect("shift_changed", self, "_on_shift_changed")
Which assumes a method something like this:
func _on_shift_changed(value) -> void:
pass
Which means that you no longer need any kind of node path at all, so there is no path that could break. Plus you can update the UI only when the value changes, instead of having it checking all the time. And there might not be something at the other side, so this works for things that are instanced dynamically too.
To be fair, you could put the variables on the autoload instead of the signals. That also works. And in some cases it is useful to put a reference to a Node there.
The advantage of placing the variables there is that it also gives you a single place to place them all, which might also be useful for a save/load feature, and also to keep them around when going from one scene to another.
Somehow I get a problem, if I call an application twice or several times but only one instance should be running (which is desired).
First some (probably necessary) background information:
working on MAC OS X El Capitan (10.11.6)
I've got an Application made by node.js, electron and build by "npm build -m" (let's call it APP.app)
I installed the application App.app into program-folder by opening the built dmg-file and move it into the program-folder
Then I start the application App.app by click on the App.app-icon in program-folder
The application starts with a visible Window and also has an hidden background process running
If I close the visible Window, the App.app-Icon remains in the dock (which is okay, since the background-process is still running)
Now (and this is the difference to the windows build and running in windows), if I click again on the App.app-Icon in the program-folder, I only get a focus on the already running application INSTEAD of opening the window and close the old application (I can see te focus switch/activation by the switching menu name beside the apple-logo on top of the screen; it becomes "App")
the closing of the previous running instance is defined in electron code in the init-method as follows:
var shouldQuit = app.makeSingleInstance(function (commandLine, workingDirectory) {
console.info('starting new instance');
initInternal(commandLine);
// Someone tried to run a second instance, we should focus our window.
var windows = options.closeWindowsOnRestart ? Browser.getAllWindows() : appWindows.slice();
windows.forEach(function (val, index) {
val.close();
});
readyCallback();
});
So, I did some homework and could figure out following:
my App.app-icon-click should look something like open /Applications/App.app
with this, I get the same problem as descriped above if I call it again (it only focusses the already opened window)
Now comes the funny part. If I open the application by calling directly /Applications/App.app/content/MacOS/App.app the old application-instance is closed and new App.app-application is started
I read through open-manual and could figure out, that if I use the -n-flag, the application starts succesful a new instance, too. (open /Applications/App.app -n)
I was wondering WHY? Do you have any clues? What is the difference between open and directly call of the application?
I suggest that the info.pklist in App.app-package is making the open-call different to the direct call of App.app.
BTW:
Unfortunately I already tried to add try-catch-blocks to debug the problem, but open does not give an output to terminal, it just opens the call whereas the direct call does not throw an error and everything works fine.
Anyway, I believe it's more a MAC OS X problem than an App.app-problem.
Hopefully there's somebody with same problems and a solution for me
Don't hesitate to ask for more details, if needed.
Short Version:
open starts the application with the LaunchService (and possible
added launch-parameters in info.plist)
whereas direct call of the
application just starts the application iself without any other
launch-options
*Long Version *
I read through the open manual (man open; Source: https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man1/open.1.html) and could figure out, that "the default application as determined via LaunchServices is used to open the specified files" if you click on an item.
So I read through the LaunchService-documentation and could figure out following:
"Opening Items by File-System Reference [...] default way:
If the designated item is an application: [...]
If the application is already running, it is activated (brought to the front of the screen) and sent an 'rapp' (“reopen
application”) Apple event."
(Source: https://developer.apple.com/library/content/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCTasks/LSCTasks.html#//apple_ref/doc/uid/TP30000999-CH203-TP9)
This reflects exactly my watching that if I click on the icon the second time, the app is just focussed/activated.
Thus I need the possibility to tell the application to open a new instance (oapp-event) instead of activating the already opened application (rapp-event)
Further reading lead me to following informations:
"Launch Options When opening an application (whether by itself or to
open one or more documents or URLs), you can specify various launch
options to control the manner in which it is launched or activated.
These can include: [...] Whether to launch a new instance of the
application, even if another instance is already running"
(Source: https://developer.apple.com/library/content/documentation/Carbon/Conceptual/LaunchServicesConcepts/LSCConcepts/LSCConcepts.html#//apple_ref/doc/uid/TP30000999-CH202-BABBJHID)
Thus I only need to add the "launch option" to define, that a new instance should be created instead of activating the existing one. But there's not written what the launch-option for it is and how they will be applied to the application (I suggest it belongs into info.plist-file).
So at least this is the answer to my original question, so I posted it here.
open - starts the application with the Launch-Service (and the defined option-parameters in info.plist)
whereas direct call of the application just starts the application iself without any other launch-options
What I mean by this is:
I have a program. The end user is currently using it. I submit a new piece of source code and expect it to run as if it were always there?
I can't find an answer that specifically answers the point.
I'd like to be able to say, "extend" or add new features (rather than fix something that's already there on the fly) to the program without requiring a termination of the program (eg. Restart or exit).
Yes, you can definitely do that in python.
Although, it opens a security hole, so be very careful.
You can easily do this by setting up a "loader" class that can collect the source code you want it to use and then call the exec builtin function, just pass some python source code in and it will be evaluated.
Check the package
http://opensourcehacker.com/2011/11/08/sauna-reload-the-most-awesomely-named-python-package-ever/ . It allows to overcome certain raw edges of plain exec. Also it may be worth to check Dynamically reload a class definition in Python
I am using visual studio 2008 and MFC. I accept arguments using a subclass of CCommandLineInfo and overriding ParseParam().
Now I want to pass these arguments to the application while running. For example "test.exe /start" and then to type in the console "test.exe /initialize" to be initialized again.
is there any way to do that?
Edit 1: Some clarifications. My program starts with "test.exe /start". I want to type "test.exe /initialize" and initialize the one and only running process (without closing/opening). And by initialize I mean to read a different XML file, to change some values of the interface and other things.
I cannot think of an easy way to accomplish what you're asking about.
However, you could develop your application to specifically receive commands, and given those commands take any actions you wanted based upon receiving them. Since you're already using MFC, you can do this rather easily. Create a Window (HWND) for your application and register it. It doesn't have to be visible (this won't necessarily make you application a GUI application). Implement a WndProc, and define specific messages that you will receive based on WM_USER + <xxx>.
First and obvious question is why you want to have threads, instead of processes.
You may use GetCommandLine and CommandLineToArgvW to get the fully formatted command line. Detect the arguments, and the call CreateProcess or ShellExecute passing /watever to spawn the process. You may also want to use GetModuleBaseName to get the name of your own EXE.