dis/assemblering_maniac

Friday, October 01, 2004

Reimplementing Event Handlers

Reimplementing Event Handlers

Events are generated by the window system or by Qt in response to various occurrences. When the user presses or releases a key or mouse button, a key or mouse event is generated. When a window is moved to reveal a window that was underneath, a paint event is generated to tell the newly visible window that it needs to repaint itself. An event is also generated whenever a widget gains or loses keyboard focus. Most events are generated in response to user actions, but some, like timer events, are generated independently by the system.

Events should not be confused with signals. Signals are useful when using a widget, whereas events are useful when implementing a widget. For example, when we are using QPushButton, we are more interested in its clicked() signal than in the low-level mouse or key events that caused the signal to be emitted. But if we are implementing a class like QPushButton, we need to write code to handle mouse and key events and emit the clicked() signal when necessary.

Events are notified to objects through their event() function, inherited from QObject. The event() implementation in QWidget forwards the most common types of events to specific event handlers, such as mousePressEvent(), keyPressEvent(), and paintEvent(), and ignores other kinds of events.

We have already seen many event handlers when implementing MainWindow, IconEditor, Plotter, ImageEditor, and Editor in the previous chapters. There are many other types of events, listed in the QEvent reference documentation, and it is also possible to create custom event types and dispatch custom events ourselves. Custom events are particularly useful in multithreaded applications, so they are discussed in Chapter 17 (Multithreading). Here, we will review two event types that deserve more explanation: key events and timer events.

Key events are handled by reimplementing keyPressEvent() and keyReleaseEvent(). The Plotter widget reimplements keyPressEvent(). Normally, we only need to reimplement keyPressEvent() since the only keys for which release is important are the modifier keys Ctrl, Shift, and Alt, and these can be checked for in a keyPressEvent() using state(). For example, if we were implementing a CodeEditor widget, its stripped-down keyPressEvent() that distinguishes between Home and Ctrl+Home would look like this:

void CodeEditor::keyPressEvent(QKeyEvent *event)
{
switch (event->key()) {
case Key_Home:
if (event->state() & ControlButton)
goToBeginningOfDocument();
else
goToBeginningOfLine();
break;
case Key_End:
...
default:
QWidget::keyPressEvent(event);
}
}

The Tab and Backtab (Shift+Tab) keys are special cases. They are handled by QWidget::event() before it calls keyPressEvent(), with the semantic of passing the focus to the next or previous widget in the focus chain. This behavior is usually what we want, but in a CodeEditor widget, we might prefer to make Tab indent a line. The event() reimplementation would then look like this:

bool CodeEditor::event(QEvent * event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = (QKeyEvent *) event;
if (keyEvent->key() == Key_Tab) {
insertAtCurrentPosition('\t');
return true;
}
}
return QWidget::event(event);
}

If the event is a key press, we cast the QEvent object to a QKeyEvent and check which key was pressed. If the key is Tab, we do some processing and return true to tell Qt that we have handled the event. If we returned false, Qt would propagate the event to the parent widget.

A higher-level approach for implementing key bindings is to use a QAction. For example, if goToBeginningOfLine() and goToBeginningOfDocument() are public slots in the CodeEditor widget, and the CodeEditor is used as the central widget in a MainWindow class, we could add the key bindings with the following code:

MainWindow::MainWindow(QWidget *parent, const char *name)
: QMainWindow(parent, name)
{
editor = new CodeEditor(this);
setCentralWidget(editor);

goToBeginningOfLineAct =
new QAction(tr("Go to Beginning of Line"),
tr("Home"), this);
connect(goToBeginningOfLineAct, SIGNAL(activated()),
editor, SLOT(goToBeginningOfLine()));

goToBeginningOfDocumentAct =
new QAction(tr("Go to Beginning of Document"),
tr("Ctrl+Home"), this);
connect(goToBeginningOfDocumentAct, SIGNAL(activated()),
editor, SLOT(goToBeginningOfDocument()));
...
}

This makes it easy to add the commands to a menu or a toolbar, as we saw in Chapter 3. If the commands don't appear in the user interface, the QAction objects could be replaced with a QAccel object, the class used by QAction internally to support key bindings.

