Embedding Z-Script in wxWindows

The wxWidnows library is one of the best open source libraries for developing cross-platform user interface. Its smart API for event handling makes it very easy to embed a scripting language for widget creation. The embedded Z-Script uses a derived wxEvtHandler to handle most events of menu bar, tool bar, key, mouse, and standard control widgets.

Upon startup, the widget program creates a main frame window and then executes the Z-Script file of the same name as the widget program but the extension of ".zs". The script should first obtain the frame and then add menubar, toolbar, or controls in it. For instance,

frame = wxframe();

a = frame.menubar("&File", "&Item-1\tCtrl-I", "_", "I&tem-2\tCtrl-t",
                  ">", "Submenu", "&Sub-1\tCtrl-S", "S&ub-2\tCtrl-u",
                  "<", "It&em-3\tCtrl-e");

b = frame.menubar("&Help", "&About\tCtrl-A");

na = size(a);
nb = size(b);
for (i = 0; i < nb; i++) a[na+i] = b[i];

frame = wxcast(frame);
                  
frame.push("event_handler", "menu", a);

function event_handler(id)
{
    s = " " + id;
    wxmsg(s);
}

You may have noticed in the reference that the function push() is registered to the base window, when we want to push a event-handling callback function to handle menu event in the frame, we have to cast the frame from top window type to base window type.

Here is example that demonstrates using libraries of wxWindow and Excel to make a timekeeper for symposium. The program reads speaker’s name, speech time, question time, and his/her subject from Excel file. The page-up/down keys can be used to change speaker list, i.e., worskeet, and the arrow-up/down keys can be used to change the speaker. The timer can be activated or stopped by the t-character key. In a worksheet, each row should contain the spealer’s name and the affiliation in the first column, speech time (min) in the second, question time (min) in the third, and subject in the forth. Depending on the display size, line breaks should be inserted into a long subject line by holding down the.Alt-key and then pressing the Enter-key.

 

// Excel object to read speaker list from worksheets

load("excel.dll");
xls = excel();
xls.open(mydir() + "\\timekeeper.xls");

sheet = 1;              // current worksheet
row = 2;                // current row number of speaker
end = 1;                // last row number of speaker
speech = 0;             // speech time (min)
question = 0;           // question time (min)
second = 0;             // for clock ticking
timer = false;          // timer not active

check_sheet();

// Symposium title

title = "\nThird International Workshop\non Greenhouse Gas Measurements\nfrom Space";

// foreground color
fg_color = [0, 0, 0];

// background color
bg_color = [255, 255, 255];

// color for symposium title
title_color = [0, 0, 225];

// title font size
title_font = 30;

// color for subject
subject_color = [0, 128, 0];
subject_font = 26;

// color for timer
clock_color = [255, 0, 0];
clock_font = 38;

// default font size
default_font = 24;

left_margin = 50;

default_size = [900, 700];

// main frame window

topsizer = null;

frame = wxframe("frame_handler");
frame = wxcast(frame);
frame.size(default_size[0], default_size[1]);
// handle some key event
frame.push("key_handler", "key");

// panel in the main frame

panel = frame.panel();
panel.bgcolor(bg_color[0], bg_color[1], bg_color[2]);

// top box sizer

topsizer = panel.sizer("verbox");

// title

title = panel.statictext(title, 0);
title.fgcolor(title_color[0], title_color[1], title_color[2]);
title.font(title_font);
topsizer.add(title,"aligncenter");
topsizer.add(10, 50);    // space above speaker

// use grid sizer for layout

grid = topsizer.sizer("flexgrid", 3, 30, 3, 10, "aligncenter");

// speaker

label = panel.statictext("Speaker: ", 1);
label.font(default_font);
grid.add(label, "alignright");
speaker = panel.statictext("Name (affiliation)", 0);
speaker.font(default_font);
grid.add(speaker);
grid.add(left_margin, 10);

// subject

label = panel.statictext("Subject: ", 1);
label.font(default_font);
grid.add(label, "alignright");
subject = panel.statictext("The subjecy may take multiple lines\nlike his one.\n\n");
subject.font(subject_font);
subject.fgcolor(subject_color[0], subject_color[1], subject_color[2]);
grid.add(subject);
grid.add(left_margin, 10);

