[Previous] [Contents] [Index] [Next]

Programming Photon without PhAB

We strongly recommend that you use PhAB to develop Photon applications-this chapter is for those who insist on not using PhAB.

This chapter discusses the following:

Basic steps

All applications using the Photon widget library follow the same basic sequence:

  1. Include <Pt.h>, the standard header file for the widget library.
  2. Initialize the Photon widget toolkit and create the main window using a call to PtAppInit().
  3. Create the widgets that make up the UI. The function PtCreateWidget() is used to create the widget and make it a child of a widget that has already been created, such as the main window.
  4. Register any callback functions in the application with the appropriate widgets using PtAddCallback() or PtAddCallbacks().
  5. Realize the widgets by calling PtRealizeWidget(). This function needs to be called only once by the application.

    The realize step actually creates any Photon regions that are required and maps them to the screen. Until this step is performed, no regions exist, and nothing is displayed on the screen.

  6. Begin processing photon events by calling PtMainLoop().

    At this point, the Photon widget toolkit takes control over the application and manages the widgets. If any widgets are to call functions in your application, they must have been registered as callbacks before this.

Compiling and linking a non-PhAB application

To compile and run an application that uses the Photon widget library, you must link against the library. There are both static and shared versions of this library. The names depend on whether you're running under QNX 4 or QNX Neutrino:

Library QNX 4 QNX Neutrino
Static photon3r.lib libphoton.a
Shared photon_s.lib libphoton.so

We recommended that you always link against the shared library. This lets you keep your applications smaller and allows them to inherit new features that are added to the widget library when new releases of the shared library are installed.

The Photon library includes most of the function and widget definitions. If your application uses Px (extended) functions or realtime widgets, you'll also need to link with the following:

Library QNX 4 QNX Neutrino
Extended phexlib3r.lib libex.a
Realtime phrtlib3r.lib libphrt.a

Linking under QNX 4

To link against the shared library, specify the following link option for the cc command:

-lphoton_s

To link against the static library, specify the following link option:

-lphoton

For example, if we have an application called hello.c, the command to compile and link is:

cc -o hello hello.c -lphoton_s

Linking under QNX Neutrino

Under Neutrino, the names of the shared and static libraries are the same. By default, qcc links against the shared library; to link against the static library, specify the -Bstatic option for qcc.


Note: You'll also need to link against the math library, libm.so or libm.a.

For example, if we have an application called hello.c, the command to compile and link against the shared libraries is:

qcc -o hello hello.c -lphoton -lm

To link against the static libraries, the command is:

qcc -o hello hello.c -Bstatic -lphoton -lm

Sample application

The following example illustrates a very simple application using the widget library. The program creates a window that contains a single pushbutton.

/*
 * File: hello.c
 */
#include <Pt.h>

int main( int argc, char *argv[] )
{
   PtWidget_t *window;
   PtArg_t    args[1];

   if ((window = PtAppInit(NULL, &argc, argv, 0, NULL))
        == NULL)
      exit(1);

   PtSetArg(&args[0], Pt_ARG_TEXT_STRING, 
             "Press to exit", 0); 
   PtCreateWidget(PtButton, window, 1, args);
   PtRealizeWidget(window);

   PtMainLoop();
   return (EXIT_SUCCESS);
}

What's going on

Although this is a simple application, a lot of work is being done by each of these calls.

PtAppInit()

This PtAppInit() call:

The first argument to this function passes the address of PtAppContext_t, which is a pointer to a structure that manages all the data associated with this application. For Photon 1.1x, this must be specified as NULL, so that the default values are used.

The second and third arguments are the common argc and argv. The toolkit parses these for standard toolkit options.

The final two arguments are an argument list, preceded by the number of elements in the list. They're used to provide initial values for resources of the top-level window widget when it's created. A pointer to this widget is returned by PtAppInit(). This top-level widget will become the parent of subsequent widgets created in the application. See the Widget Reference for more information on this widget and its available resources.

PtSetArg()

The PtSetArg() macro is used to set up an argument list that will be used to initialize the button's resources when it's created. For more information, see the Manipulating Resources in Application Code chapter.

PtCreateWidget()

The call to PtCreateWidget() creates a push-button widget as a child of the window widget, using the argument list to initialize the button's resources. All the widgets in the application - except the top-level shell window - have a container widget as a parent. Container widgets may have other containers within them. Creating the widgets in the application produces a structure called the widget family.

PtRealizeWidget()

The function PtRealizeWidget() realizes the widget and all its descendants in the widget family. Realizing a widget causes it to be displayed. In our sample application, PtRealizeWidget() is called on the top-level shell window (which is the ancestor of all the widgets in the application), so all the widgets in the application are displayed when this routine is called.