The choice between reimplementing keyPressEvent() and using QAction (or QAccel) is similar to that between reimplementing resizeEvent() and using a QLayout subclass. If we are implementing a custom widget by subclassing QWidget, it's straightforward to reimplement a few more event handlers and hard-code the behavior there. But if we are merely using a widget, the higherlevel interfaces provided by QAction and QLayout are more convenient.

Another common type of event is the timer event. While most types of events occur as a result of a user action, timer events allow applications to perform processing at regular time intervals. Timer events can be used to implement blinking cursors and other animations, or simply to refresh the display.

To demonstrate timer events, we will implement a Ticker widget. This widget shows a text banner that scrolls left by one pixel every 30 milliseconds. If the widget is wider than the text, the text is repeated as often as necessary to fill the entire width of the widget.

Figure 7.1. The Ticker widget


Here's the header file:

#ifndef TICKER_H
#define TICKER_H

#include

class Ticker : public QWidget
{
Q_OBJECT
Q_PROPERTY(QString text READ text WRITE setText)

public:
Ticker(QWidget *parent = 0, const char *name = 0);

void setText(const QString &newText);
QString text() const { return myText; }
QSize sizeHint() const;

protected:
void paintEvent(QPaintEvent *event);
void timerEvent(QTimerEvent *event);
void showEvent(QShowEvent *event);
void hideEvent(QHideEvent *event);

private:
QString myText;
int offset;
int myTimerId;
};

#endif

We reimplement four event handlers in Ticker, three of which we have not seen before: timerEvent(), showEvent(), and hideEvent().

Now let's review the implementation:

#include

#include "ticker.h"

Ticker::Ticker(QWidget *parent, const char *name)
: QWidget(parent, name)
{
offset = 0;
myTimerId = 0;
}

The constructor initializes the offset variable to 0. The x coordinate at which the text is drawn is derived from the offset value.

void Ticker::setText(const QString &newText)
{
myText = newText;
update();
updateGeometry();
}

The setText() function sets the text to display. It calls update() to force a repaint and updateGeometry() to notify any layout manager responsible for the Ticker widget about a size hint change.

QSize Ticker::sizeHint() const
{
return fontMetrics().size(0, text());
}

The sizeHint() function returns the space needed by the text as the widget's ideal size. The QWidget::fontMetrics() function returns a QFontMetrics object that can be queried to obtain information relating to the widget's font. In this case, we ask for the size required by the given text.

void Ticker::paintEvent(QPaintEvent *)
{
QPainter painter(this);

int textWidth = fontMetrics().width(text());
if (textWidth < 1)
return;
int x = -offset;
while (x < width()) {
painter.drawText(x, 0, textWidth, height(),
AlignLeft | AlignVCenter, text());
x += textWidth;
}
}

The paintEvent() function draws the text using QPainter::drawText(). It uses fontMetrics() to ascertain how much horizontal space the text requires, and then draws the text as many times as necessary to fill the entire width of the widget, taking offset into account.

void Ticker::showEvent(QShowEvent *)
{
myTimerId = startTimer(30);
}

The showEvent() function starts a timer. The call to QObject::startTimer() returns an ID number, which we can use later to identify the timer. QObject supports multiple independent timers, each with its own time interval. After the call to startTimer(), Qt will generate a timer event approximately every 30 milliseconds; the accuracy depends on the underlying operating system.

We could have called startTimer() in the Ticker constructor, but we save some resources by having Qt generate timer events only when the widget is actually visible.

void Ticker::timerEvent(QTimerEvent *event)
{
if (event->timerId() == myTimerId) {
++offset;
if (offset >= fontMetrics().width(text()))
offset = 0;
scroll(-1, 0);
} else {
QWidget::timerEvent(event);
}
}

The timerEvent() function is called at intervals by the system. It increments offset by 1 to simulate movement, wrapping at the width of the text. Then it scrolls the contents of the widget one pixel to the left using QWidget::scroll(). It would have been sufficient to call update() instead of scroll(), but scroll() is more efficient and prevents flicker, because it simply moves the existing pixels on screen and only generates a paint event for the widget's newly revealed area (a 1-pixel-wide strip in this case).

If the timer event isn't for the timer we are interested in, we pass it on to our base class.

void Ticker::hideEvent(QHideEvent *)
{
killTimer(myTimerId);
}

