Viadeo Twitter Google Bookmarks ! Facebook Digg del.icio.us MySpace Yahoo MyWeb Blinklist Netvouz Reddit Simpy StumbleUpon Bookmarks Windows Live Favorites 
Logo Documentation Qt ·  Page d'accueil  ·  Toutes les classes  ·  Classes principales  ·  Annotées  ·  Classes groupées  ·  Modules  ·  Fonctions  · 

Pixelator Example

Files:

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

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:

  • 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.
  • 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.

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 Spin Box Delegate example.

ImageModel Class Definition

The ImageModel class is defined as follows:

    class ImageModel : public QAbstractTableModel
    {
        Q_OBJECT

    public:
        ImageModel(const QImage &image, QObject *parent = 0);

        int rowCount(const QModelIndex &parent) const;
        int columnCount(const QModelIndex &parent) const;

        QVariant data(const QModelIndex &index, int role) const;

    private:
        QImage modelImage;
    };

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.

ImageModel Class Implementation

The constructor is only used to store the image that will be used by the model:

    ImageModel::ImageModel(const QImage &image, QObject *parent)
        : QAbstractTableModel(parent)
    {
        modelImage = QImage(image);
    }

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

    int ImageModel::rowCount(const QModelIndex & /* parent */) const
    {
        return modelImage.height();
    }

    int ImageModel::columnCount(const QModelIndex & /* parent */) const
    {
        return modelImage.width();
    }

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

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

    QVariant ImageModel::data(const QModelIndex &index, int role) const
    {
        if (!index.isValid())
            return QVariant();
        else if (role != Qt::DisplayRole)
            return QVariant();

        return qGray(modelImage.pixel(index.column(), index.row()));
    }

In this implementation, we only check that the model index is valid, and that the role requested is the 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.

PixelDelegate Class Definition

The PixelDelegate class is defined as follows:

    class PixelDelegate : public QAbstractItemDelegate
    {
        Q_OBJECT

    public:
        PixelDelegate(QObject *parent = 0);

        void paint(QPainter *painter, const QStyleOptionViewItem &option,
                   const QModelIndex &index) const;

        QSize sizeHint(const QStyleOptionViewItem &option,
                       const QModelIndex &index ) const;

    public slots:
        void setPixelSize(int size);

    private:
        int pixelSize;
    };

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

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

PixelDelegate Class Implementation

The 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:

    PixelDelegate::PixelDelegate(QObject *parent)
        : QAbstractItemDelegate(parent)
    {
        pixelSize = 12;
    }

Each item is rendered by the delegate's 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:

    void PixelDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
                              const QModelIndex &index) const
    {
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setPen(Qt::NoPen);

For this delegate, we do not need to use a pen, but we would like the output to be antialiased.

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 setting the painter's brush accordingly:

        if (option.state & QStyle::State_Selected)
            painter->setBrush(option.palette.highlight());
        else
            painter->setBrush(QBrush(Qt::white));

This ensures that the background of each item in the view has the correct style for both selected and unselected items. Ideally, we would also test for other states and draw each item in the appropriate style, too.

The foreground of the item (the circle representing a pixel) must also 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:

        if (option.state & QStyle::State_Selected)
            painter->setBrush(option.palette.highlightedText());
        else
            painter->setBrush(QBrush(Qt::black));

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

        int size = qMin(option.rect.width(), option.rect.height());
        int brightness = index.model()->data(index, Qt::DisplayRole).toInt();
        double radius = (size/2.0) - (brightness/255.0 * size/2.0);

First, the largest possible radius of the circle is determined by taking the smallest dimension of the style option's 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.

Finally, we paint the circle within the rectangle specified by the style option:

        painter->drawEllipse(QRectF(option.rect.x() + option.rect.width()/2 - radius,
                                    option.rect.y() + option.rect.height()/2 - radius,
                                    2*radius, 2*radius));
    }

The 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 sizeHint() function returns a size for the item based on the predefined pixel size, initially set up in the constructor:

    QSize PixelDelegate::sizeHint(const QStyleOptionViewItem & /* option */,
                                  const QModelIndex & /* index */) const
    {
        return QSize(pixelSize, pixelSize);
    }

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

    void PixelDelegate::setPixelSize(int size)
    {
        pixelSize = size;
    }

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 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:

    MainWindow::MainWindow() : QMainWindow()
    {
        ...
        view = new QTableView;
        view->setShowGrid(false);
        view->horizontalHeader()->hide();
        view->verticalHeader()->hide();

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.

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.

        PixelDelegate *delegate = new PixelDelegate(this);
        view->setItemDelegate(delegate);

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

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

        QLabel *pixelSizeLabel = new QLabel(tr("Pixel size:"));
        QSpinBox *pixelSizeSpinBox = new QSpinBox;
        pixelSizeSpinBox->setMinimum(1);
        pixelSizeSpinBox->setMaximum(32);
        pixelSizeSpinBox->setValue(12);

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

        connect(pixelSizeSpinBox, SIGNAL(valueChanged(int)),
                delegate, SLOT(setPixelSize(int)));
        connect(pixelSizeSpinBox, SIGNAL(valueChanged(int)),
                this, SLOT(updateView()));
        ...
    }

We also connect the spin box to a slot in the 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 sizeHint() function.

    void MainWindow::updateView()
    {
        for (int row = 0; row < model->rowCount(QModelIndex()); ++row)
            view->resizeRowToContents(row);
        for (int column = 0; column < model->columnCount(QModelIndex()); ++column)
            view->resizeColumnToContents(column);
    }

The view will automatically update after receiving this information. Note that we only have to iterate over each row and column, rather than over each item in the model, since they all have the same dimensions.

Cette page est une traduction d'une page de la documentation de Qt, écrite par Nokia Corporation and/or its subsidiary(-ies). Les éventuels problèmes résultant d'une mauvaise traduction ne sont pas imputables à Nokia. Qt 4.1
Copyright © 2012 Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon, vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérêts. Cette page est déposée à la SACD.
Vous avez déniché une erreur ? Un bug ? Une redirection cassée ? Ou tout autre problème, quel qu'il soit ? Ou bien vous désirez participer à ce projet de traduction ? N'hésitez pas à nous contacter ou par MP !
 
 
 
 
Partenaires

Hébergement Web