Next Previous Contents

7. Opie programming guidelines

This chapter is a try to give some hints regarding software development guidelines for PDAs like the iPaq and Zaurus using opie (and also other platforms).

As there are no official opie guidelines existing, the chapter should be understood as a discussion base. I would like to hear comments via email, to be able to store and later work through them. Send mails to werner@schulte-ac.de and the opie mailing list.

The chapter will grow due to the discussion I hope. At the end we will hopefully have a usable style guide to be used for new (and old) programs.

7.1 Memory and processor speed

As PDAs have e.g. restricted memory and processor speed, it is always a quandary, to have optimization for speed or memory usage. For the iPaqs (I own a 3630 with only 32MB RAM) and the Zauruses (mine is a 5500 with 64MB RAM - 58 usable) it was always a good idea, to create all dialogs the program uses on demand hide them after usage instead on destroying them. If not ( creation while necessary and destroying if closed ), the program feels slow.

If large data has to be loaded (e.g. addressbook) it is advisable, to inform the user via e.g. a progress bar or to load data partly, show the programs main dialog and load the rest of the data in the background or on demand. For this, you may use a singleshot Timer, that loads the data, after all first dialog stuff is done.

Addressbook is a bad example for that. It loads the total data base file into memory, before the main dialog is shown. In my case (approx. 850 addresses) that takes nearly 20 secs, where nothing happens on the screen. Ok, ok, you can go arround that using the fast load algorithm, but that costs memory again.

If the data loaded is to be expected to fill the total memory, the program should inform the user before crashing the system. A bad example is ( my own ) program qphoto. If the jpeg images are larger than 1024x768x24 mostly the program is canceled by the kernel, because it wants to have more memory than existing.

Code example

The next paragraph shows an example used in qtravel. During class-construction the program just initializes the dialog pointers (e.g. trEdit).

QtravelView::QtravelView(QWidget *parent, QTravelData *d) :
                         MainDlg1(parent), ScQtUtil()
{
  qdata = d;

  ...

  cuEdit = 0;
  trEdit = 0;

  ...

}
        

To bring the dialog up, the program creates it (call defTrEdit()), and just calls exec(). Maybe you want to prepare() some stuff, before bringing it up (prepare() call). The next paragraph shows an example.

Note, that in case of OPIE usage, the Dialog is executed using the static QPEApplication function execDialog() instead of using exec(). This helps us to care for dialog size hints (see further down - Geometry).

void QtravelView::slotTripChange()
{
  QListViewItem *qlv;

  qwdebug->doDebug( 10, "%s[%d] : slotTripChange \n",__FILE__, __LINE__ );
  qlv = TripListView->currentItem();
  if (qlv != NULL)
  {
    actutrip = SearchItemInTripList( qlv );
          if (actutrip != NULL)
          {
              defTrEdit();
              trEdit->prepare( actutrip, calEdit );
#ifdef QWS_OPIE
              QPEApplication::execDialog( trEdit );
#else
              trEdit->exec();
#endif
              trEdit->hide( );
          }
  }
  showTripList();
}
        

Here we have the creation of the dialog. It is only done once in the livetime of the QTravelView class.

Note, that the Ok- and CancelButton of the trEdit dialog are hided, when OPIE is used. In this case you can use the Dialogs Cancel/OK Button.

void QtravelView::defTrEdit()
{
  qwdebug->doDebug( 8, "%s[%d] : defTrEdit \n",__FILE__, __LINE__ );
  if ( !trEdit )
  {
    trEdit = new tripEdit ( (QDialog *)this, tr("Trip"), true, qdata );
    trEdit->hide();
#ifdef QWS_OPIE
    trEdit->OkButton->hide();
    trEdit->CancelButton->hide();
#endif
    qwdebug->doDebug( 12, "%s[%d] : defTrEdit trEdit created \n",__FILE__, __LINE__ );
  }
}
        

7.2 Geometry

As PDAs and equal devices have somewhat "strange" display resolutions, you also have to think about the programs geometry management.

