Home · All Namespaces · All Classes · Grouped Classes · Modules · Functions codeless banner

Image Viewer Tutorial: Part 3

The Image Screen

In this part, the image screen will be introduced. Navigation from the list screen can be done by selecting an image. From the image screen, navigating back to the list screen is possible. The image screen will show the selected image. Later rotating and zooming functions will be added to this screen.

Setup the Image Screen

In order to set up the image screen, a similar pattern as for the first screen is used. Instead of deriving from a QListWidget, the Image Screen derives from a QWidget for displaying the image.

File: imagescreen.h

    #ifndef IMAGESCREEN_H
    #define IMAGESCREEN_H

    #include <QWidget>
    #include <QKeyEvent>
    #include <QContent>
    #include <QImage>

    class IViewer;
    class QSlider;

    class ImageScreen : public QWidget
    {
        Q_OBJECT
    public:
        ImageScreen(IViewer *viewer);
        void setImage(const QContent &content);
    private:
        void createActions();
        void createMenu();
    protected:
        void keyPressEvent (QKeyEvent *event);
    private:
        IViewer *_viewer;
        QContent _content;
    };

    #endif

The skeleton to create actions, menu methods and to receive key events has already been added. As a parent, the IViewer class is passed. A reference to IViewer is also held in the _viewer field.

The setImage() method will be used to set the image file path to this widget.

File:imagescreen.cpp

    #include "imagescreen.h"
    #include "iviewer.h"
    ImageScreen::ImageScreen(IViewer *viewer)
    : QWidget(viewer), _viewer(viewer)
    {
        createActions();
        createMenu();

        QSoftMenuBar::setLabel(this,Qt::Key_Select,"FullScreen","FullScreen");
    }

    void ImageScreen::keyPressEvent (QKeyEvent *event)
    {
        switch (event->key()) {
        default:
            QWidget::keyPressEvent(event);
            break;
        }
    }

The screen has to be added to our QStackedWidget to be able to switch between the screens. This screen will also get lazily created. For that purpose, a imageScreen() method is added to the IViewer class.

    ImageScreen* IViewer::imageScreen()
    {
        if (!_imageScreen) {
            _imageScreen = new ImageScreen(this);
            addWidget(_imageScreen);
        }
        return _imageScreen;
    }

The _imageScreen needs to be initialized with 0 in the IViewer constructor. Before giving it a run, a way to go back to the list screen from the image screen is needed. The Qt::Key_Back key will be used to come back to the list screen.

The doBack() method is added in that purpose:

    void ImageScreen::doBack()
    {
        _viewer->setCurrentWidget((QWidget*)_viewer->listScreen());
    }

Here, there is a new dependency to the list screen and the list screen header file has to be included. To react to the key, the keyPressEvent() method is modified to add this case:

        case Qt::Key_Back:
            doBack();
            break;

Now the image screen is ready for a first run. it just needs to be called from the list screen. The ListScreen openImage() method should be created to show the image screen. The content is retrieved from the content set and the content object contains a reference to the file path in the system. This file will be used to set it to the image screen. This is later used to load an image and paint it on the widget.

    void ListScreen::openImage(int row)
    {
        QContent c     = _cs->content(row);
        ImageScreen *s = _viewer->imageScreen();
        s->setImage(c);
        _viewer->setCurrentWidget(s);
    }

Finally the screen is shown by using the setCurrentWidget on the QStackedWidget.

Now its time to compile and launch the application. When an image is selected and the select key is pressed, a new screen should be opened, but nothing will be painted yet. The back key returns to the list screen.

Great! Well done.

Drawing the image

The way to draw the image is to load a QImage from a file and to paint it with a QPainter on the QWidget. The image should be centred on the screen. To centre the image, the centre of the widget is calculated and half the image's width and height are subtracted.

File: imagescreen.cpp

    void ImageScreen::paintEvent(QPaintEvent* event)
    {
        _image = new QImage(_content.file());

        int x  = width()/2-_image->width()/2;
        int y  = height()/2-_image->height()/2;
        painter.drawImage(QPoint(x, y), *_image);
        painter.end();
    }

Now, if the application is tested, the image should be printed on the screen! Yeah!

Now, it is time to add some functionality to this screen, like rotating and zooming.

Adding Rotate Functions

