Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
[FEATURE] Better UI for embedding SVG files
Adds a common widget for SVG sources, with a tool button with some handy options: - select file (old behaviour), pick a file from disk - embed file (pick a file from disk, is embedded into project/symbol) - extract embedded file (for embedded files, allows you to save these back to a disk based svg file) - from url (opens a dialog prompting for a url, exposing the previously hidden functionality that svgs can be retrieved from a remote url (eg github)) Sponsored by SMEC/SJ
- Loading branch information
Showing
with
951 additions
and 662 deletions.
- +71 −0 python/gui/auto_generated/qgssvgsourcelineedit.sip.in
- +1 −0 python/gui/gui_auto.sip
- +2 −0 src/gui/CMakeLists.txt
- +212 −0 src/gui/qgssvgsourcelineedit.cpp
- +92 −0 src/gui/qgssvgsourcelineedit.h
- +1 −0 src/gui/qgstextformatwidget.cpp
- +7 −51 src/gui/symbology/qgssvgselectorwidget.cpp
- +1 −4 src/gui/symbology/qgssvgselectorwidget.h
- +15 −97 src/gui/symbology/qgssymbollayerwidget.cpp
- +2 −6 src/gui/symbology/qgssymbollayerwidget.h
- +159 −161 src/ui/symbollayer/widget_svgfill.ui
- +311 −309 src/ui/symbollayer/widget_svgmarker.ui
- +27 −34 src/ui/symbollayer/widget_svgselector.ui
- +1 −0 tests/src/python/CMakeLists.txt
- +49 −0 tests/src/python/test_qgssvgsourcelineedit.py
@@ -0,0 +1,71 @@ | ||
/************************************************************************ | ||
* This file has been generated automatically from * | ||
* * | ||
* src/gui/qgssvgsourcelineedit.h * | ||
* * | ||
* Do not edit manually ! Edit header and run scripts/sipify.pl again * | ||
************************************************************************/ | ||
|
||
|
||
|
||
|
||
class QgsSvgSourceLineEdit : QWidget | ||
{ | ||
%Docstring | ||
A line edit widget with toolbutton for setting an SVG image path. | ||
|
||
.. versionadded:: 3.4 | ||
%End | ||
|
||
%TypeHeaderCode | ||
#include "qgssvgsourcelineedit.h" | ||
%End | ||
public: | ||
|
||
QgsSvgSourceLineEdit( QWidget *parent /TransferThis/ = 0 ); | ||
%Docstring | ||
Constructor for QgsSvgSourceLineEdit, with the specified ``parent`` widget. | ||
%End | ||
|
||
QString source() const; | ||
%Docstring | ||
Returns the current SVG source. | ||
|
||
.. seealso:: :py:func:`setSource` | ||
|
||
.. seealso:: :py:func:`sourceChanged` | ||
%End | ||
|
||
void setLastPathSettingsKey( const QString &key ); | ||
%Docstring | ||
Sets a specific settings ``key`` to use when storing the last | ||
used path for the SVG source. | ||
%End | ||
|
||
public slots: | ||
|
||
void setSource( const QString &source ); | ||
%Docstring | ||
Sets a new ``source`` to show in the widget. | ||
|
||
.. seealso:: :py:func:`source` | ||
|
||
.. seealso:: :py:func:`sourceChanged` | ||
%End | ||
|
||
signals: | ||
|
||
void sourceChanged( const QString &source ); | ||
%Docstring | ||
Emitted whenever the SVG source is changed in the widget. | ||
%End | ||
|
||
}; | ||
|
||
/************************************************************************ | ||
* This file has been generated automatically from * | ||
* * | ||
* src/gui/qgssvgsourcelineedit.h * | ||
* * | ||
* Do not edit manually ! Edit header and run scripts/sipify.pl again * | ||
************************************************************************/ |
@@ -0,0 +1,212 @@ | ||
/*************************************************************************** | ||
qgssvgsourcelineedit.cpp | ||
----------------------- | ||
begin : July 2018 | ||
copyright : (C) 2018 by Nyall Dawson | ||
email : nyall dot dawson at gmail dot com | ||
*************************************************************************** | ||
* * | ||
* This program is free software; you can redistribute it and/or modify * | ||
* it under the terms of the GNU General Public License as published by * | ||
* the Free Software Foundation; either version 2 of the License, or * | ||
* (at your option) any later version. * | ||
* * | ||
***************************************************************************/ | ||
|
||
#include "qgssvgsourcelineedit.h" | ||
#include "qgssettings.h" | ||
#include <QMenu> | ||
#include <QLineEdit> | ||
#include <QToolButton> | ||
#include <QHBoxLayout> | ||
#include <QFileDialog> | ||
#include <QInputDialog> | ||
|
||
QgsSvgSourceLineEdit::QgsSvgSourceLineEdit( QWidget *parent ) | ||
: QWidget( parent ) | ||
{ | ||
QHBoxLayout *layout = new QHBoxLayout( this ); | ||
mFileLineEdit = new QLineEdit( this ); | ||
mFileToolButton = new QToolButton( this ); | ||
mFileToolButton->setText( tr( "…" ) ); | ||
layout->addWidget( mFileLineEdit, 1 ); | ||
layout->addWidget( mFileToolButton ); | ||
setLayout( layout ); | ||
|
||
QMenu *sourceMenu = new QMenu( mFileToolButton ); | ||
|
||
QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu ); | ||
connect( selectFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::selectFile ); | ||
sourceMenu->addAction( selectFileAction ); | ||
|
||
QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu ); | ||
connect( embedFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::embedFile ); | ||
sourceMenu->addAction( embedFileAction ); | ||
|
||
QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu ); | ||
connect( extractFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::extractFile ); | ||
sourceMenu->addAction( extractFileAction ); | ||
|
||
connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction] | ||
{ | ||
extractFileAction->setEnabled( mFileLineEdit->text().startsWith( QStringLiteral( "base64:" ), Qt::CaseInsensitive ) ); | ||
} ); | ||
|
||
QAction *enterUrlAction = new QAction( tr( "From URL…" ), sourceMenu ); | ||
connect( enterUrlAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::selectUrl ); | ||
sourceMenu->addAction( enterUrlAction ); | ||
|
||
mFileToolButton->setMenu( sourceMenu ); | ||
mFileToolButton->setPopupMode( QToolButton::MenuButtonPopup ); | ||
connect( mFileToolButton, &QToolButton::clicked, this, &QgsSvgSourceLineEdit::selectFile ); | ||
|
||
connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsSvgSourceLineEdit::mFileLineEdit_textEdited ); | ||
connect( mFileLineEdit, &QLineEdit::editingFinished, this, &QgsSvgSourceLineEdit::mFileLineEdit_editingFinished ); | ||
} | ||
|
||
QString QgsSvgSourceLineEdit::source() const | ||
{ | ||
return mFileLineEdit->text(); | ||
} | ||
|
||
void QgsSvgSourceLineEdit::setLastPathSettingsKey( const QString &key ) | ||
{ | ||
mLastPathKey = key; | ||
} | ||
|
||
void QgsSvgSourceLineEdit::setSource( const QString &source ) | ||
{ | ||
if ( source == mFileLineEdit->text() ) | ||
return; | ||
|
||
mFileLineEdit->setText( source ); | ||
emit sourceChanged( source ); | ||
} | ||
|
||
void QgsSvgSourceLineEdit::selectFile() | ||
{ | ||
QgsSettings s; | ||
QString file = QFileDialog::getOpenFileName( nullptr, | ||
tr( "Select SVG file" ), | ||
defaultPath(), | ||
tr( "SVG files" ) + " (*.svg)" ); | ||
QFileInfo fi( file ); | ||
if ( file.isEmpty() || !fi.exists() || file == source() ) | ||
{ | ||
return; | ||
} | ||
mFileLineEdit->setText( file ); | ||
s.setValue( settingsKey(), fi.absolutePath() ); | ||
emit sourceChanged( mFileLineEdit->text() ); | ||
} | ||
|
||
void QgsSvgSourceLineEdit::selectUrl() | ||
{ | ||
bool ok = false; | ||
const QString path = QInputDialog::getText( this, tr( "SVG From URL" ), tr( "Enter SVG URL" ), QLineEdit::Normal, mFileLineEdit->text(), &ok ); | ||
if ( ok && path != source() ) | ||
{ | ||
mFileLineEdit->setText( path ); | ||
emit sourceChanged( mFileLineEdit->text() ); | ||
} | ||
} | ||
|
||
void QgsSvgSourceLineEdit::embedFile() | ||
{ | ||
QgsSettings s; | ||
QString file = QFileDialog::getOpenFileName( nullptr, | ||
tr( "Embed SVG File" ), | ||
defaultPath(), | ||
tr( "SVG files" ) + " (*.svg)" ); | ||
QFileInfo fi( file ); | ||
if ( file.isEmpty() || !fi.exists() ) | ||
{ | ||
return; | ||
} | ||
|
||
s.setValue( settingsKey(), fi.absolutePath() ); | ||
|
||
// encode file as base64 | ||
QFile fileSource( file ); | ||
if ( !fileSource.open( QIODevice::ReadOnly ) ) | ||
{ | ||
return; | ||
} | ||
|
||
QByteArray blob = fileSource.readAll(); | ||
QByteArray encoded = blob.toBase64(); | ||
|
||
QString path( encoded ); | ||
path.prepend( QLatin1String( "base64:" ) ); | ||
if ( path == source() ) | ||
return; | ||
|
||
mFileLineEdit->setText( path ); | ||
emit sourceChanged( mFileLineEdit->text() ); | ||
} | ||
|
||
void QgsSvgSourceLineEdit::extractFile() | ||
{ | ||
QgsSettings s; | ||
QString file = QFileDialog::getSaveFileName( nullptr, | ||
tr( "Extract SVG File" ), | ||
defaultPath(), | ||
tr( "SVG files" ) + " (*.svg)" ); | ||
if ( file.isEmpty() ) | ||
{ | ||
return; | ||
} | ||
|
||
QFileInfo fi( file ); | ||
s.setValue( settingsKey(), fi.absolutePath() ); | ||
|
||
// decode current base64 embedded file | ||
QString path = mFileLineEdit->text().trimmed(); | ||
if ( path.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) ) | ||
{ | ||
QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix | ||
QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals ); | ||
|
||
QFile fileOut( file ); | ||
fileOut.open( QIODevice::WriteOnly ); | ||
fileOut.write( decoded ); | ||
fileOut.close(); | ||
} | ||
} | ||
|
||
void QgsSvgSourceLineEdit::mFileLineEdit_textEdited( const QString &text ) | ||
{ | ||
if ( !QFileInfo::exists( text ) ) | ||
{ | ||
return; | ||
} | ||
emit sourceChanged( text ); | ||
} | ||
|
||
void QgsSvgSourceLineEdit::mFileLineEdit_editingFinished() | ||
{ | ||
if ( !QFileInfo::exists( mFileLineEdit->text() ) ) | ||
{ | ||
QUrl url( mFileLineEdit->text() ); | ||
if ( !url.isValid() ) | ||
{ | ||
return; | ||
} | ||
} | ||
|
||
emit sourceChanged( mFileLineEdit->text() ); | ||
} | ||
|
||
QString QgsSvgSourceLineEdit::defaultPath() const | ||
{ | ||
if ( QFileInfo::exists( source() ) ) | ||
return source(); | ||
|
||
return QgsSettings().value( settingsKey(), QDir::homePath() ).toString(); | ||
} | ||
|
||
QString QgsSvgSourceLineEdit::settingsKey() const | ||
{ | ||
return mLastPathKey.isEmpty() ? QStringLiteral( "/UI/lastSVGDir" ) : mLastPathKey; | ||
} | ||
|
Oops, something went wrong.