Scoping
Scoping of Widget References
Widgets created within a user interface definition can be named and registered in an object registry for easy lookup reference by script code and other widgets. Complete interface definitions can also be reused within other definitions and when the issue of scoping access to widget references arises. Here are two design issues to keep in mind when working with widget references.
- By default, widget references are scoped by a single interface definition expression or file. All widget references are accessible across all Script[] blocks and WidgetReference[] requests within a single definition.
- When another interface definition is loaded using Widget["path",{…},…] by a wrapping definition, only the outermost root widget is exposed by default and can be named within the wrapping definition using the option Name->"myName". If it is desired that other widgets be exposed from within this external definition, you can use the option ExposeWidgetReferences->{"name1","name2"->"newName2",…} to include named access to other widgets inside the child definition.
Scoping of Scripts
Script blocks are typically used within interface definitions to define Wolfram Language programming functions, dynamically change the state of interface widgets, and bind events from widgets into calls to Wolfram Language functions. Since this involves the Wolfram Language kernel and multiple interface definitions with their own script code running at the same time, the question of script code scoping and their use of kernel contexts arises. Here are several design issues to keep in mind when developing Script code blocks.
- All Script[] blocks are scoped by a single interface definition expression or file. A new private kernel context is created for each instance of an interface definition and all Script[] blocks are evaluated within this new context. Even when BindEvent Script blocks are subsequently called when the interface is live, this same private context is set while the script code is executing, thus also making available any functions defined by other top-level Script[] blocks defined when the interface definition was first loaded. This is quite similar to the loading of JavaScript code in HTML pages. This private new context for each instance of a definition also allows you to use global variables within your scripts without worrying about them being overwritten by other definitions.
- When another interface definition is accessed using Widget["path",{…},…] , it receives its own new internal kernel context to run its internal scripts in, so external definition Script blocks will not by default share or have access to the parent or wrapping definition and the parent Script code and function definitions.
- The use of Script[{},ScriptSource->"file.m"] to break out large script code blocks will still use the same shared kernel context with Script[] blocks that explicitly contain the code in the definition, as this form of Script does not qualify as another separate external definition.
- $ContextPath is initially defined to {"GUIKit`","JLink`","System`"} for each new interface definition instance and is reset to the external global $ContextPath when exiting a Script call. This means that you must explicitly call Needs on any Wolfram Language packages that are used by our scripts. Since a unique $ContextPath is maintained for scripts inside each user interface instance, GUIKit definitions will also not expose their use of Wolfram Language packages to the global $ContextPath, preventing the potential symbol shadowing by multiple packages using the same symbol names.
- You can still use explicit contexted symbol and function calls to access shared kernel code. In certain cases, during development or for interactive notebook work, it may be more desirable to allow the scripts of a GUIKit user interface definition to access shared Global` contexted symbols and functions, though this may break the ability of this definition to work when multiple instances of it are active at the same time. GUIRun, GUIRunModal, and similar functions support the IncludedScriptContexts option for specifying a list of additional contexts that should be included on a user interface's scripts $ContextPath.
- Script is HoldAllComplete so code blocks will not automatically evaluate their code. If you are interactively building up an interface definition in a notebook, you may notice some of the Script functions showing up in the Global` context while working with the expressions, but at runtime the definition and scripts will still get defined within a new private context when the interface is live.
Here are a few examples that demonstrate the nature of GUIKit's Script context and $ContextPath management.
Here you can show the default global $Context and $ContextPath state.
Here you run a GUIKit definition that shows its $Context and $ContextPath state and prints these values each time you click its button.
Now load a Wolfram Language package that updates the global $ContextPath.
Notice that if you run a new definition, the definition's $ContextPath does not include the externally loaded Wolfram Language package, and conversely, if you call Needs on a package internal to the Script block of the GUIKit definition, it does not change the global $ContextPath.
Here you see that the external $ContextPath is unaffected by the GUIKit's Script block loading of packages.
Because GUIKit definitions use their own private $ContextPath and $Context by default, access to globally stored symbols or functions is not available within Script code.
Here you see that the user interface does not see globally defined symbols and functions by default.
If you were instead to use the IncludedScriptContexts option of GUIRun to include the "Global`" context as a visible context for the user interface, then the Script blocks would see these externally defined symbols.