Home · Examples 


Screenshot Example

Code:

The Screenshot example shows how to take a screenshot of the desktop using QApplication and QDesktopWidget. It also shows how to use QTimer to provide a single-shot timer, and how to reimplement the QWidget.resizeEvent() event handler to make sure that an application resizes smoothly and without data loss.

With the application the users can take a screenshot of their desktop. They are provided with a couple of options: In addition, the application allows the users to save their screenshot if they want to.

Screenshot Class Implementation

The Screenshot class inherits
QWidget and is the application's main widget. It displays the application options and a preview of the screenshot.
public class Screenshot extends QWidget {

    volatile QPixmap originalPixmap;

    QLabel screenshotLabel;
    QGroupBox optionsGroupBox;
    QSpinBox delaySpinBox;
    QLabel delaySpinBoxLabel;
    QCheckBox hideThisWindowCheckBox;
    QPushButton newScreenshotButton;
    QPushButton saveScreenshotButton;
    QPushButton quitScreenshotButton;

    QVBoxLayout mainLayout;
    QGridLayout optionsGroupBoxLayout;
    QHBoxLayout buttonsLayout;

    public Screenshot(QWidget parent) {
        super(parent);
        screenshotLabel = new QLabel();
        screenshotLabel.setSizePolicy(Policy.Expanding, Policy.Expanding);
        screenshotLabel.setAlignment(AlignmentFlag.AlignCenter);
        screenshotLabel.setMinimumSize(240, 160);

        createOptionsGroupBox();
        createButtonsLayout();

        mainLayout = new QVBoxLayout();
        mainLayout.addWidget(screenshotLabel);
        mainLayout.addWidget(optionsGroupBox);
        mainLayout.addLayout(buttonsLayout);
        setLayout(mainLayout);

        shootScreen();
        delaySpinBox.setValue(5);

        setWindowIcon(new QIcon("classpath:com/trolltech/images/qt-logo.png"));
        setWindowTitle(tr("Screenshot"));
        resize(300, 200);
    }
In the constructor we first create the QLabel displaying the screenshot preview.

We set the QLabel's size policy to be Policy.Expanding both horizontally and vertically. This means that the QLabel's size hint is a sensible size, but the widget can be shrunk and still be useful. Also, the widget can make use of extra space, so it should get as much space as possible. Then we make sure the QLabel is aligned in the center of the Screenshot widget, and set its minimum size.

We create the applications's buttons and the group box containing the application's options, and put it all into a main layout. Finally, we take the initial screenshot and set the inital delay and the window title, before we resize the widget to a suitable size.

    public void resizeEvent(QResizeEvent event) {
        QSize scaledSize = originalPixmap.size();
        scaledSize.scale(screenshotLabel.size(),
                         AspectRatioMode.KeepAspectRatio);
        if (screenshotLabel.pixmap() != null
            || scaledSize != screenshotLabel.pixmap().size())
                updateScreenshotLabel();
    }
The resizeEvent() method is reimplemented to receive the resize events dispatched to the widget. The purpose is to scale the preview screenshot pixmap without deformation of its content, and also make sure that the application can be resized smoothly.

To achieve the first goal, we scale the screenshot pixmap using AspectRatioMode.KeepAspectRatio. We scale the pixmap to a rectangle as large as possible inside the current size of the screenshot preview label, preserving the aspect ratio. This means that if the user resizes the application window in only one direction, the preview screenshot keeps the same size.

To reach our second goal, we make sure that the preview screenshot only is repainted (using the private updateScreenshotLabel() method) when it actually changes its size.

    void newScreenshot() {
        if (hideThisWindowCheckBox.isChecked())
            hide();
        newScreenshotButton.setDisabled(true);

        QTimer.singleShot(delaySpinBox.value() * 1000,
                          this, "shootScreen()");
    }
The private newScreenshot() method is called when the user requests a new screenshot; but the method only prepares a new screenshot.

First we check if the Hide This Window option is checked, and if it is we hide the Screenshot widget. Then we disable the New Screenshot button, to make sure the user only can request one screenshot at a time.

We create a timer using the QTimer class which provides repetitive and single-shot timers. We set the timer to time out only once, using the static QTimer.singleShot() method. This method calls the private shootScreen() method after the time interval specified by the Screenshot Delay option. It is shootScreen() that actually performs the screenshot.

    void saveScreenshot() {
        String format = "png";
        String initialPath = QDir.currentPath() + tr("/untitled.") + format;
        String filter = String.format(tr("%1$s Files (*.%2$s);;All Files (*)"),
                                      format.toUpperCase(), format);
        String fileName;
        fileName = QFileDialog.getSaveFileName(this, tr("Save As"), initialPath,
                                               new QFileDialog.Filter(filter));

        if (!fileName.equals(""))
            originalPixmap.save(fileName, format);
    }
The saveScreenshot() method is called when the user push the Save button, and it presents a file dialog using the QFileDialog class.

QFileDialog enables a user to traverse the file system in order to select one or many files or a directory. The easiest way to create a QFileDialog is to use the static convenience methods.

We define the default file format to be png, and we make the file dialog's initial path the path the application is run from. We create the file dialog using the static QFileDialog.getSaveFileName() method which returns a file name selected by the user. The file does not have to exist. If the file name is valid, we use the QPixmap.save() method to save the screenshot's original pixmap in that file.

