7.3.2 Simple Button using PyEval

This example demonstrates the use of PyEval() in UIL to evaluate a string that invokes a callback. If the callback were "pre-defined", then a UIL-writer would not have to write any Python code to use this technique, other than the string passed to PyEval() to evaluate.

There are three files required--a .uil file named button2.uil, a .uid file named button2.uid, and a Python script named testbutton2.py.

button2.uil contains the following text:

    module button_test_2
    version = 'V1.0'
    names = case_sensitive

    procedure
    PyEval(string);                     ! declare PyEval

    object main : XmBulletinBoard {     ! bulletin board parent
        arguments {
            XmNwidth = 200;
            XmNheight = 200;
        };
        controls {
            XmPushButton button;        ! main is parent of button
        };
    };

    object button : XmPushButton {      ! button is labelled Push Me
        arguments {
            XmNx = 0;
            XmNy = 0;
            XmNwidth = 100;
            XmNheight = 40;
            XmNlabelString = 'Push Me';
        };
        callbacks {
            XmNactivateCallback = procedure PyEval("MyPrint(g_var)");
        };
    };

    end module;

button2.uid is created by the uil compiler as noted in the first example.

testbutton2.py (which must have execute permission) contains the following left-justified text:

    #!/usr/local/bin/python

    import os, sys, Mrm, Xt

    def MyPrint(msg):                   # MyPrint is defined in __main__
        print msg

    def main():
        global g_var                    # g_var has global scope in __main__
        g_var = 'a global value'
        print 'g_var =',g_var

        top_level = Xt.Initialize()

        # fetch the hierarchy
        mrm_hier = Mrm.OpenHierarchy('button2.uid')
        main_w = mrm_hier.FetchWidget('main', top_level)

        # manage widgets, realize shell
        main_w.ManageChild()
        top_level.RealizeWidget()

        Xt.MainLoop()

    main()

The script is executed by the statement:

    testbutton2.py

When executed, the testbutton2.py script first declares the function MyPrint(). MyPrint() is in the scope of the Python "unnamed" module __main__ because no other module was named for MyPrint() to be in the scope of. This is an important point, because PyEval evaluates its string in this namespace, and so can find MyPrint().

Then the script declares the function main(). main() declares the global g_var. g_var is also in the __main__ namespace, for the same reason as MyPrint(). g_var is defined in this namespace so that it can be used in the tag argument of the PyEval call. The script opens the hierarchy defined by button.uid, fetches the widget named "main", which is a bulletin board, and its child, a pushbutton, manages these widgets and realizes the shell, and enters the Xt Event Loop.

Finally, the script invokes the main() function, which executes the code and builds the interface.

When the user pushes the pushbutton (labelled "Push Me" in the .uil file), the PyEval() function is called with the string "MyPrint(g_var)". This string is evaluated by Python in the context of module __main__, and calls MyPrint() passing as the argument the variable g_var. MyPrint() simply prints it's argument's value, which is that of g_var--"a global value".

Note that if the testbutton2.py script was imported, then MyPrint() would not be in __main__ namespace. testbutton2.py could be imported by the statement:

    import testbutton2

In this case, MyPrint and g_var would be globals in the module testbutton2's namespace; and so the .uil file would have to be coded differently to invoke MyPrint(), as shown below:

    callbacks {
        XmNactivateCallback = procedure PyEval(
            "import testbutton2; testbutton2.MyPrint(testbutton2.g_var)");
    };