" Dont judge those who try and fail, judge those who fail to try "

Beginning KDevelop Programming Version 3.x

Chapter One :- Introducing KDevelop Chapter Two :- The KDE Application Chapter Three :- Common Widgets Chapter Four :- Containers And Views Chapter Five :- Database Programming With MySQL Chapter Six :- Input And Display Chapter Seven :- KDE Display Widgets Chapter Eight :- KDE Buttons And Input Chapter Nine :- KDE Containers And Views Chapter Ten :- Custom Widgets Chapter Eleven :- Events Chapter Twelve :- Drawing Chapter Thirteen :- Global Information and Configuration Files Chapter Fourteen :- A Simple Editor Application

Appendices

Appendix A :- Upgrading KDevelop

Downloads

PDF

Beginning KDevelop Programming ( 13.4 mb )

ZIP

Chapter One Source ( 1.2mb ) Chapter Two Source ( 3.8 mb ) Chapter Three Source ( 6.9 mb ) Chapter Four Source ( 6.4 mb ) Chapter Five Source ( 6.6 mb ) Chapter Six Source ( 3.6 mb ) Chapter Seven Source ( 2.2 mb ) Chapter Eight Source ( 2.2 mb ) Chapter Nine Source ( 1.1 mb ) Chapter Ten Source ( 5.3 mb ) Chapter Eleven Source ( 1.6 mb ) Chapter Twelve Source ( 5.2 mb ) Chapter Thirteen Source ( 3.8 mb ) Chapter Fourteen Source ( 616 kb )

Contributions

Contributors Page

Contacts

Author

[email protected]

Page Designer

[email protected]

Chapter 11 :- Events

Events are generally the low level inputs from the computer the most common of which are generated from the computer hardware itself. If you remember from the chapter eight dates and times project we set up a timer and created a function that was triggered everytime the timer fired. Well in the background the timer triggered an event that called the function that we had specified through the connect function. If we look through the QWidget class we can see a whole list of events declared as protected virtual functions.

virtual bool event ( QEvent * e )
virtual void mousePressEvent ( QMouseEvent * e )
virtual void mouseReleaseEvent ( QMouseEvent * e )
virtual void mouseDoubleClickEvent ( QMouseEvent * e )
virtual void mouseMoveEvent ( QMouseEvent * e )
virtual void wheelEvent ( QWheelEvent * e )
virtual void keyPressEvent ( QKeyEvent * e )
virtual void keyReleaseEvent ( QKeyEvent * e )
virtual void focusInEvent ( QFocusEvent * )
virtual void focusOutEvent ( QFocusEvent * )
virtual void enterEvent ( QEvent * )
virtual void leaveEvent ( QEvent * )
virtual void paintEvent ( QPaintEvent * )
virtual void moveEvent ( QMoveEvent * )
virtual void resizeEvent ( QResizeEvent * )
virtual void closeEvent ( QCloseEvent * e )
virtual void contextMenuEvent ( QContextMenuEvent * e )
virtual void imStartEvent ( QIMEvent * e )
virtual void imComposeEvent ( QIMEvent * e )
virtual void imEndEvent ( QIMEvent * e )
virtual void tabletEvent ( QTabletEvent * e )
virtual void dragEnterEvent ( QDragEnterEvent * )
virtual void dragMoveEvent ( QDragMoveEvent * )
virtual void dragLeaveEvent ( QDragLeaveEvent * )
virtual void dropEvent ( QDropEvent * )
virtual void showEvent ( QShowEvent * )
virtual void hideEvent ( QHideEvent * )
virtual bool macEvent ( MSG * )
virtual bool winEvent ( MSG * )
virtual bool x11Event ( XEvent * )
virtual bool qwsEvent ( QWSEvent * )

The paintEvent we have already come across when we did our own custom drawing in previous chapters and we'll be dealling more with paintEvent later in this chapter and again when we get to the chapter on drawing.

KeyBoard Basics

For this section we will concentrate on the standard keyboard and mouse events and pretty much ignore the rest, unless we find we need them in later projects.

The functions are declared as protected virtual functions which means that if we want to override them by subclassing the widget that implements them. In the case of the key demonstration code we will override the QTextEdit class with the class,