    void shootScreen() {
        if (delaySpinBox.value() != 0)
            QApplication.beep();
The shootScreen() method is called to take the screenshot. If the user has chosen to delay the screenshot, we make the application beep when the screenshot is taken using the static QApplication.beep() method.

The QApplication class manages the GUI application's control flow and main settings. It contains the main event loop, where all events from the window system and other sources are processed and dispatched.

        originalPixmap = null;

        originalPixmap = QPixmap.grabWindow(
                QApplication.desktop().winId());
        updateScreenshotLabel();

        newScreenshotButton.setDisabled(false);
        if (hideThisWindowCheckBox.isChecked())
            show();
    }
We take the screenshot using the static QPixmap.grabWindow() method. The method grabs the contents of the window passed as an argument, makes a pixmap out of it and returns that pixmap.

We identify the argument window using the QWidget.winId() method which returns the window system identifier. Here it returns the identifier of the current QDesktopWidget retrieved by the QApplication.desktop() method. The QDesktopWidget class provides access to screen information, and inherits QWidget::winId().

We update the screenshot preview label using the private updateScreenshotLabel() method. Then we enable the New Screenshot button, and finally we make the Screenshot widget visible if it was hidden during the screenshot.

    void updateCheckBox() {
        if (delaySpinBox.value() == 0)
            hideThisWindowCheckBox.setDisabled(true);
        else
            hideThisWindowCheckBox.setDisabled(false);
    }
The Hide This Window option is enabled or disabled depending on the delay of the screenshot. If there is no delay, the application window cannot be hidden and the option's checkbox is disabled.

The updateCheckBox() method is called whenever the user changes the delay using the Screenshot Delay option.

    void createOptionsGroupBox() {
        optionsGroupBox = new QGroupBox(tr("Options"));

        delaySpinBox = new QSpinBox();
        delaySpinBox.setSuffix(tr(" s"));
        delaySpinBox.setMaximum(60);
        delaySpinBox.valueChanged.connect(this, "updateCheckBox()");

        delaySpinBoxLabel = new QLabel(tr("Screenshot Delay:"));

        hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window"));

        optionsGroupBoxLayout = new QGridLayout();
        optionsGroupBoxLayout.addWidget(delaySpinBoxLabel, 0, 0);
        optionsGroupBoxLayout.addWidget(delaySpinBox, 0, 1);
        optionsGroupBoxLayout.addWidget(hideThisWindowCheckBox, 1, 0, 1, 2);
        optionsGroupBox.setLayout(optionsGroupBoxLayout);
    }
The private createOptionsGroupBox() method is called from the constructor.

First we create a group box that will contain all of the options' widgets. Then we create a QSpinBox and a QLabel for the Screenshot Delay option, and connect the spinbox to the updateCheckBox() method. Finally, we create a QCheckBox for the Hide This Window option, add all the options' widgets to a QGridLayout and install the layout on the group box.

Note that we don't have to specify any parents for the widgets when we create them. The reason is that when we add a widget to a layout and install the layout on another widget, the layout's widgets are automatically reparented to the widget the layout is installed on.

    void createButtonsLayout() {
        newScreenshotButton = createButton(tr("New Screenshot"), this,
                                           "newScreenshot()");

        saveScreenshotButton = createButton(tr("Save Screenshot"), this,
                                            "saveScreenshot()");

        quitScreenshotButton = createButton(tr("Quit"), this, "close()");

        buttonsLayout = new QHBoxLayout();
        buttonsLayout.addStretch();
        buttonsLayout.addWidget(newScreenshotButton);
        buttonsLayout.addWidget(saveScreenshotButton);
        buttonsLayout.addWidget(quitScreenshotButton);
    }
The private createButtonsLayout() method is called from the constructor. We create the application's buttons using the private createButton() method, and add them to a QHBoxLayout.
    QPushButton createButton(final String text, QWidget receiver,
                             String member) {
        QPushButton button = new QPushButton(text);
        button.clicked.connect(receiver, member);
        return button;
    }
The private createButton() method is called from the createButtonsLayout() method. It simply creates a QPushButton with the provided text, connects it to the provided receiver and method, and returns a pointer to the button.
    void updateScreenshotLabel() {
        screenshotLabel.setPixmap(originalPixmap.scaled(screenshotLabel.size(),
                                  AspectRatioMode.KeepAspectRatio,
                                  TransformationMode.SmoothTransformation));
    }
The private updateScreenshotLabel() method is called whenever the screenshot changes, or when a resize event changes the size of the screenshot preview label. It updates the screenshot preview's label using the QLabel.setPixmap() and QPixmap.scaled() methods.

QPixmap.scaled() returns a copy of the given pixmap scaled to a rectangle of the given size according to the given AspectRatioMode and TransformationMode. We scale the original pixmap to fit the current screenshot label's size, preserving the aspect ratio and giving the resulting pixmap smoothed edges.

    public static void main(String args[]) {
        QApplication.initialize(args);
        Screenshot screenshot = new Screenshot(null);
        screenshot.show();
        QApplication.exec();
    }

}
Finally, we provide a main() method to create and show the shaped clock when the example is run.


Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) Trademarks
Qt Jambi 4.5.2_01