summaryrefslogtreecommitdiff
path: root/doc/src/examples/tablet.qdoc
blob: adc49bf20adf0da5abd1c2a80ced7f4a68802134 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in a
** written agreement between you and Nokia.
**
** GNU Free Documentation License
** Alternatively, this file may be used under the terms of the GNU Free
** Documentation License version 1.3 as published by the Free Software
** Foundation and appearing in the file included in the packaging of this
** file.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example widgets/tablet
    \title Tablet Example

    This example shows how to use a Wacom tablet in Qt applications.

    \image tabletexample.png

    When you use a tablet with Qt applications, \l{QTabletEvent}s are
    generated. You need to reimplement the
    \l{QWidget::}{tabletEvent()} event handler if you want to handle
    tablet events. Events are generated when the device used for
    drawing enters and leaves the proximity of the tablet (i.e., when
    it is close but not pressed down on it), when a device is pushed
    down and released from it, and when a device is moved on the
    tablet.
  
    The information available in QTabletEvent depends on the device
    used. The tablet in this example has two different devices for
    drawing: a stylus and an airbrush. For both devices the event
    contains the position of the device, pressure on the tablet,
    vertical tilt, and horizontal tilt (i.e, the angle between the
    device and the perpendicular of the tablet). The airbrush has a
    finger wheel; the position of this is also available in the tablet
    event.

    In this example we implement a drawing program. You can use the
    stylus to draw on the tablet as you use a pencil on paper. When
    you draw with the airbrush you get a spray of paint; the finger
    wheel is used to change the density of the spray. The pressure and
    tilt can change the alpha and saturation values of the QColor and the
    width of the QPen used for drawing.

    The example consists of the following:

    \list
        \o The \c MainWindow class inherits QMainWindow and creates
           the examples menus and connect their slots and signals.
	\o The \c TabletCanvas class inherits QWidget and 
	   receives tablet events. It uses the events to paint on a
           offscreen pixmap, which it draws onto itself.
	\o The \c TabletApplication class inherits QApplication. This
	   class handles tablet events that are not sent to \c tabletEvent(). 
	   We will look at this later.
	\o The \c main() function creates a \c MainWindow and shows it
	   as a top level window.
    \endlist


    \section1 MainWindow Class Definition

    The \c MainWindow creates a \c TabletCanvas and sets it as its 
    center widget. 

    \snippet examples/widgets/tablet/mainwindow.h 0

    The QActions let the user select if the tablets pressure and
    tilt should change the pen width, color alpha component and color
    saturation. \c createActions() creates all actions, and \c
    createMenus() sets up the menus with the actions. We have one
    QActionGroup for the actions that alter the alpha channel, color
    saturation and line width respectively. The action groups are
    connected to the \c alphaActionTriggered(), \c
    colorSaturationActiontriggered(), and \c
    lineWidthActionTriggered() slots, which calls functions in \c
    myCanvas.


    \section1 MainWindow Class Implementation

    We start width a look at the constructor \c MainWindow():

    \snippet examples/widgets/tablet/mainwindow.cpp 0

    In the constructor we create the canvas, actions, and menus.
    We set the canvas as the center widget. We also initialize the
    canvas to match the state of our menus and start drawing with a
    red color.

    Here is the implementation of \c brushColorAct():

    \snippet examples/widgets/tablet/mainwindow.cpp 1

    We let the user pick a color with a QColorDialog. If it is valid,
    we set a new drawing color with \c setColor().

    Here is the implementation of \c alphaActionTriggered():

    \snippet examples/widgets/tablet/mainwindow.cpp 2

    The \c TabletCanvas class supports two ways by which the alpha 
    channel of the drawing color can be changed: tablet pressure and
    tilt. We have one action for each and an action if the alpha
    channel should not be changed. 

    Here is the implementation of \c lineWidthActionTriggered():

    \snippet examples/widgets/tablet/mainwindow.cpp 3

    We check which action is selected in \c lineWidthGroup, and set
    how the canvas should change the drawing line width.

    Here is the implementation of \c saturationActionTriggered():

    \snippet examples/widgets/tablet/mainwindow.cpp 4

    We check which action is selected in \c colorSaturationGroup, and
    set how the canvas should change the color saturation of the
    drawing color.

    Here is the implementation of \c saveAct():

    \snippet examples/widgets/tablet/mainwindow.cpp 5

    We use the QFileDialog to let the user select a file to save the
    drawing in. It is the \c TabletCanvas that save the drawing, so we
    call its \c saveImage() function.

    Here is the implementation of \c loadAct():

    \snippet examples/widgets/tablet/mainwindow.cpp 6

    We let the user select the image file to be opened with
    a QFileDialog; we then ask the canvas to load the image with \c
    loadImage().

    Here is the implementation of \c aboutAct():

    \snippet examples/widgets/tablet/mainwindow.cpp 7

    We show a message box with a short description of the example. 

    \c createActions() creates all actions and action groups of
    the example. We look at the creation of one action group and its
    actions. See the \l{Application Example}{application example} if
    you want a high-level introduction to QActions. 

    Here is the implementation of \c createActions:

    \snippet examples/widgets/tablet/mainwindow.cpp 8
    \dots
    \snippet examples/widgets/tablet/mainwindow.cpp 9

    We want the user to be able to choose if the drawing color's
    alpha component should be changed by the tablet pressure or tilt.
    We have one action for each choice and an action if the alpha
    channel is not to be changed, i.e, the color is opaque. We make
    the actions checkable; the \c alphaChannelGroup will then ensure
    that only one of the actions are checked at any time. The \c
    triggered() signal is emitted when an action is checked.

    \dots
    \snippet examples/widgets/tablet/mainwindow.cpp 10

    Here is the implementation of \c createMenus():

    \snippet examples/widgets/tablet/mainwindow.cpp 11

    We create the menus of the example and add the actions to them.


    \section1 TabletCanvas Class Definition

    The \c TabletCanvas class provides a surface on which the
    user can draw with a tablet. 

    \snippet examples/widgets/tablet/tabletcanvas.h 0

    The canvas can change the alpha channel, color saturation,
    and line width of the drawing. We have one enum for each of
    these; their values decide if it is the tablet pressure or tilt
    that will alter them. We keep a private variable for each, the \c
    alphaChannelType, \c colorSturationType, and \c penWidthType,
    which we provide access functions for. 

    We draw on a QPixmap with \c myPen and \c myBrush using \c
    myColor. The \c saveImage() and \c loadImage() saves and loads
    the QPixmap to disk. The pixmap is drawn on the widget in \c
    paintEvent(). The \c pointerType and \c deviceType keeps the type
    of pointer, which is either a pen or an eraser, and device
    currently used on the tablet, which is either a stylus or an
    airbrush.

    The interpretation of events from the tablet is done in \c
    tabletEvent(); \c paintPixmap(), \c updateBrush(), and \c
    brushPattern() are helper functions used by \c tabletEvent().


    \section1 TabletCanvas Class Implementation

    We start with a look at the constructor:

    \snippet examples/widgets/tablet/tabletcanvas.cpp 0

    In the constructor we initialize our class variables. We need
    to draw the background of our pixmap, as the default is gray.

    Here is the implementation of \c saveImage():

    \snippet examples/widgets/tablet/tabletcanvas.cpp 1

    QPixmap implements functionality to save itself to disk, so we
    simply call \l{QPixmap::}{save()}.

    Here is the implementation of \c loadImage():

    \snippet examples/widgets/tablet/tabletcanvas.cpp 2

    We simply call \l{QPixmap::}{load()}, which loads the image in \a
    file.

    Here is the implementation of \c tabletEvent():

    \snippet examples/widgets/tablet/tabletcanvas.cpp 3

    We get three kind of events to this function: TabletPress,
    TabletRelease, and TabletMove, which is generated when a device
    is pressed down on, leaves, or moves on the tablet. We set the \c
    deviceDown to true when a device is pressed down on the tablet;
    we then know when we should draw when we receive move events. We
    have implemented the \c updateBrush() and \c paintPixmap() helper
    functions to update \c myBrush and \c myPen after the state of \c
    alphaChannelType, \c colorSaturationType, and \c lineWidthType.

    Here is the implementation of \c paintEvent():

    \snippet examples/widgets/tablet/tabletcanvas.cpp 4

    We simply draw the pixmap to the top left of the widget.

    Here is the implementation of \c paintPixmap():

    \snippet examples/widgets/tablet/tabletcanvas.cpp 5

    In this function we draw on the pixmap based on the movement of the
    device. If the device used on the tablet is a stylus we want to draw a
    line between the positions of the stylus recorded in \c polyLine. We
    also assume that this is a reasonable handling of any unknown device,
    but update the statusbar with a warning so that the user can see that
    for his tablet he might have to implement special handling.
    If it is an airbrush we want to draw a circle of points with a
    point density based on the tangential pressure, which is the position
    of the finger wheel on the airbrush. We use the Qt::BrushStyle to
    draw the points as it has styles that draw points with different
    density; we select the style based on the tangential pressure in
    \c brushPattern().

    \snippet examples/widgets/tablet/tabletcanvas.cpp 6

    We return a brush style with a point density that increases with
    the tangential pressure.

    In \c updateBrush() we set the pen and brush used for drawing
    to match \c alphaChannelType, \c lineWidthType, \c
    colorSaturationType, and \c myColor. We will examine the code to
    set up \c myBrush and \c myPen for each of these variables:

    \snippet examples/widgets/tablet/tabletcanvas.cpp 7

    We fetch the current drawingcolor's hue, saturation, value,
    and alpha values. \c hValue and \c vValue are set to the
    horizontal and vertical tilt as a number from 0 to 255. The
    original values are in degrees from -60 to 60, i.e., 0 equals
    -60, 127 equals 0, and 255 equals 60 degrees. The angle measured
    is between the device and the perpendicular of the tablet (see
    QTabletEvent for an illustration).
         
    \snippet examples/widgets/tablet/tabletcanvas.cpp 8

    The alpha channel of QColor is given as a number between 0
    and 255 where 0 is transparent and 255 is opaque.
    \l{QTabletEvent::}{pressure()} returns the pressure as a qreal
    between 0.0 and 1.0. By subtracting 127 from the tilt values and
    taking the absolute value we get the smallest alpha values (i.e.,
    the color is most transparent) when the pen is perpendicular to
    the tablet. We select the largest of the vertical and horizontal
    tilt value. 
 
    \snippet examples/widgets/tablet/tabletcanvas.cpp 9

    The colorsaturation is given as a number between 0 and 255. It is
    set with \l{QColor::}{setHsv()}. We can set the tilt values
    directly, but must multiply the pressure to a number between 0 and
    255.

    \snippet examples/widgets/tablet/tabletcanvas.cpp 10

    The width of the pen increases with the pressure. When the pen
    width is controlled with the tilt we let the width increse with
    the angle between the device and the perpendicular of the tablet.

    \snippet examples/widgets/tablet/tabletcanvas.cpp 11
   
    We finally check wether the pointer is the stylus or the eraser.
    If it is the eraser, we set the color to the background color of
    the pixmap an let the pressure decide the pen width, else we set
    the colors we have set up previously in the function.


    \section1 TabletApplication Class Definition

    We inherit QApplication in this class because we want to
    reimplement the \l{QApplication::}{event()} function. 

    \snippet examples/widgets/tablet/tabletapplication.h 0

    We keep a \c TabletCanvas we send the device type of the events we
    handle in the \c event() function to. The TabletEnterProximity
    and TabletLeaveProximity events are not sendt to the QApplication
    object, while other tablet events are sendt to the QWidget's
    \c event(), which sends them on to \l{QWidget::}{tabletEvent()}.
    Since we want to handle these events we have implemented \c 
    TabletApplication.


    \section1 TabletApplication Class Implementation

    Here is the implementation of \c event():

    \snippet examples/widgets/tablet/tabletapplication.cpp 0

    We use this function to handle the TabletEnterProximity and
    TabletLeaveProximity events, which is generated when a device
    enters and leaves the proximity of the tablet. The intended use of these
    events is to do work that is dependent on what kind of device is
    used on the tablet. This way, you don't have to do this work
    when other events are generated, which is more frequently than the
    leave and enter proximity events. We call \c setTabletDevice() in 
    \c TabletCanvas.

    \section1 The \c main() function

    Here is the examples \c main() function:

    \snippet examples/widgets/tablet/main.cpp 0

    In the \c main() function we create a \c MainWinow and display it
    as a top level window. We use the \c TabletApplication class. We
    need to set the canvas after the application is created. We cannot
    use classes that implement event handling before an QApplication
    object is instantiated.
*/