diff --git a/python/gui/auto_generated/qgssvgsourcelineedit.sip.in b/python/gui/auto_generated/qgssvgsourcelineedit.sip.in new file mode 100644 index 00000000000..80b00ac9a07 --- /dev/null +++ b/python/gui/auto_generated/qgssvgsourcelineedit.sip.in @@ -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 * + ************************************************************************/ diff --git a/python/gui/gui_auto.sip b/python/gui/gui_auto.sip index bc4f8610156..321bd6351eb 100644 --- a/python/gui/gui_auto.sip +++ b/python/gui/gui_auto.sip @@ -195,6 +195,7 @@ %Include auto_generated/qgsstatusbar.sip %Include auto_generated/qgssublayersdialog.sip %Include auto_generated/qgssubstitutionlistwidget.sip +%Include auto_generated/qgssvgsourcelineedit.sip %Include auto_generated/qgssymbolbutton.sip %Include auto_generated/qgstablewidgetbase.sip %Include auto_generated/qgstabwidget.sip diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3671251aedd..928cef857e9 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -359,6 +359,7 @@ SET(QGIS_GUI_SRCS qgssubstitutionlistwidget.cpp qgssqlcomposerdialog.cpp qgsstatusbar.cpp + qgssvgsourcelineedit.cpp qgssymbolbutton.cpp qgstablewidgetbase.cpp qgstabwidget.cpp @@ -526,6 +527,7 @@ SET(QGIS_GUI_MOC_HDRS qgsstatusbar.h qgssublayersdialog.h qgssubstitutionlistwidget.h + qgssvgsourcelineedit.h qgssymbolbutton.h qgstablewidgetbase.h qgstabwidget.h diff --git a/src/gui/qgssvgsourcelineedit.cpp b/src/gui/qgssvgsourcelineedit.cpp new file mode 100644 index 00000000000..f7ee62f357e --- /dev/null +++ b/src/gui/qgssvgsourcelineedit.cpp @@ -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 +#include +#include +#include +#include +#include + +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; +} + diff --git a/src/gui/qgssvgsourcelineedit.h b/src/gui/qgssvgsourcelineedit.h new file mode 100644 index 00000000000..065b0c84452 --- /dev/null +++ b/src/gui/qgssvgsourcelineedit.h @@ -0,0 +1,92 @@ +/*************************************************************************** + qgssvgsourcelineedit.h + --------------------- + 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. * + * * + ***************************************************************************/ + +#ifndef QGSSVGSOURCELINEEDIT_H +#define QGSSVGSOURCELINEEDIT_H + +#include "qgis_gui.h" +#include "qgis_sip.h" +#include + +class QLineEdit; +class QToolButton; + +/** + * \ingroup gui + * \class QgsSvgSourceLineEdit + * A line edit widget with toolbutton for setting an SVG image path. + * \since QGIS 3.4 + */ +class GUI_EXPORT QgsSvgSourceLineEdit : public QWidget +{ + Q_OBJECT + Q_PROPERTY( QString source READ source WRITE setSource NOTIFY sourceChanged ) + + public: + + /** + * Constructor for QgsSvgSourceLineEdit, with the specified \a parent widget. + */ + QgsSvgSourceLineEdit( QWidget *parent SIP_TRANSFERTHIS = nullptr ); + + /** + * Returns the current SVG source. + * \see setSource() + * \see sourceChanged() + */ + QString source() const; + + /** + * Sets a specific settings \a key to use when storing the last + * used path for the SVG source. + */ + void setLastPathSettingsKey( const QString &key ); + + public slots: + + /** + * Sets a new \a source to show in the widget. + * \see source() + * \see sourceChanged() + */ + void setSource( const QString &source ); + + signals: + + /** + * Emitted whenever the SVG source is changed in the widget. + */ + void sourceChanged( const QString &source ); + + private slots: + void selectFile(); + void selectUrl(); + void embedFile(); + void extractFile(); + void mFileLineEdit_textEdited( const QString &text ); + void mFileLineEdit_editingFinished(); + + private: + + QLineEdit *mFileLineEdit = nullptr; + QToolButton *mFileToolButton = nullptr; + QString mLastPathKey; + + QString defaultPath() const; + QString settingsKey() const; + +}; + +#endif diff --git a/src/gui/qgstextformatwidget.cpp b/src/gui/qgstextformatwidget.cpp index 89de38648ab..ab491139027 100644 --- a/src/gui/qgstextformatwidget.cpp +++ b/src/gui/qgstextformatwidget.cpp @@ -1310,6 +1310,7 @@ void QgsTextFormatWidget::mShapeSVGSelectorBtn_clicked() if ( !svgPath.isEmpty() ) { mShapeSVGPathLineEdit->setText( svgPath ); + updatePreview(); } } } diff --git a/src/gui/symbology/qgssvgselectorwidget.cpp b/src/gui/symbology/qgssvgselectorwidget.cpp index 5e730218ef9..74bfbe97850 100644 --- a/src/gui/symbology/qgssvgselectorwidget.cpp +++ b/src/gui/symbology/qgssvgselectorwidget.cpp @@ -32,6 +32,7 @@ #include #include #include +#include // QgsSvgSelectorLoader @@ -373,8 +374,8 @@ QgsSvgSelectorWidget::QgsSvgSelectorWidget( QWidget *parent ) { // TODO: in-code gui setup with option to vertically or horizontally stack SVG groups/images widgets setupUi( this ); - connect( mFilePushButton, &QPushButton::clicked, this, &QgsSvgSelectorWidget::mFilePushButton_clicked ); - connect( mFileLineEdit, &QLineEdit::textChanged, this, &QgsSvgSelectorWidget::mFileLineEdit_textChanged ); + + connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSvgSelectorWidget::svgSourceChanged ); mIconSize = std::max( 30, static_cast< int >( std::round( Qgis::UI_SCALE_FACTOR * fontMetrics().width( QStringLiteral( "XXXX" ) ) ) ) ); mImagesListView->setGridSize( QSize( mIconSize * 1.2, mIconSize * 1.2 ) ); @@ -392,9 +393,7 @@ void QgsSvgSelectorWidget::setSvgPath( const QString &svgPath ) { mCurrentSvgPath = svgPath; - mFileLineEdit->blockSignals( true ); - mFileLineEdit->setText( svgPath ); - mFileLineEdit->blockSignals( false ); + whileBlocking( mSvgSourceLineEdit )->setSource( svgPath ); mImagesListView->selectionModel()->blockSignals( true ); QAbstractItemModel *m = mImagesListView->model(); @@ -427,7 +426,7 @@ void QgsSvgSelectorWidget::updateCurrentSvgPath( const QString &svgPath ) void QgsSvgSelectorWidget::svgSelectionChanged( const QModelIndex &idx ) { QString filePath = idx.data( Qt::UserRole ).toString(); - mFileLineEdit->setText( filePath ); + whileBlocking( mSvgSourceLineEdit )->setSource( filePath ); updateCurrentSvgPath( filePath ); } @@ -442,57 +441,14 @@ void QgsSvgSelectorWidget::populateIcons( const QModelIndex &idx ) connect( mImagesListView->selectionModel(), &QItemSelectionModel::currentChanged, this, &QgsSvgSelectorWidget::svgSelectionChanged ); - } -void QgsSvgSelectorWidget::mFilePushButton_clicked() -{ - QgsSettings settings; - QString openDir = settings.value( QStringLiteral( "UI/lastSVGMarkerDir" ), QDir::homePath() ).toString(); - - QString lineEditText = mFileLineEdit->text(); - if ( !lineEditText.isEmpty() ) - { - QFileInfo openDirFileInfo( lineEditText ); - openDir = openDirFileInfo.path(); - } - - QString file = QFileDialog::getOpenFileName( nullptr, - tr( "Select SVG file" ), - openDir, - tr( "SVG files" ) + " (*.svg *.SVG)" ); - - activateWindow(); // return window focus - - if ( file.isNull() ) - return; - - QFileInfo fi( file ); - if ( !fi.exists() || !fi.isReadable() ) - { - updateCurrentSvgPath( QString() ); - updateLineEditFeedback( false ); - return; - } - settings.setValue( QStringLiteral( "UI/lastSVGMarkerDir" ), fi.absolutePath() ); - mFileLineEdit->setText( file ); - updateCurrentSvgPath( file ); -} - -void QgsSvgSelectorWidget::updateLineEditFeedback( bool ok, const QString &tip ) -{ - // draw red text for path field if invalid (path can't be resolved) - mFileLineEdit->setStyleSheet( QString( !ok ? "QLineEdit{ color: rgb(225, 0, 0); }" : "" ) ); - mFileLineEdit->setToolTip( !ok ? tr( "File not found" ) : tip ); -} - -void QgsSvgSelectorWidget::mFileLineEdit_textChanged( const QString &text ) +void QgsSvgSelectorWidget::svgSourceChanged( const QString &text ) { QString resolvedPath = QgsSymbolLayerUtils::svgSymbolNameToPath( text, QgsProject::instance()->pathResolver() ); bool validSVG = !resolvedPath.isNull(); - updateLineEditFeedback( validSVG, resolvedPath ); - updateCurrentSvgPath( validSVG ? resolvedPath : QString() ); + updateCurrentSvgPath( validSVG ? resolvedPath : text ); } void QgsSvgSelectorWidget::populateList() diff --git a/src/gui/symbology/qgssvgselectorwidget.h b/src/gui/symbology/qgssvgselectorwidget.h index 2f82319c961..d79f5f28a95 100644 --- a/src/gui/symbology/qgssvgselectorwidget.h +++ b/src/gui/symbology/qgssvgselectorwidget.h @@ -276,10 +276,7 @@ class GUI_EXPORT QgsSvgSelectorWidget : public QWidget, private Ui::WidgetSvgSel void populateIcons( const QModelIndex &idx ); void svgSelectionChanged( const QModelIndex &idx ); void updateCurrentSvgPath( const QString &svgPath ); - - void mFilePushButton_clicked(); - void updateLineEditFeedback( bool ok, const QString &tip = QString() ); - void mFileLineEdit_textChanged( const QString &text ); + void svgSourceChanged( const QString &text ); private: diff --git a/src/gui/symbology/qgssymbollayerwidget.cpp b/src/gui/symbology/qgssymbollayerwidget.cpp index a2c0b21f3e5..5fff9a074b3 100644 --- a/src/gui/symbology/qgssymbollayerwidget.cpp +++ b/src/gui/symbology/qgssymbollayerwidget.cpp @@ -52,6 +52,9 @@ #include #include #include +#include +#include +#include QgsExpressionContext QgsSymbolLayerWidget::createExpressionContext() const { @@ -1804,9 +1807,10 @@ QgsSvgMarkerSymbolLayerWidget::QgsSvgMarkerSymbolLayerWidget( QgsVectorLayer *vl mLayer = nullptr; setupUi( this ); - connect( mFileToolButton, &QToolButton::clicked, this, &QgsSvgMarkerSymbolLayerWidget::mFileToolButton_clicked ); - connect( mFileLineEdit, &QLineEdit::textEdited, this, &QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_textEdited ); - connect( mFileLineEdit, &QLineEdit::editingFinished, this, &QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_editingFinished ); + + mSvgSourceLineEdit->setLastPathSettingsKey( QStringLiteral( "/UI/lastSVGMarkerDir" ) ); + + connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSvgMarkerSymbolLayerWidget::svgSourceChanged ); connect( mChangeColorButton, &QgsColorButton::colorChanged, this, &QgsSvgMarkerSymbolLayerWidget::mChangeColorButton_colorChanged ); connect( mChangeStrokeColorButton, &QgsColorButton::colorChanged, this, &QgsSvgMarkerSymbolLayerWidget::mChangeStrokeColorButton_colorChanged ); connect( mStrokeWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSvgMarkerSymbolLayerWidget::mStrokeWidthSpinBox_valueChanged ); @@ -1944,9 +1948,7 @@ void QgsSvgMarkerSymbolLayerWidget::setGuiForSvg( const QgsSvgMarkerSymbolLayer mChangeStrokeColorButton->setColor( stroke ); } - mFileLineEdit->blockSignals( true ); - mFileLineEdit->setText( layer->path() ); - mFileLineEdit->blockSignals( false ); + whileBlocking( mSvgSourceLineEdit )->setSource( layer->path() ); mStrokeWidthSpinBox->blockSignals( true ); mStrokeWidthSpinBox->setValue( hasDefaultStrokeWidth ? defaultStrokeWidth : layer->strokeWidth() ); @@ -2070,7 +2072,7 @@ void QgsSvgMarkerSymbolLayerWidget::setName( const QModelIndex &idx ) { QString name = idx.data( Qt::UserRole ).toString(); mLayer->setPath( name ); - mFileLineEdit->setText( name ); + whileBlocking( mSvgSourceLineEdit )->setSource( name ); setGuiForSvg( mLayer ); emit changed(); @@ -2154,55 +2156,13 @@ void QgsSvgMarkerSymbolLayerWidget::setOffset() emit changed(); } -void QgsSvgMarkerSymbolLayerWidget::mFileToolButton_clicked() +void QgsSvgMarkerSymbolLayerWidget::svgSourceChanged( const QString &text ) { - QgsSettings s; - QString file = QFileDialog::getOpenFileName( nullptr, - tr( "Select SVG file" ), - s.value( QStringLiteral( "/UI/lastSVGMarkerDir" ), QDir::homePath() ).toString(), - tr( "SVG files" ) + " (*.svg)" ); - QFileInfo fi( file ); - if ( file.isEmpty() || !fi.exists() ) - { - return; - } - mFileLineEdit->setText( file ); - mLayer->setPath( file ); - s.setValue( QStringLiteral( "/UI/lastSVGMarkerDir" ), fi.absolutePath() ); - setGuiForSvg( mLayer ); - emit changed(); -} - -void QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_textEdited( const QString &text ) -{ - if ( !QFileInfo::exists( text ) ) - { - return; - } mLayer->setPath( text ); setGuiForSvg( mLayer ); emit changed(); } -void QgsSvgMarkerSymbolLayerWidget::mFileLineEdit_editingFinished() -{ - if ( !QFileInfo::exists( mFileLineEdit->text() ) ) - { - QUrl url( mFileLineEdit->text() ); - if ( !url.isValid() ) - { - return; - } - } - - QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); - mLayer->setPath( mFileLineEdit->text() ); - QApplication::restoreOverrideCursor(); - - setGuiForSvg( mLayer ); - emit changed(); -} - void QgsSvgMarkerSymbolLayerWidget::mChangeColorButton_colorChanged( const QColor &color ) { if ( !mLayer ) @@ -2288,10 +2248,8 @@ QgsSVGFillSymbolLayerWidget::QgsSVGFillSymbolLayerWidget( QgsVectorLayer *vl, QW { mLayer = nullptr; setupUi( this ); - connect( mBrowseToolButton, &QToolButton::clicked, this, &QgsSVGFillSymbolLayerWidget::mBrowseToolButton_clicked ); connect( mTextureWidthSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged ); - connect( mSVGLineEdit, &QLineEdit::textEdited, this, &QgsSVGFillSymbolLayerWidget::mSVGLineEdit_textEdited ); - connect( mSVGLineEdit, &QLineEdit::editingFinished, this, &QgsSVGFillSymbolLayerWidget::mSVGLineEdit_editingFinished ); + connect( mSvgSourceLineEdit, &QgsSvgSourceLineEdit::sourceChanged, this, &QgsSVGFillSymbolLayerWidget::svgSourceChanged ); connect( mRotationSpinBox, static_cast < void ( QDoubleSpinBox::* )( double ) > ( &QDoubleSpinBox::valueChanged ), this, &QgsSVGFillSymbolLayerWidget::mRotationSpinBox_valueChanged ); connect( mChangeColorButton, &QgsColorButton::colorChanged, this, &QgsSVGFillSymbolLayerWidget::mChangeColorButton_colorChanged ); connect( mChangeStrokeColorButton, &QgsColorButton::colorChanged, this, &QgsSVGFillSymbolLayerWidget::mChangeStrokeColorButton_colorChanged ); @@ -2335,7 +2293,7 @@ void QgsSVGFillSymbolLayerWidget::setSymbolLayer( QgsSymbolLayer *layer ) mTextureWidthSpinBox->blockSignals( true ); mTextureWidthSpinBox->setValue( width ); mTextureWidthSpinBox->blockSignals( false ); - mSVGLineEdit->setText( mLayer->svgFilePath() ); + whileBlocking( mSvgSourceLineEdit )->setSource( mLayer->svgFilePath() ); mRotationSpinBox->blockSignals( true ); mRotationSpinBox->setValue( mLayer->angle() ); mRotationSpinBox->blockSignals( false ); @@ -2372,16 +2330,6 @@ QgsSymbolLayer *QgsSVGFillSymbolLayerWidget::symbolLayer() return mLayer; } -void QgsSVGFillSymbolLayerWidget::mBrowseToolButton_clicked() -{ - QString filePath = QFileDialog::getOpenFileName( nullptr, tr( "Select SVG Texture File" ), QDir::homePath(), tr( "SVG file" ) + " (*.svg);;" + tr( "All files" ) + " (*.*)" ); - if ( !filePath.isNull() ) - { - mSVGLineEdit->setText( filePath ); - emit changed(); - } -} - void QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged( double d ) { if ( mLayer ) @@ -2391,53 +2339,23 @@ void QgsSVGFillSymbolLayerWidget::mTextureWidthSpinBox_valueChanged( double d ) } } -void QgsSVGFillSymbolLayerWidget::mSVGLineEdit_textEdited( const QString &text ) +void QgsSVGFillSymbolLayerWidget::svgSourceChanged( const QString &text ) { if ( !mLayer ) { return; } - QFileInfo fi( text ); - if ( !fi.exists() ) - { - return; - } mLayer->setSvgFilePath( text ); updateParamGui(); emit changed(); } -void QgsSVGFillSymbolLayerWidget::mSVGLineEdit_editingFinished() -{ - if ( !mLayer ) - { - return; - } - - QFileInfo fi( mSVGLineEdit->text() ); - if ( !fi.exists() ) - { - QUrl url( mSVGLineEdit->text() ); - if ( !url.isValid() ) - { - return; - } - } - - QApplication::setOverrideCursor( QCursor( Qt::WaitCursor ) ); - mLayer->setSvgFilePath( mSVGLineEdit->text() ); - QApplication::restoreOverrideCursor(); - - updateParamGui(); - emit changed(); -} - void QgsSVGFillSymbolLayerWidget::setFile( const QModelIndex &item ) { QString file = item.data( Qt::UserRole ).toString(); mLayer->setSvgFilePath( file ); - mSVGLineEdit->setText( file ); + whileBlocking( mSvgSourceLineEdit )->setSource( file ); updateParamGui(); emit changed(); @@ -2492,7 +2410,7 @@ void QgsSVGFillSymbolLayerWidget::updateParamGui( bool resetValues ) QColor defaultFill, defaultStroke; double defaultStrokeWidth, defaultFillOpacity, defaultStrokeOpacity; bool hasDefaultFillColor, hasDefaultFillOpacity, hasDefaultStrokeColor, hasDefaultStrokeWidth, hasDefaultStrokeOpacity; - QgsApplication::svgCache()->containsParams( mSVGLineEdit->text(), hasFillParam, hasDefaultFillColor, defaultFill, + QgsApplication::svgCache()->containsParams( mSvgSourceLineEdit->source(), hasFillParam, hasDefaultFillColor, defaultFill, hasFillOpacityParam, hasDefaultFillOpacity, defaultFillOpacity, hasStrokeParam, hasDefaultStrokeColor, defaultStroke, hasStrokeWidthParam, hasDefaultStrokeWidth, defaultStrokeWidth, diff --git a/src/gui/symbology/qgssymbollayerwidget.h b/src/gui/symbology/qgssymbollayerwidget.h index e0717c5a9e7..8360c5d3116 100644 --- a/src/gui/symbology/qgssymbollayerwidget.h +++ b/src/gui/symbology/qgssymbollayerwidget.h @@ -546,9 +546,7 @@ class GUI_EXPORT QgsSvgMarkerSymbolLayerWidget : public QgsSymbolLayerWidget, pr private slots: void setName( const QModelIndex &idx ); void populateIcons( const QModelIndex &idx ); - void mFileToolButton_clicked(); - void mFileLineEdit_textEdited( const QString &text ); - void mFileLineEdit_editingFinished(); + void svgSourceChanged( const QString &text ); void mChangeColorButton_colorChanged( const QColor &color ); void mChangeStrokeColorButton_colorChanged( const QColor &color ); void mStrokeWidthSpinBox_valueChanged( double d ); @@ -668,10 +666,8 @@ class GUI_EXPORT QgsSVGFillSymbolLayerWidget : public QgsSymbolLayerWidget, priv void updateParamGui( bool resetValues = true ); private slots: - void mBrowseToolButton_clicked(); void mTextureWidthSpinBox_valueChanged( double d ); - void mSVGLineEdit_textEdited( const QString &text ); - void mSVGLineEdit_editingFinished(); + void svgSourceChanged( const QString &text ); void setFile( const QModelIndex &item ); void populateIcons( const QModelIndex &item ); void mRotationSpinBox_valueChanged( double d ); diff --git a/src/ui/symbollayer/widget_svgfill.ui b/src/ui/symbollayer/widget_svgfill.ui index e0f2ed34abe..7fbf83850c2 100644 --- a/src/ui/symbollayer/widget_svgfill.ui +++ b/src/ui/symbollayer/widget_svgfill.ui @@ -7,27 +7,104 @@ 0 0 328 - 497 + 509 Form - - + + + + + + + + + - + + + + 1 + 0 + + + + 6 + + + 999999999.999999046325684 + + + 0.200000000000000 + + + false + + - - - + + + Qt::StrongFocus + + + + + + + + + + + true + + + ° + + + -360.000000000000000 + + + 360.000000000000000 + + + 0.500000000000000 + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 16777215 + 16777215 + + + + + + + @@ -41,7 +118,52 @@ - + + + + + 0 + 0 + + + + Fill color + + + + + + + + 0 + 0 + + + + + 120 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + + + Rotation + + + + @@ -84,40 +206,22 @@ - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - + + - - + + + + Texture width + + + + + @@ -194,135 +298,24 @@ - - + + - - + + - - - - - 0 - 0 - - - - - 120 - 0 - - - - - 16777215 - 16777215 - - - - - - - - - - - - 0 - 0 - - - - Fill color - - - - - - - Rotation - - - - - - - true - - - ° - - - -360.000000000000000 - - - 360.000000000000000 - - - 0.500000000000000 - - - - - - - - - - - - - - Texture width - - - - - - - - - - 1 - 0 - - - - 6 - - - 999999999.999999046325684 - - - 0.200000000000000 - - - false - - - - - - - Qt::StrongFocus - - - - - - - - - + + + + Qt::StrongFocus @@ -330,15 +323,21 @@ - QgsPropertyOverrideButton - QToolButton -
qgspropertyoverridebutton.h
+ QgsColorButton + QPushButton +
qgscolorbutton.h
+ 1
QgsDoubleSpinBox QDoubleSpinBox
qgsdoublespinbox.h
+ + QgsPropertyOverrideButton + QToolButton +
qgspropertyoverridebutton.h
+
QgsUnitSelectionWidget QWidget @@ -346,9 +345,9 @@ 1 - QgsColorButton - QToolButton -
qgscolorbutton.h
+ QgsSvgSourceLineEdit + QWidget +
qgssvgsourcelineedit.h
1
@@ -367,8 +366,7 @@ mRotationDDBtn mSvgTreeView mSvgListView - mSVGLineEdit - mBrowseToolButton + mSvgSourceLineEdit mSVGDDBtn diff --git a/src/ui/symbollayer/widget_svgmarker.ui b/src/ui/symbollayer/widget_svgmarker.ui index 7f77f646e55..69f520ce55c 100644 --- a/src/ui/symbollayer/widget_svgmarker.ui +++ b/src/ui/symbollayer/widget_svgmarker.ui @@ -13,191 +13,17 @@ Form - - - - - - - - - - - - - - - - - - - - - - - Rotation + + + + + + 0 + 0 + - - - - - Offset - - - - - - - Stroke width - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Width - - - - - - - - 0 - 0 - - - - 6 - - - 100000.000000000000000 - - - 0.200000000000000 - - - 1.000000000000000 - - - false - - - - - - - Height - - - - - - - - 0 - 0 - - - - 6 - - - 100000.000000000000000 - - - 0.200000000000000 - - - 1.000000000000000 - - - false - - - - - - - 2 - - - 2 - - - 0 - - - 2 - - - - - - 0 - 0 - - - - Lock aspect ratio - - - 13 - - - - - - - - - - 0 - 0 - - - - Qt::StrongFocus - - - - - - - - - - - - - - - - + Fill color @@ -282,90 +108,41 @@
- - - - + + + + true + + + ° + + + 2 + + + -360.000000000000000 + + + 360.000000000000000 + + + 0.500000000000000 - - - - - - - - - - - - 0 - 0 - - - - Stroke color - - - - - - - - 0 - 0 - - - - - - - - Size - - - - - + + - - - - 1 - 0 - - - - No stroke - - - 6 - - - 999999999.999999046325684 - - - 0.200000000000000 - - - 0.200000000000000 - - - false + + + - - - - 0 - 0 - - - - Qt::StrongFocus + + + @@ -396,67 +173,79 @@ - - - - true - - - ° - - - 2 - - - -360.000000000000000 - - - 360.000000000000000 - - - 0.500000000000000 - - - - - - - - Left - - - - - HCenter - - - - - Right - - - - - - - - - 0 - 0 - - - - Fill color - - - - + + + + + + + + + + + + Offset + + + + + + + + + + + + + + + + + 1 + 0 + + + + No stroke + + + 6 + + + 999999999.999999046325684 + + + 0.200000000000000 + + + 0.200000000000000 + + + false + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + @@ -476,7 +265,14 @@ - + + + + Rotation + + + + Qt::Horizontal @@ -567,13 +363,43 @@
- - + + + + + + Size + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Stroke color + + + @@ -581,13 +407,180 @@ - + + + + + + + Width + + + + + + + + 0 + 0 + + + + 6 + + + 100000.000000000000000 + + + 0.200000000000000 + + + 1.000000000000000 + + + false + + + + + + + Height + + + + + + + + 0 + 0 + + + + 6 + + + 100000.000000000000000 + + + 0.200000000000000 + + + 1.000000000000000 + + + false + + + + + + + 2 + + + 2 + + + 0 + + + 2 + + + + + + 0 + 0 + + + + Lock aspect ratio + + + 13 + + + + + + + + + + 0 + 0 + + + + Qt::StrongFocus + + + + + + + + + + + + + + + + Stroke width + + + + + + + + + + + + + + + Left + + + + + HCenter + + + + + Right + + + + + + + + + + + + + + + Qt::StrongFocus + + +
@@ -613,11 +606,21 @@
qgsunitselectionwidget.h
1 + + QgsRatioLockButton + QWidget +
qgsratiolockbutton.h
+
+ + QgsSvgSourceLineEdit + QWidget +
qgssvgsourcelineedit.h
+ 1 +
spinWidth spinHeight - mLockAspectRatio mSizeUnitWidget mWidthDDBtn mHeightDDBtn @@ -640,8 +643,7 @@ mHorizontalAnchorDDBtn viewGroups viewImages - mFileLineEdit - mFileToolButton + mSvgSourceLineEdit mFilenameDDBtn diff --git a/src/ui/symbollayer/widget_svgselector.ui b/src/ui/symbollayer/widget_svgselector.ui index 405d7504620..65b487d7116 100644 --- a/src/ui/symbollayer/widget_svgselector.ui +++ b/src/ui/symbollayer/widget_svgselector.ui @@ -26,20 +26,6 @@ 0 - - - - SVG Images - - - - - - - SVG Groups - - - @@ -56,6 +42,13 @@ + + + + SVG Images + + + @@ -102,33 +95,33 @@ + + + + SVG Groups + + + - - - - - - - - - 50 - 16777215 - - - - - - - - + + + Qt::StrongFocus + +
+ + + QgsSvgSourceLineEdit + QWidget +
qgssvgsourcelineedit.h
+ 1 +
+
mGroupsTreeView mImagesListView - mFileLineEdit - mFilePushButton diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 6aad1180851..9fb813c3a95 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -183,6 +183,7 @@ ADD_PYTHON_TEST(PyQgsArrowSymbolLayer test_qgsarrowsymbollayer.py) ADD_PYTHON_TEST(PyQgsSymbolExpressionVariables test_qgssymbolexpressionvariables.py) ADD_PYTHON_TEST(PyQgsSyntacticSugar test_syntactic_sugar.py) ADD_PYTHON_TEST(PyQgsStringUtils test_qgsstringutils.py) +ADD_PYTHON_TEST(PyQgsSvgSourceLineEdit test_qgssvgsourcelineedit.py) ADD_PYTHON_TEST(PyQgsSymbol test_qgssymbol.py) ADD_PYTHON_TEST(PyQgsSymbolLayerUtils test_qgssymbollayerutils.py) ADD_PYTHON_TEST(PyQgsTaskManager test_qgstaskmanager.py) diff --git a/tests/src/python/test_qgssvgsourcelineedit.py b/tests/src/python/test_qgssvgsourcelineedit.py new file mode 100644 index 00000000000..5b3cd78b608 --- /dev/null +++ b/tests/src/python/test_qgssvgsourcelineedit.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsSvgSourceLineEdit + +.. note:: 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. +""" +__author__ = 'Nyall Dawson' +__date__ = '19/07/2018' +__copyright__ = 'Copyright 2018, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.gui import QgsSvgSourceLineEdit + +from qgis.PyQt.QtTest import QSignalSpy +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsSvgSourceLineEdit(unittest.TestCase): + + def testGettersSetters(self): + """ test widget getters/setters """ + w = QgsSvgSourceLineEdit() + spy = QSignalSpy(w.sourceChanged) + + w.setSource('source') + self.assertEqual(w.source(), 'source') + self.assertEqual(len(spy), 1) + self.assertEqual(spy[0][0], 'source') + + # no signal for same value + w.setSource('source') + self.assertEqual(w.source(), 'source') + self.assertEqual(len(spy), 1) + + w.setSource('another') + self.assertEqual(w.source(), 'another') + self.assertEqual(len(spy), 2) + self.assertEqual(spy[1][0], 'another') + + +if __name__ == '__main__': + unittest.main()