summaryrefslogtreecommitdiff
path: root/doc/src/examples/pixelator.qdoc
blob: 2a8b43d7210cb4759d6e02f5b009c3703068dca9 (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
/****************************************************************************
**
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the either Technology Preview License Agreement or the
** Beta Release License Agreement.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
** $QT_END_LICENSE$
**
****************************************************************************/

/*!
    \example itemviews/pixelator
    \title Pixelator Example

    The Pixelator example shows how delegates can be used to customize the way that
    items are rendered in standard item views.

    \image pixelator-example.png

    By default, QTreeView, QTableView, and QListView use a standard item delegate
    to display and edit a set of common data types that are sufficient for many
    applications. However, an application may need to represent items of data in a
    particular way, or provide support for rendering more specialized data types,
    and this often requires the use of a custom delegate.

    In this example, we show how to use custom delegates to modify the appearance
    of standard views. To do this, we implement the following components:

    \list
    \i A model which represents each pixel in an image as an item of data, where each
       item contains a value for the brightness of the corresponding pixel.
    \i A custom delegate that uses the information supplied by the model to represent
       each pixel as a black circle on a white background, where the radius of the
       circle corresponds to the darkness of the pixel.
    \endlist

    This example may be useful for developers who want to implement their own table
    models or custom delegates. The process of creating custom delegates for editing
    item data is covered in the \l{Spin Box Delegate Example}{Spin Box Delegate}
    example.

    \section1 ImageModel Class Definition

    The \c ImageModel class is defined as follows:

    \snippet examples/itemviews/pixelator/imagemodel.h 0

    Since we only require a simple, read-only table model, we only need to implement
    functions to indicate the dimensions of the image and supply data to other
    components.

    For convenience, the image to be used is passed in the constructor.

    \section1 ImageModel Class Implementation

    The constructor is trivial:

    \snippet examples/itemviews/pixelator/imagemodel.cpp 0

    The \c setImage() function sets the image that will be used by the model:
   
    \snippet examples/itemviews/pixelator/imagemodel.cpp 1

    The QAbstractItemModel::reset() call tells the view(s) that the model
    has changed.

    The \c rowCount() and \c columnCount() functions return the height and width of
    the image respectively:

    \snippet examples/itemviews/pixelator/imagemodel.cpp 2
    \snippet examples/itemviews/pixelator/imagemodel.cpp 3

    Since the image is a simple two-dimensional structure, the \c parent arguments
    to these functions are unused. They both simply return the relevant size from
    the underlying image object.

    The \c data() function returns data for the item that corresponds to a given
    model index in a format that is suitable for a particular role:

    \snippet examples/itemviews/pixelator/imagemodel.cpp 4

    In this implementation, we only check that the model index is valid, and that
    the role requested is the \l{Qt::ItemDataRole}{DisplayRole}. If so, the function
    returns the grayscale value of the relevant pixel in the image; otherwise, a null
    model index is returned.

    This model can be used with QTableView to display the integer brightness values
    for the pixels in the image. However, we will implement a custom delegate to
    display this information in a more artistic way.

    The \c headerData() function is also reimplemented:

    \snippet examples/itemviews/pixelator/imagemodel.cpp 5

    We return (1, 1) as the size hint for a header item. If we
    didn't, the headers would default to a larger size, preventing
    us from displaying really small items (which can be specified
    using the \gui{Pixel size} combobox).

    \section1 PixelDelegate Class Definition

    The \c PixelDelegate class is defined as follows:

    \snippet examples/itemviews/pixelator/pixeldelegate.h 0

    This class provides only basic features for a delegate so, unlike the
    \l{Spin Box Delegate Example}{Spin Box Delegate} example, we subclass
    QAbstractItemDelegate instead of QItemDelegate.

    We only need to reimplement \l{QAbstractItemDelegate::paint()}{paint()} and
    \l{QAbstractItemDelegate::sizeHint()}{sizeHint()} in this class.
    However, we also provide a delegate-specific \c setPixelSize() function so
    that we can change the delegate's behavior via the signals and slots mechanism.

    \section1 PixelDelegate Class Implementation

    The \c PixelDelegate constructor is used to set up a default value for
    the size of each "pixel" that it renders. The base class constructor is
    also called to ensure that the delegate is set up with a parent object,
    if one is supplied:

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 0

    Each item is rendered by the delegate's
    \l{QAbstractItemDelegate::paint()}{paint()} function. The view calls this
    function with a ready-to-use QPainter object, style information that the
    delegate should use to correctly draw the item, and an index to the item in
    the model:

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 1

    The first task the delegate has to perform is to draw the item's background
    correctly. Usually, selected items appear differently to non-selected items,
    so we begin by testing the state passed in the style option and filling the
    background if necessary.

    The radius of each circle is calculated in the following lines of code:

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 3
    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 4

    First, the largest possible radius of the circle is determined by taking the
    smallest dimension of the style option's \c rect attribute.
    Using the model index supplied, we obtain a value for the brightness of the
    relevant pixel in the image. The radius of the circle is calculated by
    scaling the brightness to fit within the item and subtracting it from the
    largest possible radius.

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 5
    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 6
    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 7

    We save the painter's state, turn on antialiasing (to obtain smoother
    curves), and turn off the pen.

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 8
    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 9

    The foreground of the item (the circle representing a pixel) must be
    rendered using an appropriate brush. For unselected items, we will use a
    solid black brush; selected items are drawn using a predefined brush from
    the style option's palette.

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 10

    Finally, we paint the circle within the rectangle specified by the style
    option and we call \l{QPainter::}{restore()} on the painter.

    The \c paint() function does not have to be particularly complicated; it is
    only necessary to ensure that the state of the painter when the function
    returns is the same as it was when it was called. This usually
    means that any transformations applied to the painter must be preceded by
    a call to QPainter::save() and followed by a call to QPainter::restore().

    The delegate's \l{QAbstractItemDelegate::}{sizeHint()} function
    returns a size for the item based on the predefined pixel size, initially set
    up in the constructor:

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 11

    The delegate's size is updated whenever the pixel size is changed.
    We provide a custom slot to do this:

    \snippet examples/itemviews/pixelator/pixeldelegate.cpp 12

    \section1 Using The Custom Delegate

    In this example, we use a main window to display a table of data, using the
    custom delegate to render each cell in a particular way. Much of the
    \c MainWindow class performs tasks that are not related to item views. Here,
    we only quote the parts that are relevant. You can look at the rest of the
    implementation by following the links to the code at the top of this
    document.

    In the constructor, we set up a table view, turn off its grid, and hide its
    headers:

    \snippet examples/itemviews/pixelator/mainwindow.cpp 0
    \dots
    \snippet examples/itemviews/pixelator/mainwindow.cpp 1

    This enables the items to be drawn without any gaps between them. Removing
    the headers also prevents the user from adjusting the sizes of individual
    rows and columns. 
    
    We also set the minimum section size to 1 on the headers. If we
    didn't, the headers would default to a larger size, preventing
    us from displaying really small items (which can be specified
    using the \gui{Pixel size} combobox).

    The custom delegate is constructed with the main window as its parent, so
    that it will be deleted correctly later, and we set it on the table view.

    \snippet examples/itemviews/pixelator/mainwindow.cpp 2

    Each item in the table view will be rendered by the \c PixelDelegate
    instance.

    We construct a spin box to allow the user to change the size of each "pixel"
    drawn by the delegate:

    \snippet examples/itemviews/pixelator/mainwindow.cpp 3

    This spin box is connected to the custom slot we implemented in the
    \c PixelDelegate class. This ensures that the delegate always draws each
    pixel at the currently specified size:

    \snippet examples/itemviews/pixelator/mainwindow.cpp 4
    \dots
    \snippet examples/itemviews/pixelator/mainwindow.cpp 5

    We also connect the spin box to a slot in the \c MainWindow class. This
    forces the view to take into account the new size hints for each item;
    these are provided by the delegate in its \c sizeHint() function.

    \snippet examples/itemviews/pixelator/mainwindow.cpp 6

    We explicitly resize the columns and rows to match the
    \gui{Pixel size} combobox.
*/