Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nemo-qml-plugin-utilities] Add Screenshots component #1

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 146 additions & 0 deletions src/declarativescreenshots.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/*
* Copyright (C) 2013 Jolla Ltd.
* Contact: Richard Braakman <[email protected]>
*
* 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 <QApplication>
#include <QDateTime>
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDir>
#include <QFile>
#include <QGraphicsScene>
#include <QImageWriter>
#include <QPainter>

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(),
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the issues with winId() in client applications, can this cause any issues? I'd assume not, since the desktop() should always have a native rendering window, but thought I'd ask, since I don't know much about that stuff.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, what are these issues? The only one I know of is that only top-level widgets have windows, but that should be ok here as you say. Are there other known issues with winId()?

About the WId type itself, the 4.8 docs say "Portable in principle, but if you use it you are probably about to do something non-portable. Be careful."
I think it's ok here because all I do is pass it right back to a Qt function that knows what to do with it. In fact I was quite happy to not need any X11 specific code :)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only issue is that of performance - and as the top level widget has one anyway, this shouldn't be a problem. I just thought I'd mention it in case it hadn't been considered. (eg, I've seen the use of winId() cause framerates in client applications to drop from 60 fps to 12 fps, after a call to winId() to get an id to use in a call to XSetTransientFor()).

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);
}
138 changes: 138 additions & 0 deletions src/declarativescreenshots.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright (C) 2013 Jolla Ltd.
* Contact: Richard Braakman <[email protected]>
*
* 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 <QDeclarativeItem>

/*!
\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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MSameer told me that I can just use a QObject here because it's a non-graphical component, and that might even work for both QtQuick1 and QtQuick2 (whereas QDeclarativeItem usage would definitely need porting)

{
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
2 changes: 2 additions & 0 deletions src/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <QtGlobal>
#include <QtDeclarative>
#include "declarativewindowattributes.h"
#include "declarativescreenshots.h"

class Q_DECL_EXPORT NemoUtilsPlugin : public QDeclarativeExtensionPlugin
{
Expand All @@ -55,6 +56,7 @@ class Q_DECL_EXPORT NemoUtilsPlugin : public QDeclarativeExtensionPlugin
Q_ASSERT(uri == QLatin1String("org.nemomobile.utilities"));

qmlRegisterType<DeclarativeWindowAttributes>(uri, 1, 0, "WindowAttributes");
qmlRegisterType<DeclarativeScreenshots>(uri, 1, 0, "Screenshots");
}
};

Expand Down
6 changes: 4 additions & 2 deletions src/src.pro
Original file line number Diff line number Diff line change
Expand Up @@ -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