Skip to content

Commit

Permalink
[QgsQuick] - externalResource widget handler (#9232)
Browse files Browse the repository at this point in the history
* [QgsQuick] - extended externalResource widget

Added removeFile function and modified fileName function - former has been missing and photoPanel is using it. The latter needed modification due to a new option to choose image from a gallery.
Added externalResource handler for externalResource widget which enables following features:
* option to choose an image from a gallery - selected image is copied to projects folder, if it doesnt exists there. Added "ic_gallery" icon.
* ability to remove value for externalResource field. Optionally removes referenced image as well ("Ok" option in dialog)
* ability to interact with image preview onClick - the main idea is to have ability to enlarge preview image. Currently its possible only in edit state of the form since the whole field is disabled otherwise.

Fixed resizing of icon/previewImage and component itself as well.

* [QgsQuick] - extended externalResource widget
Commit contains following fixes/changes/additions after review:
* Added QgsQuickUtils::getRelativePath which replaced QgsQuickUtils::getFileName + related changes in photoPanel
* Added test for new QgsQuickUtils functionality
* fixed weird or redundant size definitions in externalResource widget
* Some changes in docs.

* [QgsQuick] Changed "default" case result for QgsQuickUtils::getRelativePath

* [QgsQuick] Fixed test after changed functionality in QgsQuickUtils
  • Loading branch information
sklencar authored and wonder-sk committed Feb 25, 2019
1 parent d49dc89 commit 77f500b
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 13 deletions.
1 change: 1 addition & 0 deletions src/quickgui/images/ic_gallery.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/quickgui/images/images.qrc
Expand Up @@ -10,5 +10,6 @@
<file>ic_photo_notavailable_white.svg</file>
<file>ic_save_white.svg</file>
<file>ic_camera.svg</file>
<file>ic_gallery.svg</file>
</qresource>
</RCC>
74 changes: 72 additions & 2 deletions src/quickgui/plugin/editor/qgsquickexternalresource.qml
Expand Up @@ -31,13 +31,13 @@ Item {
property var notavailableImageSource: QgsQuick.Utils.getThemeIcon("ic_photo_notavailable_white")

id: fieldItem
height: image.hasValidSource? customStyle.height * 3 : customStyle.height
anchors {
left: parent.left
right: parent.right
rightMargin: 10 * QgsQuick.Utils.dp
}

height: Math.max(image.height, button.height)
QgsQuick.PhotoCapture {
id: photoCapturePanel
visible: false
Expand All @@ -52,11 +52,21 @@ Item {
property bool hasValidSource: false

id: image
height: hasValidSource? customStyle.height * 3 : customStyle.height
height: fieldItem.height
sourceSize.height: height
autoTransform: true
fillMode: Image.PreserveAspectFit
visible: hasValidSource

MouseArea {
anchors.fill: parent
onClicked: externalResourceHandler.previewImage(homePath + "/" + image.currentValue)
}

onCurrentValueChanged: {
image.source = image.getSource()
}

onSourceChanged: {
hasValidSource = (image.source === fieldItem.brokenImageSource ||
image.source === fieldItem.notavailableImageSource) ? false : true
Expand All @@ -81,6 +91,66 @@ Item {
visible: !image.hasValidSource
}

Button {
id: deleteButton
visible: fieldItem.enabled && image.hasValidSource
width: customStyle.height
height: width
padding: 0

anchors.right: imageBrowserButton.left
anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter

onClicked: externalResourceHandler.removeImage(fieldItem, homePath + "/" + image.currentValue)

background: Image {
id: deleteIcon
source: QgsQuick.Utils.getThemeIcon("ic_delete_forever_white")
width: deleteButton.width
height: deleteButton.height
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
}

ColorOverlay {
anchors.fill: deleteIcon
source: deleteIcon
color: customStyle.fontColor
}
}

Button {
id: imageBrowserButton
visible: fieldItem.enabled
width: customStyle.height
height: width
padding: 0

anchors.right: button.left
anchors.bottom: parent.bottom
anchors.verticalCenter: parent.verticalCenter

onClicked:externalResourceHandler.chooseImage(fieldItem)

background: Image {
id: browseIcon
source: QgsQuick.Utils.getThemeIcon("ic_gallery")
width: imageBrowserButton.width
height: imageBrowserButton.height
sourceSize.width: width
sourceSize.height: height
fillMode: Image.PreserveAspectFit
}

ColorOverlay {
anchors.fill: browseIcon
source: browseIcon
color: customStyle.fontColor
}
}

Button {
id: button
visible: fieldItem.enabled
Expand Down
29 changes: 29 additions & 0 deletions src/quickgui/plugin/qgsquickfeatureform.qml
Expand Up @@ -36,6 +36,34 @@ Item {
*/
signal canceled

/**
* A handler for extra events in externalSourceWidget.
*/
property var externalResourceHandler: QtObject {

/**
* Called when clicked on the gallery icon to choose a file in a gallery.
* \param itemWidget editorWidget for modified field to send valueChanged signal.
*/
property var chooseImage: function chooseImage(itemWidget) {
}

/**
* Called when clicked on the photo image. Suppose to be used to bring a bigger preview.
* \param imagePath Absolute path to the image.
*/
property var previewImage: function previewImage(imagePath) {
}

/**
* Called when clicked on the trash icon. Suppose to delete the value and optionally also the image.
* \param itemWidget editorWidget for modified field to send valueChanged signal.
* \param imagePath Absolute path to the image.
*/
property var removeImage: function removeImage(itemWidget, imagePath) {
}
}

/**
* AttributeFormModel binded on a feature supporting auto-generated editor layouts and "tab" layout.
*/
Expand Down Expand Up @@ -320,6 +348,7 @@ Item {
property var constraintValid: ConstraintValid
property var homePath: form.project ? form.project.homePath : ""
property var customStyle: form.style.fields
property var externalResourceHandler: form.externalResourceHandler

active: widget !== 'Hidden'

Expand Down
6 changes: 3 additions & 3 deletions src/quickgui/plugin/qgsquickphotopanel.qml
Expand Up @@ -73,7 +73,7 @@ Drawer {
Component.onDestruction: {
if (!captureItem && camera.imageCapture.capturedImagePath != ""){
captureItem.saveImage = false
QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath)
QgsQuick.Utils.removeFile(camera.imageCapture.capturedImagePath)
}
captureItem.saveImage = false
}
Expand Down Expand Up @@ -164,7 +164,7 @@ Drawer {
captureItem.saveImage = false
photoPreview.visible = false
if (camera.imageCapture.capturedImagePath != "") {
QgsQuick.Utils.remove(camera.imageCapture.capturedImagePath)
QgsQuick.Utils.removeFile(camera.imageCapture.capturedImagePath)
}
}
}
Expand Down Expand Up @@ -200,7 +200,7 @@ Drawer {
onClicked: {
captureItem.saveImage = true
photoPanel.visible = false
photoPanel.lastPhotoName = QgsQuick.Utils.getFileName(camera.imageCapture.capturedImagePath)
photoPanel.lastPhotoName = QgsQuick.Utils.getRelativePath(camera.imageCapture.capturedImagePath, photoPanel.targetDir)
if (photoPanel.lastPhotoName !== "") {
fieldItem.image.source = photoPanel.targetDir + "/" + photoPanel.lastPhotoName
fieldItem.valueChanged(photoPanel.lastPhotoName, photoPanel.lastPhotoName === "" || photoPanel.lastPhotoName === null)
Expand Down
25 changes: 21 additions & 4 deletions src/quickgui/qgsquickutils.cpp
Expand Up @@ -95,11 +95,22 @@ bool QgsQuickUtils::fileExists( const QString &path )
return ( check_file.exists() && check_file.isFile() );
}

QString QgsQuickUtils::getFileName( const QString &path )
QString QgsQuickUtils::getRelativePath( const QString &path, const QString &prefixPath )
{
QFileInfo fileInfo( path );
QString filename( fileInfo.fileName() );
return filename;
QString resultPath = path;
QString prefixPathWithSlash;
if ( !prefixPath.endsWith( "/" ) )
prefixPathWithSlash = QStringLiteral( "%1/" ).arg( prefixPath );
else
prefixPathWithSlash = prefixPath;

if ( resultPath.startsWith( prefixPathWithSlash ) )
return resultPath.replace( prefixPathWithSlash, QString() );
QString filePrefixPath = QStringLiteral( "file://%1" ).arg( prefixPathWithSlash );
if ( resultPath.startsWith( filePrefixPath ) )
return resultPath.replace( filePrefixPath, QString() );

return QString();
}

void QgsQuickUtils::logMessage( const QString &message, const QString &tag, Qgis::MessageLevel level )
Expand Down Expand Up @@ -162,6 +173,12 @@ QString QgsQuickUtils::formatDistance( double distance,
.arg( QgsUnitTypes::toAbbreviatedString( destUnits ) );
}

bool QgsQuickUtils::removeFile( const QString &filePath )
{
QFile file( filePath );
return file.remove( filePath );
}


void QgsQuickUtils::humanReadableDistance( double srcDistance, QgsUnitTypes::DistanceUnit srcUnits,
QgsUnitTypes::SystemOfMeasurement destSystem,
Expand Down
19 changes: 16 additions & 3 deletions src/quickgui/qgsquickutils.h
Expand Up @@ -124,10 +124,13 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
Q_INVOKABLE static bool fileExists( const QString &path );

/**
* Extracts filename from path
* \since QGIS 3.4
* Returns relative path of the file to given prefixPath. If prefixPath does not match a path parameter,
* returns an empty string. If a path starts with "file://", this prefix is ignored.
* \param path Absolute path to file
* \param prefixPath
* \since QGIS 3.8
*/
Q_INVOKABLE static QString getFileName( const QString &path );
Q_INVOKABLE static QString getRelativePath( const QString &path, const QString &prefixPath );

/**
* Log message in QgsMessageLog
Expand Down Expand Up @@ -194,6 +197,16 @@ class QUICK_EXPORT QgsQuickUtils: public QObject
int decimals,
QgsUnitTypes::SystemOfMeasurement destSystem = QgsUnitTypes::MetricSystem );

/**
* Deletes file from a given path.
*
* \param filePath Absolute path to file
* \returns bool True, if removal was successful, otherwise false.
*
* \since QGIS 3.8
*/
Q_INVOKABLE static bool removeFile( const QString &filePath );

/**
* Converts distance to human readable distance in destination system of measurement
*
Expand Down
26 changes: 25 additions & 1 deletion tests/src/quickgui/testqgsquickutils.cpp
Expand Up @@ -43,6 +43,7 @@ class TestQgsQuickUtils: public QObject
void loadIcon();
void fileExists();
void loadQmlComponent();
void getRelativePath();

private:
QgsQuickUtils utils;
Expand Down Expand Up @@ -144,7 +145,8 @@ void TestQgsQuickUtils::loadIcon()
QUrl url = utils.getThemeIcon( "ic_save_white" );
Q_ASSERT( url.toString() == QStringLiteral( "qrc:/ic_save_white.svg" ) );

QString fileName = utils.getFileName( url.toString() );
QFileInfo fileInfo( url.toString() );
QString fileName( fileInfo.fileName() );
Q_ASSERT( fileName == QStringLiteral( "ic_save_white.svg" ) );
}

Expand All @@ -164,5 +166,27 @@ void TestQgsQuickUtils::loadQmlComponent()
Q_ASSERT( valuemap.path() == QString( "qgsquickvaluemap.qml" ) );
}

void TestQgsQuickUtils::getRelativePath()
{
QString prefixPath = QStringLiteral( "%1/" ).arg( TEST_DATA_DIR );
QString fileName = QStringLiteral( "quickapp_project.qgs" );
QString path = prefixPath + fileName;
QString relativePath = utils.getRelativePath( path, prefixPath );
QCOMPARE( fileName, relativePath );

QString fileName2 = QStringLiteral( "zip/test.zip" );
QString path2 = prefixPath + fileName2;
QString relativePath2 = utils.getRelativePath( path2, prefixPath );
QCOMPARE( fileName2, relativePath2 );

QString path3 = QStringLiteral( "file://" ) + path2;
QString relativePath3 = utils.getRelativePath( path3, prefixPath );
QCOMPARE( fileName2, relativePath3 );

QString relativePath4 = utils.getRelativePath( path2, QStringLiteral( "/dummy/path/" ) );
QCOMPARE( QString(), relativePath4 );
}


QGSTEST_MAIN( TestQgsQuickUtils )
#include "testqgsquickutils.moc"

0 comments on commit 77f500b

Please sign in to comment.