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
Here's the somewhat unfriendly (still) subclass of QApplication with an event handler that can preempt other handlers.
file: src/main.cpp
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
Notes:
Re. the hash lookup.
The lookup in
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.
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
:-)
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(); }
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; }
Re. the hash lookup.
The lookup in
Code:
uint qname2key(const char* name)
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
The length of the strings compared doesn't matter. It will still perform at this level of efficiency.
The Computer Mad Science Team
:-)
Total Comments 0