The hideEvent() function calls QObject::killTimer() to stop the timer.

Timer events are low-level, and if we need multiple timers, it can become cumbersome to keep track of all the timer IDs. In such situations, it is usually easier to create a QTimer object for each timer. QTimer emits the timeout() signal at each time interval. QTimer also provides a convenient interface for single-shot timers (timers that time out just once).

Thursday, September 30, 2004

INTRODUCTION TO EXCEPTION HANDLING

INTRODUCTION TO EXCEPTION HANDLING PART 1 - A SIMPLE EXAMPLE

In this and subsequent issues we will be discussing some aspects of
C++ exception handling. To start this discussion, let's consider a
simple example. Suppose that you are writing a program to manipulate
calendar dates, and want to check whether a given year is in the 20th
century (ignoring the issue of whether the 21st century starts in 2000
or 2001!).

Using exceptions, one way to do this might be:

#include

class DateException {
char* err;
public:
DateException(char* s) {err = s;}
void print() const {cerr << err << endl;}
};

// a function that operates on dates
void g(int date)
{
if (date < 1900)
throw DateException("date < 1900");
if (date > 1999)
throw DateException("date > 1999");
// process date ...
}

// some code that uses dates
void f()
{
g(1879);
}

int main()
{
try {
f();
}
catch (const DateException& de) {
de.print();
return 1;
}

return 0;
}

The basic idea here is that we have a try block:

try {
f();
}

Within this block, we execute some code, in this case a function call
f(). Then we have a list of one or more handlers:

catch (DateException de) {
de.print();
return 1;
}

If an abnormal condition arises in the code, we can throw an exception:

if (date < 1900)
throw DateException("date < 1900");

and have it caught by one of the handlers at an outer level, that is,
execution will continue at the point of the handler, with the
execution stack unwound.

An exception may be a class object type such as DateException, or a
fundamental C++ type like an integer. Obviously, a class object type
can store and convey more information about the nature of the
exception, as illustrated in this example. Saying:

throw -37;

will indeed throw an exception, which may be caught somewhere, but
this idiom is not particularly useful.

What if the handler we declare is changed slightly, as in:

catch (DateException* de) {
de->print();
return 1;
}

In this case, because an object of type DateException is thrown,
rather than a DateException* (pointer), no corresponding handler will
be found in the program. In that case, the runtime system that
handles exception processing will call a special library function
terminate(), and the program will abort. One way to avoid this
problem is to say:

main()
{
try {
body_of_program();
}
catch (...) {
// all exceptions go through here
return 1;
}

return 0;
}

where "..." will catch any exception type.

We will explore various details of exception handling in future
issues, but one general comment is in order. C++ exceptions are not
the same as low-level hardware interrupts, nor are they the same as
UNIX signals such as SIGTERM. And there's no linkage between
exceptions such as divide by zero (which may be a low-level machine
exception) and C++ exceptions.

Common Programming Mistakes

Common Programming Mistakes


Zack Rusin zack@kde.org
Till Adam adam@kde.org
Richard Moore rich@kde.org
editorial afnindar@kde.org

Intro
This document aims to combine the experience of many of the top KDE developers about Qt and KDE frameworks dos and dont's. The way they were usually passed on to the next generation was by letting the youngsters make the mistakes and then yell at them in public. After reading this document you should be a lot less fearful of having your code reviewed by one of the more grumpy KDE developers [ ] We will go over things, which are not necessarily "bugs" but which make the code, either slower or less readable.

General C++
In this section we'll guide through some of the more dusty corners of C++ which either tend to be misused or which people simply get wrong.
Anonymous namespaces vs statics
If you have a method in a class that does not access any members and therefor does not need an object to operate, make it static. If additionally it is a private helper function that is not needed outside of the file, make it a file-static function. That hides the symbol completely.

Symbols defined in a C++ anonymous namespace do not have internal linkage. Anonymous namespaces only give an unique name for that translation unit and that is it; they don't change the linkage of the symbol at all. Linkage isn't changed on those because the second phase of two-phase name lookup ignores functions with internal linkages. Also, entities with internal linkage cannot be used as template arguments.

So for now instead of using anonymous namespaces use static if you don't want a symbol to be exported.
NULL pointer issues
First and foremost: it's fine to delete a null pointer. So the constructs like:

