This module explores the remaining aspects of writing own widgets and explains how to handle low-level events, which can originate from for example the mouse or the keyboard. Low level painting, however, can not only be used to draw custom widget - a short overview gives you an idea of the power behind the QPainter class. If your application has to manage multiple graphics items or if you want to build a dynamic UI, the section about the graphics view framework will contain an overview of the classes required to build scene-graph-like user interfaces. At the end of this module, a few slides explain optimizing images, which is important to keep the file sizes and loading times down on mobile devices.
4. Events
• UI applications are event-based
– Wait for user to interact
– Then start working on the requested task
• Alternative
– Polling for status changes
“Is the user pressing a button?”
→ Events save processing time, thus battery life
Picture credits: Pearson Scott Foresman
Public domain
5. Events vs. Signals?
• Signals
– For using widgets
– e.g., push button should
return clicked() signal, not
low-level mouse or key events
• Events
– For implementing widgets
• Modifying existing widgets
• New widgets
– e.g., implement own push button: handle mouse and key events and emit clicked()
signal when necessary
6. Events and Signals
• Mouse button’s way to become a clicked() signal
QCoreApplication::exec()
Mouse button Operating
QEventLoop::exec()
released system
QEvent sent through event dispatcher
QPushButton::event()
QAbstractButton::
QAbstractButton::event()
mouseReleasedEvent()
QWidget::event()
→ emit clicked() signal
QObject::event()
Signal & slots connection
Custom slot that handles button
click
7. Event Loop
• Threads in Qt can have an event loop
– Initial thread starts its loop through QCoreApplication::exec()
– Other threads: QThread::exec()
• Events
– Derived from QEvent
– Source:
• Window system (QMouseEvent, QKeyEvent)
• Generated by Qt itself (QTimerEvent)
– Handled by QObject subclass (especially widgets)
8. QEvent
• Get type of event through QEvent::type()
– 100+ events defined through enum values in Qt
– Own events can be defined
• Event is an instance of a QEvent subclass
– Adds additional information about event
– e.g., QMouseEvent: buttons, position, ...
9. Handling Events in Widgets
– Events delivered to event() function of QObject
– Calls appropriate virtual event handler functions
qwidget.cpp
bool QWidget::event(QEvent *event)
{
Q_D(QWidget);
// ...
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
break; You only need to
case QEvent::Paint:
paintEvent((QPaintEvent*)event); override virtual, pre-
defined handler method
break;
case QEvent::Close:
closeEvent((QCloseEvent*)event);
break; from QWidget base class
// ...
default:
return QObject::event(event);
}
return true;
}
10. Example: Ticker
• Scrolling text
– 1 Pixel / 30 ms
– Demonstrates Timer events
• Handles 4 events
– Paint event: manual drawing of the text
– Timer event: regular call-backs
– Show event: start timer when widget is shown
– Hide event: end timer when widget becomes invisible
11. Ticker – General Definition
ticker.h ticker.cpp
#ifndef TICKER_H #include "ticker.h"
#define TICKER_H
Ticker::Ticker(QWidget* parent)
#include <QtGui> : QWidget(parent)
{
class Ticker : public QWidget m_offset = 0;
{ m_timerId = 0;
Q_OBJECT }
Q_PROPERTY(QString text READ text WRITE setText)
void Ticker::setText(const QString &newText)
public: {
Ticker(QWidget* parent = 0); m_text = newText;
void setText(const QString &newText); update();
QString text() const { return m_text; } updateGeometry();
QSize sizeHint() const; }
protected: // Return recommended size of this widget
void paintEvent(QPaintEvent* event); QSize Ticker::sizeHint() const
void timerEvent(QTimerEvent* event); {
void showEvent(QShowEvent* event); // Determine size required by the text
void hideEvent(QHideEvent* event); // First argument isn't needed here -> 0
return fontMetrics().size(0, text());
private: }
QString m_text;
int m_offset;
int m_timerId;
};
#endif // TICKER_H
12. Ticker – Event Handling
ticker.cpp ticker.cpp
void Ticker::paintEvent(QPaintEvent* /*event*/) void Ticker::timerEvent(QTimerEvent* event)
{ {
QPainter painter(this); // Called by the system at intervals
if (event->timerId() == m_timerId)
// Get required width of the text {
int textWidth = fontMetrics().width(text()); // Increase offset by 1 to simulate movement
if (textWidth < 1) ++m_offset;
return; if (m_offset >= fontMetrics().width(text()))
m_offset = 0;
// Draw the text as many times as necessary // Call to QWidget::scroll(), moves existing
// to fill the entire width of the widget // pixels on-screen and only paints new area.
// (taking offset into account) // Also possible: call update() to redraw
int x = -m_offset; // the whole widget.
while (x < width()) scroll(-1, 0);
{ } else {
painter.drawText(x, 0, textWidth, height(), // Event not from the timer we are
Qt::AlignLeft | Qt::AlignVCenter, text()); // interested in -> pass to base class
x += textWidth; QWidget::timerEvent(event);
} }
} }
void Ticker::showEvent(QShowEvent* /*event*/) void Ticker::hideEvent(QHideEvent* /*event*/)
{ {
// Starts a timer. Returned ID number can be // Stop the timer
// used to identify timer later killTimer(m_timerId);
m_timerId = startTimer(30); m_timerId = 0;
} }
13. Event Filters
• Look at / intercept events delivered to other object
– e.g., dialogs filter key presses for widgets to modify Return-key handling
– Set up through QObject::installEventFilter()
– Target object gets events before monitored object
– Accepts or rejects events: allow / deny further event processing
monitoredObj->installEventFilter(targetObj);
14. Ways to Influence Event Handling I
1. Incoming event first goes to virtual function QCoreApplication::notify()
– Override notify()
– Get all events before any event filters can intercept
– Only one subclass of QCoreApplication can be active at a time
2. Install an event filter on QCoreApplication::instance()
– implement eventFilter()
– Get events after sent out by notify()
– As powerful as option 1, also allows multiple application-global event
filters
15. Ways to Influence Event Handling II
3. Event filter on target object
– implement eventFilter()
– Gets all events (except Tab, Shift+Tab) before actual target object
4. Event handler of target object
– override QObject::event()
– Gets events after event filter
– Don’t forget to call event handler of base class for unhandled events!
5. Re-implement event handling functions
– implement paintEvent(), mousePressEvent()
– Easiest, most common, but least powerful
17. Painting
• Low-level drawing handled through QPainter
• Draws on paint devices (QPaintDevice):
– QWidget: most common use case
– QImage: optimized for I/O and direct pixel manipulation
– QPixmap: optimized for showing images on screen
• QBitmap: convenience for monochrome QPixmap –
e.g., QCursor, QBrush, QRegion
– QPicture: records and replays QPainter commands
– QPrinter: paint on a printer
– ...
19. Example: Drawing
– Draws line between coordinates of two mouse clicks
– Uses image to store drawing
– Text written directly on Widget surface
20. Drawing
mainWidget.cpp
MainWidget::MainWidget() {
// Use standard colors of system
setBackgroundRole(QPalette::Base);
lastX = -1;
lastY = -1;
resize( 300, 200 );
bgImg = new QPixmap(300, 200);
bgImg->fill(QColor(255, 255, 255));
}
void MainWidget::mousePressEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton && lastX > -1 && lastY > -1) {
// Create painter object for our image
QPainter painter(bgImg);
// Draw a line from previous pos to new position mainWidget.h
painter.setPen(QPen(Qt::red, 3));
painter.drawLine(lastX, lastY, event->x(), event->y()); class MainWidget : public QWidget {
update(); Q_OBJECT
} public:
lastX = event->x(); MainWidget();
lastY = event->y();
}
protected:
void mousePressEvent(QMouseEvent* event);
void MainWidget::paintEvent(QPaintEvent* event) { void paintEvent(QPaintEvent* event);
// Paint directly on the widget surface private:
QPainter painter(this); int lastX;
// Draw the image to the widget int lastY;
painter.drawPixmap(0, 0, *bgImg); QPixmap* bgImg;
// Draw text on top of the image to the widget };
painter.setPen(QPen(Qt::blue, 1));
painter.drawText(5, 15, tr("Draw using left mouse button clicks"));
painter.drawText(5, 30, tr("Set position with right mouse button"));
}
24. Graphics View Framework – Example
• Setting up a simple scene
scene = new QGraphicsScene(0, 0, 400, 400);
QGraphicsLineItem *wall = new QGraphicsLineItem
(QLineF(0.0, 200.0, 100.0, 200.0));
scene->addItem(wall);
view = new QGraphicsView(scene);
view->setRenderHint(QPainter::Antialiasing);
26. Using Widgets as Items?
• Possible through two ways:
– QGraphicsWidget
• New subclass for widgets in QGraphicsScenes
• Resembles QWidget, only few limitations
– QGraphicsProxyWidget
• Embeds a standard QWidget into QGraphicsScene
• Translates coordinate systems
• Automatically manages child widgets
• Not suitable for high performance scenarios
28. Scene: QGraphicsScene
• Surface for managing items on a 2D surface
– 3 layers: background, item layer, foreground
– Scene can be parent of items, or items are children of other items
– Manages items: position & transformation, visibility, collision, locate
items
– Propagates events to items
– Indexes items with BSP tree by default
• Suitable for large scenes with relatively static items
29. View: QGraphicsView
• Widget that presents a scene
– Multiple views for a single scene
possible
– Supports transformations
(zooming, rotation, etc.)
– Provides scroll bars if necessary
Image Credit: Ricardo J. Reyes
Public Domain
– By default uses 2D paint engine,
possible to use OpenGL
30. Coordinate Systems
• Item Coordinates
– Each item has its own coordinate system
– Usually centered around its center point (0,0)
– Relative to parent’s coordinates
• Scene Coordinates
– Base coordinate system for all items
– Describes position for all top-level items
• View Coordinates
– Coordinates relative to the widget
31. Coordinate Mappings / Transformations
• Coordinate mappings with utility functions
– mapToScene() / mapFromScene()
– Available in view / scene / item classes
• Affine Transformations
– Extra methods (rotate(), etc.)
– QMatrix
QTransform transform;
transform.rotate(newPos.x() / 6.0, Qt::YAxis);
transform.rotate(newPos.y() / 6.0, Qt::XAxis);
baseItem->setTransform(transform);
32. Animation Framework
• Part of Qt Kinetic project (→ Qt 4.6)
– Animate Qt properties of widgets and
QObjects
– Can be used on its own or with state
machine
– Supports easing curves, animation
groups
34. Pixel Images
• Most common pixel-based formats:
– .png (Portable Network Graphics)
• Similar to .gif (which was patented until 2003 because of
its LZW compression)
• Compression: works well for graphics, not so well for
photos
• Transparency: support depends on device – 1 bit increasing JPEG compression
transparency or full alpha-channel.
– .jpg (Joint Photographic Experts Group)
• Compression: for photos (wavelength), not for graphics
• Transparency: not supported Image Credit: Ilmari_Karonen / Brianski
Creative Commons
35. Save PNGs – File Size Reduction
1. Optimized export – Photoshop: Save for Web
2. Further optimization – Pngcrush: http://pmt.sourceforge.net/pngcrush/
Use as few colors as
possible (fine
No dithering gradients compress
(compression gets worse)
more difficult)
Transparenter Kanal
You can set a
transparency
kann gesetzt
channel
werden
36. PNG Optimization – Example: 472 x 472 px
256 colours, no dither 64 colours, no dither 8 colours, no dither
30,0 kB 16,3 kB 6,3 kB
Results:
- Not much difference between 256
and 64 colours (especially on a device
display), but only half of the file size
- Limits of optimization: 8 colours not
enough
- Dithering at 8 colours: same file size
as visually better 64 colours image
8 colours, Diffusion dither
→ often, dithering is problematic! 15,9 kB