class KKeyDemoEdit : public QTextEdit
{
        Q_OBJECT

which somewhat unfortunately gives us a starting project that looks like,


with the subclassed Edit widget being added in the constructor with the code,

KKeyDemoEdit *textEdit = new KKeyDemoEdit( this, "demoEdit" );
textEdit->setGeometry( QRect( 11, 16, 390, 300 ) );
textEdit->setSizePolicy( QSizePolicy( (QSizePolicy::SizeType)7, (QSizePolicy::SizeType)7, 	0, 0, textEdit->sizePolicy().hasHeightForWidth() ) );
textEdit->setMinimumSize( QSize( 390, 300 ) );      

All the functionality for the demo program takes place within the KKeyDemoEdit which overrides the functions KeyPress and Key Release

protected:
        virtual void keyPressEvent( QKeyEvent *keyEvent );
        virtual void keyReleaseEvent( QKeyEvent *keyEvent );

it also declares some simple state variables,

private:
        bool bControlPressed;
        bool bShiftPressed;
        bool bAltPressed;

and implements the gets and sets for them.

The main part of the program takes place when a key is pressed and the keyPressEvent function is called.

The function starts with the code,

bool bStateSet = false;
int nKey = keyEvent->key();
append( "KeyPressed" );
append( "Key = " + QString::number( keyEvent->key() ) );
append( "KeyText = " + keyEvent->text() );
append( "Ascii Code = " + QString::number( keyEvent->ascii() ) );
append( "Button State = " );

The code adds text to our edit control that tells us about the key that was pressed. To start with the data is collected using the functions available from the QKeyEvent pointer passed to it. The values returned are pretty simple in that the key is the integer value for the key the text is the text of the key which is the letter or number that was pressed and the ascii code represents the ascii value of the key. Typically though life isn't always simple as the state value which in the qevent header file reads as,

ButtonState state() const   { return ButtonState(s); }

should return the state for the button so that we can tell if the Control, Shift or the Alt have been pressed, this doesn't appear to ever get set as in the debugger the value s is almost always zero, I say almost because if you add the code,

if( keyEvent->state() == QKeyEvent::ControlButton )
{
  int i=0;
}

and set a breakpoint at i = 0 you will occasionally catch it though on my development system it misses more than it catches and therefore comes under the heading of unreliable, still we have the key value as an integer so we can manage.

if( nKey == 4128 )
{
   moveCursor( QTextEdit::MoveEnd, false );
   insert( "Shift pressed " );
   bStateSet = true;
   setShiftPressed( true );
}

This code checks to see if the shift button has been pressed and if so sets the ShiftPressed value to true which means if the key is still pressed when another key is pressed the code at the end of the function,

if( nKey != 4128 && shiftPressed() == true )
{
   moveCursor( QTextEdit::MoveEnd, false );
   append( "Shift + " + keyEvent->text() + " pressed" );
}

is activated. Other parts of the function check to see if the keypad has been pressed or if the key is repeating. The final part is the isAccepted function. This if set to true, tells the system that we have dealt with the key press and effectively swallows it. If it is set to false then the key stroke is passed through the system to anything that wants to use it.

The running program looks like,


There is however, another way of doing getting the keyboard input and various other events from within a class that doesn't rely on subclassing the widget that you are using. This is done through a process called event filtering. What happens is that as event such as keystrokes are passed through the system then you can tell Qt that you wish to handle events within your own code. We use this technique in the keyboardBasicsFilter project.


This project is almost identical to the previous project except that we are using a standard edit widget and in the constructor we call,

textEdit->installEventFilter( this );

The event filter itself is declared as.

virtual bool eventFilter( QObject *watched, QEvent *e );

Note that we are overriding the eventFilter function we are not declaring a user function to handle the events, we are simple saying by calling installEventFilter that we have overriden the eventFilter function.

In the eventFilter function we have this code.

if( watched == textEdit )
{
   if( event->type() == QEvent::KeyPress )
   {
      QKeyEvent *keyEvent = static_cast< QKeyEvent * >( event );
      return FilterKeyPressedEvent( keyEvent );
   }
   if( event->type() == QEvent::KeyRelease )
   {
      QKeyEvent *keyEvent = static_cast< QKeyEvent * >( event );
      return FilterKeyReleasedEvent( keyEvent );
   }
}
else
{
   return this->eventFilter( watched, event );
}

What we are doing here is testing to see if the event occurred in the textEdit widget which is the one that we wanted the keyboard input for and then we check the event type. If the type is a QEvent KeyPress then we cast the passed in QEvent to a QKeyEvent and pass it to the function that we are using to deal with the keypresses. We do exactly the same for the key release and end up with a program that looks suspiciously like the previous program.


And that's how we get the keyboard input.

Tip Of The Day

When we talk about event filters the theory makes it sound as if we are adding an event filter that will be able to filter all events that are available on the computer. This is not entirely true in that certain items an widgets get certain events and certain others don't. On the whole this makes sense but it can be a stumbling block if you are trying to do something different, wether it is considered good gui design or not. You will notice that the MouseBasic code contains,

if( e->type() == QEvent::KeyPress )
{
    QMessageBox::information( this, "info", "key pressed" );
    return true;
}

This code is never executed, which when you think about it, it is not unreasonable to assume that a standard widget will not require keyboard input.

Mouse Basics

When dealling with the mouse there are three events that we are going to focus on these are the MouseButtonPress and the Wheel events, which we will then test to see which button was pressed or which way the wheel was turned. Of course as we are focusing on the mouse there isn't much to look at so we start off with an unispiring looking project.


As with the previous project this project is written using the installEventFilter way of handling event filters.

Popup Menus

Everyone who has ever used a computer knows what a popup or a context menu is. You right click on the form or widget and a relevant menu pops up giving you a limited range of options that you can perform on you current task. For instance you may be able to set some item data within the program or choose a shape or pen but you will rarely be able to access the file save menu from a popup menu.

In this project we will using the mouse to place text characters on to the form, using the popup menu to select the characters and the mouse wheel to select between upper and lower case characters.

To create a popup menu declare it in the header file.

QPopupMenu *popupMenu;
QPopupMenu *capsPopupMenu;

Note here that we are creating two menus one for standard characters and one for capital characters, which means that in the constructor we have,

popupMenu = new QPopupMenu();
popupMenu->insertItem( "a", this, SLOT( setA() ) );
popupMenu->insertItem( "b", this, SLOT( setB() ) );

and

capsPopupMenu = new QPopupMenu();
capsPopupMenu->insertItem( "A", this, SLOT( setA() ) );
capsPopupMenu->insertItem( "B", this, SLOT( setB() ) );

As you can see the popup menus are allocated the same as any other QObject and then we call insertItem to add the menu item that will be viewed when the popup is shown. There are a number of insertItem overrides defined in the QPopup class and you should look at them in order to choose the one that best suits what you want to do. The one used in the project is takes the form of

int insertItem ( const QString & text, const QObject * receiver, const char * member, const QKeySequence & accel = 0, int id = -1, int index = -1 )

With the text string being the text to be shown, the reciever being the class that will receive the signal when the item is clicked, the member being the slot that will be called when the menu item is clicked, the QKeySequence being the accelerator keys to quickly access the menu item, the id being the identification number and the index being the index number for placing the item in the menu which if left to its default will add the item at the end.

The slots are defined in the header as you would expect,

public slots:
    /*$PUBLIC_SLOTS$*/
        
