Skip to content

Commit 1a7ade7

Browse files
committedJul 23, 2018
[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

+951
-662
lines changed
 
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+

‎src/gui/qgssvgsourcelineedit.h

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/***************************************************************************
2+
qgssvgsourcelineedit.h
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+
#ifndef QGSSVGSOURCELINEEDIT_H
17+
#define QGSSVGSOURCELINEEDIT_H
18+
19+
#include "qgis_gui.h"
20+
#include "qgis_sip.h"
21+
#include <QWidget>
22+
23+
class QLineEdit;
24+
class QToolButton;
25+
26+
/**
27+
* \ingroup gui
28+
* \class QgsSvgSourceLineEdit
29+
* A line edit widget with toolbutton for setting an SVG image path.
30+
* \since QGIS 3.4
31+
*/
32+
class GUI_EXPORT QgsSvgSourceLineEdit : public QWidget
33+
{
34+
Q_OBJECT
35+
Q_PROPERTY( QString source READ source WRITE setSource NOTIFY sourceChanged )
36+
37+
public:
38+
39+
/**
40+
* Constructor for QgsSvgSourceLineEdit, with the specified \a parent widget.
41+
*/
42+
QgsSvgSourceLineEdit( QWidget *parent SIP_TRANSFERTHIS = nullptr );
43+
44+
/**
45+
* Returns the current SVG source.
46+
* \see setSource()
47+
* \see sourceChanged()
48+
*/
49+
QString source() const;
50+
51+
/**
52+
* Sets a specific settings \a key to use when storing the last
53+
* used path for the SVG source.
54+
*/
55+
void setLastPathSettingsKey( const QString &key );
56+
57+
public slots:
58+
59+
/**
60+
* Sets a new \a source to show in the widget.
61+
* \see source()
62+
* \see sourceChanged()
63+
*/
64+
void setSource( const QString &source );
65+
66+
signals:
67+
68+
/**
69+
* Emitted whenever the SVG source is changed in the widget.
70+
*/
71+
void sourceChanged( const QString &source );
72+
73+
private slots:
74+
void selectFile();
75+
void selectUrl();
76+
void embedFile();
77+
void extractFile();
78+
void mFileLineEdit_textEdited( const QString &text );
79+
void mFileLineEdit_editingFinished();
80+
81+
private:
82+
83+
QLineEdit *mFileLineEdit = nullptr;
84+
QToolButton *mFileToolButton = nullptr;
85+
QString mLastPathKey;
86+
87+
QString defaultPath() const;
88+
QString settingsKey() const;
89+
90+
};
91+
92+
#endif

‎src/gui/qgstextformatwidget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1310,6 +1310,7 @@ void QgsTextFormatWidget::mShapeSVGSelectorBtn_clicked()
13101310
if ( !svgPath.isEmpty() )
13111311
{
13121312
mShapeSVGPathLineEdit->setText( svgPath );
1313+
updatePreview();
13131314
}
13141315
}
13151316
}

‎src/gui/symbology/qgssvgselectorwidget.cpp

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <QPixmapCache>
3333
#include <QStyle>
3434
#include <QTime>
35+
#include <QMenu>
3536

3637
// QgsSvgSelectorLoader
3738

@@ -373,8 +374,8 @@ QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget *parent )
373374
{
374375
// TODO: in-code gui setup with option to vertically or horizontally stack SVG groups/images widgets
375376
setupUi( this );
376-
connect( mFilePushButton, &QPushButton::clicked, this, &QgsSvgSelectorWidget::mFilePushButton_clicked );
377-
connect( mFileLineEdit, &QLineEdit::textChanged, this, &QgsSvgSelectorWidget::mFileLineEdit_textChanged );
377+
378+
connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSvgSelectorWidget::svgSourceChanged );
378379

