QGIS API Documentation  3.27.0-Master (bef583a8ef)
qgsexternalstoragefilewidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsexternalstoragefilewidget.cpp
3  --------------------------------------
4  Date : August 2021
5  Copyright : (C) 2021 by Julien Cabieces
6  Email : julien dot cabieces at oslandia 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 
17 
18 #include <QLineEdit>
19 #include <QToolButton>
20 #include <QLabel>
21 #include <QGridLayout>
22 #include <QUrl>
23 #include <QDropEvent>
24 #include <QRegularExpression>
25 #include <QProgressBar>
26 
27 #include "qgssettings.h"
28 #include "qgsfilterlineedit.h"
29 #include "qgsfocuskeeper.h"
30 #include "qgslogger.h"
31 #include "qgsproject.h"
32 #include "qgsapplication.h"
33 #include "qgsfileutils.h"
34 #include "qgsmimedatautils.h"
35 #include "qgsexternalstorage.h"
37 #include "qgsmessagebar.h"
38 
39 #define FILEPATH_VARIABLE "selected_file_path"
40 
42  : QgsFileWidget( parent )
43 {
44  mProgressLabel = new QLabel( this );
45  mLayout->addWidget( mProgressLabel );
46  mProgressLabel->hide();
47 
48  mProgressBar = new QProgressBar( this );
49  mLayout->addWidget( mProgressBar );
50  mProgressBar->hide();
51 
52  mCancelButton = new QToolButton( this );
53  mLayout->addWidget( mCancelButton );
54  mCancelButton->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mTaskCancel.svg" ) ) );
55  mCancelButton->hide();
56 
57  updateAcceptDrops();
58 }
59 
60 void QgsExternalStorageFileWidget::setStorageType( const QString &storageType )
61 {
62  if ( storageType.isEmpty() )
63  mExternalStorage = nullptr;
64 
65  else
66  {
68  if ( !mExternalStorage )
69  {
70  QgsDebugMsg( QStringLiteral( "Invalid storage type: %1" ).arg( storageType ) );
71  }
72  else
73  {
75  }
76  }
77 
78  updateAcceptDrops();
79 }
80 
82 {
83  QgsFileWidget::setReadOnly( readOnly );
84  updateAcceptDrops();
85 }
86 
87 void QgsExternalStorageFileWidget::updateAcceptDrops()
88 {
89  setAcceptDrops( !mReadOnly && mExternalStorage );
90 }
91 
93 {
94  return mExternalStorage ? mExternalStorage->type() : QString();
95 }
96 
98 {
99  return mExternalStorage;
100 }
101 
103 {
104  mAuthCfg = authCfg;
105 }
106 
108 {
109  return mAuthCfg;
110 }
111 
112 void QgsExternalStorageFileWidget::setStorageUrlExpression( const QString &urlExpression )
113 {
114  mStorageUrlExpression.reset( new QgsExpression( urlExpression ) );
115 }
116 
118 {
119  return mStorageUrlExpression.get();
120 }
121 
123 {
124  return mStorageUrlExpression ? mStorageUrlExpression->expression() : QString();
125 }
126 
127 
129 {
130  mScope = nullptr; // deleted by old context when we override it with the new one
131  mExpressionContext = context;
133 }
134 
136 {
137  if ( !mExternalStorage || mScope )
138  return;
139 
140  mScope = createFileWidgetScope();
141  mExpressionContext << mScope;
142 }
143 
145 {
146  QgsExpressionContextScope *scope = new QgsExpressionContextScope( QObject::tr( "FileWidget" ) );
148  QStringLiteral( FILEPATH_VARIABLE ),
149  QString(), true, false, tr( "User selected absolute filepath" ) ) );
150  return scope;
151 }
152 
154 {
155  return mExpressionContext;
156 }
157 
158 
160 {
161  mMessageBar = messageBar;
162 }
163 
165 {
166  return mMessageBar;
167 }
168 
170 {
171  mProgressLabel->setVisible( mStoreInProgress );
172  mProgressBar->setVisible( mStoreInProgress );
173  mCancelButton->setVisible( mStoreInProgress );
174 
175  const bool linkVisible = mUseLink && !mIsLinkEdited;
176 
177  mLineEdit->setVisible( !mStoreInProgress && !linkVisible );
178  mLinkLabel->setVisible( !mStoreInProgress && linkVisible );
179  mLinkEditButton->setVisible( !mStoreInProgress && mUseLink && !mReadOnly );
180 
181  mFileWidgetButton->setVisible( mButtonVisible && !mStoreInProgress );
182  mFileWidgetButton->setEnabled( !mReadOnly );
183  mLineEdit->setEnabled( !mReadOnly );
184 
185  mLinkEditButton->setIcon( linkVisible && !mReadOnly ?
186  QgsApplication::getThemeIcon( QStringLiteral( "/mActionToggleEditing.svg" ) ) :
187  QgsApplication::getThemeIcon( QStringLiteral( "/mActionSaveEdits.svg" ) ) );
188 }
189 
191 {
192  Q_ASSERT( fileNames.count() );
193 
194  // store files first, update filePath later
195  if ( mExternalStorage )
196  {
197  if ( !mStorageUrlExpression->prepare( &mExpressionContext ) )
198  {
199  if ( messageBar() )
200  {
201  messageBar()->pushWarning( tr( "Storing External resource" ),
202  tr( "Storage URL expression is invalid : %1" ).arg( mStorageUrlExpression->evalErrorString() ) );
203  }
204 
205  QgsDebugMsg( tr( "Storage URL expression is invalid : %1" ).arg( mStorageUrlExpression->evalErrorString() ) );
206  return;
207  }
208 
209  storeExternalFiles( fileNames );
210  }
211  else
212  {
214  }
215 }
216 
217 void QgsExternalStorageFileWidget::storeExternalFiles( QStringList fileNames, QStringList storedUrls )
218 {
219  if ( fileNames.isEmpty() )
220  return;
221 
222  const QString filePath = fileNames.takeFirst();
223 
224  mProgressLabel->setText( tr( "Storing file %1 ..." ).arg( QFileInfo( filePath ).fileName() ) );
225  mStoreInProgress = true;
226  mProgressBar->setValue( 0 );
227  updateLayout();
228 
229  Q_ASSERT( mScope );
230  mScope->setVariable( QStringLiteral( FILEPATH_VARIABLE ), filePath );
231 
232  QVariant url = mStorageUrlExpression->evaluate( &mExpressionContext );
233  if ( !url.isValid() )
234  {
235  if ( messageBar() )
236  {
237  messageBar()->pushWarning( tr( "Storing External resource" ),
238  tr( "Storage URL expression is invalid : %1" ).arg( mStorageUrlExpression->evalErrorString() ) );
239  }
240 
241  mStoreInProgress = false;
242  updateLayout();
243 
244  return;
245  }
246 
247  QgsExternalStorageStoredContent *storedContent = mExternalStorage->store( filePath, url.toString(), mAuthCfg );
248 
249  connect( storedContent, &QgsExternalStorageStoredContent::progressChanged, mProgressBar, &QProgressBar::setValue );
250  connect( mCancelButton, &QToolButton::clicked, storedContent, &QgsExternalStorageStoredContent::cancel );
251 
252  auto onStoreFinished = [ = ]
253  {
254  mStoreInProgress = false;
255  updateLayout();
256  storedContent->deleteLater();
257 
258  if ( storedContent->status() == Qgis::ContentStatus::Failed && messageBar() )
259  {
260  messageBar()->pushWarning( tr( "Storing External resource" ),
261  tr( "Storing file '%1' to url '%2' has failed : %3" ).arg( filePath, url.toString(), storedContent->errorString() ) );
262  }
263 
264  if ( storedContent->status() != Qgis::ContentStatus::Finished )
265  return;
266 
267  QStringList newStoredUrls = storedUrls;
268  newStoredUrls << storedContent->url();
269 
270  // every thing has been stored, we update filepath
271  if ( fileNames.isEmpty() )
272  {
273  setFilePaths( newStoredUrls );
274  }
275  else
276  storeExternalFiles( fileNames, newStoredUrls );
277  };
278 
279  connect( storedContent, &QgsExternalStorageStoredContent::stored, onStoreFinished );
280  connect( storedContent, &QgsExternalStorageStoredContent::canceled, onStoreFinished );
281  connect( storedContent, &QgsExternalStorageStoredContent::errorOccurred, onStoreFinished );
282 
283  storedContent->store();
284 }
285 
286 void QgsExternalStorageFileWidget::dragEnterEvent( QDragEnterEvent *event )
287 {
288  const QStringList filePaths = mLineEdit->acceptableFilePaths( event );
289  if ( !filePaths.isEmpty() )
290  {
291  event->acceptProposedAction();
292  }
293  else
294  {
295  event->ignore();
296  }
297 }
298 
300 {
301  storeExternalFiles( mLineEdit->acceptableFilePaths( event ) );
302  event->acceptProposedAction();
303 }
@ Finished
Content fetching/storing is finished and successful.
@ Failed
Content fetching/storing has failed.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
static QgsExternalStorageRegistry * externalStorageRegistry()
Returns registry of available external storage implementations.
Single scope for storing variables and functions for use within a QgsExpressionContext.
void addVariable(const QgsExpressionContextScope::StaticVariable &variable)
Adds a variable into the context scope.
void setVariable(const QString &name, const QVariant &value, bool isStatic=false)
Convenience method for setting a variable in the context scope by name name and value.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for parsing and evaluation of expressions (formerly called "search strings").
void canceled()
The signal is emitted when content fetching/storing has been canceled.
void progressChanged(double progress)
The signal is emitted whenever content fetching/storing estimated progression value progress has chan...
void errorOccurred(const QString &errorString)
The signal is emitted when an error occurred.
Qgis::ContentStatus status() const
Returns content status.
virtual void cancel()
Cancels content fetching/storing.
const QString & errorString() const
Returns error textual description if an error occurred and status() returns Failed.
void updateLayout() override
Update buttons visibility.
void setSelectedFileNames(QStringList fileNames) override
Called whenever user select fileNames from dialog.
void setMessageBar(QgsMessageBar *messageBar)
Set messageBar to report messages.
const QString & storageAuthConfigId() const
Returns the authentication configuration ID used for the current external storage (if defined)
void setStorageType(const QString &storageType)
Set storageType storage type unique identifier as defined in QgsExternalStorageRegistry or null QStri...
void setExpressionContext(const QgsExpressionContext &context)
Set expression context to be used when for storage URL expression evaluation.
QString storageUrlExpressionString() const
Returns the original, unmodified expression string, which once evaluated, provide the URL used to sto...
void setStorageUrlExpression(const QString &urlExpression)
Set urlExpression expression, which once evaluated, provide the URL used to store selected documents.
static QgsExpressionContextScope * createFileWidgetScope()
Creates and Returns an expression context scope specific to QgsExternalStorageFileWidget It defines t...
QgsExternalStorageFileWidget(QWidget *parent=nullptr)
QgsExternalStorageFileWidget creates a widget for selecting a file or a folder.
void setReadOnly(bool readOnly) override
Sets whether the widget should be read only.
QgsExternalStorage * externalStorage() const
Returns external storage used to store selected file names, nullptr if none have been defined.
void setStorageAuthConfigId(const QString &authCfg)
Sets the authentication configuration ID to be used for the current external storage (if defined)
QgsMessageBar * messageBar() const
Returns message bar used to report messages.
void addFileWidgetScope()
Add file widget specific scope to expression context.
void dragEnterEvent(QDragEnterEvent *event) override
const QgsExpressionContext & expressionContext() const
Returns expression context used for storage url expression evaluation.
void dropEvent(QDropEvent *event) override
QgsExternalStorage * externalStorageFromType(const QString &type) const
Returns external storage implementation if the storage type matches one.
Class for QgsExternalStorage stored content.
void stored()
The signal is emitted when the resource has successfully been stored.
virtual QString url() const =0
Returns stored resource URL.
virtual void store()=0
Starts storing.
Abstract interface for external storage - to be implemented by various backends and registered in Qgs...
virtual QString type() const =0
Unique identifier of the external storage type.
QgsExternalStorageStoredContent * store(const QString &filePath, const QString &url, const QString &authCfg=QString(), Qgis::ActionStart storingMode=Qgis::ActionStart::Deferred) const
Stores file filePath to the url for this project external storage.
The QgsFileWidget class creates a widget for selecting a file or a folder.
Definition: qgsfilewidget.h:39
QString filePath()
Returns the current file path(s).
void setFilePaths(const QStringList &filePaths)
Update filePath according to filePaths list.
QHBoxLayout * mLayout
virtual void setSelectedFileNames(QStringList fileNames)
Called whenever user select fileNames from dialog.
QgsFileDropEdit * mLineEdit
QToolButton * mLinkEditButton
QToolButton * mFileWidgetButton
QLabel * mLinkLabel
virtual void setReadOnly(bool readOnly)
Sets whether the widget should be read only.
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:61
void pushWarning(const QString &title, const QString &message)
Pushes a warning message that must be manually dismissed by the user.
#define FILEPATH_VARIABLE
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
Single variable definition for use within a QgsExpressionContextScope.