From 81951dfae0d62cdc5921ce7171b7fc1cebdbf770 Mon Sep 17 00:00:00 2001 From: Richard Braakman Date: Tue, 30 Apr 2013 13:43:10 +0300 Subject: [PATCH 1/3] [nemo-qml-plugin-utilities] Add Screenshots component --- src/declarativescreenshots.cpp | 146 +++++++++++++++++++++++++++++++++ src/declarativescreenshots.h | 138 +++++++++++++++++++++++++++++++ src/plugin.cpp | 2 + src/src.pro | 6 +- 4 files changed, 290 insertions(+), 2 deletions(-) create mode 100644 src/declarativescreenshots.cpp create mode 100644 src/declarativescreenshots.h diff --git a/src/declarativescreenshots.cpp b/src/declarativescreenshots.cpp new file mode 100644 index 0000000..2c224d5 --- /dev/null +++ b/src/declarativescreenshots.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2013 Jolla Ltd. + * Contact: Richard Braakman + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +#include "declarativescreenshots.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +DeclarativeScreenshots::DeclarativeScreenshots(QDeclarativeItem *parent) + : QDeclarativeItem(parent), m_name("screenshot"), m_target(0), + m_format("png"), m_formatSupported(true) { + m_path = QDesktopServices::storageLocation( + QDesktopServices::PicturesLocation); +} + +void DeclarativeScreenshots::setPath(const QString & path) { + if (path == m_path) + return; + m_path = path; + emit pathChanged(); +} + +void DeclarativeScreenshots::setName(const QString & name) { + if (name == m_name) + return; + m_name = name; + emit nameChanged(); +} + +void DeclarativeScreenshots::setTarget(QDeclarativeItem *target) { + if (target == m_target) + return; + + if (m_target) + disconnect(m_target, SIGNAL(destroyed()), + this, SLOT(targetDestroyed())); + if (target) + connect(target, SIGNAL(destroyed()), + this, SLOT(targetDestroyed())); + + m_target = target; + emit targetChanged(); +} + +// This is connected to m_target's destroyed() signal. +// The connection is maintained by setTarget(). +// This logic effectively makes m_target a weak reference. +void DeclarativeScreenshots::targetDestroyed() { + setTarget(0); +} + +void DeclarativeScreenshots::setFormat(const QString & format) { + if (format == m_format) + return; + bool old_supported = m_formatSupported; + + // Set both m_format and m_formatSupported before sending any signals, + // so that receivers see a consistent state. + + m_format = format; + m_formatSupported = false; + QByteArray cmpformat = format.toAscii().toLower(); + Q_FOREACH(QByteArray fmt, QImageWriter::supportedImageFormats()) { + if (fmt.toLower() == cmpformat) { + m_formatSupported = true; + break; + } + } + + emit formatChanged(); + if (m_formatSupported != old_supported) + emit formatSupportedChanged(); +} + +bool DeclarativeScreenshots::take(qreal x, qreal y, qreal width, qreal height) { + QString timestamp = QDateTime::currentDateTimeUtc() + .toString("-yyyyMMdd-hhmmsszzz."); + QPixmap snapshot; + + if (m_target) { + QGraphicsScene *scene = m_target->scene(); + if (!scene) + return false; + + if (width < 0) + width = m_target->width() - x; + if (height < 0) + height = m_target->height() - y; + + QRectF mappedRect = m_target->mapRectToScene(x, y, width, height); + QRectF targetRect(0, 0, mappedRect.width(), mappedRect.height()); + snapshot = QPixmap(mappedRect.size().toSize()); + snapshot.fill(Qt::transparent); + + QPainter painter(&snapshot); + scene->render(&painter, targetRect, mappedRect); + } else { + // If there's no target item, then take an actual screenshot of the + // desktop root window. + snapshot = QPixmap::grabWindow(QApplication::desktop()->winId(), + x, y, width, height); + } + + QString pathname = QDir(m_path).filePath(m_name + timestamp + m_format); + return snapshot.save(pathname); +} + +bool DeclarativeScreenshots::take() { + return take(0, 0, -1, -1); +} diff --git a/src/declarativescreenshots.h b/src/declarativescreenshots.h new file mode 100644 index 0000000..28b67ca --- /dev/null +++ b/src/declarativescreenshots.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2013 Jolla Ltd. + * Contact: Richard Braakman + * + * You may use this file under the terms of the BSD license as follows: + * + * "Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Nemo Mobile nor the names of its contributors + * may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." + */ + +#ifndef DECLARATIVESCREENSHOTS_H +#define DECLARATIVESCREENSHOTS_H + +#include + +/*! + \qmlclass Screenshots DeclarativeScreenshots + \brief Utility component to take screenshots and save them as files. + + Screenshots can be taken of the root window or of specific components. + */ + +class DeclarativeScreenshots : public QDeclarativeItem +{ + Q_OBJECT + +public: + DeclarativeScreenshots(QDeclarativeItem *parent = 0); + + /*! + \qmlproperty string path + + Path to the directory where screenshots will be saved. + The default is the the user's pictures directory according to + QDesktopServices. + */ + Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) + QString path() const { return m_path; } + void setPath(const QString & path); + + /*! + \qmlproperty string name + + Base filename for screenshots. The final filename will be constructed + from this property, a timestamp, and the image format. + The default is "screenshot". + */ + Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) + QString name() const { return m_name; } + void setName(const QString & name); + + /*! + \qmlproperty Item target + + Component from which to take screenshots. + Leave null to take screenshots from the desktop root window. + */ + Q_PROPERTY(QDeclarativeItem *target READ target WRITE setTarget NOTIFY targetChanged) + QDeclarativeItem *target() const { return m_target; } + void setTarget(QDeclarativeItem *target); + + /*! + \qmlproperty string format + + Image format to use when encoding the screenshots. This string will + also be used as the filename extension. The default is "png". + */ + Q_PROPERTY(QString format READ format WRITE setFormat NOTIFY formatChanged) + QString format() const { return m_format; } + void setFormat(const QString & format); + + /*! + \qmlproperty bool formatSupported + + Read-only property that signals whether the currently selected + image format can be used. + */ + Q_PROPERTY(bool formatSupported READ formatSupported NOTIFY formatSupportedChanged) + bool formatSupported() const { return m_formatSupported; } + + /*! + \qmlmethod bool Screenshots::take() + + Takes a screenshot of the current target and saves it as a file. + Returns true if the operation was successful, otherwise false. + */ + Q_INVOKABLE bool take(); + + /*! + \qmlmethod bool Screenshots::take(real x, real y, real width, real height) + + Takes a screenshot of the current target, cropped to the specified + rectangle. + Returns true if the operation was successful, otherwise false. + */ + Q_INVOKABLE bool take(qreal x, qreal y, qreal height, qreal width); + +signals: + void pathChanged(); + void nameChanged(); + void targetChanged(); + void formatChanged(); + void formatSupportedChanged(); + +private slots: + void targetDestroyed(); + +private: + QString m_path; + QString m_name; + QDeclarativeItem *m_target; + QString m_format; + bool m_formatSupported; +}; + +#endif diff --git a/src/plugin.cpp b/src/plugin.cpp index be5afc7..fca55f3 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -33,6 +33,7 @@ #include #include #include "declarativewindowattributes.h" +#include "declarativescreenshots.h" class Q_DECL_EXPORT NemoUtilsPlugin : public QDeclarativeExtensionPlugin { @@ -55,6 +56,7 @@ class Q_DECL_EXPORT NemoUtilsPlugin : public QDeclarativeExtensionPlugin Q_ASSERT(uri == QLatin1String("org.nemomobile.utilities")); qmlRegisterType(uri, 1, 0, "WindowAttributes"); + qmlRegisterType(uri, 1, 0, "Screenshots"); } }; diff --git a/src/src.pro b/src/src.pro index a2d6965..b81f7b0 100644 --- a/src/src.pro +++ b/src/src.pro @@ -13,6 +13,8 @@ qmldir.path += $$[QT_INSTALL_IMPORTS]/$$$$PLUGIN_IMPORT_PATH INSTALLS += qmldir SOURCES += plugin.cpp \ - declarativewindowattributes.cpp + declarativewindowattributes.cpp \ + declarativescreenshots.cpp -HEADERS += declarativewindowattributes.h +HEADERS += declarativewindowattributes.h \ + declarativescreenshots.h From 600458e964d35ce5e471e74bc8569f6a2d343fea Mon Sep 17 00:00:00 2001 From: Richard Braakman Date: Wed, 8 May 2013 15:48:33 +0300 Subject: [PATCH 2/3] Make Screenshots inherit QObject It doesn't need to inherit QDeclarativeItem because it's not a visual item. --- src/declarativescreenshots.cpp | 4 ++-- src/declarativescreenshots.h | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/declarativescreenshots.cpp b/src/declarativescreenshots.cpp index 2c224d5..6ca9bba 100644 --- a/src/declarativescreenshots.cpp +++ b/src/declarativescreenshots.cpp @@ -42,8 +42,8 @@ #include #include -DeclarativeScreenshots::DeclarativeScreenshots(QDeclarativeItem *parent) - : QDeclarativeItem(parent), m_name("screenshot"), m_target(0), +DeclarativeScreenshots::DeclarativeScreenshots(QObject *parent) + : QObject(parent), m_name("screenshot"), m_target(0), m_format("png"), m_formatSupported(true) { m_path = QDesktopServices::storageLocation( QDesktopServices::PicturesLocation); diff --git a/src/declarativescreenshots.h b/src/declarativescreenshots.h index 28b67ca..8dd7bce 100644 --- a/src/declarativescreenshots.h +++ b/src/declarativescreenshots.h @@ -42,12 +42,12 @@ Screenshots can be taken of the root window or of specific components. */ -class DeclarativeScreenshots : public QDeclarativeItem +class DeclarativeScreenshots : public QObject { Q_OBJECT public: - DeclarativeScreenshots(QDeclarativeItem *parent = 0); + DeclarativeScreenshots(QObject *parent = 0); /*! \qmlproperty string path @@ -112,7 +112,7 @@ class DeclarativeScreenshots : public QDeclarativeItem \qmlmethod bool Screenshots::take(real x, real y, real width, real height) Takes a screenshot of the current target, cropped to the specified - rectangle. + rectangle. Returns true if the operation was successful, otherwise false. */ Q_INVOKABLE bool take(qreal x, qreal y, qreal height, qreal width); From f41d8f30d572778eec50035a797a1b8b440f22be Mon Sep 17 00:00:00 2001 From: Richard Braakman Date: Wed, 8 May 2013 18:28:28 +0300 Subject: [PATCH 3/3] [screenshots] Allow screenshots to be rotated This helps when the application is not in the device's default orientation. Implementing this also involved changing the snapshot to be a QImage instead of a QPixmap. This is ok since Qt used to do that conversion internally to save the pixmap anyway. --- src/declarativescreenshots.cpp | 20 +++++++++++++++++--- src/declarativescreenshots.h | 13 +++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/declarativescreenshots.cpp b/src/declarativescreenshots.cpp index 6ca9bba..16d981c 100644 --- a/src/declarativescreenshots.cpp +++ b/src/declarativescreenshots.cpp @@ -39,8 +39,11 @@ #include #include #include +#include #include #include +#include +#include DeclarativeScreenshots::DeclarativeScreenshots(QObject *parent) : QObject(parent), m_name("screenshot"), m_target(0), @@ -108,10 +111,17 @@ void DeclarativeScreenshots::setFormat(const QString & format) { emit formatSupportedChanged(); } +void DeclarativeScreenshots::setRotation(qreal rotation) { + if (rotation == m_rotation) + return; + m_rotation = rotation; + emit rotationChanged(); +} + bool DeclarativeScreenshots::take(qreal x, qreal y, qreal width, qreal height) { QString timestamp = QDateTime::currentDateTimeUtc() .toString("-yyyyMMdd-hhmmsszzz."); - QPixmap snapshot; + QImage snapshot; if (m_target) { QGraphicsScene *scene = m_target->scene(); @@ -125,7 +135,8 @@ bool DeclarativeScreenshots::take(qreal x, qreal y, qreal width, qreal height) { QRectF mappedRect = m_target->mapRectToScene(x, y, width, height); QRectF targetRect(0, 0, mappedRect.width(), mappedRect.height()); - snapshot = QPixmap(mappedRect.size().toSize()); + // starting with QPixmap.toImage to get the matching image format + snapshot = QPixmap(mappedRect.size().toSize()).toImage(); snapshot.fill(Qt::transparent); QPainter painter(&snapshot); @@ -134,9 +145,12 @@ bool DeclarativeScreenshots::take(qreal x, qreal y, qreal width, qreal height) { // If there's no target item, then take an actual screenshot of the // desktop root window. snapshot = QPixmap::grabWindow(QApplication::desktop()->winId(), - x, y, width, height); + x, y, width, height).toImage(); } + if (m_rotation) + snapshot = snapshot.transformed(QTransform().rotate(m_rotation)); + QString pathname = QDir(m_path).filePath(m_name + timestamp + m_format); return snapshot.save(pathname); } diff --git a/src/declarativescreenshots.h b/src/declarativescreenshots.h index 8dd7bce..837d4cf 100644 --- a/src/declarativescreenshots.h +++ b/src/declarativescreenshots.h @@ -100,6 +100,17 @@ class DeclarativeScreenshots : public QObject Q_PROPERTY(bool formatSupported READ formatSupported NOTIFY formatSupportedChanged) bool formatSupported() const { return m_formatSupported; } + /*! + \qmlproperty real rotation + + Rotate the screenshot before saving it. This is most useful when taking + desktop screenshots of a rotated application. Rotation is clockwide, + measured in degrees. + */ + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation NOTIFY rotationChanged) + qreal rotation() const { return m_rotation; } + void setRotation(qreal rotation); + /*! \qmlmethod bool Screenshots::take() @@ -123,6 +134,7 @@ class DeclarativeScreenshots : public QObject void targetChanged(); void formatChanged(); void formatSupportedChanged(); + void rotationChanged(); private slots: void targetDestroyed(); @@ -133,6 +145,7 @@ private slots: QDeclarativeItem *m_target; QString m_format; bool m_formatSupported; + qreal m_rotation; }; #endif