379380
mIconSize = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) );
380381
mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) );
@@ -392,9 +393,7 @@ void QgsSvgSelectorWidget::setSvgPath( const QString &svgPath )
392393
{
393394
mCurrentSvgPath = svgPath;
394395

395-
mFileLineEdit->blockSignals( true );
396-
mFileLineEdit->setText( svgPath );
397-
mFileLineEdit->blockSignals( false );
396+
whileBlocking( mSvgSourceLineEdit )->setSource( svgPath );
398397

399398
mImagesListView->selectionModel()->blockSignals( true );
400399
QAbstractItemModel *m = mImagesListView->model();
@@ -427,7 +426,7 @@ void QgsSvgSelectorWidget::updateCurrentSvgPath( const QString &svgPath )
427426
void QgsSvgSelectorWidget::svgSelectionChanged( const QModelIndex &idx )
428427
{
429428
QString filePath = idx.data( Qt::UserRole ).toString();
430-
mFileLineEdit->setText( filePath );
429+
whileBlocking( mSvgSourceLineEdit )->setSource( filePath );
431430
updateCurrentSvgPath( filePath );
432431
}
433432

@@ -442,57 +441,14 @@ void QgsSvgSelectorWidget::populateIcons( const QModelIndex &idx )
442441

443442
connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged,
444443
this, &QgsSvgSelectorWidget::svgSelectionChanged );
445-
446-
}
447-
448-
void QgsSvgSelectorWidget::mFilePushButton_clicked()
449-
{
450-
QgsSettings settings;
451-
QString openDir = settings.value( QStringLiteral( "UI/lastSVGMarkerDir" ), QDir::homePath() ).toString();
452-
453-
QString lineEditText = mFileLineEdit->text();
454-
if ( !lineEditText.isEmpty() )
455-
{
456-
QFileInfo openDirFileInfo( lineEditText );
457-
openDir = openDirFileInfo.path();
458-
}
459-
460-
QString file = QFileDialog::getOpenFileName( nullptr,
461-
tr( "Select SVG file" ),
462-
openDir,
463-
tr( "SVG files" ) + " (*.svg *.SVG)" );
464-
465-
activateWindow(); // return window focus
466-
467-
if ( file.isNull() )
468-
return;
469-
470-
QFileInfo fi( file );
471-
if ( !fi.exists() || !fi.isReadable() )
472-
{
473-
updateCurrentSvgPath( QString() );
474-
updateLineEditFeedback( false );
475-
return;
476-
}
477-
settings.setValue( QStringLiteral( "UI/lastSVGMarkerDir" ), fi.absolutePath() );
478-
mFileLineEdit->setText( file );
479-
updateCurrentSvgPath( file );
480-
}
481-
482-
void QgsSvgSelectorWidget::updateLineEditFeedback( bool ok, const QString &tip )
483-
{
484-
// draw red text for path field if invalid (path can't be resolved)
485-
mFileLineEdit->setStyleSheet( QString( !ok ? "QLineEdit{ color: rgb(225, 0, 0); }" : "" ) );
486-
mFileLineEdit->setToolTip( !ok ? tr( "File not found" ) : tip );
487444
}
488445

489-
void QgsSvgSelectorWidget::mFileLineEdit_textChanged( const QString &text )
446+
void QgsSvgSelectorWidget::svgSourceChanged( const QString &text )
490447
{
491448
QString resolvedPath = QgsSymbolLayerUtils::svgSymbolNameToPath( text, QgsProject::instance()->pathResolver() );
492449
bool validSVG = !resolvedPath.isNull();
493450

494-
updateLineEditFeedback( validSVG, resolvedPath );
495-
updateCurrentSvgPath( validSVG ? resolvedPath : QString() );
451+
updateCurrentSvgPath( validSVG ? resolvedPath : text );
496452
}
497453

498454
void QgsSvgSelectorWidget::populateList()

‎src/gui/symbology/qgssvgselectorwidget.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,10 +276,7 @@ class GUI_EXPORT QgsSvgSelectorWidget : public QWidget, private Ui::WidgetSvgSel
276276
void populateIcons( const QModelIndex &idx );
277277
void svgSelectionChanged( const QModelIndex &idx );
278278
void updateCurrentSvgPath( const QString &svgPath );
279-
280-
void mFilePushButton_clicked();
281-
void updateLineEditFeedback( bool ok, const QString &tip = QString() );
282-
void mFileLineEdit_textChanged( const QString &text );
279+
void svgSourceChanged( const QString &text );
283280

284281
private:
285282

‎src/gui/symbology/qgssymbollayerwidget.cpp

Lines changed: 15 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@
5252
#include <QStandardItemModel>
5353
#include <QSvgRenderer>
5454
#include <QMessageBox>
55+
#include <QMenu>
56+
#include <QAction>
57+
#include <QInputDialog>
5558

