LinuxQuestions.org
Review your favorite Linux distribution.
Go Back   LinuxQuestions.org > Blogs > rainbowsally
User Name
Password

Notices

Rate this Entry

GUI Programming: QT4 Event Preeption Part 2-B (temp)

Posted 05-02-2012 at 08:38 PM by rainbowsally

Here's the code to run the examples and create a subclass of QApplication customized for our event preeption test. (See part 2-A)

file: src/utils.h
Code:
// utils.h

#ifndef utils_h
#define utils_h

#include <QObject>

// See explanation in utils.cpp
int lq_hash32(const char* name);

// See explanation in utils.cpp
typedef struct 
{
  int hashid; // computed once
  const char* name;
  uint value;
}HASH_ENTRY;

extern HASH_ENTRY lq_key_name_tbl[];

const char* qkey2name(uint value);
char* qmodkeys2name(uint value);  // returns a temp static string
uint qname2key(const char* name); // key value for name or 0x01ffffff ("Key_unknown")

bool handle_return_key(QObject* p);

#endif // utils_h

Here's the somewhat unfriendly (still) subclass of QApplication with an event handler that can preempt other handlers.

file: src/main.cpp
Code:
#include <QApplication>   // app
#include <QTextEdit>      // editor
#include <QKeyEvent>      // QKeyEvent
#include "utils.h"        // qkey2name(), handle_return_key(QTextEditor*), etc.
#include "lib/utils.cpp"  // for type MULTI mc2 compilation, include, not link
#include <stdio.h>        // printf()

#define OLD_SCHOOL 1 /* change this to 0 to compare old to new */

void dbg(){}

////////////////////////////////////////////////////////
// LQApp class declaration

// type used to set the event handler aka 'notify'
typedef bool (*handler_func_t)(QObject* target, QObject* object, QEvent* ev);


class LQApp : public QApplication
{
  public:
    LQApp(int &argc, char **argv);
    ~LQApp();
    bool notify(QObject *receiver, QEvent *ev);
    void setHandler(QObject* target, handler_func_t handler);
    void getTargetHandler(QObject** pobj, handler_func_t* pfunc);

    // globally accessible funcs and vars
    static bool (*handler)(QObject* target, QObject* object, QEvent* ev);
    static bool defaultHandler(QObject* target, QObject* object, QEvent* ev);
    QObject* target;
    static LQApp* self;
    
};

////////////////////////////////////////////////////////
// LQApp class implementation

// these are globally accessible for now so our handler
// functions can get at them through LQApp::self (aka 
// a static 'this') ->

// a 'this' pointer
LQApp* LQApp::self;

// the currently effective event handler
bool (*LQApp::handler)(QObject* target, QObject* object, QEvent* ev);

// the default handler, usually not called by user
bool LQApp::defaultHandler(QObject* notused, QObject* receiver, QEvent* ev)
{
  notused = 0; // not used, just something to 'reference' the variable  
  return self->QApplication::notify(receiver, ev);
}

// these need the object in order to call them ->
bool LQApp::notify(QObject *receiver, QEvent *ev) 
{
  handler(target, receiver, ev);
  return true;
}

// sets new target object and handler func.
void LQApp::setHandler(QObject* new_target, handler_func_t new_handler)
{
  target = new_target;
  handler = new_handler;
}

// Saves previous target and handler in user supplied var ptrs.
// Used to generate a chain of handlers, call with the address of 
// an object and a function that can be used by the new handler 
// to call prev_handler(prev_target, object, event);
void LQApp::getTargetHandler(QObject** prev_targ, handler_func_t* prev_func)
{
  LQApp* This = LQApp::self;
  *prev_targ = This->target;
  *prev_func = This->handler;
}

// App contructor
LQApp::LQApp(int &argc, char **argv)
  : QApplication(argc, argv) 
{
  // additional setup
  self = this;
  handler = defaultHandler;
}

// App destructor
LQApp::~LQApp()
{
  // delete additional setup as req'd
}


//////////////////////////////////////////////////////
// Usage example

static QObject* prev_target;
static handler_func_t prev_handler;