When the widget is realized, it uses the values of its resources to determine how big it must be to display its contents. Before realizing a widget, you should set any of the resources that may affect its size. You may change some of the resources after the widget has been realized, but it's up to the widget to determine if it can or will resize to accommodate the change in the resource's value. You can set flags that the widget will consult to determine whether or not to adjust its size in response to such changes, but note that if the widget exceeds the dimensions allocated to it by its parent, it will be clipped to the parent's size. There's no mechanism for the widget to negotiate with its parent to obtain more space. See the Geometry Management chapter for more information.

If a Photon region is required to display the widget correctly, it's created each time the widget is realized. A region is required under any of the following conditions:

You can unrealize a widget by calling the PtUnrealizeWidget() function. This affects the visibility of the widget and its descendants, but not the rest of the widget family hierarchy. You can then redisplay the widget later by calling PtRealizeWidget() again.

You can prevent a widget and its descendants from being realized when the widget's ancestor is realized. To do this, set Pt_DELAY_REALIZE in the widget's Pt_ARG_FLAGS resource. If you set this flag, it's the application's responsibility to call PtRealizeWidget() on the widget when the widget is to appear.

PtMainLoop()

Calling PtMainLoop() transfers control of the application to the Photon widget library.

The widget library waits for Photon events and passes them on to the widgets to handle them. Application code is executed only when callback functions that the application has registered with a widget are invoked as a result of some event.

Connecting application code to widgets

If you compile, link, and run the sample application, you'll see that a window appears with a button in it. If you push the button, nothing happens because no application code has been associated with it.

The Photon widget library is designed so that the UI code can be kept distinctly separate from the application code. The UI is composed of the code to create and manipulate the widget family hierarchy, and must call the application code in response to particular events or user actions. The connection between the application code and the UI that allows it to use the application code is the single point where these two parts have intimate knowledge of each other.

Connections are made between the UI and the application code using callbacks and event handlers.

A callback is a special type of widget resource that allows the application to take advantage of existing widget features. Using a callback, the application can register a function to be called by the widget library later in response to a particular occurrence within the widget.

Event handlers are normally used to add capabilities to a widget. For example, you could add behavior to a button press inside a widget that has no callbacks associated with button-press events.

Callbacks

A callback resource is a special form of resource used to notify the application that a specific action has occurred for a widget (e.g. the user selects a button). Every callback resource for the widget represents some particular user behavior that the widget's author anticipated would be of interest to the application.

As with all resources, a widget has its callbacks defined by its widget class, and it inherits the callbacks defined by all the ancestors of its class. This means that a widget may have several user actions that it can notify the application about.

The value of a callback resource is a callback list. Each element of the list is an application function to be called in response to the behavior and client data associated with the callback. Client data is a pointer to any arbitrary application data the application may need to provide to the callback function for it to work correctly.

For information about callbacks, see "Manipulating callbacks in your code" in the Creating Widgets in Application Code chapter.

Event handling

The writer of a widget class can't possibly anticipate every need of the application. The application may want to be notified of some occurrence on a widget that doesn't have an associated callback resource. In such cases, the application may use event handling functions.

For information about event handlers, see "Manipulating event handlers in your code" in the Creating Widgets in Application Code chapter.

Complete sample application

We can now use our newly acquired knowledge of resources and callbacks to create a more functional version of the sample application given earlier.

Using resources, we can give the pushbutton widget the same dimensions as the window, and specify which font to display the label in. We can also define the callback to be executed when the pushbutton is pressed. We'll make the callback function display a simple message and exit.

Here's the complete source code for our sample program with these changes:

#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>

int main( int argc, char *argv[] )
{
    PtArg_t args[3];
    PtWidget_t *window;
    int push_button_cb( PtWidget_t *, void *, 
                        PtCallbackInfo_t *); 
    PtCallback_t callbacks[] = {{push_button_cb, NULL}};

    if ((window = PtAppInit(NULL, &argc, argv, 0, NULL))
        == NULL) 
        exit(EXIT_FAILURE);

    PtSetArg(&args[0], Pt_ARG_TEXT_STRING, 
             "Press to exit", 0); 
    PtSetArg(&args[1], Pt_ARG_TEXT_FONT, "helv14b", 0); 
    PtSetArg(&args[2], Pt_CB_ACTIVATE, callbacks, 
        sizeof(callbacks)/sizeof(callbacks[0])); 
    PtCreateWidget(PtButton, window, 3, args);

    PtRealizeWidget(window); 
    PtMainLoop(); 
    return (EXIT_SUCCESS);
}

int
push_button_cb(PtWidget_t *w, void *data, 
               PtCallbackInfo_t *cbinfo) 
{ 
    printf( "I was pushed\n" ); 
    exit( EXIT_SUCCESS ); 

    /* This line won't be reached, but it keeps
       the compiler happy. */

    return( Pt_CONTINUE );
}

[Previous] [Contents] [Index] [Next]