Skip to content

Commit 1a7ade7

Browse files
committed
[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
1 parent 19f8a00 commit 1a7ade7

15 files changed

Lines changed: 951 additions & 662 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/************************************************************************
2+
* This file has been generated automatically from *
3+
* *
4+
* src/gui/qgssvgsourcelineedit.h *
5+
* *
6+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
7+
************************************************************************/
8+
9+
10+
11+
12+
class QgsSvgSourceLineEdit : QWidget
13+
{
14+
%Docstring
15+
A line edit widget with toolbutton for setting an SVG image path.
16+
17+
.. versionadded:: 3.4
18+
%End
19+
20+
%TypeHeaderCode
21+
#include "qgssvgsourcelineedit.h"
22+
%End
23+
public:
24+
25+
QgsSvgSourceLineEdit( QWidget *parent /TransferThis/ = 0 );
26+
%Docstring
27+
Constructor for QgsSvgSourceLineEdit, with the specified ``parent`` widget.
28+
%End
29+
30+
QString source() const;
31+
%Docstring
32+
Returns the current SVG source.
33+
34+
.. seealso:: :py:func:`setSource`
35+
36+
.. seealso:: :py:func:`sourceChanged`
37+
%End
38+
39+
void setLastPathSettingsKey( const QString &key );
40+
%Docstring
41+
Sets a specific settings ``key`` to use when storing the last
42+
used path for the SVG source.
43+
%End
44+
45+
public slots:
46+
47+
void setSource( const QString &source );
48+
%Docstring
49+
Sets a new ``source`` to show in the widget.
50+
51+
.. seealso:: :py:func:`source`
52+
53+
.. seealso:: :py:func:`sourceChanged`
54+
%End
55+
56+
signals:
57+
58+
void sourceChanged( const QString &source );
59+
%Docstring
60+
Emitted whenever the SVG source is changed in the widget.
61+
%End
62+
63+
};
64+
65+
/************************************************************************
66+
* This file has been generated automatically from *
67+
* *
68+
* src/gui/qgssvgsourcelineedit.h *
69+
* *
70+
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
71+
************************************************************************/

python/gui/gui_auto.sip

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
%Include auto_generated/qgsstatusbar.sip
196196
%Include auto_generated/qgssublayersdialog.sip
197197
%Include auto_generated/qgssubstitutionlistwidget.sip
198+
%Include auto_generated/qgssvgsourcelineedit.sip
198199
%Include auto_generated/qgssymbolbutton.sip
199200
%Include auto_generated/qgstablewidgetbase.sip
200201
%Include auto_generated/qgstabwidget.sip

src/gui/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ SET(QGIS_GUI_SRCS
359359
qgssubstitutionlistwidget.cpp
360360
qgssqlcomposerdialog.cpp
361361
qgsstatusbar.cpp
362+
qgssvgsourcelineedit.cpp
362363
qgssymbolbutton.cpp
363364
qgstablewidgetbase.cpp
364365
qgstabwidget.cpp
@@ -526,6 +527,7 @@ SET(QGIS_GUI_MOC_HDRS
526527
qgsstatusbar.h
527528
qgssublayersdialog.h
528529
qgssubstitutionlistwidget.h
530+
qgssvgsourcelineedit.h
529531
qgssymbolbutton.h
530532
qgstablewidgetbase.h
531533
qgstabwidget.h

src/gui/qgssvgsourcelineedit.cpp

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/***************************************************************************
2+
qgssvgsourcelineedit.cpp
3+
-----------------------
4+
begin : July 2018
5+
copyright : (C) 2018 by Nyall Dawson
6+
email : nyall dot dawson at gmail dot com
7+
***************************************************************************
8+
* *
9+
* This program is free software; you can redistribute it and/or modify *
10+
* it under the terms of the GNU General Public License as published by *
11+
* the Free Software Foundation; either version 2 of the License, or *
12+
* (at your option) any later version. *
13+
* *
14+
***************************************************************************/
15+
16+
#include "qgssvgsourcelineedit.h"
17+
#include "qgssettings.h"
18+
#include <QMenu>
19+
#include <QLineEdit>
20+
#include <QToolButton>
21+
#include <QHBoxLayout>
22+
#include <QFileDialog>
23+
#include <QInputDialog>
24+
25+
QgsSvgSourceLineEdit::QgsSvgSourceLineEdit( QWidget *parent )
26+
: QWidget( parent )
27+
{
28+
QHBoxLayout *layout = new QHBoxLayout( this );
29+
mFileLineEdit = new QLineEdit( this );
30+
mFileToolButton = new QToolButton( this );
31+
mFileToolButton->setText( tr( "" ) );
32+
layout->addWidget( mFileLineEdit, 1 );
33+
layout->addWidget( mFileToolButton );
34+
setLayout( layout );
35+
36+
QMenu *sourceMenu = new QMenu( mFileToolButton );
37+
38+
QAction *selectFileAction = new QAction( tr( "Select File…" ), sourceMenu );
39+
connect( selectFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::selectFile );
40+
sourceMenu->addAction( selectFileAction );
41+
42+
QAction *embedFileAction = new QAction( tr( "Embed File…" ), sourceMenu );
43+
connect( embedFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::embedFile );
44+
sourceMenu->addAction( embedFileAction );
45+
46+
QAction *extractFileAction = new QAction( tr( "Extract Embedded File…" ), sourceMenu );
47+
connect( extractFileAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::extractFile );
48+
sourceMenu->addAction( extractFileAction );
49+
50+
connect( sourceMenu, &QMenu::aboutToShow, this, [this, extractFileAction]
51+
{
52+
extractFileAction->setEnabled( mFileLineEdit->text().startsWith( QStringLiteral( "base64:" ), Qt::CaseInsensitive ) );
53+
} );
54+
55+
QAction *enterUrlAction = new QAction( tr( "From URL…" ), sourceMenu );
56+
connect( enterUrlAction, &QAction::triggered, this, &QgsSvgSourceLineEdit::selectUrl );
57+
sourceMenu->addAction( enterUrlAction );
58+
59+
mFileToolButton->setMenu( sourceMenu );
60+
mFileToolButton->setPopupMode( QToolButton::MenuButtonPopup );
61+
connect( mFileToolButton, &QToolButton::clicked, this, &QgsSvgSourceLineEdit::selectFile );
62+
63+
connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsSvgSourceLineEdit::mFileLineEdit_textEdited );
64+
connect( mFileLineEdit, &QLineEdit::editingFinished, this, &QgsSvgSourceLineEdit::mFileLineEdit_editingFinished );
65+
}
66+
67+
QString QgsSvgSourceLineEdit::source() const
68+
{
69+
return mFileLineEdit->text();
70+
}
71+
72+
void QgsSvgSourceLineEdit::setLastPathSettingsKey( const QString &key )
73+
{
74+
mLastPathKey = key;
75+
}
76+
77+
void QgsSvgSourceLineEdit::setSource( const QString &source )
78+
{
79+
if ( source == mFileLineEdit->text() )
80+
return;
81+
82+
mFileLineEdit->setText( source );
83+
emit sourceChanged( source );
84+
}
85+
86+
void QgsSvgSourceLineEdit::selectFile()
87+
{
88+
QgsSettings s;
89+
QString file = QFileDialog::getOpenFileName( nullptr,
90+
tr( "Select SVG file" ),
91+
defaultPath(),
92+
tr( "SVG files" ) + " (*.svg)" );
93+
QFileInfo fi( file );
94+
if ( file.isEmpty() || !fi.exists() || file == source() )
95+
{
96+
return;
97+
}
98+
mFileLineEdit->setText( file );
99+
s.setValue( settingsKey(), fi.absolutePath() );
100+
emit sourceChanged( mFileLineEdit->text() );
101+
}
102+
103+
void QgsSvgSourceLineEdit::selectUrl()
104+
{
105+
bool ok = false;
106+
const QString path = QInputDialog::getText( this, tr( "SVG From URL" ), tr( "Enter SVG URL" ), QLineEdit::Normal, mFileLineEdit->text(), &ok );
107+
if ( ok && path != source() )
108+
{
109+
mFileLineEdit->setText( path );
110+
emit sourceChanged( mFileLineEdit->text() );
111+
}
112+
}
113+
114+
void QgsSvgSourceLineEdit::embedFile()
115+
{
116+
QgsSettings s;
117+
QString file = QFileDialog::getOpenFileName( nullptr,
118+
tr( "Embed SVG File" ),
119+
defaultPath(),
120+
tr( "SVG files" ) + " (*.svg)" );
121+
QFileInfo fi( file );
122+
if ( file.isEmpty() || !fi.exists() )
123+
{
124+
return;
125+
}
126+
127+
s.setValue( settingsKey(), fi.absolutePath() );
128+
129+
// encode file as base64
130+
QFile fileSource( file );
131+
if ( !fileSource.open( QIODevice::ReadOnly ) )
132+
{
133+
return;
134+
}
135+
136+
QByteArray blob = fileSource.readAll();
137+
QByteArray encoded = blob.toBase64();
138+
139+
QString path( encoded );
140+
path.prepend( QLatin1String( "base64:" ) );
141+
if ( path == source() )
142+
return;
143+
144+
mFileLineEdit->setText( path );
145+
emit sourceChanged( mFileLineEdit->text() );
146+
}
147+
148+
void QgsSvgSourceLineEdit::extractFile()
149+
{
150+
QgsSettings s;
151+
QString file = QFileDialog::getSaveFileName( nullptr,
152+
tr( "Extract SVG File" ),
153+
defaultPath(),
154+
tr( "SVG files" ) + " (*.svg)" );
155+
if ( file.isEmpty() )
156+
{
157+
return;
158+
}
159+
160+
QFileInfo fi( file );
161+
s.setValue( settingsKey(), fi.absolutePath() );
162+
163+
// decode current base64 embedded file
164+
QString path = mFileLineEdit->text().trimmed();
165+
if ( path.startsWith( QLatin1String( "base64:" ), Qt::CaseInsensitive ) )
166+
{
167+
QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
168+
QByteArray decoded = QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
169+
170+
QFile fileOut( file );
171+
fileOut.open( QIODevice::WriteOnly );
172+
fileOut.write( decoded );
173+
fileOut.close();
174+
}
175+
}
176+
177+
void QgsSvgSourceLineEdit::mFileLineEdit_textEdited( const QString &text )
178+
{
179+
if ( !QFileInfo::exists( text ) )
180+
{
181+
return;
182+
}
183+
emit sourceChanged( text );
184+
}
185+
186+
void QgsSvgSourceLineEdit::mFileLineEdit_editingFinished()
187+
{
188+
if ( !QFileInfo::exists( mFileLineEdit->text() ) )
189+
{
190+
QUrl url( mFileLineEdit->text() );
191+
if ( !url.isValid() )
192+
{
193+
return;
194+
}
195+
}
196+
197+
emit sourceChanged( mFileLineEdit->text() );
198+
}
199+
200+
QString QgsSvgSourceLineEdit::defaultPath() const
201+
{
202+
if ( QFileInfo::exists( source() ) )
203+
return source();
204+
205+
return QgsSettings().value( settingsKey(), QDir::homePath() ).toString();
206+
}
207+
208+
QString QgsSvgSourceLineEdit::settingsKey() const
209+
{
210+
return mLastPathKey.isEmpty() ? QStringLiteral( "/UI/lastSVGDir" ) : mLastPathKey;
211+
}
212+

0 commit comments

Comments
 (0)