5659
QgsExpressionContext QgsSymbolLayerWidget::createExpressionContext() const
5760
{
@@ -1804,9 +1807,10 @@ QgsSvgMarkerSymbolLayerWidget::QgsSvgMarkerSymbolLayerWidget( QgsVectorLayer *vl
18041807
mLayer = nullptr;
18051808

18061809
setupUi( this );
1807-
connect( mFileToolButton, &QToolButton::clicked, this, &QgsSvgMarkerSymbolLayerWidget::mFileToolButton_clicked );
1808-
connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_textEdited );
1809-
connect( mFileLineEdit, &QLineEdit::editingFinished, this, &QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_editingFinished );
1810+
1811+
mSvgSourceLineEdit->setLastPathSettingsKey( QStringLiteral( "/UI/lastSVGMarkerDir" ) );
1812+
1813+
connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSvgMarkerSymbolLayerWidget::svgSourceChanged );
18101814
connect( mChangeColorButton, &QgsColorButton::colorChanged, this, &QgsSvgMarkerSymbolLayerWidget::mChangeColorButton_colorChanged );
18111815
connect( mChangeStrokeColorButton, &QgsColorButton::colorChanged, this, &QgsSvgMarkerSymbolLayerWidget::mChangeStrokeColorButton_colorChanged );
18121816
connect( mStrokeWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSvgMarkerSymbolLayerWidget::mStrokeWidthSpinBox_valueChanged );
@@ -1944,9 +1948,7 @@ void QgsSvgMarkerSymbolLayerWidget::setGuiForSvg( const QgsSvgMarkerSymbolLayer
19441948
mChangeStrokeColorButton->setColor( stroke );
19451949
}
19461950

1947-
mFileLineEdit->blockSignals( true );
1948-
mFileLineEdit->setText( layer->path() );
1949-
mFileLineEdit->blockSignals( false );
1951+
whileBlocking( mSvgSourceLineEdit )->setSource( layer->path() );
19501952

19511953
mStrokeWidthSpinBox->blockSignals( true );
19521954
mStrokeWidthSpinBox->setValue( hasDefaultStrokeWidth ? defaultStrokeWidth : layer->strokeWidth() );
@@ -2070,7 +2072,7 @@ void QgsSvgMarkerSymbolLayerWidget::setName( const QModelIndex &idx )
20702072
{
20712073
QString name = idx.data( Qt::UserRole ).toString();
20722074
mLayer->setPath( name );
2073-
mFileLineEdit->setText( name );
2075+
whileBlocking( mSvgSourceLineEdit )->setSource( name );
20742076

20752077
setGuiForSvg( mLayer );
20762078
emit changed();
@@ -2154,55 +2156,13 @@ void QgsSvgMarkerSymbolLayerWidget::setOffset()
21542156
emit changed();
21552157
}
21562158

2157-
void QgsSvgMarkerSymbolLayerWidget::mFileToolButton_clicked()
2158-
{
2159-
QgsSettings s;
2160-
QString file = QFileDialog::getOpenFileName( nullptr,
2161-
tr( "Select SVG file" ),
2162-
s.value( QStringLiteral( "/UI/lastSVGMarkerDir" ), QDir::homePath() ).toString(),
2163-
tr( "SVG files" ) + " (*.svg)" );
2164-
QFileInfo fi( file );
2165-
if ( file.isEmpty() || !fi.exists() )
2166-
{
2167-
return;
2168-
}
2169-
mFileLineEdit->setText( file );
2170-
mLayer->setPath( file );
2171-
s.setValue( QStringLiteral( "/UI/lastSVGMarkerDir" ), fi.absolutePath() );
2172-
setGuiForSvg( mLayer );
2173-
emit changed();
2174-
}
2175-
2176-
void QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_textEdited( const QString &text )
2159+
void QgsSvgMarkerSymbolLayerWidget::svgSourceChanged( const QString &text )
21772160
{
2178-
if ( !QFileInfo::exists( text ) )
2179-
{
2180-
return;
2181-
}
21822161
mLayer->setPath( text );
21832162
setGuiForSvg( mLayer );
21842163
emit changed();
21852164
}
21862165

