Event Processing - NUS Computing - Homecs3249/lecture/event processing.pdf · 2.Reimplement...
Transcript of Event Processing - NUS Computing - Homecs3249/lecture/event processing.pdf · 2.Reimplement...
CS3249 Event Processing 1CS3249
Event Processing 11CS3249 Event Processing
Event Processing
Leow Wee KhengCS3249 User Interface Development
CS3249 Event Processing 2CS3249 Event Processing 2CS3249Event Processing 2Event Processing 2CS3249
Event Processing
Events are created in response to user inputs.
CS3249 Event Processing 3CS3249 3Event Processing
Event Processing
Questions: Where are the event handlers (in which part of application)?
Who receive events?
CS3249 Event Processing 4CS3249 Event Processing 4CS3249Event Processing 4Event Processing 4CS3249
Different frameworks process events differently.
Let's examine each of them.
CS3249 Event Processing 5CS3249 5Event Processing
X Window Event Processing
XtAppMainLoop() runs event loop.
Event is sent to widget by calling action routine.
Action routine calls application's callback if registered.
CS3249 Event Processing 6CS3249 6Event Processing
Qt Event Processing
Qt application class QCoreApplication: for non-GUI applications.
QApplication: for GUI applications,inherits QCoreApplication.
qApp: points to unique Qt application instance.
CS3249 Event Processing 7CS3249 Event Processing 7CS3249Event Processing 7Event Processing 7CS3249
QObject
QCoreApplication
QApplication
QWidget
QEvent
Class Hierarchy
CS3249 Event Processing 8CS3249 Event Processing 8CS3249Event Processing 8Event Processing 8CS3249
QCoreApplication::exec() runs event loop. Gets native window event from event queue. Translates into QEvent (or subclass). Sends QEvent to QObject by calling QObject::event().
QObject::event()
Main event handler. Forwards event to specific event handler.
CS3249 Event Processing 9CS3249 Event Processing 9CS3249Event Processing 9Event Processing 9CS3249
In Qt, event ≠ signal. Events are useful when implementing a widget.
Signals are useful when using a widget.
Event handling is lower-level mechanism.
Signal-slot is higher-level mechanism.
CS3249 Event Processing 10CS3249 Event Processing 10CS3249Event Processing 10Event Processing 10CS3249
Qt provides 5 levels of event processing:1. Reimplement specific event handlers.
Change behaviour of event handlers.
2. Reimplement QObject::event() main event handler. Catch events before they reach specific event handlers.
3. Install event filer on an object. Events for the object are first sent to its event filter.
4. Install event filter on QApplication object. All events for all objects are first sent to its event filter.
5. Subclass QApplication and reimplement notify(). Catch all events before they are sent to any event filer.
CS3249 Event Processing 11CS3249 Event Processing 11CS3249Event Processing 11Event Processing 11CS3249
Event propagation Events can be propagated to parent if not handled.
MyWindow::mouseEvent(QMouseEvent *event)
{
if (don't want to handle)
QWidget::mouseEvent(event);
else ...
}
CS3249 Event Processing 12CS3249 12Event Processing
Cocoa Event Processing
Cocoa application class NSApplication
NSApp: NSApplication object.
CS3249 Event Processing 13CS3249 Event Processing 13CS3249Event Processing 13Event Processing 13CS3249
Responder Model NSApplication, NSWindow, NSView are subclasses of
NSResponder, which can receive and handle events.
Event messages Messages that correspond to input event, e.g., mouse click.
Action messages Messages describing higher-level commnad, e.g., copy.
Responder chain A series of responder objects that handle message.
CS3249 Event Processing 14CS3249 Event Processing 14CS3249Event Processing 14Event Processing 14CS3249
NSObject
NSApplication NSViewNSWindow
NSEvent
Class Hierarchy
NSResponder
CS3249 Event Processing 15CS3249 15Event Processing
Event Routing
responder chain:forward events
not handled
CS3249 Event Processing 16CS3249 16Event Processing
Event Delegation
Allows an object to delegate responsibility to another.
Delegate Receives messages from another object when events occur.
Helps to perform tasks for sender object.
Change object's behaviour without creating subclass.
CS3249 Event Processing 17CS3249 17Event Processing
Notification
Broadcast messages to objects in application.
Notification center Object of NSNotificationCenter class,
has default center.
sendingobject
NotificationCenter
receivingobject
receivingobject
receivingobject
CS3249 Event Processing 18CS3249 Event Processing 18CS3249Event Processing 18Event Processing 18CS3249
Notification Sender defines names of notifications to post as NSString.
Receiver registers with (default) notification center notification to receive method to invoke
Sender posts notification (NSNotification) to notification center.
Notification center sends notification to receiver by calling receiver's method.
CS3249 Event Processing 19CS3249 19Event Processing
Windows API Event Handling
Window API is developed in C.
WinMain() contains message (event) loop
WindowProc() main message (event) handler
CS3249 Event Processing 20CS3249 Event Processing 20CS3249Event Processing 20Event Processing 20CS3249
int WINAPI WinMain(HINSTANCE instance, HINSTANCE notused, LPSTR commandLine, int showOptions){ WNDCLASS WindowClass; ... // set WindowClass parameters RegisterClass(&WindowClass);
window = CreateWindow(...); ShowWindow(window, showOptions); UpdateWindow(window);
// message loop MSG msg; while(GetMessage(&msg, 0, 0, 0) == TRUE) { TranslateMessage(&msg); DispatchMessage(&msg); } return static_cast<int>(msg.wParam);}
CS3249 Event Processing 21CS3249 Event Processing 21CS3249Event Processing 21Event Processing 21CS3249
LRESULT CALLBACK WindowProc(HWND window, UINT msgId, WPARAM wParam, LPARAM lParam){ switch(msgId) { case WM_CREATE: ... // create window return 0;
case WM_DESTROY: PostQuitMessage(0); return 0;
default: // call default message handler return DefWindowProc(window, msgId, wParam, lParam); }}
CS3249 Event Processing 22CS3249 22Event Processing
MFC Event Handling
MFC encapsulates Windows API in C++ classes.
CWinApp::Run() runs message (event) loop.
CWinApp and CWnd have message (event) handlers.
CS3249 Event Processing 23CS3249 Event Processing 23CS3249Event Processing 23Event Processing 23CS3249
CObject
CWinThread CWnd
Class Hierarchy
CCmdTarget
CFrameWndCWinApp
MyApp MyWindow
CS3249 Event Processing 24CS3249 24Event Processing
Event Receiver
Two kinds
Top-level window Examples: Windows API / MFC, ...
Widget / object Examples: X, Qt, Cocoa
CS3249 Event Processing 25CS3249 25Event Processing
Top-Level Window Handles Events
spin box
sliderwindow
spinBox pointerslider pointer
spinBoxHandlersliderHandler
CS3249 Event Processing 26CS3249 Event Processing 26CS3249Event Processing 26Event Processing 26CS3249
Advantages Top-level window contains all UI elements.
Easy to synchronise states of UI elements.
Simple mechanism.
Disadvantages Top-level window is tightly coupled to UI elements.
UI elements' functions are incomplete;some are in top-level window (low cohesion).
Cannot define part of the window as widget for reuse.
CS3249 Event Processing 27CS3249 27Event Processing
Widget Handles Events
window
spinBox pointerslider pointer
spin box
spinBoxHandler
slider
sliderHandler
CS3249 Event Processing 28CS3249 Event Processing 28CS3249Event Processing 28Event Processing 28CS3249
Advantages Top-level window can be loosely coupled to UI elements.
UI elements' functions are more complete (high cohesion).
Can define part of the window as widget for reuse.
Powerful mechanism.
Complication How to coordinate actions of UI elements?
Centralised at top-level window Simple but tight coupling with UI elements.
Decentralised to UI elements Possible tight coupling between UI elements, or
need observer mechanism.
CS3249 Event Processing 29CS3249 Event Processing 29CS3249Event Processing 29Event Processing 29CS3249
X Window's solution: callback Application synchronises states of UI elements.
Easy to use, but tightly coupled.
Cocoa's solution: target-action Top-level window synchronises states of UI elements.
Easy to use, but tightly coupled.
Qt's solution: signal-slot Widgets synchronise their states by signals and slots.
Easy to use, and yet loosely coupled.
CS3249 Event Processing 30CS3249 30Event Processing
Comparison
Qt X11/Motif Cocoa MFC
event loop QApplication::exec()
XtAppMainLoop() in NSApplication
CWinApp::Run()
event receiver
QWidget widget,application
target object,top-level window
top-level window
event handler
QWidgetevent handler
widget's action routine,
application's callback
target'saction function
top-level window
message handler
synchroni-sation
signal-slot application target top-level window
top-level window
CS3249 Event Processing 31CS3249 31Event Processing
Qt Event Handlers
Reimplement event handlers We have used this technique before.
We'll see more of it in later lectures.
void MyEditor::closeEvent(QCloseEvent *event){ if (okToContinue()) event->accept(); else event->ignore();}
closeEvent() is reimplemented in MyEditor.
QCloseEvent is a subclass of QEvent.
CS3249 Event Processing 32CS3249 32Event Processing
Qt Event Filter
show /hide
hide
hide
check /uncheck
uncheck
CS3249 Event Processing 33CS3249 Event Processing 33CS3249Event Processing 33Event Processing 33CS3249
Two ways to create volume render dialog: Subclass
VolumeRenderDialog as subclass of QWidget QWidget does not emit close signal when hiding. Define closed() signal in VolumeRenderDialog. Use closed() to uncheck action.
Composition VolumeRenderDialog is a QWidget in main window. Use close button's clicked() signal to uncheck action. Use event filter to catch close event and uncheck action.
CS3249 Event Processing 34CS3249 Event Processing 34CS3249Event Processing 34Event Processing 34CS3249
Subclass Methodclass VolumeRenderDialog:: public QWidget{ Q_OBJECT
public: VolumeRenderDialog(); ...
protected: void closeEvent(QCloseEvent *event);
signals: void closed(); ...};
CS3249 Event Processing 35CS3249 Event Processing 35CS3249Event Processing 35Event Processing 35CS3249
Hide by clicking close button:
void VolumeRenderDialog::createWidgets(){ QPushButton *closeButton = new QPushButton(“close”); ... connect(closeButton, SIGNAL(clicked()), this, SLOT(hide())); connect(closeButton, SIGNAL(clicked()), this, SIGNAL(closed())); // emit signal}
Hide by clicking close window button:
void VolumeRenderDialog::closeEvent(QCloseEvent *event){ event->accept(); emit closed();}
CS3249 Event Processing 36CS3249 Event Processing 36CS3249Event Processing 36Event Processing 36CS3249
class MainWindow:: public QMainWindow
{ Q_OBJECT
public: MainWindow(); ...
private: QAction *volumeRenderAction; VolumeRenderDialog *volumeRenderDialog; ...};
CS3249 Event Processing 37CS3249 Event Processing 37CS3249Event Processing 37Event Processing 37CS3249
void MainWindow::createWidgets()
{ volumeRenderDialog = new VolumeRenderDialog; ...}
void MainWindow::createActions(){ volumeRenderAction = new QAction(“Volume Render”, this); volumeRenderAction->setCheckable(true); ... connect(volumeRenderAction, SIGNAL(toggled(bool)), volumeRenderDialog, SLOT(setVisibility(bool))); connect(volumeRenderDialog, SIGNAL(closed()), VolumeRenderAction, SLOT(toggle()));}
CS3249 Event Processing 38CS3249 Event Processing 38CS3249Event Processing 38Event Processing 38CS3249
Composition Methodclass MainWindow:: public QMainWindow{ Q_OBJECT
public: MainWindow(); ...
protected: bool eventFilter(QObject *object, QEvent *event);
private: QAction *volumeRenderAction; QWidget *volumeRenderDialog; ...};
CS3249 Event Processing 39CS3249 Event Processing 39CS3249Event Processing 39Event Processing 39CS3249
Hide by clicking close button:
void MainWindow::createWidgets(){ volumeRenderDialog = new QWidget; volumeRenderDialog->installEventFilter(this);
QPushButton *closeButton = new QPushButton(“close”); ... connect(closeButton, SIGNAL(clicked()), volumeRenderDialog, SLOT(hide())); ...}
CS3249 Event Processing 40CS3249 Event Processing 40CS3249Event Processing 40Event Processing 40CS3249
Hide by clicking close button:
void MainWindow::createActions(){ volumeRenderAction = new QAction(“Volume Render”, this); volumeRenderAction->setCheckable(true); ... connect(volumeRenderAction, SIGNAL(toggled(bool)), volumeRenderDialog, SLOT(setVisibility(bool))); connect(closeButton, SIGNAL(clicked()), volumeRenderAction, SLOT(toggle()));}
CS3249 Event Processing 41CS3249 Event Processing 41CS3249Event Processing 41Event Processing 41CS3249
Hide by clicking close window button:
bool MainWindow::eventFilter(QObject *object, QEvent *event){ if (object == volumeRenderDialog) if (event->type() == QEvent::Close) { volumeRenderAction->setChecked(false); return false; // Let volumeRenderDialog // handle close event }
// Let parent widget handle event. return QMainWindow::eventFilter(object, event);}
CS3249 Event Processing 42CS3249 42Event Processing
Staying Responsive
Event processing should be fast. Other events are waiting to be dispatched and processed.
Slow event processing can freeze the whole GUI.
But some processes just need more time. Example: Saving a large spread sheet into disk.
Possible solutions Show wait cursor to inform the user to just wait.
Call dispatcher to dispatch events regularly.
Run long operations when system is idle (further reading).
Use multithreading (later lecture).
Use lazy evaluation, level-of-details (later lecture).
CS3249 Event Processing 43CS3249 Event Processing 43CS3249Event Processing 43Event Processing 43CS3249
bool Spreadsheet::writeFile(const QString &fileName){ QFile file(fileName); QTextStream out(&file);
for (int row = 0; row < RowCount; ++row) { for (int col = 0; col < ColumnCount; ++col) { QString str = formula(row, col); if (!str.isEmpty()) out << quint16(row) << quint16(col) << str; }
qApp->processEvents(); // Dispatch some events. } return true;}
CS3249 Event Processing 44CS3249 Event Processing 44CS3249Event Processing 44Event Processing 44CS3249
Calling qApp->processEvents() is a bit dangerous User may close the application while it is writing. Solution:
qApp->processEvents( QEventLoop::ExcludeUserInputEvents);
Ignore key and mouse inputs.
CS3249 Event Processing 45CS3249 Event Processing 45CS3249Event Processing 45Event Processing 45CS3249
For long-running process, can show progress dialog.
bool Spreadsheet::writeFile(const QString &fileName){ QFile file(fileName); QTextStream out(&file);
QProgressDialog progress(this); progress.setLabelText(tr("Saving %1").arg(fileName)); progress.setRange(0, RowCount); progress.setModal(true);
CS3249 Event Processing 46CS3249 Event Processing 46CS3249Event Processing 46Event Processing 46CS3249
for (int row = 0; row < RowCount; ++row) { progress.setValue(row); // Update progress bar. qApp->processEvents(); // Dispatch some events.
if (progress.wasCanceled()) { file.remove(); return false; }
for (int col = 0; col < ColumnCount; ++col) { QString str = formula(row, col); if (!str.isEmpty()) out << quint16(row) << quint16(col) << str; } } return true;}
CS3249 Event Processing 47CS3249 47Event Processing
Timer Event
Timer events are delivered at “regular interval”. Allow applications to perform processing at regular interval.
Implement blinking cursors, animations, video player, etc.
Illustrate with an animated banner
CS3249 Event Processing 48CS3249 Event Processing 48CS3249Event Processing 48Event Processing 48CS3249
// Ticker.h
#ifndef TICKER_H#define TICKER_H
#include <QWidget>
class Ticker : public QWidget{ Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText)
public: Ticker(QWidget *parent = 0); QString text() const { return myText; } void setText(const QString &newText); QSize sizeHint() const;
CS3249 Event Processing 49CS3249 Event Processing 49CS3249Event Processing 49Event Processing 49CS3249
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
Reimplement 4 event handlers.
CS3249 Event Processing 50CS3249 Event Processing 50CS3249Event Processing 50Event Processing 50CS3249
// Ticker.cpp
#include <QtGui>#include "Ticker.h"
Ticker::Ticker(QWidget *parent): QWidget(parent){ offset = 0; myTimerId = 0; // Indicate no timer has started.}
void Ticker::setText(const QString &newText){ myText = newText; update(); // Repaint. updateGeometry(); // Update if size has changed.}
CS3249 Event Processing 51CS3249 Event Processing 51CS3249Event Processing 51Event Processing 51CS3249
QSize Ticker::sizeHint() const{ return fontMetrics().size(0, text());}
fontMetrics() returns QFontMetrics object.
Use it to check info about the widget's font.
fontMetrics().size() returns pixel length of text().
First argument is not needed; so set to 0.
CS3249 Event Processing 53CS3249 Event Processing 53CS3249Event Processing 53Event Processing 53CS3249
void Ticker::showEvent(QShowEvent * /* event */){ myTimerId = startTimer(30);}
Starts timer when widget is visible.
Qt will generate a timer event once every 30 msec.
void Ticker::hideEvent(QHideEvent * /* event */){ killTimer(myTimerId); // Stops timer.}
CS3249 Event Processing 54CS3249 Event Processing 54CS3249Event Processing 54Event Processing 54CS3249
void Ticker::timerEvent(QTimerEvent *event){ if (event->timerId() == myTimerId) { ++offset; if (offset >= fontMetrics().width(text())) offset = 0; scroll(-1, 0); } else QWidget::timerEvent(event); // propagate}
scroll(-1, 0) scrolls text displayed in widget left by one pixel.
It generates paint event only for newly revealed 1-pixel strip.
More efficient than calling update().
CS3249 Event Processing 56CS3249 Event Processing 56CS3249Event Processing 56Event Processing 56CS3249
// main.cpp
#include <QApplication>#include "Ticker.h"
int main(int argc, char *argv[]){ QApplication app(argc, argv); Ticker *ticker = new Ticker; ticker->setText("++ Hello, world! Have a great day! "); ticker->show(); return app.exec();}
CS3249 Event Processing 57CS3249 Event Processing 57CS3249Event Processing 57Event Processing 57CS3249
Notes on using QTimer: Timer events are delivered after, not before, period is up.
Timer events are delivered only when event loop is running. When an operation is executing, timer event is not delivered
until control returns to event loop.
Timer resolution depends on operating systems: Some support 1 msec interval,
some support 15 msec interval.
CS3249 Event Processing 58CS3249 58Event Processing
Summary
Handle events in response to user actions.
Event receiver Top-level window: common type.
Any object / widget: more sophisticated type.
Qt event handling Reimplement event handler to change behaviour.
Install event filter to catch event.
If long-running, clear event queue regularly.
Use timer event for regular activities.
CS3249 Event Processing 59CS3249 59Event Processing
Further Reading
Event processing details: [Blan2008] chap. 7.
User timer event to indicate system idle: [Blan2008] chap. 7.
Installing event filter: [Blan2008] chap. 7.
CS3249 Event Processing 60CS3249 60Event Processing
References
J. Blanchette and M. Summerfield, C++ GUI Programming with Qt 4, 2nd ed., Prentice Hall, 2008.
J. D. Davidson and Apple Computer, Inc. Learning Cocoa with Objective-C, 2nd ed., O'Reilly, 2002.
A. Fountain, J. Huxtable, P. Ferguson and D. Heller, Motif Programming Manual, O'Reilly, 2001.
I. Horton, Beginning Visual C++ 2010, Wiley, 2010. G. Shepherd and S. Wingo, MFC Internals, Addison-Wesley, 1996.