Bob,
The details of when scripts run are messy (long story). Many of the bugs in 4.11.0 - 4.11.2 were caused by changing the timing of script execution so we’re reluctant to change it again. Suffice it to say that by the time a window or widget is visible or its set()
function has been called, all of its scripts have been run.
Scripts are loaded in a specific order:
- inheritance hierarchy (e.g.
display
will be run before dspWoSchedule
)
-
script_order
(e.g. dspWoSchedule
order 5 will be run before dspWoSchedule
order 10)
- schema name (e.g.
display
scripts with order 0 in xtbatch
before xtdash
)
As you learned empirically scripts don’t run automatically when you call toolbox.loadUi()
. Calling include()
is a reasonable solution, as is putting the code inline.
Dave Anderson’s suggestion to use showEvent()
for populating your custom table is probably the safest route to go to make sure stuff happens before the user sees it. As Dave noted, there are problems when you have multiple scripts on the same widget, particularly related to variables (and functions in JavaScript are variables) with the same names. There are three solutions to that problem and you should use at least two of them:
- Always wrap calls to
set()
and showEvent()
- Either create namespaces (like the example you cite above in xTuple Connect)
- and/or write closures & immediately executed function expressions (IIFE)
Depending on the age of the xTuple code you look at, you’ll see all three. The oldest code ignores the problems entirely.
The following example represents my current recommendations. There are differing opinions on the best way to handle set()
and closeEvent()
; the version here is the simpler of them.
// "display" script
// add an Expand All pushbutton that only works the first time you click it
var self = this; // get the runtime context set up by prior events
(function () { // create a "function expression"
var _expand = new QPushButton(qsTr("Expand All"), mywindow),
_list = mywindow.list(), // _list is only visible inside the function expression
_lyt = toolbox.widgetGetLayout(_list);
_lyt.addWidget(_expand, 3, 0); // row 3 works for most displays
function expandAll()
{
if (_list && "expandAll" in _list) // if _list is valid
_list.expandAll(); // show all indented items in _list
_list = false; // make _list invalid for demonstration purposes, any value would do
} // but this only changes `_list` inside the function expression
_expand.clicked.connect(expandAll);
// if a set() function already exists, save it. otherwise save a dummy version
var oldSet = (typeof self.set == "function") ? self.set : function () { return mainwindow.NoError; };
self.set = function (pParams) // NOT function set(pParams) { ... }
{ // replace the prior set() with a new one
var result = oldSet(pParams); // call the function we just saved
if (result != mainwindow.NoError)
return result; // return if an error occurred
print("pParams", JSON.stringify(pParams)); // do whatever this `set` should do
return mainwindow.NoError;
}
})(); // execute (run) the function expression
Think carefully before you write multiple scripts on the same window, even if you code them well. The more scripts there are, the harder it is to understand what’s really going on and the more code duplication there will be.
If you’re going to create a lot of include
scripts then I agree with Caleb — each include
d script should create an object that can be treated like a class, or at least a class instance.
Gil