2187-
void QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_editingFinished()
2188-
{
2189-
if ( !QFileInfo::exists( mFileLineEdit->text() ) )
2190-
{
2191-
QUrl url( mFileLineEdit->text() );
2192-
if ( !url.isValid() )
2193-
{
2194-
return;
2195-
}
2196-
}
2197-
2198-
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
2199-
mLayer->setPath( mFileLineEdit->text() );
2200-
QApplication::restoreOverrideCursor();
2201-
2202-
setGuiForSvg( mLayer );
2203-
emit changed();
2204-
}
2205-
22062166
void QgsSvgMarkerSymbolLayerWidget::mChangeColorButton_colorChanged( const QColor &color )
22072167
{
22082168
if ( !mLayer )
@@ -2288,10 +2248,8 @@ QgsSVGFillSymbolLayerWidget::QgsSVGFillSymbolLayerWidget( QgsVectorLayer *vl, QW
22882248
{
22892249
mLayer = nullptr;
22902250
setupUi( this );
2291-
connect( mBrowseToolButton, &QToolButton::clicked, this, &QgsSVGFillSymbolLayerWidget::mBrowseToolButton_clicked );
22922251
connect( mTextureWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged );
2293-
connect( mSVGLineEdit, &QLineEdit::textEdited, this, &QgsSVGFillSymbolLayerWidget::mSVGLineEdit_textEdited );
2294-
connect( mSVGLineEdit, &QLineEdit::editingFinished, this, &QgsSVGFillSymbolLayerWidget::mSVGLineEdit_editingFinished );
2252+
connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSVGFillSymbolLayerWidget::svgSourceChanged );
22952253
connect( mRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSVGFillSymbolLayerWidget::mRotationSpinBox_valueChanged );
22962254
connect( mChangeColorButton, &QgsColorButton::colorChanged, this, &QgsSVGFillSymbolLayerWidget::mChangeColorButton_colorChanged );
22972255
connect( mChangeStrokeColorButton, &QgsColorButton::colorChanged, this, &QgsSVGFillSymbolLayerWidget::mChangeStrokeColorButton_colorChanged );
@@ -2335,7 +2293,7 @@ void QgsSVGFillSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer )
23352293
mTextureWidthSpinBox->blockSignals( true );
23362294
mTextureWidthSpinBox->setValue( width );
23372295
mTextureWidthSpinBox->blockSignals( false );
2338-
mSVGLineEdit->setText( mLayer->svgFilePath() );
2296+
whileBlocking( mSvgSourceLineEdit )->setSource( mLayer->svgFilePath() );
23392297
mRotationSpinBox->blockSignals( true );
23402298
mRotationSpinBox->setValue( mLayer->angle() );
23412299
mRotationSpinBox->blockSignals( false );
@@ -2372,16 +2330,6 @@ QgsSymbolLayer *QgsSVGFillSymbolLayerWidget::symbolLayer()
23722330
return mLayer;
23732331
}
23742332

2375-
void QgsSVGFillSymbolLayerWidget::mBrowseToolButton_clicked()
2376-
{
2377-
QString filePath = QFileDialog::getOpenFileName( nullptr, tr( "Select SVG Texture File" ), QDir::homePath(), tr( "SVG file" ) + " (*.svg);;" + tr( "All files" ) + " (*.*)" );
2378-
if ( !filePath.isNull() )
2379-
{
2380-
mSVGLineEdit->setText( filePath );
2381-
emit changed();
2382-
}
2383-
}
2384-
23852333
void QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged( double d )
23862334
{
23872335
if ( mLayer )
@@ -2391,53 +2339,23 @@ void QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged( double d )
23912339
}
23922340
}
23932341

2394-
void QgsSVGFillSymbolLayerWidget::mSVGLineEdit_textEdited( const QString &text )
2342+
void QgsSVGFillSymbolLayerWidget::svgSourceChanged( const QString &text )
23952343
{
23962344
if ( !mLayer )
23972345
{
23982346
return;
23992347
}
24002348

2401-
QFileInfo fi( text );
2402-
if ( !fi.exists() )
2403-
{
2404-
return;
2405-
}
24062349
mLayer->setSvgFilePath( text );
24072350
updateParamGui();
24082351
emit changed();
24092352
}
24102353