To rotate the image to the left and right, two actions are needed. These will be arranged in a sub menu.

    void ImageScreen::createActions()
    {

        _rotateLeftAction = new QAction("Left", this);
        connect(_rotateLeftAction, SIGNAL(triggered()),
                this, SLOT(onRotateRight()));

        _rotateRightAction = new QAction("Right", this);
        connect(_rotateRightAction, SIGNAL(triggered()),
    }

The rotate left and rotate right actions are connected to the onRotateLeft and onRotateRight slots. In the rotate slots, the rotation by 90 degres counter clockwise or clockwise is changed and an update is triggered.

To see the actions in the menu, they have to be added to the options menu. The left and right rotation actions will appear in the rotate sub-menu.

    void ImageScreen::createMenu()
    {
        QMenu* menu = QSoftMenuBar::menuFor(this);

        QMenu* rotateMenu = menu->addMenu("Rotate");
        rotateMenu->addAction(_rotateLeftAction);
        rotateMenu->addAction(_rotateRightAction);

        QMenu* zoomMenu = menu->addMenu("Zoom");
    }

The onRotateLeft/Right methods are quite simple.

    void ImageScreen::onRotateLeft()
    {
        _rotation -= 90.0;
        update();
    }

    void ImageScreen::onRotateRight()
    {
        _rotation += 90.0;
        update();
    }

The _rotation field is a qreal field, which holds our current rotation. Every time the roation is called, 90 degres is added/substracted on the rotation. The _rotation field must be initialized to 0 in the constructor.

Now the paintEvent method needs some changes in order to reflect the rotation.

    void ImageScreen::paintEvent(QPaintEvent* event)
    {
        _image = new QImage(_content.file());

        int x  = width()/2-_image->width()/2;
        int y  = height()/2-_image->height()/2;
        int x2 = width()/2;
        int y2 = height()/2;

        QPointF c(x2, y2);
        QPainter painter(this);
        painter.translate(c);
        painter.rotate(_rotation);
        painter.translate(-c);
        painter.drawImage(QPoint(x, y), *_image);
        painter.end();
    }

The translate() methods are used to translate every x-y coordinates to the center of the widget. Otherwise the image would rotate around the top-left corner ( QPoint(0,0) ). Now, the image can be rotated with the rotate method. The second translate() call translates every call back to the top left corner of the now rotated x-y coordinate system. The image is then painted on the top-left corner of this coordination system.

Instead of calculating the center of the screen manually, the QRect.center() method of the QWidget.rect() could also have been used. This would also be the preferred way. The documentation provides useful information about the translation and rotation.

After a compilation, the application can be run. Now the rotate actions are available from the rotate menu in the options menu of the image screen.

Zooming in and out

The zoom functionality is similarly structured to the rotate functionality. The creation of the zoom in and out actions and the addition to a zoom sub-menu are left to the reader.

The implementation of the zoom in/out functions just add/substract a 0.5 value to the zoom level.

    void ImageScreen::onZoomOut()
    {
        _zoom -= 0.5;
        update();
    }

    void ImageScreen::onZoomIn()
    {
        _zoom += 0.5;
        update();
    }

Again the _zoom field is a qreal, which needs to be initialized in the constructor to 1 this time.

In the paint method, the zooming functionalities are added by modifying the scale.

    void ImageScreen::paintEvent(QPaintEvent* event)
    {
        ...
        QPainter painter(this);
        painter.translate(c);
        painter.rotate(_rotation);
        painter.scale(_zoom, _zoom);
        painter.translate(-c);
        ...
    }

We scale the image symetrically by width and height.

Now, in the options menu of the image screen, the Zoom sub-menu with in and out sub-menu items is available. Pressing the In action should zoom in the image by 50%.

Summary

This part of the tutorial has described how to paint an image on the screen and how to use the rotate, scale and translate functionalities of the painter. The painter is a very powerful object. Moreover, some repeating patterns in designing screens for mobile phones have been shown. These patterns will repeat for each screen. The screens are lazily created and added to our stacked widget. Currently if a screen is created, it will only be destroyed when the application closes.

Note: The images in paintEvent will never be destroyed. if(_image) delete _image should be added in the front of the paintEvent method to get rid of an old image.

The next part will introduce some actions to the list screen, like rename and delete.

Prev|Top|Start Page|Next


Copyright © 2009 Trolltech Trademarks
Qt Extended 4.4.3