blob: 5ad2158e7ed7c2d7811ac6e1b5ddade9d7ab0ceb [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the documentation of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:FDL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Free Documentation License Usage
** 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. Please review the following information to ensure
** the GNU Free Documentation License version 1.3 requirements
** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
** $QT_END_LICENSE$
**
****************************************************************************/
/*!
\example itemviews/pixelator
\title Pixelator Example
\ingroup examples-itemviews
\brief 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
\li 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.
\li 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 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.
\section1 ImageModel Class Implementation
The constructor is trivial:
\snippet itemviews/pixelator/imagemodel.cpp 0
The \c setImage() function sets the image that will be used by the model:
\snippet 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 itemviews/pixelator/imagemodel.cpp 2
\snippet 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 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 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 \uicontrol{Pixel size} combobox).
\section1 PixelDelegate Class Definition
The \c PixelDelegate class is defined as follows:
\snippet 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 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 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 itemviews/pixelator/pixeldelegate.cpp 3
\snippet 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 itemviews/pixelator/pixeldelegate.cpp 5
\snippet itemviews/pixelator/pixeldelegate.cpp 6
\snippet itemviews/pixelator/pixeldelegate.cpp 7
We save the painter's state, turn on antialiasing (to obtain smoother
curves), and turn off the pen.
\snippet itemviews/pixelator/pixeldelegate.cpp 8
\snippet 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 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 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 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 itemviews/pixelator/mainwindow.cpp 0
\dots
\snippet 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 \uicontrol{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 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 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 itemviews/pixelator/mainwindow.cpp 4
\dots
\snippet 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 itemviews/pixelator/mainwindow.cpp 6
We explicitly resize the columns and rows to match the
\uicontrol{Pixel size} combobox.
*/