if (ptr)
delete ptr;

are simply redundant.
Second of all you'll see null pointers marked with three types: 0, 0L and NULL. The argument against using NULL was that while C defines it as a 0 void pointer, C++ defines it to not be a 0 void pointer. All conforming C++ implementations will define NULL correctly so it's really not a problem. The argument for 0L was that it was handled correctly in variable argument functions, while 0 wasn't. Nowadays that's also an artifact.
It's more a question of getting used to something. As far as the code in CVS goes you'll see 0 used more commonly than NULL.
When you delete a pointer, make sure you also set it to 0. So the idiom is:

delete ptr;
ptr = 0;

Member variables
You'll encounter four major styles of marking member variables in KDE:
m_variable - lowercase m, underscore and the name of the variable starting with a lowercase letter
mVariable - lowercase m and the name of variable starting with a uppercase letter
variable_ - variable name starting with a lowercase letter and then an underscore
_variable - underscore and the name of variable starting with a lowercase letter. It's being often frowned upon.
As it often happens there's no one correct way of doing it, so remember to always follow the syntax used by the application/library to which you are committing.
Static variables
Try to limit the number of static variables used in your code, especially when committing to a library. Construction and initialization of large number of static variables really hurts the startup times.
Forward Declarations
You will reduce compile times by forward declaring classes when possible instead of including their respective headers. For example:

#include //bad
#include //bad
#include //bad
class SomeInterface
{
public:
virtual void widgetAction( QWidget *widget ) =0;
virtual void stringAction( const QString& str ) =0;
virtual void stringListAction( const QStringList& strList ) =0;
};

The above should look as follows:

class QWidget; //good
class QStringList; //good
class QString; //good
class SomeInterface
{
//as above
};

Iterators
There's few issues at hand here, so in no particular order:
Cache the return of the end() method call before doing iteration over large containers. For example:

QValueList container;
//code which inserts a large number of elements to the container
QValueListConstIterator end( container.end() );
for( QValueListConstIterator itr( container.begin() ); itr != end; ++itr ) {
}

This avoids the unnecessary creation of the temporary end() return object on each loop iteration, largely speeding it up.
Prefer to use const_iterators over normal iterators when possible. Containers, which are being implicitly shared often detach when a call to a non-const begin() or end() methods is made (QValueList is an example of such a container). When using a const_iterator also watch out that you're really calling the const version of begin() and end(). Unless your container is actually const itself this probably won't be the case, possibly causing an unnecessary detach of your container. So basically whenever you use const_iterator initialize them using constBegin()/constEnd() instead of begin()/end(), to be on the safe side.
Prefer to use pre-increment over post-increment operators on iterators. You'll avoid creating an unnecessary temporary object.
Data Structures
In this section we'll go over some our most common pet-peeves which affect data structures very commonly seen in Qt/KDE applications.
QPtrList
QPtrList, as the name describes, is a list of pointers. QPtrList's are not implicitly shared. That means that every time you have constructs like :

class SomeClass
{
public:
QPtrList returnList() const;
private:
QPtrList m_list;
};

each invocation of the returnList method will copy the full QPtrList by iterating over all of its elements and inserting them in the newly created QPtrList.
So we recommend to use QValueList instead of QPtrList. You'll loose the ability to auto delete the pointers on destruction of the list, but it's trivial to add code which does that in the destructor of your class.
QCString
One of the most common mistakes is using QCString::length() method in a loop. QCString::length() calls strlen on each invocation, so calling it in a loop, as in

QCString someCString = ...;
for( int i = 0; i < someCString.length(); ++i ) {
//Do something
}

Will result in exactly strlen(someCString)+1 calls of strlen(someCString).
If you have to use QCString, remember to cache the result of QCString::length() method if you're planning to use it in a loop.
QByteArray
Often in KDE code developers want to transform the QByteArray to a QCString. The code which is often tried looks as follows:

void someFunc( const QByteArray& data )
{
QCString str( data ); //this is wrong!
}

The trick here is that QByteArray don't have to be null terminated. This introduces a subtle problem of trying to create a non-null terminated string. The correct code for constructing QCString's from QByteArray's is: QCString str( data, data.length() + 1 );
QString
It is common to want to test to see if a QString is null. It is faster to do this using the isNull() method than by comparing to the static QString::null value.

