Introducing Zinzala
First published: April 21, 2003 *** Last update: August 30, 2004
hexaZen
Abstract
This white paper introduces a new sophisticated Software Development Kit (SDK) for QNX. This kit offers labor-saving technologies to help develop solutions for embedded or desktop markets.
With a simple demonstration, we will show how the Zinzala SDK can help increase development productivity and application flexibility when time-to-market matters.
Intended audience
This paper is intended for developers who have knowledge of C++ and who are familiar with Graphical User Interface (GUI) concepts.
Specific QNX or Photon knowledge is not required.
Contents
Zinzala is a Software Development Kit designed to take full advantage of modern computers and operating systems, such as QNX. It offers:
- Object oriented framework (in C++)
- Multi-threaded, network-ready application
- Easy inter-application messaging
- Multi-threaded, message passing, Photon based GUI kit (fully scriptable)
The SDK gives the opportunity to build complex applications using a high-level language for desktop and embedded targets, which are supported by the QNX Momentics. Zinzala provides:
- Developer friendly Application Programming Interface (API)
- Rapid Development
- Powerful functionality
- Easy networking and connectivity
Zinzala is composed of several libraries split into two Kits: Cincinella and Farfalla. Some of the libraries are important to all applications, while others are only used by applications that are concerned with specific topics. For instance, the GUI library would only be used by applications which require an user interface. Each library is a dynamically linked library (DLL) resident on the user's machine and applications link to it when it's launched.
If you are a runner, you may want to know how many calories you have burned after a run. There are several web sites which provide a way for calculating this. Here, we are going to implement a QNX calorie-burned calculator using Zinzala.
From the Internet, we can find the following simple formula:
calories = distance * weight * 1.036
Where the distance is in Kilometers and the weight is in Kilograms.
The user inputs the following information :
- distance ran
- user's weight
We will allow the user to select between Miles and Kilometers for their distance
and between Pounds and Kilograms for their weight.
In the sections which follow, we will take a step-by-step approach to arrive at the following application:
The proposed C++ framework provides the developer with a significant amount of ready-to-use classes. When specific needs arise, some classes may need to be specialized.
When developing with Zinzala SDK, the developer is required to use a cApplication object or an instantiation of a class derived from it. This class establishes an application as an identifiable entity (ie. it needs to be named). This entity can cooperate and communicate with the outside world (local or distant applications). This class also handles the connection with the Photon server on QNX.
The code below shows the skeleton of our Zinzala application:
01 // Create the application
02 cApplication lMyApp("hexaZen/phalorieburn",0);
03
04 // Check that the application is initialized correctly
05 if(lMyApp.IsValid())
06 {
07 // Start the application
08 lMyApp.Run();
09
10 // do some stuff
11
12 // Wait for the end of the app
13 lMyApp.WaitEnd();
14 }
15 else
16 fprintf(stderr,"Failed to init the application");
We are now going to add a simple window to our application. This is done
by instantiating the cWindow class:
01 // Create the application
02 cApplication lMyApp("hexaZen/phalorieburn",0);
03
04 // Check that the application is initialized correctly
05 if(lMyApp.IsValid())
06 {
07 // Start the application
08 lMyApp.Run();
09
10 // Create window
11 cWindow lWin(uRect(250,100),"calc",eWinNormal,0,kCurrentWorkspace);
12 lWin.SetTitle("Calorie burner Calculator");
13 lWin.MoveTo(10,10);
14
15 // show the window now
16 lWin.Show();
17
18 // Wait for the end of the app
19 lMyApp.WaitEnd();
20 }
21 else
22 fprintf(stderr,"Failed to init the application");
Line 11 instantiates the window and sets its size to 250x100. The flag kCurrentWorkspace indicates that we want this window to be located on the current workspace. If we wanted to have this window positioned on the third workspace for example, we would have to put the value 3 instead. Line 12 and 13 sets the window's title and the window's position respectively.
The result of the above code can be seen below. The source code can be downloaded here:
The philosophy behind the GUI development with Zinzala SDK is based on message passing. Every component of the user interface has the ability to send, receive and process messages. For example, when a button is pressed, a message is sent to the window that contains it. This message has been specified earlier by the developer and will be the basis for determining what response the user will receive. The developer can then perform processing accordingly.
The class cMessage implements a bundle of data that is exchanged between widgets and windows or even between applications. It provides a way of storing and retreiving typed and named information.
Next, we will derive the class cWindow. This derived class is necessary to process messages such as the one from a Compute button of the user interface:
01 class ccCalcWindowdow : public cWindow
02 {
03 public:
04 cCalcWindow();
05 protected:
06 void MessageReceived(cMessage *aMessage);
07 };
08
09 cCalcWindow::cCalcWindow()
10 : cWindow(uRec(250,100),"calc",eWinNormal,
11 0,
12 kCurrentWorkspace)
13 {
14 // Set some settings of the window
15 SetTitle("Calorie burner Calculator");
16 MoveTo(10,10);
17 }
18
19 void cCalcWindow::MessageReceived(cMessage *aMessage)
20 {
21 switch(aMessage->iWhat)
22 {
23 default:
24 cWindow::MessageReceived(aMessage);
25 }
26 }
The protected method MessageReceived performs the processing of the messages received by the window. If there is nothing to be done with the message received, identified by its iWhat field, the parent method needs to be called, otherwise the window will not work correctly.
Now, we will modify the main of our application to use this class cCalcWindow
01 // Create the application
02 cApplication lMyApp("hexaZen/phalorieburn",0);
03
04 // Check that the application is initialized correctly
05 if(lMyApp.IsValid())
06 {
07 // Start the application
08 lMyApp.Run();
09
10 // Create window
11 cCalcWindow lWin;
12
13 // show the window now
14 lWin.Show();
15
16 // Wait for the end of the app
17 lMyApp.WaitEnd();
18 }
19 else
20 fprintf(stderr,"Failed to init the application");
The result of this simple application is the same as earlier. The source code can be downloaded here.
Let's now add the widgets for the user interface of the calculator. Zinzala SDK offers a complete range of widgets for the developer to choose from. Most of these widgets are based on the ones which already exist in Photon. For our example, we are going to use the following:
cGroupView, allows the grouping of widgets inside a container
cFloatEntryView, allows the user to enter a float value
cComboBoxView, allows the user to select an item from a drop list
cStringView, displays a string
We will modify the constructor of our class cCalcWindow as follows:
01 cCalcWindow::cCalcWindow()
02 : cWindow(uRect(250,100),"calc",eWinNormal,
03 0,
04 kCurrentWorkspace)
05 {
06 cGroupView *lFrame;
07 cFloatEntryView *lDistance;
08 cFloatEntryView *lWeight;
09 cComboBoxView *lDistanceUnit;
10 cComboBoxView *lWeightUnit;
11 cStringView *lResult;
12
13 // Set some settings of the window
14 SetTitle("Calorie burner Calculator");
15 MoveTo(10,10);
16
17 // Create widget to group all the widgets to put on the window
18 lFrame = new cGroupView(uRect(0,0),NULL,eHorizontal,0,
19 kFlgForceEqualSize | kFlgResizeToFit);
20 lFrame->SetSlicing(3);
21 lFrame->SetSpacing(5,5);
22
23 // Create and set widget to enter the distance
24 lDistance = new cFloatEntryView(uRect(60,20),NULL,0,NULL,0,kFlgUpDown);
25 lDistance->SetPrecision(1);
26
27 // Create and set widget to enter weight
28 lWeight = new cFloatEntryView(uRect(60,20),NULL,0,NULL,0,kFlgUpDown);
29 lWeight->SetPrecision(1);
30
31 // Create and set widget to select distance unit
32 lDistanceUnit = new cComboBoxView(uRect(60,20),NULL,NULL,0,kFlgNotEditable);
33
34 // Create and set widget to select weight unit
35 lWeightUnit = new cComboBoxView(uRect(60,20),NULL,NULL,0,kFlgNotEditable);
36
37 // Create and set widget to display the computation result
38 lResult = new cStringView(uRect(0,0),NULL,"a string",0,0);
39 lResult->SetFGColor(kColorBlue);
40 lResult->SetAlignment(eCenter);
41
42 // Add all the widget to the group
43 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Distance run",0,0));
44 lFrame->AddChild(lDistance);
45 lFrame->AddChild(lDistanceUnit);
46 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Weight",0,0));
47 lFrame->AddChild(lWeight);
48 lFrame->AddChild(lWeightUnit);
49 lFrame->AddChild(new cButtonView(uRect(0,0),NULL,"Compute",NULL,0,0));
50 lFrame->AddChild(lResult);
51
52 // Add the group to the window
53 AddChild(lFrame);
54 }
The source code can be downloaded here. This result is shown below:
A couple of things can be done to improve the appearance and behavior of the application. First, specifying a size for the window is not always elegant. It is best to let the window decide on its own based on its contents. Secondly, for a simple application such as this calculator, we don't want the user to resize the window.
Implementing this can be easily done in the call to the cWindow constructor in the cCalcWindow constructor:
01 cCalcWindow::cCalcWindow()
02 : cWindow(uRect(0,0),"calc",eWinNormal,
03 kFlgResizeToFit | kFlgNotResizable | kFlgNotZoomable,
04 kCurrentWorkspace)
05 {
06 cGroupView *lFrame;
07 cFloatEntryView *lDistance;
08 cFloatEntryView *lWeight;
09 cComboBoxView *lDistanceUnit;
10 cComboBoxView *lWeightUnit;
11 cStringView *lResult;
12
13 // Set some settings of the window
14 SetPadding(5,5);
15 SetTitle("Calorie burner Calculator");
16 MoveTo(10,10);
17
18 // Create widget to group all the widgets to put on the window
19 lFrame = new cGroupView(uRect(0,0),NULL,eHorizontal,0,
20 kFlgForceEqualSize | kFlgResizeToFit);
21 lFrame->SetSlicing(3);
22 lFrame->SetSpacing(5,5);
23
24 // Create and set widget to enter the distance
25 lDistance = new cFloatEntryView(uRect(60,20),NULL,0,NULL,0,kFlgUpDown);
26 lDistance->SetPrecision(1);
27
28 // Create and set widget to enter weight
29 lWeight = new cFloatEntryView(uRect(60,20),NULL,0,NULL,0,kFlgUpDown);
30 lWeight->SetPrecision(1);
31
32 // Create and set widget to select distance unit
33 lDistanceUnit = new cComboBoxView(uRect(60,20),NULL,NULL,0,kFlgNotEditable);
34
35 // Create and set widget to select weight unit
36 lWeightUnit = new cComboBoxView(uRect(60,20),NULL,NULL,0,kFlgNotEditable);
37
38 // Create and set widget to display the computation result
39 lResult = new cStringView(uRect(0,0),NULL,"a string",0,0);
40 lResult->SetFGColor(kColorBlue);
41 lResult->SetAlignment(eCenter);
42
43 // Add all the widget to the group
44 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Distance run",0,0));
45 lFrame->AddChild(lDistance);
46 lFrame->AddChild(lDistanceUnit);
47 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Weight",0,0));
48 lFrame->AddChild(lWeight);
49 lFrame->AddChild(lWeightUnit);
50 lFrame->AddChild(new cButtonView(uRect(0,0),NULL,"Compute",NULL,0,0));
51 lFrame->AddChild(lResult);
52
53 // Add the group to the window
54 AddChild(lFrame);
55 }
The changes from the previous code are located on lines 2,3 and 14. Line 3 specifies flags that set the behavior of the window. The first flag kFlgResizeToFit instructs the window to resize to fit its contents. We no longer need to specify a given size for the window on line 2. The addition of SetPadding on line 14 is just for cosmetic purposes. This line adds a 5 pixel padding around the cGroupView widget.
We now have something which is a bit nicer. You may consult the source code here:
Next, we will add items to the two cComboBoxView widgets which will be used to select the weight and distance units. This is done by calling the method SetItems using the following array of chars:
01 // Defines units labels
02
03 const tChar *kDistanceLabels[2] = {
04 "Miles",
05 "Kilometers"
06 };
07
08 const tChar *kWeightLabels[2] = {
09 "Pounds",
10 "Kilograms"
11 };
The cCalcWindow constructor will be modified as follows:
32 // Create and set widget to select distance unit
33 ldistance = new cComboBoxView(cRect(60,20),NULL,NULL,0,kFlgNotEditable);
34 ldistance->SetItems(kDistanceLabels,2);
35 ldistance->SetSelectedItem(2);
36
37 // Create and set widget to select weight unit
38 lweight = new cComboBoxView(cRect(60,20),NULL,NULL,0,kFlgNotEditable);
39 lweight->SetItems(kWeightLabels,2);
40 lweight->SetSelectedItem(2);
Now, the user can select between the units (see the source code here):
We are now going to specify which message will be sent when the user interacts with the interface. Each message that will be sent by a widget needs to be identified. For this, we are going to define several tUint32 constants, one for each message:
01 const tUint32 kUidDistanceChanged = 'MdCh';
02 const tUint32 kUidWeightChanged = 'MwCh';
03 const tUint32 kUidDistanceUnitChanged = 'MdCT';
04 const tUint32 kUidWeightUnitChanged = 'MwCT';
05 const tUint32 kUidCompute = 'MCmp';
We then modify the cCalcWindow constructor as follows:
01 cCalcWindow::cCalcWindow()
02 : cWindow(cRect(0,0),"calc",eWinNormal,
03 kFlgResizeToFit | kFlgNotResizable | cFlgNotZoomable,
04 kCurrentWorkspace)
05 {
06 cGroupView *lFrame;
07 cFloatEntryView *lDistance;
08 cFloatEntryView *lWeight;
09 cComboBoxView *lDistanceUnit;
10 cComboBoxView *lWeightUnit;
11 cStringView *lResult;
12
13 // Set some settings of the window
14 SetPadding(5,5);
15 SetTitle("Calorie burner Calculator");
16 MoveTo(10,10);
17
18 // Create widget to group all the widgets to put on the window
19 lFrame = new cGroupView(cRect(0,0),NULL,eHorizontal,0,
20 kFlgForceEqualSize | kFlgResizeToFit);
21 lFrame->SetSlicing(3);
22 lFrame->SetSpacing(5,5);
23
24 // Create and set widget to enter the distance
25 lDistance = new cFloatEntryView(cRect(60,20),NULL,0,
26 new cMessage(kUidDistanceChanged),0,kFlgUpDown);
27 lDistance->SetPrecision(1);
28
29 // Create and set widget to enter weight
30 lWeight = new cFloatEntryView(cRect(60,20),NULL,0,
31 new cMessage(kUidWeightChanged),0,kFlgUpDown);
32 lWeight->SetPrecision(1);
33
34 // Create and set widget to select distance unit
35 lDistanceUnit = new cComboBoxView(cRect(60,20),NULL,
36 new cMessage(kUidDistanceUnitChanged),0,kFlgNotEditable);
37 lDistanceUnit->SetItems(kDistanceLabels,2);
38 lDistanceUnit->SetSelectedItem(2);
39
40 // Create and set widget to select weight uni
41 lWeightUnit = new cComboBoxView(cRect(60,20),NULL,
42 new cMessage(kUidWeightUnitChanged),0,kFlgNotEditable);
43 lWeightUnit->SetItems(kWeightLabels,2);
44 lWeightUnit->SetSelectedItem(2);
45
46 // Create and set widget to display the computation result
47 lResult = new cStringView(cRect(0,0),NULL,"a string",0,0);
48 lResult->SetFGColor(z_COLOR_BLUE);
49 lResult->SetAlignment(z_ALIGN_CENTER);
50
51 // Add all the widget to the group
52 lFrame->AddChild(new cStringView(cRect(0,0),NULL,"Distance ran",0,0));
53 lFrame->AddChild(lDistance);
54 lFrame->AddChild(lDistanceUnit);
55 lFrame->AddChild(new cStringView(cRect(0,0),NULL,"Weight",0,0));
56 lFrame->AddChild(lWeight);
57 lFrame->AddChild(lWeightUnit);
58 lFrame->AddChild(new cButtonView(cRect(0,0),NULL,"Compute",
59 new cMessage(kUidCompute),0,0));
60 lFrame->AddChild(lResult);
61
62 // Add the group to the window
63 AddChild(lFrame);
64 }
In the call to the widget constructors, we have added the creation of a cMessage object (lines 26 31 36 42 and 59),
with the message identifier as its argument. When the user uses the interface, these messages will be
sent to the window.
The MessageReceived method of the cCalcWindow class will now be modified to handle the messages that will be received by the window:
01 void cCalcWindow::MessageReceived(cMessage *aMessage)
02 {
03 switch(aMessage->iWhat)
04 {
05 case kUidDistanceChanged:
06 {
07 break;
08 }
09 case kUidWeightChanged:
10 {
11 break;
12 }
13 case kUidDistanceUnitChanged:
14 {
15 break;
16 }
17 case kUidWeightUnitChanged:
18 {
19 break;
20 }
21 case kUidCompute:
22 {
23 break;
24 }
25 default:
26 cWindow::MessageReceived(message);
27 }
28 }
The intended application can now be easily completed, mainly by modifying the MessageReceived as follows:
01 void cCalcWindow::MessageReceived(cMessage *aMessage)
02 {
03 switch(aMessage->iWhat)
04 {
05 case kUidDistanceChanged:
06 {
07 aMessage->Getfloat64("value",&iDistance);
08 break;
09 }
10 case kUidWeightChanged:
11 {
12 aMessage->Getfloat64("value",&iWeight);
13 break;
14 }
15 case kUidDistanceUnitChanged:
16 {
17 aMessage->Getuint16("pos",&iDistanceUnit);
18 break;
19 }
20 case kUidWeightUnitChanged:
21 {
22 aMessage->Getuint16("pos",&iWeightUnit);
23 break;
24 }
25 case kUidCompute:
26 {
27 tChar lString[128];
28
29 tFloat64 lDistance;
30 tFloat64 lWeight;
31 tFloat64 lCalorie;
32
33 // convert the distance and weight if needed
34 if(iDistanceUnit == kUnitMiles)
35 lDistance = iDistance * kMilesToKm;
36 else
37 lDistance = iDistance;
38 if(iWeightUnit == kUnitPounds)
39 lWeight = iWeight * lPoundsToKg;
40 else
41 lWeight = iWeight;
42
43 lCalorie = iDistance * iWeight * kFormula;
44
45 sprintf(lString,"%0.2f calories",lCalorie);
46
47 // Display the result in the stringview widget
48 iResult->SetText(lString);
49
50 break;
51 }
52 default:
53 cWindow::MessageReceived(aMessage);
54 }
55 }
Line 7 retreives the value from the received message which is entered by the user in the cFloatEntryView widget. We simply store this value in a private member of the cCalcWindow class, and then reuse it when we have received the message telling us that the button Compute has been pressed. On line 48, we modify the text displayed by the cStringView widget. This widget is now a private member of the window class.
Since the cWindow handles the destruction of its widgets, we don't have to worry
about any possible memory leakage. When the window is closed by the user and the application terminates,
all the widgets are destroyed.
The complete source code of the application can be seen here.
Now that we have a working application, we can use it to find out how many calories you and your buddies have burned during your last marathon.
With a normal application, if you wanted to compute the calories burned in a marathon for a series of different weights (eg. 50 55 60 65 70 75 80 85), you would have to manually enter each weight and then press the Compute button each time. This is fine, however, with the Zinzala SDK, there is an easier
way to go about it, if you are confortable with the command line.
By making just minor changes to the application, we are now going to make it fully compliant for outside control through the yo tool (available with the SDK). This command line tool allows the user to send special messages to an application. It is the responsibility of the application to process these messages. Fortunately, the windows and widgets in Zinzala are already setup for this. Scripting is not allowed by default, but we can allow these requests by setting the "allow scripting" flag.
First, we are going to allow the application to be scripted:
01 int main()
02 {
03 // Create the application
04 cApplication lMyApp("hexaZen/phalorieburn",0);
05
06 if(lMyApp.IsValid())
07 {
08 // Start the app (and the Photon connection)
09 lMyApp.Run();
10
11 // Allow application scripting from local source
12 lMyApp.AllowScripting(eLocal);
13
14 // Create window
15 cCalcWindow lWin;
16
17 // show the window now
18 lWin.Show();
19
20 // Wait for the end of the app
21 lMyApp.WaitEnd();
22 }
23 else
24 fprintf(stderr,"Failed to init the application");
25 }
Line 12 allows the application to honor scripting messages from the local computer. We are now going to use the same AllowScripting method on all the widgets that we want to be scriptable. To be able to target a specific widget from outside the application, we need to give each of these widgets a distinct name:
01 cCalcWindow::cCalcWindow()
02 : cWindow(uRect(0,0),"calc",eWinNormal,
03 kFlgResizeToFit | kFlgNotResizable | kFlgNotZoomable,
04 kFlgCurrentWorkspace)
05 {
06 cGroupView *lFrame;
07 cFloatEntryView *lDistance;
08 cFloatEntryView *lWeight;
09 cComboBoxView *lDistanceUnit;
10 cComboBoxView *lWeightUnit;
11 cButtonView *lCompute;
12
13 // Set the inital values
14 iDistance = 0;
15 iWeight = 0;
16 iDistanceUnit = 2;
17 iWeightUnit = 2;
18
19 // Set some settings of the window
20 SetPadding(5,5);
21 SetTitle("Calorie burner Calculator");
22 MoveTo(10,10);
23 AllowScripting(eLocal);
24
25 // Create widget to group all the widgets to put on the window
26 lFrame = new cGroupView(uRect(0,0),NULL,eHorizontal,0,
27 kFlgForceEqualSize | kFlgResizeToFit);
28 lFrame->SetSlicing(3);
29 lFrame->SetSpacing(5,5);
30
31 // Create and set widget to enter the distance
32 lDistance = new cFloatEntryView(uRect(60,20),"distance",d,
33 new cMessage(kUidDistanceChanged),0,kFlgUpDown);
34 lDistance->SetPrecision(1);
35 lDistance->SetRange(0,200);
36 lDistance->AllowScripting(eLocal);
37
38 // Create and set widget to enter weight
39 lWeight = new cFloatEntryView(uRect(60,20),"weight",w,
40 new cMessage(kUidWeightChanged),0,kFlgUpDown);
41 lWeight->SetPrecision(1);
42 lWeight->SetRange(0,400);
43 lWeight->AllowScripting(eLocal);
44
45 // Create and set widget to select distance unit
46 lDistanceUnit = new cComboBoxView(uRect(60,20),"dunit",
47 new cMessage(kUidDistanceUnitChanged),0,kFlgNotEditable);
48 lDistanceUnit->SetItems(kDistanceLabels,2);
49 lDistanceUnit->SetSelectedItem(iDistanceUnit);
50 lDistanceUnit->AllowScripting(eLocal);
51
52 // Create and set widget to select weight unit
53 lWeightUnit = new cComboBoxView(uRect(60,20),"wunit",
54 new cMessage(kUidWeightUnitChanged),0,kFlgNotEditable);
55 lWeightUnit->SetItems(kWeightLabels,2);
56 lWeightUnit->SetSelectedItem(iWeightUnit);
57 lWeightUnit->AllowScripting(eLocal);
58
59 // Create and set widget to display the computation result
60 iResult = new cStringView(uRect(0,0),"result","",0,0);
61 iResult->SetFGColor(kColorBlue);
62 iResult->SetAlignment(eCenter);
63 iResult->AllowScripting(eLocal);
64
65 // Create and set the compute button
66 lCompute = new cButtonView(uRect(0,0),"compute","Compute",
67 new cMessage(kUidCompute),0,0);
68 lCompute->AllowScripting(eLocal);
69
70 // Add all the widget to the group
71 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Distance run",0,0));
72 lFrame->AddChild(lDistance)
73 lFrame->AddChild(lDistanceUnit);
74 lFrame->AddChild(new cStringView(uRect(0,0),NULL,"Weight",0,0));
75 lFrame->AddChild(lWeight);
76 lFrame->AddChild(lWeightUnit);
77 lFrame->AddChild(lCompute);
78 lFrame->AddChild(iResult);
79
80 // Add the group to the window
81 AddChild(lFrame);
82 }
Et voila ... the calculator is ready to be scripted. You may consult the complete source code of the application here.
We can verify that the application can be scripted by typing the following command from the command line once we have started the calculator application:
@> yo hexaZen/phalorieburn info
got answer in 1.000047 ms
What = 0x7a415279
Context = 0x0
Item 'zSDK' have 1 value(s)
0 string (5) 'v0.1'
Item 'Username' have 1 value(s)
0 string (4) 'jlv'
Item 'MaxInstances' have 1 value(s)
0 uint8 (1) 125
Item 'Scripting' have 1 value(s)
0 uint8 (1) 2
Item 'AuthRequested' have 1 value(s)
0 bool (4) false
Item 'MaxMSGLength' have 1 value(s)
0 uint16 (2) 300
Item 'GUIEngine' have 1 value(s)
0 string (7) 'Photon'
We can get a list of the targetable widgets of the window named "calc":
@> yo hexaZen/phalorieburn tell calc list
distance
dunit
weight
wunit
compute
result
We can then simply change the value of the distance ran by doing:
@> yo hexaZen/phalorieburn tell calc.distance set value 23.6