bool editor_handler(QObject* target, QObject* receiver, QEvent* event)
{
  // return true; // preempt everything - testing
  // screen for our receiver
  if(receiver == target)
  {
    // screen by event types
    switch(event->type())
    {
      // screen by event types
      case QEvent::KeyRelease:
      case QEvent::KeyPress:
      {
        // this is funky...
        // This 'firsttime' flag just blocks recursion.
        // QT or some error in my program runs this loop twice
        // and I don't yet know why it does this. -rs ->
        
//        static int firsttime;
//        firsttime = !firsttime;
//        if(!firsttime)
//          return true; // consumed
        // <- rs Try commenting this out and see what happens.
        // We might want to explore the blocking or using timestamps for 
        // the event, or even the "accept()" flag in the event.  At least
        // at this level.
        
        // cast the QEvent to the right type
        QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
        uint keyval = keyEvent->key();
        // screen by key value
        switch(keyval)
        {
          case Qt::Key_Return:
#if OLD_SCHOOL
            // explain in great detail what was received
            printf("\n<ching-smash-grind-BAM>\n"); 
            fflush(stdout);
#else
            // less less old school, but perhaps still somewhat informative. ;-)
            printf("Key: %-20s  Flags: %s\n", qkey2name(keyval), qmodkeys2name(keyval));
#endif             
            ((QTextEdit*)receiver)->append(""); // newline
            return true; // consumed
          default:

#if OLD_SCHOOL
            // explain in great detail what was received
            printf("<tappity>"); 
            fflush(stdout);
#else
            // less less old school, but perhaps still somewhat informative. ;-)
            printf("Key: %-20s  Flags: %s\n", qkey2name(keyval), qmodkeys2name(keyval));
#endif             
                goto CONTINUE;
            ; // or just fall through
        } // end switch key
      } // end case keypress
          default:
            goto CONTINUE;   // or just fall through
    } // end switch event type 
    // fall through, do not preempt
  }
CONTINUE:
  return prev_handler(prev_target, receiver, event);
}

////////////////////////////////////////////////////
// standard-ish main except that it adds a TextEdit 
// set to all the defaults.
int main(int argc, char *argv[])
{
  dbg();
  LQApp app(argc, argv);
  QTextEdit editor;
  
  // save the previous pointers if we need to use or restore them.
  app.getTargetHandler(&prev_target, &prev_handler);
  
  app.setHandler(&editor, editor_handler);
  editor.show();
  return app.exec();
}
And here's a second executable. If you get an error that 'main' is redifined in the link cycle, set OUTNAME = MULTI in your mc2.def file or if you use another makefile system, do what you must.

file: src/lookup-test.cpp
Code:
// lookup-test.cpp
#include <stdio.h>


#include "utils.h"

#define LOOKUP_TEST 1
#include "lib/utils.cpp" // for type MULTI mc2 build
#undef LOOKUP_TEST

int main()
{
  for(HASH_ENTRY* p = lq_key_name_tbl; p->name != 0; p++)
  {
    const char* keyname = p->name;
    uint keyval = qname2key(keyname);
    printf("keyval: Ox%-10X keyname: %s\n", keyval, keyname);
  }
  // defined in lib/utils.cpp
  // int hash_false_hits = 0;
  // int hash_lookups = 0;
  printf("Total lookups = %d\n", hash_lookups);
  printf("Total string comparisons = %d\n", string_comparisons);
  printf("Total false hits = %d\n", hash_false_hits);
  return 0;
}
Notes:

Re. the hash lookup.
The lookup in
Code:
uint qname2key(const char* name)
the call to do a strcmp is usually only done once because the hash algorithm is mixed up enough to only generate about one false hit per several thousand text entries. (See dump fragment from lookup-test in part 2-A)

Considering the fact that it's called once per lookup, check out the loop as it appears in 'objdump -d' slightly edited for clarity.

Code:
// precomputing the hash value takes a little time, so this is for longish lists
// where the hash32 call and return and the iteration over the entire string to 
// generate the id vanish due to the simplicity of the hash matching below.

jmp loop_start:

// increment i and exit if *i == 0
next_i:
80495e8: add    $0xc,%ebx         
80495eb: mov    0x4(%ebx),%edx
80495ee: test   %edx,%edx
80495f0: je     not_found;

loop_start:
80495f2: cmp    %esi,(%ebx)
80495f4: jne    80495e8 next_i;
// this is the bottom of the loop except when doing strcmp

80495f6: mov    %edi,0x4(%esp)
80495fa: mov    %edx,(%esp)
80495fd: call   8048f2c <strcmp@plt>
8049602: test   %eax,%eax
8049604: jne    next_i;
// this only loops back if the string comparison fails which is almost never

// usually falls through from successful strcmp to return i
As we can see, there are two conditional branches and four other instructions per loop.

The length of the strings compared doesn't matter. It will still perform at this level of efficiency.

The Computer Mad Science Team

:-)
Posted in Uncategorized
Views 311 Comments 0
« Prev     Main     Next »
Total Comments 0

Comments

 

  



All times are GMT -5. The time now is 02:53 PM.

Main Menu
Advertisement

My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
identi.ca: @linuxquestions
Facebook: linuxquestions Google+: linuxquestions
Open Source Consulting | Domain Registration