forked from qt-creator/qt-creator
ImageViewer: Add option to export images from SVG.
Add a tool button showing a dialog with file name and size for exporting images from SVG. [ChangeLog][ImageViewer] Added option to export images from SVG. Change-Id: I84e04dc166e70b0359eba0f19703a75b882a2bc2 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com> Reviewed-by: Alessandro Portale <alessandro.portale@theqtcompany.com>
This commit is contained in:
204
src/plugins/imageviewer/exportdialog.cpp
Normal file
204
src/plugins/imageviewer/exportdialog.cpp
Normal file
@@ -0,0 +1,204 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "exportdialog.h"
|
||||
|
||||
#include <coreplugin/coreicons.h>
|
||||
|
||||
#include <utils/pathchooser.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopWidget>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QFileDialog>
|
||||
#include <QFormLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMessageBox>
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
#include <QPushButton>
|
||||
#include <QSpinBox>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <QImageWriter>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QMimeDatabase>
|
||||
#include <QMimeType>
|
||||
|
||||
namespace ImageViewer {
|
||||
namespace Internal {
|
||||
|
||||
enum { exportMinimumSize = 1, exportMaximumSize = 2000 };
|
||||
|
||||
static QString imageNameFilterString()
|
||||
{
|
||||
static QString result;
|
||||
if (result.isEmpty()) {
|
||||
QMimeDatabase mimeDatabase;
|
||||
const QString separator = QStringLiteral(";;");
|
||||
foreach (const QByteArray &mimeType, QImageWriter::supportedMimeTypes()) {
|
||||
const QString filter = mimeDatabase.mimeTypeForName(QLatin1String(mimeType)).filterString();
|
||||
if (!filter.isEmpty()) {
|
||||
if (mimeType == QByteArrayLiteral("image/png")) {
|
||||
if (!result.isEmpty())
|
||||
result.prepend(separator);
|
||||
result.prepend(filter);
|
||||
} else {
|
||||
if (!result.isEmpty())
|
||||
result.append(separator);
|
||||
result.append(filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExportDialog::ExportDialog(QWidget *parent)
|
||||
: QDialog(parent)
|
||||
, m_pathChooser(new Utils::PathChooser(this))
|
||||
, m_widthSpinBox(new QSpinBox(this))
|
||||
, m_heightSpinBox(new QSpinBox(this))
|
||||
, m_aspectRatio(1)
|
||||
{
|
||||
typedef void (QSpinBox::*QSpinBoxIntSignal)(int);
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
QFormLayout *formLayout = new QFormLayout(this);
|
||||
|
||||
m_pathChooser->setMinimumWidth(QApplication::desktop()->availableGeometry(this).width() / 5);
|
||||
m_pathChooser->setExpectedKind(Utils::PathChooser::SaveFile);
|
||||
m_pathChooser->setPromptDialogFilter(imageNameFilterString());
|
||||
formLayout->addRow(tr("File:"), m_pathChooser);
|
||||
|
||||
QHBoxLayout *sizeLayout = new QHBoxLayout;
|
||||
m_widthSpinBox->setMinimum(exportMinimumSize);
|
||||
m_widthSpinBox->setMaximum(exportMaximumSize);
|
||||
connect(m_widthSpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
|
||||
this, &ExportDialog::exportWidthChanged);
|
||||
sizeLayout->addWidget(m_widthSpinBox);
|
||||
//: Multiplication, as in 32x32
|
||||
sizeLayout->addWidget(new QLabel(tr("x")));
|
||||
m_heightSpinBox->setMinimum(exportMinimumSize);
|
||||
m_heightSpinBox->setMaximum(exportMaximumSize);
|
||||
connect(m_heightSpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
|
||||
this, &ExportDialog::exportHeightChanged);
|
||||
sizeLayout->addWidget(m_heightSpinBox);
|
||||
QToolButton *resetButton = new QToolButton(this);
|
||||
resetButton->setIcon(QIcon(QStringLiteral(":/qt-project.org/styles/commonstyle/images/refresh-32.png")));
|
||||
sizeLayout->addWidget(resetButton);
|
||||
sizeLayout->addStretch();
|
||||
connect(resetButton, &QAbstractButton::clicked, this, &ExportDialog::resetExportSize);
|
||||
formLayout->addRow(tr("Size:"), sizeLayout);
|
||||
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this);
|
||||
connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||
formLayout->addRow(buttonBox);
|
||||
}
|
||||
|
||||
void ExportDialog::accept()
|
||||
{
|
||||
if (!m_pathChooser->isValid()) {
|
||||
QMessageBox::warning(this, windowTitle(), m_pathChooser->errorMessage());
|
||||
return;
|
||||
}
|
||||
const QString fileName = exportFileName();
|
||||
if (QFileInfo::exists(fileName)) {
|
||||
const QString question = tr("%1 already exists.\nWould you like to overwrite it?")
|
||||
.arg(QDir::toNativeSeparators(fileName));
|
||||
if (QMessageBox::question(this, windowTitle(), question, QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
|
||||
return;
|
||||
}
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
QSize ExportDialog::exportSize() const
|
||||
{
|
||||
return QSize(m_widthSpinBox->value(), m_heightSpinBox->value());
|
||||
}
|
||||
|
||||
void ExportDialog::setExportSize(const QSize &size)
|
||||
{
|
||||
m_defaultSize = size;
|
||||
const QSizeF defaultSizeF(m_defaultSize);
|
||||
m_aspectRatio = defaultSizeF.width() / defaultSizeF.height();
|
||||
setExportWidthBlocked(size.width());
|
||||
setExportHeightBlocked(size.height());
|
||||
}
|
||||
|
||||
void ExportDialog::resetExportSize()
|
||||
{
|
||||
setExportWidthBlocked(m_defaultSize.width());
|
||||
setExportHeightBlocked(m_defaultSize.height());
|
||||
}
|
||||
|
||||
void ExportDialog::setExportWidthBlocked(int width)
|
||||
{
|
||||
if (m_widthSpinBox->value() != width) {
|
||||
const bool blockSignals = m_widthSpinBox->blockSignals(true);
|
||||
m_widthSpinBox->setValue(width);
|
||||
m_widthSpinBox->blockSignals(blockSignals);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::setExportHeightBlocked(int height)
|
||||
{
|
||||
if (m_heightSpinBox->value() != height) {
|
||||
const bool blockSignals = m_heightSpinBox->blockSignals(true);
|
||||
m_heightSpinBox->setValue(height);
|
||||
m_heightSpinBox->blockSignals(blockSignals);
|
||||
}
|
||||
}
|
||||
|
||||
void ExportDialog::exportWidthChanged(int width)
|
||||
{
|
||||
const bool square = m_defaultSize.width() == m_defaultSize.height();
|
||||
setExportHeightBlocked(square ? width : qRound(qreal(width) / m_aspectRatio));
|
||||
}
|
||||
|
||||
void ExportDialog::exportHeightChanged(int height)
|
||||
{
|
||||
const bool square = m_defaultSize.width() == m_defaultSize.height();
|
||||
setExportWidthBlocked(square ? height : qRound(qreal(height) * m_aspectRatio));
|
||||
}
|
||||
|
||||
QString ExportDialog::exportFileName() const
|
||||
{
|
||||
return m_pathChooser->fileName().toString();
|
||||
}
|
||||
|
||||
void ExportDialog::setExportFileName(const QString &f)
|
||||
{
|
||||
m_pathChooser->setFileName(Utils::FileName::fromString(f));
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ImageViewer
|
||||
71
src/plugins/imageviewer/exportdialog.h
Normal file
71
src/plugins/imageviewer/exportdialog.h
Normal file
@@ -0,0 +1,71 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef EXPORTDIALOG_H
|
||||
#define EXPORTDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QSpinBox)
|
||||
|
||||
namespace Utils { class PathChooser; }
|
||||
|
||||
namespace ImageViewer {
|
||||
namespace Internal {
|
||||
|
||||
class ExportDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ExportDialog(QWidget *parent = nullptr);
|
||||
|
||||
QSize exportSize() const;
|
||||
void setExportSize(const QSize &);
|
||||
|
||||
QString exportFileName() const;
|
||||
void setExportFileName(const QString &);
|
||||
|
||||
void accept() override;
|
||||
|
||||
private slots:
|
||||
void resetExportSize();
|
||||
void exportWidthChanged(int width);
|
||||
void exportHeightChanged(int height);
|
||||
|
||||
private:
|
||||
void setExportWidthBlocked(int width);
|
||||
void setExportHeightBlocked(int height);
|
||||
|
||||
Utils::PathChooser *m_pathChooser;
|
||||
QSpinBox *m_widthSpinBox;
|
||||
QSpinBox *m_heightSpinBox;
|
||||
QSize m_defaultSize;
|
||||
qreal m_aspectRatio;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace ImageViewer
|
||||
|
||||
#endif // EXPORTDIALOG_H
|
||||
@@ -50,12 +50,29 @@
|
||||
|
||||
#include "imageview.h"
|
||||
|
||||
#include "exportdialog.h"
|
||||
#include "imageviewerfile.h"
|
||||
|
||||
#include <coreplugin/messagemanager.h>
|
||||
|
||||
#include <utils/fileutils.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QSvgRenderer>
|
||||
#include <QGraphicsSvgItem>
|
||||
|
||||
#include <QMessageBox>
|
||||
#include <QGraphicsRectItem>
|
||||
|
||||
#include <QWheelEvent>
|
||||
#include <QMouseEvent>
|
||||
#include <QGraphicsRectItem>
|
||||
#include <QImage>
|
||||
#include <QPainter>
|
||||
#include <QPixmap>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <qmath.h>
|
||||
|
||||
namespace ImageViewer {
|
||||
@@ -137,6 +154,48 @@ void ImageView::drawBackground(QPainter *p, const QRectF &)
|
||||
p->restore();
|
||||
}
|
||||
|
||||
void ImageView::exportImage()
|
||||
{
|
||||
#ifndef QT_NO_SVG
|
||||
QGraphicsSvgItem *svgItem = qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem);
|
||||
QTC_ASSERT(svgItem, return);
|
||||
|
||||
const QFileInfo origFi = m_file->filePath().toFileInfo();
|
||||
const QString suggestedFileName = origFi.absolutePath() + QLatin1Char('/')
|
||||
+ origFi.baseName() + QStringLiteral(".png");
|
||||
|
||||
ExportDialog exportDialog(this);
|
||||
exportDialog.setWindowTitle(tr("Export %1").arg(origFi.fileName()));
|
||||
exportDialog.setExportSize(svgItem->boundingRect().size().toSize());
|
||||
exportDialog.setExportFileName(suggestedFileName);
|
||||
|
||||
while (true) {
|
||||
if (exportDialog.exec() != QDialog::Accepted)
|
||||
break;
|
||||
|
||||
const QSize imageSize = exportDialog.exportSize();
|
||||
QImage image(imageSize, QImage::Format_ARGB32);
|
||||
image.fill(Qt::transparent);
|
||||
QPainter painter;
|
||||
painter.begin(&image);
|
||||
svgItem->renderer()->render(&painter, QRectF(QPointF(), QSizeF(imageSize)));
|
||||
painter.end();
|
||||
|
||||
const QString fileName = exportDialog.exportFileName();
|
||||
if (image.save(fileName)) {
|
||||
const QString message = tr("Exported \"%1\", %2x%3, %4 bytes")
|
||||
.arg(QDir::toNativeSeparators(fileName)).arg(imageSize.width()).arg(imageSize.height())
|
||||
.arg(QFileInfo(fileName).size());
|
||||
Core::MessageManager::write(message);
|
||||
break;
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Export Image"),
|
||||
tr("Could not write file \"%1\".").arg(QDir::toNativeSeparators(fileName)));
|
||||
}
|
||||
}
|
||||
#endif // !QT_NO_SVG
|
||||
}
|
||||
|
||||
void ImageView::setViewBackground(bool enable)
|
||||
{
|
||||
m_showBackground = enable;
|
||||
|
||||
@@ -74,6 +74,7 @@ signals:
|
||||
void imageSizeChanged(const QSize &size);
|
||||
|
||||
public slots:
|
||||
void exportImage();
|
||||
void setViewBackground(bool enable);
|
||||
void setViewOutline(bool enable);
|
||||
void zoomIn();
|
||||
|
||||
@@ -100,10 +100,11 @@ void ImageViewer::ctor()
|
||||
// toolbar
|
||||
d->toolbar = new QWidget();
|
||||
d->ui_toolbar.setupUi(d->toolbar);
|
||||
d->ui_toolbar.toolButtonExportImage->setIcon(QIcon::fromTheme(QLatin1String("document-save"),
|
||||
Core::Icons::SAVEFILE.icon()));
|
||||
d->ui_toolbar.toolButtonZoomIn->setIcon(Core::Icons::PLUS.icon());
|
||||
d->ui_toolbar.toolButtonZoomOut->setIcon(Core::Icons::MINUS.icon());
|
||||
d->ui_toolbar.toolButtonFitToScreen->setIcon(Core::Icons::ZOOM.icon());
|
||||
|
||||
// icons update - try to use system theme
|
||||
updateButtonIconByTheme(d->ui_toolbar.toolButtonZoomIn, QLatin1String("zoom-in"));
|
||||
updateButtonIconByTheme(d->ui_toolbar.toolButtonZoomOut, QLatin1String("zoom-out"));
|
||||
@@ -115,6 +116,7 @@ void ImageViewer::ctor()
|
||||
// (photograph has outline - piece of paper)
|
||||
updateButtonIconByTheme(d->ui_toolbar.toolButtonOutline, QLatin1String("emblem-photos"));
|
||||
|
||||
d->ui_toolbar.toolButtonExportImage->setCommandId(Constants::ACTION_EXPORT_IMAGE);
|
||||
d->ui_toolbar.toolButtonZoomIn->setCommandId(Constants::ACTION_ZOOM_IN);
|
||||
d->ui_toolbar.toolButtonZoomOut->setCommandId(Constants::ACTION_ZOOM_OUT);
|
||||
d->ui_toolbar.toolButtonOriginalSize->setCommandId(Constants::ACTION_ORIGINAL_SIZE);
|
||||
@@ -124,6 +126,8 @@ void ImageViewer::ctor()
|
||||
d->ui_toolbar.toolButtonPlayPause->setCommandId(Constants::ACTION_TOGGLE_ANIMATION);
|
||||
|
||||
// connections
|
||||
connect(d->ui_toolbar.toolButtonExportImage, &QAbstractButton::clicked,
|
||||
d->imageView, &ImageView::exportImage);
|
||||
connect(d->ui_toolbar.toolButtonZoomIn, &QAbstractButton::clicked,
|
||||
d->imageView, &ImageView::zoomIn);
|
||||
connect(d->ui_toolbar.toolButtonZoomOut, &QAbstractButton::clicked,
|
||||
@@ -150,6 +154,12 @@ void ImageViewer::ctor()
|
||||
this, &ImageViewer::updatePauseAction);
|
||||
connect(d->imageView, &ImageView::scaleFactorChanged,
|
||||
this, &ImageViewer::scaleFactorUpdate);
|
||||
|
||||
connect(d->file.data(), &ImageViewerFile::openFinished,
|
||||
this, [this](bool success)
|
||||
{
|
||||
d->ui_toolbar.toolButtonExportImage->setEnabled(success && d->file->type() == ImageViewerFile::TypeSvg);
|
||||
});
|
||||
}
|
||||
|
||||
ImageViewer::~ImageViewer()
|
||||
@@ -176,6 +186,12 @@ Core::IEditor *ImageViewer::duplicate()
|
||||
return other;
|
||||
}
|
||||
|
||||
void ImageViewer::exportImage()
|
||||
{
|
||||
if (d->file->type() == ImageViewerFile::TypeSvg)
|
||||
d->ui_toolbar.toolButtonExportImage->click();
|
||||
}
|
||||
|
||||
void ImageViewer::imageSizeUpdated(const QSize &size)
|
||||
{
|
||||
QString imageSizeText;
|
||||
|
||||
@@ -56,6 +56,7 @@ public:
|
||||
IEditor *duplicate() override;
|
||||
|
||||
public slots:
|
||||
void exportImage();
|
||||
void imageSizeUpdated(const QSize &size);
|
||||
void scaleFactorUpdate(qreal factor);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
include(../../qtcreatorplugin.pri)
|
||||
|
||||
HEADERS += \
|
||||
exportdialog.h \
|
||||
imageviewerplugin.h \
|
||||
imageviewerfactory.h \
|
||||
imageviewerfile.h \
|
||||
@@ -9,6 +10,7 @@ HEADERS += \
|
||||
imageviewerconstants.h
|
||||
|
||||
SOURCES += \
|
||||
exportdialog.cpp \
|
||||
imageviewerplugin.cpp \
|
||||
imageviewerfactory.cpp \
|
||||
imageviewerfile.cpp \
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace Constants {
|
||||
const char IMAGEVIEWER_ID[] = "Editors.ImageViewer";
|
||||
const char IMAGEVIEWER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("OpenWith::Editors", "Image Viewer");
|
||||
|
||||
const char ACTION_EXPORT_IMAGE[] = "ImageViewer.ExportImage";
|
||||
const char ACTION_ZOOM_IN[] = "ImageViewer.ZoomIn";
|
||||
const char ACTION_ZOOM_OUT[] = "ImageViewer.ZoomOut";
|
||||
const char ACTION_ORIGINAL_SIZE[] = "ImageViewer.OriginalSize";
|
||||
|
||||
@@ -113,6 +113,13 @@ void ImageViewerPlugin::extensionsInitialized()
|
||||
if (ImageViewer *iv = currentImageViewer())
|
||||
iv->togglePlay();
|
||||
});
|
||||
|
||||
a = registerNewAction(Constants::ACTION_EXPORT_IMAGE, tr("Export Image"),
|
||||
QKeySequence());
|
||||
connect(a, &QAction::triggered, this, [this]() {
|
||||
if (ImageViewer *iv = currentImageViewer())
|
||||
iv->exportImage();
|
||||
});
|
||||
}
|
||||
|
||||
QAction *ImageViewerPlugin::registerNewAction(Core::Id id,
|
||||
|
||||
@@ -17,6 +17,13 @@
|
||||
<property name="margin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="Core::CommandButton" name="toolButtonExportImage">
|
||||
<property name="toolTipBase">
|
||||
<string>Export as Image</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="Core::CommandButton" name="toolButtonBackground">
|
||||
<property name="toolTipBase">
|
||||
|
||||
Reference in New Issue
Block a user