Input Dialogs¶
From the beginning pyFormex was intended as a framework for creating parametric models. This means that some parameter values need to be fed to the models to instantiate them. Often it is more appropriate to ask these values interactively from the user, rather than hardcoding them into a script.
The pyFormex user has full access to the Qt framework on which the GUI was built. Therefore he can built input dialogs as complex and powerful as he can imagine. However, directly dealing with the Qt libraries requires some skills and, for simple input widgets, more effort than needed.
Therefore pyFormex has a very powerful but still easy to use system for the creation of such dialogs.
Modal Dialogs¶
A simple dialog¶
In many cases we can just make use of the gui.draw.askItems()
function. Here’s a simple example (dialog1). It creates an (nrows,
ncols) array filled with numbers from arange(nrows*ncols). The values
nrows and ncols are asked from the user.
res = askItems([
dict(name='nrows', value=3),
dict(name='ncols', value=6),
])
if res:
nrows = res['nrows']
ncols = res['ncols']
A = np.arange(nrows*ncols).reshape(nrows,ncols)
print(A)
The askItems function takes a list of input items as its first (and only
required) argument. Each input item is a dict where the keys have predefined
meanings. There are many optional keys, but at least the name and value keys
should be present. The whole dict should be acceptable as keyword arguments
for the gui.widgets.InputItem
class. See Input Items
for more details.
The name specifies the key in the result dict that will contain the value
entered by the user. The value is the initial value that will be displayed
in the dialog. The value also serves in simple cases to identify the type
of the result: thus a str, int, float given as value will automatically
require that the same type is returned. If you want to get an integer back
as a string, you should specify: value='3'
.
The askItems function constructs a dialog, displays it to the user:

It then lets the user interact with the dialog until he either accepts or rejects the input data. Accepting the data is done by clicking the OK button or pressing the ENTER key. The data can be rejected by pushing the CANCEL button or hitting the ESC key. The dialog created by askItems is a modal one, meaning that all interaction with the rest of the pyFormex interface is blocked until the user either accepts or rejects the data. When the results are accepted or rejected, the dialog is closed and the user can again interact with other pyFormex windows. In Modeless Dialogs we describe how to create Dialogs that can stay open and allow the user to interact with other windows.
The return value of askItems is always a dict. If the user accepts the data, the dict contains a value for each of the items specified, with the name of the item as key. The value will be of the type implied by or specified in the input item. If the user rejects the data, it will be an empty dict. It is good practice to always test the result to see if it contains values, as is done in the example above.
If you run this example and accept the default values, you will see this printed:
[[ 0 1 2 3 4 5]
[ 6 7 8 9 10 11]
[12 13 14 15 16 17]]
Convenience functions¶
To avoid excessive typing pyFormex offers some convenience functions
to create input items with the required name and value fields. The
most important one is _I
, which creates a single input item, taking
the name and value as required positional arguments.
We will study this function in detail in Input Items.
Using the _I
function, we can simplify the dialog1 script to (dialog2):
res = askItems([
_I('nrows', 3),
_I('ncols', 6),
])
if res:
globals().update(res)
A = np.arange(nrows*ncols).reshape(nrows,ncols)
print(A)
The globals().update(res)
is a convenient shortcut to convert
all the values returned from the dialog to global variables. Of course
this only works if all then field names are valid Python variable names.
It is only recommended in small and simple scripts, where you can avoid
name clashes in the global name space.
Input validation¶
When an input item uses a string editing field as interactor, it is usually not possible to type anything that would obviously lead to an invalid value. For example, the integer input fields in the above example will not allow you to enter a non-digit (except for the minus sign).
It is however still possible to temporarily have an invalid value in the edit field. You can for example delete the input field contents: this has to be possible in order to replace a value with another one. But the empty string is not a valid integer. If this value would be accepted, the remainder of the script would fail.
There are often more restrictions needed on the input values to guarantee a further proper operation. For example, in the dialog1 above, it is possible to enter negative integers. If you only enter one negative value, the script will still continue normally, producing an empty array. If you enter both values negative though, the script exits with an exception traceback. We could test the values nrow and ncol in the script, and only proceed if the values are not negative. But pyFormex dialogs offer a better way: specify the value bounds on the input item, and let the pyFormex GUI handle the validation (dialog3):
res = askItems([
_I('nrows', 3, min=0, text='Number of rows'),
_I('ncols', 6, min=2, max=10, text='Number of columns'),
])
if res:
globals().update(res)
A = np.arange(nrows*ncols).reshape(nrows,ncols)
print(A)
The min and max values specify the lowest and highest acceptable int value for the field. For nrows, we accept any non-negative value. For ncols, we restrict the values to the range 2..10. Notice also the added text option: it specifies the text displayed before the field in place of the name. The dialog now looks like this:

While it is now not possible to enter negative values, specifying bounds
may raise other possibilities for intermediate invalid values. Consider the
case of ncols, which specifies the range 2..10 as valid. It is however possible
to enter 1
in the field, since that could be the first digit of the valid
number 10. Again, if this value would be accepted, an invalid value would be
produced.
The problem of intermediate invalid values is handled by pyFormex Dialogs as follows: when the user performs an action to accept the dialog (pressing OK or hitting ENTER), a validation of all data is performed, and if any fields hold invalid data, these fields are flagged and the data is not accepted:

Hovering the mouse over the red invalid button will pop up a tip with the reason:

Then the user interaction continues until all data are valid and accepted, or the data are rejected.
Modeless Dialogs¶
Modal Dialogs are a good solution when some information from the user is required to continue. But there are cases where we want to construct a dialog that stays open while allowing to continue working with the other windows. Such modeless dialogs need to be handled somewhat different. The system can not wait for results. Instead you will need to define actions that need to be executed when activated.
Here is a simple modeless dialog, containing the same input items as dialog3 above (dialog4):
def create_array(nrows, ncols):
A = np.arange(nrows*ncols).reshape(nrows,ncols)
print(A)
def show():
if dialog.validate():
create_array(**dialog.results)
def close():
dialog.close()
dialog = Dialog([
_I('nrows', 3, min=0, text='Number of rows'),
_I('ncols', 6, min=2, max=10, text='Number of columns'),
], actions=[('Close', close), ('Show', show)])
dialog.show()
The first function, create_array
, is the function we
actually want to accomplish: print an (nrows, ncols) array. The next two
functions are the actions that we want to be provided by the dialog.
The show
function validates the current input data, and if they’re valid,
calls create_array
to do the work.
Let’s disect this, starting with the creation of the dialog near the bottom.
The Dialog class class initializer requires a list of input items as first
argument, just like the askItems
function for creating
Modal Dialogs. But it has many optional arguments.
The actions
options defines the buttons that will be provided on the
dialog to initiate actions. Each button is defined by a tuple of a string and
a function. The string is the text shown on the button. The function should
take no arguments and will be executed when the button is pressed. In our
case we defined two buttons: Show
, to create and print the array
according to the user input; and Close
, to close the dialog when we’re done.