     void setA();
     void setB();
     void setC();

with standard implementations,

void MouseBasicsWidget::setA()
{
     if( capsLocked() == true )
         setText( 'A' );
     else
         setText( 'a' );
}

which set the internal text value to the character required. As mentioned earlier the program uses the installEventFilter function in the constructor so we check for the right button click with the code

if( e->type() == QEvent::MouseButtonPress )
{
    QMouseEvent *mouseEvent = static_cast< QMouseEvent * >( e );
    if( mouseEvent->button() == Qt::LeftButton )
    {
        drawText( mouseEvent->x(), mouseEvent->y(), QString( QChar( text() ) ) ); 
        TextData textData;
        textData.setText( QChar( text() ) );
        textData.setXpos( mouseEvent->x() );
        textData.setYpos( mouseEvent->y() );
        textList.append( textData );
                                
        return true;
    }
                        
    if( mouseEvent->button() == Qt::RightButton )
    {
        if( capsLocked() == true )
            capsPopupMenu->exec( mouseEvent->globalPos() );
        else
            popupMenu->exec( mouseEvent->globalPos() );
                                        
        return true;
    }
}

When the event comes through we check forst of all that it is a mouse button press event and then convert the QEvent passed to the filter to a QMouseEvent before checking which button was actually pressed. Don't worry about the left button press for now we'll get to that in a minute. When the right button is pressed we execute either the normal small character popup menu or the capitals popup menu which looks like,


To set the menu to capitals we, check for the wheel movement, if the wheel is rotated forwards then we use the capital popup menu and if it is rotated backwards then we use the standard lower case characters. This is done in the eventFilter function with the code.

if( e->type() == QEvent::Wheel )
{
    QWheelEvent *wheelEvent = static_cast< QWheelEvent * >( e );
    if( wheelEvent->delta() > 0 )
        setCapsLocked( true );
    else
        setCapsLocked( false );
                                
    return true;
}
Collection Basics and Drawing

Later on we will look at using Collection classes in your projects in some detail but for now we will just use a basic collection class that will be needed when we draw the text. The reason for this is that when the left button is clicked with the code,

if( mouseEvent->button() == Qt::LeftButton )
{
    drawText( mouseEvent->x(), mouseEvent->y(), QString( QChar( text() ) ) ); 
    TextData textData;
    textData.setText( QChar( text() ) );
    textData.setXpos( mouseEvent->x() );
    textData.setYpos( mouseEvent->y() );
    textList.append( textData );
                                
    return true;
}

We initially draw the text using the drawText function. This is fine for a start but if we then use the popup menu again and it covers the characters that we have already placed on the widget or if the widget is minimised and then resized the characters will not be drawn again. The simple reason for this being that we have effectively drawn over them and not put in any commands to tell the program that when this happens and they become visible again they should be redrawn.

To do this we need to use the paintEvent function again with the code.

void MouseBasicsWidget::paintEvent( QPaintEvent *paintEvent )
{
   QPainter painter( this );
        
   TextList::iterator it;
        
   for( it=textList.begin(); it != textList.end(); it++ )
   {
      drawText( ( *it ).xpos(), ( *it ).ypos(), QString( QChar( ( *it ).text() ) ) ); 
   }
        
}

What is happening here is that we have a TextList that we move/iterate through and then draw all the characters in the list with the drawText function. The list itself is made up of the class TextData which is declared as,

class TextData
{
public:
        TextData();
        virtual ~TextData();