2411-
void QgsSVGFillSymbolLayerWidget::mSVGLineEdit_editingFinished()
2412-
{
2413-
if ( !mLayer )
2414-
{
2415-
return;
2416-
}
2417-
2418-
QFileInfo fi( mSVGLineEdit->text() );
2419-
if ( !fi.exists() )
2420-
{
2421-
QUrl url( mSVGLineEdit->text() );
2422-
if ( !url.isValid() )
2423-
{
2424-
return;
2425-
}
2426-
}
2427-
2428-
QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) );
2429-
mLayer->setSvgFilePath( mSVGLineEdit->text() );
2430-
QApplication::restoreOverrideCursor();
2431-
2432-
updateParamGui();
2433-
emit changed();
2434-
}
2435-
24362354
void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex &item )
24372355
{
24382356
QString file = item.data( Qt::UserRole ).toString();
24392357
mLayer->setSvgFilePath( file );
2440-
mSVGLineEdit->setText( file );
2358+
whileBlocking( mSvgSourceLineEdit )->setSource( file );
24412359

24422360
updateParamGui();
24432361
emit changed();
@@ -2492,7 +2410,7 @@ void QgsSVGFillSymbolLayerWidget::updateParamGui( bool resetValues )
24922410
QColor defaultFill, defaultStroke;
24932411
double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity;
24942412
bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity;
2495-
QgsApplication::svgCache()->containsParams( mSVGLineEdit->text(), hasFillParam, hasDefaultFillColor, defaultFill,
2413+
QgsApplication::svgCache()->containsParams( mSvgSourceLineEdit->source(), hasFillParam, hasDefaultFillColor, defaultFill,
24962414
hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity,
24972415
hasStrokeParam, hasDefaultStrokeColor, defaultStroke,
24982416
hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth,

‎src/gui/symbology/qgssymbollayerwidget.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -546,9 +546,7 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, pr
546546
private slots:
547547
void setName( const QModelIndex &idx );
548548
void populateIcons( const QModelIndex &idx );
549-
void mFileToolButton_clicked();
550-
void mFileLineEdit_textEdited( const QString &text );
551-
void mFileLineEdit_editingFinished();
549+
void svgSourceChanged( const QString &text );
552550
void mChangeColorButton_colorChanged( const QColor &color );
553551
void mChangeStrokeColorButton_colorChanged( const QColor &color );
554552
void mStrokeWidthSpinBox_valueChanged( double d );
@@ -668,10 +666,8 @@ class GUI_EXPORT QgsSVGFillSymbolLayerWidget : public QgsSymbolLayerWidget, priv
668666
void updateParamGui( bool resetValues = true );
669667

670668
private slots:
671-
void mBrowseToolButton_clicked();
672669
void mTextureWidthSpinBox_valueChanged( double d );
673-
void mSVGLineEdit_textEdited( const QString &text );
674-
void mSVGLineEdit_editingFinished();
670+
void svgSourceChanged( const QString &text );
675671
void setFile( const QModelIndex &item );
676672
void populateIcons( const QModelIndex &item );
677673
void mRotationSpinBox_valueChanged( double d );

‎src/ui/symbollayer/widget_svgfill.ui

Lines changed: 159 additions & 161 deletions
Large diffs are not rendered by default.

‎src/ui/symbollayer/widget_svgmarker.ui

Lines changed: 311 additions & 309 deletions
Large diffs are not rendered by default.

‎src/ui/symbollayer/widget_svgselector.ui

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,20 +26,6 @@
2626
<property name="bottomMargin">
2727
<number>0</number>
2828
</property>
29-
<item row="1" column="1">
30-
<widget class="QLabel" name="mImagesLabel">
31-
<property name="text">
32-
<string>SVG Images</string>
33-
</property>
34-
</widget>
35-
</item>
36-
<item row="1" column="0">
37-
<widget class="QLabel" name="mGroupsLabel">
38-
<property name="text">
39-
<string>SVG Groups</string>
40-
</property>
41-
</widget>
42-
</item>
4329
<item row="2" column="0">
4430
<widget class="QTreeView" name="mGroupsTreeView">
4531
<property name="sizePolicy">
@@ -56,6 +42,13 @@
5642
</property>
5743
</widget>
5844
</item>
45+
<item row="1" column="1">
46+
<widget class="QLabel" name="mImagesLabel">
47+
<property name="text">
48+
<string>SVG Images</string>
49+
</property>
50+
</widget>
51+
</item>
5952
<item row="2" column="1">
6053
<widget class="QListView" name="mImagesListView">
6154
<property name="sizePolicy">
@@ -102,33 +95,33 @@
10295
</property>
10396
</widget>
10497
</item>
98+
<item row="1" column="0">
99+
<widget class="QLabel" name="mGroupsLabel">
100+
<property name="text">
101+
<string>SVG Groups</string>
102+
</property>
103+
</widget>
104+
</item>
105105
<item row="3" column="0" colspan="2">
106-
<layout class="QHBoxLayout" name="mFileLayout">
107-
<item>
108-
<widget class="QLineEdit" name="mFileLineEdit"/>
109-
</item>
110-
<item>
111-
<widget class="QPushButton" name="mFilePushButton">
112-
<property name="maximumSize">
113-
<size>
114-
<width>50</width>
115-
<height>16777215</height>
116-
</size>
117-
</property>
118-
<property name="text">
119-
<string>…</string>
120-
</property>
121-
</widget>
122-
</item>
123-
</layout>
106+
<widget class="QgsSvgSourceLineEdit" name="mSvgSourceLineEdit" native="true">
107+
<property name="focusPolicy">
108+
<enum>Qt::StrongFocus</enum>
109+
</property>
110+
</widget>
124111
</item>
125112
</layout>
126113
</widget>
114+
<customwidgets>
115+
<customwidget>
116+
<class>QgsSvgSourceLineEdit</class>
117+
<extends>QWidget</extends>
118+
<header>qgssvgsourcelineedit.h</header>
119+
<container>1</container>
120+
</customwidget>
121+
</customwidgets>
127122
<tabstops>
128123
<tabstop>mGroupsTreeView</tabstop>
129124
<tabstop>mImagesListView</tabstop>
130-
<tabstop>mFileLineEdit</tabstop>
131-
<tabstop>mFilePushButton</tabstop>
132125
</tabstops>
133126
<resources/>
134127
<connections/>

‎tests/src/python/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ ADD_PYTHON_TEST(PyQgsArrowSymbolLayer test_qgsarrowsymbollayer.py)
183183
ADD_PYTHON_TEST(PyQgsSymbolExpressionVariables test_qgssymbolexpressionvariables.py)
184184
ADD_PYTHON_TEST(PyQgsSyntacticSugar test_syntactic_sugar.py)
185185
ADD_PYTHON_TEST(PyQgsStringUtils test_qgsstringutils.py)
186+
ADD_PYTHON_TEST(PyQgsSvgSourceLineEdit test_qgssvgsourcelineedit.py)
186187
ADD_PYTHON_TEST(PyQgsSymbol test_qgssymbol.py)
187188
ADD_PYTHON_TEST(PyQgsSymbolLayerUtils test_qgssymbollayerutils.py)
188189
ADD_PYTHON_TEST(PyQgsTaskManager test_qgstaskmanager.py)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# -*- coding: utf-8 -*-
2+
"""QGIS Unit tests for QgsSvgSourceLineEdit
3+
4+
.. note:: This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License as published by
6+
the Free Software Foundation; either version 2 of the License, or
7+
(at your option) any later version.
8+
"""
9+
__author__ = 'Nyall Dawson'
10+
__date__ = '19/07/2018'
11+
__copyright__ = 'Copyright 2018, The QGIS Project'
12+
# This will get replaced with a git SHA1 when you do a git archive
13+
__revision__ = '$Format:%H$'
14+
15+
import qgis # NOQA
16+
17+
from qgis.gui import QgsSvgSourceLineEdit
18+
19+
from qgis.PyQt.QtTest import QSignalSpy
20+
from qgis.testing import start_app, unittest
21+
22+
start_app()
23+
24+
25+
class TestQgsSvgSourceLineEdit(unittest.TestCase):
26+
27+
def testGettersSetters(self):
28+
""" test widget getters/setters """
29+
w = QgsSvgSourceLineEdit()
30+
spy = QSignalSpy(w.sourceChanged)
31+
32+
w.setSource('source')
33+
self.assertEqual(w.source(), 'source')
34+
self.assertEqual(len(spy), 1)
35+
self.assertEqual(spy[0][0], 'source')
36+
37+
# no signal for same value
38+
w.setSource('source')
39+
self.assertEqual(w.source(), 'source')
40+
self.assertEqual(len(spy), 1)
41+
42+
w.setSource('another')
43+
self.assertEqual(w.source(), 'another')
44+
self.assertEqual(len(spy), 2)
45+
self.assertEqual(spy[1][0], 'another')
46+
47+
48+
if __name__ == '__main__':
49+
unittest.main()

0 commit comments

Comments
 (0)
Please sign in to comment.