Although the value in the widget has changed, the message that is supposed to be sent by the widget to the window hasn't been cast away. We need to enforce this by scripting the user's action. This is done by the following command:
@> yo hexaZen/phalorieburn tell calc.distance invoke
We can now set a value for the weight and invoke the widget in the same call to yo:
@> yo hexaZen/phalorieburn tell calc.weight set value 60 , tell calc.weight invoke

Finally, we are going to invoke the compute button widget, as though the user just clicked on it:
@> yo hexaZen/phalorieburn tell calc.compute invoke

If we want, we can retreive the text displayed by the result widget:
@> yo hexaZen/phalorieburn tell calc.result get text
1466.98 calories
We can put together all these commands in a simple shell script, to compute and retreive the calories burned for
a series of different weights for the distance of 42 kms:
#!/bin/sh
for w in $@; do
yo hexaZen/phalorieburn \
tell calc.distance set value 42 , \
tell calc.weight set value $w , \
tell calc.distance invoke , \
tell calc.weight invoke , \
tell calc.compute invoke , \
tell calc.result get text
done
The resulting script gives us:
@> calories.sh 50 55 60 65 70 75 80 85
2175.60 calories
2393.16 calories
2610.72 calories
2828.28 calories
3045.84 calories
3263.40 calories
3480.96 calories
3698.52 calories
Using the building blocks provided by the Zinzala SDK, we are able to assemble an application with relative ease. The simplicity and power of the API gives developers of all levels the possibility of creating innovative solutions for the QNX platform.
The built-in support for scripting expands the user's experience in ways which are not forseen by the developers. Applications can be bent, within certain limits, to better suit the user's needs and usage.
The SDK should be available to third-party developers in a few months, for x86 and StrongARM architectures.
Zinzala and zSDK are trademarks, registered trademarks, or service marks of hexaZen.
All other names mentioned are trademarks, registered trademarks, or service marks of their respective companies.
Copyright © hexaZen 2003-2004. All rights reserved.
Zinzala/Docs/Papers/Introducing