// timer

grid.add(left_margin, 10);
hsizer =  grid.sizer("herbox");
//hsizer =  grid.sizer("flexgrid", 1, 10, 2, 20);
clocklabel = panel.statictext("Speech (^o^) ", 1);
clocklabel.font(default_font);
hsizer.add(clocklabel, "aligncenter");
clock = panel.statictext("00:00");
clock.font(clock_font);
clock.fgcolor(clock_color[0], clock_color[1], clock_color[2]);
hsizer.add(clock);
grid.add(left_margin, 10);


// next speaker

label = panel.statictext("Next: ", 1);
label.font(default_font);
grid.add(label, "alignright");
next = panel.statictext("Name (affiliation)", 0);
next.font(default_font);
grid.add(next);
grid.add(left_margin, 10);

//

update_dislpay();

// callback functions

function key_handler(key, ctrl, alt, shift)
{
    switch (key) {

    case 317:                        // up arrow
        wxtimer(0);
        timer = false;
        row--;
        if (row < 2) global:row = end;
        update_dislpay();
        break;

    case 319:                        // down arrow
        wxtimer(0);
        timer = false;
        row++;
        if (row > end) global:row = 2;
        update_dislpay();
        break;

    case 312:                        // page up
        wxtimer(0);
        timer = false;
        if (sheet > 0) sheet--;
        if (check_sheet()) update_dislpay();
        break;

    case 313:                        // page down
        wxtimer(0);
        timer = false;
        sheet++;
        if (check_sheet()) update_dislpay();
        break;

    case 84:                        // t
        if (timer == true) {
            global:timer = false;
            wxtimer(0);
        }
        else {
            global:timer = true;
            wxtimer(1000);
        }
        break;
//    default:
//        wxmsg("key=" + key);
    }
}


function frame_handler(event, p1, p2)
{
    if (event == "timer") {
        second--;
        if (second <= 0) {
            if (speech > 0) {
                speech--;
                global:second = 59;
                clock.label(format("%02d",speech)+format(":%02d",second));
            }
            else if (question > 0) {
                question--;
                global:second = 59;
                clocklabel.label("Question (^?^) ");
                clock.label(format("%02d",question)+format(":%02d",second));
                hsizer.update();
            }
            else {
                clocklabel.label("(@!@) ");
                clock.label("Time out");
                topsizer.update();
                sleep(500);
                clock.label("                         ");
                sleep(500);
                hsizer.update();
            }
        }
        else {
            if (speech >= 0) {
               clock.label(format("%02d",speech)+format(":%02d",second));
            }
            else {
               clock.label(format("%02d",question)+format(":%02d",second));
            }
        }
        hsizer.update();
        return;
    }
    if (event == "paint") {
        if (exist("timekeeper.bmp")) panel.bitmap("timekeeper.bmp", 0, 0);
        return;
    }
}


function check_sheet()
{
    if (xls.activate(sheet) != 0) {
        global:sheet = 1;
        xls.activate(sheet);
    }
    sleep(100);
    global:row = 2;
    // check worksheet assuming no more than 15 speakers in a list
    for (i = 2; i <= 15; i++) {
        global:end = i - 1;
        if (!isstring(xls[i,1])) {
            break;
        }
        if (!isreal(xls[i,2])) {
            wxmsg("Invalid total time.");
            return false;
        }
        if (!isreal(xls[i,3])) {
            wxmsg("Invalid question time.");
            return false;
        }
        if (!isstring(xls[i,4])) {
            wxmsg("Invalid subject.");
            return false;
        }
    }
    if (end < row) {
        wxmsg("Speaker list is empty.");
        return false;
    }
    return true;
}


function update_dislpay()
{
    global:second = 0;
    speaker.label(xls[row,1]);
    global:speech = integer(xls[row,2]);
    clocklabel.label("Speech (^o^) ");
    clock.label(" " + speech + " minutes");
    global:question = integer(xls[row,3]);
    subject.label(xls[row,4]);
    if (row < end) {
        next.label(xls[row+1,1]);
    }
    else {
        next.label(" ");
    }
    if (isuser(topsizer)) topsizer.update();
}