if ( mystring.isNull() ) { // YES!
}
if ( mystring == QString:: null ) { // NO!
}

Another twist to this comes when you want to test for the empty string rather that a null string - again Qt provides a method for the job. In many cases this can be preferred to testing for null:

if ( mystring.isEmpty() ) { // YES!
}
if ( mystring == "" ) { // NO!
}

QString
If you're reading in a file it is faster to convert it from the local encoding to unicode (QString) in one go rather than line by line. This means that methods like QIODevice::readAll() are often a good solution, followed by a single QString instantiation.

For larger files consider reading a block of lines then performing the conversion, that way you get the opportunity to update your GUI. This can be accomplished either by using qApp->processEvents() or by reentering the event loop normally and using a timer to read in the blocks in the background.
QString
Pass QStrings as const QString&. Even though QString is implicitly shared it is still more efficient (and safer) to pass const references as opposed to objects by value. So the canonical signature of a method taking QString arguments would be:

void myMethod( const QString & foo, const QString & bar );

Actually, pretty much everything should be passed by const reference if possible.
QString
While QString is the tool of choice for many string handling situations there is one where it is particularly inefficient. If you are pushing about and working on data in QCStrings or QByteArrays take care not to pass it through methods which take QString parameters and then make QCStrings or QByteArrays from them again. For example:


QCString myData;
QString myNewData = mangleData( myData );

QString mangleData( const QString data )
{
QCString str = data.latin1();
// mangle
return QString(str);
}

The expensive thing happening here is the conversion to QString which does conversion to unicode internally. That is unnecessary as the first thing the method does is convert back to latin1(). So if you are sure that the unicode conversion is not needed, try to avoid inadvertedly using QString along the way. so the above example should read:

QCString myData;
QCString myNewData = mangleData( myData );

QCString mangleData( const QCString& data )

Saturday, September 18, 2004

An Object-Oriented Design

Our implementations of min() and max() make no special assumptions about the storage of the
array elements and therefore require that we examine each element. Had we required the elements to
be sorted, the implementation of both operations would become a simple index into, respectively, the
first and last element. Moreover, a search for the presence of an element is considerably more
efficient if the elements are known to be sorted. Supporting the elements in sorted order, however,
adds to the complexity of the Array class implementation. Have we made a mistake in our design?
We haven't made a mistake so much as we've made a choice. A sorted array is a specialized
implementation: when it is necessary, it is absolutely necessary; otherwise, the overhead of its
support is a burden. Our implementation is more general and, in most circumstances, adequate; it
supports a wider range of users. Unfortunately, if the user absolutely needs the behavior of a sorted
array, our implementation cannot support that. There is no way for the user to override the more
general implementations of min(), max(), and find(). In effect, we have chosen a generally useful
implementation that is inappropriate under special circumstances.
On the other hand, for another category of user our implementation is too specialized: rangechecking
of the index adds overhead to each access of an element. We discounted the cost of this in
our design (see item 8 in Section 2.3) with the assumption that being fast is of little value if we are
incorrect. This assumption, however, does not hold true for at least one of our major users: a realtime
virtual-immersion or virtualreality provider. The arrays, in this case, represent shared vertices of
complex 3D geometry. The scene flies by too quickly for an occasional error to be generally visible,
but if the general access is too slow, the immersion effect breaks down. Our implementation,
although considerably safer than a non-range-checking array class, is impractical for this application
domain.
How might we support the needs of these three sets of users? The solutions are already present in the
code, more or less. For example, our range-checking is localized within the subscript operator.
Remove the invocation of check_range(), rename the array, and we now have two
implementations: one with and one without range-checking. Copy more of the code, modify it to
treat the array as sorted, and we now have support for a sorted array:
// Unsorted, with no bounds-checking
class IntArray{ ... };
// Unsorted, with bounds-checking
class IntArrayRC{ ... };
// Sorted, without bounds-checking
class IntSortedArray{ ... };
What are the drawbacks with this solution?

Thursday, September 16, 2004

First Post at myBlog ....

welcome to the jungle boy,
this blog is your and make it life,
as your life in earth .....................

regards,
slickenside