The program should nearly always extend to maximum size after startup. If you want to use the program also on you desktop or e.g. a tablett PC, this has to be restricted a little bit. OPIE provides the special functions.

Opie also supports rotation, which means, that all GUI elements are arranged somewhat different. If you use QT-Designer the appropriate way (do not use grid layout), that should work out of the box, until you do not design your application for e.g. only upright displays (like iPaq, or Zaurus). Try to rotate your application before distributing and correct the problems occured.

Code example

The next paragraph shows the mainWindow creation in the main.cpp function. If QWS_OPIE is set, the program calls showMainWidget. The standard QT function setMainWidget is called otherwise.

#ifdef QWS_OPIE
  a.showMainWidget(qtravel);
#else
  a.setMainWidget(qtravel);
#endif
        

Here we have the dialog call example. if QWS_OPIE is set, the static function QPEApplication::execDialog() is called, with the dialogPointer as an argument. If not, we use the standard dialog->exec() call.

#ifdef QWS_OPIE
#include <qpe/qpeapplication.h>
#endif

    defTrEdit();
    trEdit->prepare( actutrip, calEdit );
#ifdef QWS_OPIE
    QPEApplication::execDialog( trEdit );
#else
    trEdit->exec();
#endif
    trEdit->hide( );

        

The next paragraph shows a part of the class constructor. If QWS is not defined (which is always using opie), the program resizes to defined sizes. Otherwise it relies in OPIE.


#ifndef QWS
  resize( data->config.maxx, data->config.maxy );
  setMaximumSize( data->config.maxx, data->config.maxy );
#endif
        

7.3 LookAndFeel

Use a TabView as the main view, if you need to present a lot of quick accessible data. TabViews allow the programmer to sort the presented data and input methods.

Use ListViews to present data which size is unpredictable. As the ListView comes with an automatic ScrollBar, it has normally no problem to resize correctly to different resolutions. ListViews have the advantage, that you may arrange your data in a tree to group it somewhat.

Use always ScrollBars. Think about rotation or less resoltions.

Do not use MenuBars. Use a small ToolBar instead. If the amount of ToolButtons is not fitting the size, you may want to use additional PopupMenus to extend single ToolButtons (e.g. save, save as, backup). This paragraph fits the handheld needs. If you want your opie programs to be used also on larger devices (e.g. simpad), it maybe is a good idea to use a MenuBar. There is a rumour, that Opie will take the decision to use a Toolbar or a Menubar itself (however they will do that).

Use ToolButtons related to your data fields to open subdialogs (a doubleClick is not very nice on a touchscreen). If extended functions have to be presented, use the "right" mouseButton (the stylus stays on the data element) to open a context menu.

Provide copy and paste, whereever possible (and necessary).

Arrange your text input fields always on top of your application. This e.g. prevents the virtual keyboard to overlap input fiels, where the user has to input blind data. This implies, that output fields (like ListViews) have to be arranged on the bottom of the dialog (resizes automatically).

7.4 MultiLanguage

All output texts (strings) must use the tr() macro to be able to be translated to the other languages. If you fill messageboxes, use simple sentences. The complete sentence should be included in one macro even, if it includes format entries like e.g. formfeeds. This makes it dangerous for the translator on the one hand (e.g. destroy a formfeed by clearing the n), but allows to translate the complete sentence on the other.

If you use the QT-Designer (and you should always), all "what is this" texts, "tooltip" texts and so on automatically use the tr() macro.

Hint
The tr() macro is provided by QObject. If your class does not inherit QObject (or any class inheriting QObject), tr is not available. In this case, you may inherit QObject, or use the static Variant QObject::tr() directly.

Code example

The following paragraph shows the call of a MessageBox using the tr() macro.

           int exit=QMessageBox::information(this, tr("Cancel..."),
                                    tr("you did not save !\nDo you really want to cancel ?"),
                                    QMessageBox::Ok, QMessageBox::Cancel);

        

7.5 MultiTarget

still to be done.

Code example

still to be done.

7.6 Theme support

I have no idea about that. Would like to hear some hints (whether there are some).


Next Previous Contents