        inline void setYpos( int pos ){ nYpos = pos; };
        inline int ypos(){ return nYpos; };
        inline void setXpos( int pos ){ nXpos = pos; };
        inline int xpos(){ return nXpos; };
        inline void setText( char text ){ cText = text; };
        inline char text(){ return cText; };
        
private:
        int nYpos;
        int nXpos;
        char cText;
};

Literally that is all it is the cpp file merely contains an empty constructor and destructor. As you can see it contains everything we need for drawing a single character to the screen. The y position, the x position and the character itself. So if we look at what happens when we click the left button again,

if( mouseEvent->button() == Qt::LeftButton )
{
   drawText( mouseEvent->x(), mouseEvent->y(), QString( QChar( text() ) ) ); 
   TextData textData;
   textData.setText( QChar( text() ) );
   textData.setXpos( mouseEvent->x() );
   textData.setYpos( mouseEvent->y() );
   textList.append( textData );
                                
   return true;
}

You can see that we create an object of the type TextData fill it out and append it to the textList which if you have any experience of programming you should have used before the onl;y difference here being that Qt provides its own collection classes that take value types as well as classes that take references this means that when using a value class we don't have to worry about memory management as we aren't allocating anything, we are ineffect telling the list to deal with all that for us until we either remove the item or let the list go out of scope.

The type of list we are using is a QValueList which is effectively a template class so in C++ we must define the type we need before qwe use it. This is done in the header with the code,

typedef QValueList< TextData > TextList;

This declares a type of object called a TextList that is defined as a QValueList taking a data type of TextData, to use this type we create an object as normal in the MouseBasicsWidget class

private:
        QPopupMenu *popupMenu;
        QPopupMenu *capsPopupMenu;
        QPoint mousePoint; 
        char cText;
        bool bCapsLock;
        TextList textList;

Note that it is a value type so that we don't allocate any memory for it in the constructor. Once the list is created and data is added through adding characters to the widget by left clicking on the place where we want the characters to appear we can the return to the paintEvent function which draws it with the code,

TextList::iterator it;
        
for( it=textList.begin(); it != textList.end(); it++ )
{
  drawText( ( *it ).xpos(), ( *it ).ypos(), QString( QChar( ( *it ).text() ) ) ); 
}

An iterator is effectively a pointer that is used to move through the list one item at a time. If it helps think of it a TextData pointer or as a pointer of the type contained within the list and and we move it through the list using a for loop, getting the values that we require and passing them to the drawText function with each iteration through the loop.

Ulitmately giving us a program that looks like,


Summary

In this chapter we have looked at the basics of keyboard and mouse input and also been introduced to poup menus and container classes. In the next chapter we take a closer look at files and directories in KDE.