Skip to content
Permalink
Browse files

Merge pull request #6505 from 3nids/form_url

[FEATURE] Allow to use a URL for a custom attribute form (UI file)
  • Loading branch information
3nids committed May 9, 2018
2 parents d99a27f + 5d7a6c1 commit 4c92de9e98fe2ae263fd29ecf24686b773179a19
@@ -111,15 +111,17 @@ Set the active layout style for the attribute editor for this layer

QString uiForm() const;
%Docstring
Get path to the .ui form. Only meaningful with EditorLayout.UiFileLayout.
Get path or URL to the .ui form. Only meaningful with EditorLayout.UiFileLayout
%End

void setUiForm( const QString &ui );
%Docstring
Set path to the .ui form.
When a string is provided, the layout style will be set to EditorLayout.UiFileLayout,
When a string is provided in ``ui``, the layout style will be set to EditorLayout.UiFileLayout,
if an empty or a null string is provided, the layout style will be set to
EditorLayout.GeneratedLayout.
If ``ui`` is a URL, a local copy of the file will be made and will be used to create the forms
``context`` is provided to save error messages
%End

bool setWidgetConfig( const QString &widgetName, const QVariantMap &config );
@@ -35,7 +35,7 @@ FetchedContent holds useful information about a network content being fetched
Failed
};

explicit QgsFetchedContent( QTemporaryFile *file = 0, ContentStatus status = NotStarted );
explicit QgsFetchedContent( const QString &url, QTemporaryFile *file = 0, ContentStatus status = NotStarted );
%Docstring
Constructs a FetchedContent with pointer to the downloaded file and status of the download
%End
@@ -76,21 +76,6 @@ Return the potential error of the download
void fetched();
%Docstring
Emitted when the file is fetched and accessible
%End

void downloadStarted( const bool redownload );
%Docstring
Emitted when the download actually starts
%End

void cancelTriggered();
%Docstring
Emitted when download is canceled.
%End

void taskCompleted();
%Docstring
Emitted when the download is finished (although file not accessible yet)
%End

};
@@ -125,7 +110,7 @@ Create the registry for temporary downloaded files

~QgsNetworkContentFetcherRegistry();

const QgsFetchedContent *fetch( const QUrl &url, const FetchingMode fetchingMode = DownloadLater );
const QgsFetchedContent *fetch( const QString &url, const FetchingMode fetchingMode = DownloadLater );
%Docstring
Initialize a download for the given URL

@@ -17,6 +17,7 @@
#include "qgsattributetypedialog.h"
#include "qgsattributerelationedit.h"
#include "qgsattributesforminitcode.h"
#include "qgisapp.h"

QgsAttributesFormProperties::QgsAttributesFormProperties( QgsVectorLayer *layer, QWidget *parent )
: QWidget( parent )
@@ -14,6 +14,7 @@
***************************************************************************/
#include "qgseditformconfig_p.h"
#include "qgseditformconfig.h"
#include "qgsnetworkcontentfetcherregistry.h"
#include "qgspathresolver.h"
#include "qgsproject.h"
#include "qgsreadwritecontext.h"
@@ -153,7 +154,13 @@ QString QgsEditFormConfig::uiForm() const

void QgsEditFormConfig::setUiForm( const QString &ui )
{
if ( ui.isEmpty() || ui.isNull() )
if ( !ui.isEmpty() && !QUrl::fromUserInput( ui ).isLocalFile() )
{
// any existing download will not be restarted!
QgsApplication::instance()->networkContentFetcherRegistry()->fetch( ui, QgsNetworkContentFetcherRegistry::DownloadImmediately );
}

if ( ui.isEmpty() )
{
setLayout( GeneratedLayout );
}
@@ -268,7 +275,7 @@ void QgsEditFormConfig::readXml( const QDomNode &node, QgsReadWriteContext &cont
if ( !editFormNode.isNull() )
{
QDomElement e = editFormNode.toElement();
d->mUiFormPath = context.pathResolver().readPath( e.text() );
setUiForm( context.pathResolver().readPath( e.text() ) );
}

QDomNode editFormInitNode = node.namedItem( QStringLiteral( "editforminit" ) );
@@ -25,8 +25,8 @@
#include <QDomDocument>

#include "qgsattributeeditorelement.h"
#include "qgsreadwritecontext.h"

class QgsReadWriteContext;
class QgsRelationManager;
class QgsEditFormConfigPrivate;

@@ -135,14 +135,18 @@ class CORE_EXPORT QgsEditFormConfig
//! Set the active layout style for the attribute editor for this layer
void setLayout( EditorLayout editorLayout );

//! Get path to the .ui form. Only meaningful with EditorLayout::UiFileLayout.
/**
* \brief Get path or URL to the .ui form. Only meaningful with EditorLayout::UiFileLayout
*/
QString uiForm() const;

/**
* Set path to the .ui form.
* When a string is provided, the layout style will be set to EditorLayout::UiFileLayout,
* When a string is provided in \a ui, the layout style will be set to EditorLayout::UiFileLayout,
* if an empty or a null string is provided, the layout style will be set to
* EditorLayout::GeneratedLayout.
* If \a ui is a URL, a local copy of the file will be made and will be used to create the forms
* \a context is provided to save error messages
*/
void setUiForm( const QString &ui );

@@ -65,7 +65,7 @@ class QgsEditFormConfigPrivate : public QSharedData
//! Defines the default layout to use for the attribute editor (Drag and drop, UI File, Generated)
QgsEditFormConfig::EditorLayout mEditorLayout = QgsEditFormConfig::GeneratedLayout;

//! Init form instance
//! Path or URL to the UI form
QString mUiFormPath;
//! Name of the Python form init function
QString mInitFunction;
@@ -27,119 +27,44 @@ QgsNetworkContentFetcherRegistry::QgsNetworkContentFetcherRegistry()

QgsNetworkContentFetcherRegistry::~QgsNetworkContentFetcherRegistry()
{
QMap<QUrl, QgsFetchedContent *>::const_iterator it = mFileRegistry.constBegin();
QMap<QString, QgsFetchedContent *>::const_iterator it = mFileRegistry.constBegin();
for ( ; it != mFileRegistry.constEnd(); ++it )
{
delete it.value();
}
mFileRegistry.clear();
}

const QgsFetchedContent *QgsNetworkContentFetcherRegistry::fetch( const QUrl &url, const FetchingMode fetchingMode )
const QgsFetchedContent *QgsNetworkContentFetcherRegistry::fetch( const QString &url, const FetchingMode fetchingMode )
{
QMutexLocker locker( &mMutex );

if ( mFileRegistry.contains( url ) )
{
return mFileRegistry.value( url );
}

QgsFetchedContent *content = new QgsFetchedContent( nullptr, QgsFetchedContent::NotStarted );

// start
QObject::connect( content, &QgsFetchedContent::downloadStarted, this, [ = ]( const bool redownload )
{
if ( redownload && content->status() == QgsFetchedContent::Downloading )
{
{
QMutexLocker locker( &mMutex );
if ( content->mFetchingTask )
disconnect( content->mFetchingTask, &QgsNetworkContentFetcherTask::fetched, content, &QgsFetchedContent::taskCompleted );
}
// no locker when calling cancel!
content->cancel();
}
QMutexLocker locker( &mMutex );
if ( redownload ||
content->status() == QgsFetchedContent::NotStarted ||
content->status() == QgsFetchedContent::Failed )
{
content->mFetchingTask = new QgsNetworkContentFetcherTask( url );
connect( content->mFetchingTask, &QgsNetworkContentFetcherTask::fetched, content, &QgsFetchedContent::taskCompleted );
QgsApplication::instance()->taskManager()->addTask( content->mFetchingTask );
content->mStatus = QgsFetchedContent::Downloading;
}
} );

// cancel
QObject::connect( content, &QgsFetchedContent::cancelTriggered, this, [ = ]()
{
QMutexLocker locker( &mMutex );
if ( content->mFetchingTask && content->mFetchingTask->canCancel() )
{
content->mFetchingTask->cancel();
}
if ( content->mFile )
{
content->mFile->deleteLater();
content->mFilePath = QString();
}
} );

// finished
connect( content, &QgsFetchedContent::taskCompleted, this, [ = ]()
{
QMutexLocker locker( &mMutex );
if ( !content->mFetchingTask || !content->mFetchingTask->reply() )
{
// if no reply, it has been canceled
content->mStatus = QgsFetchedContent::Failed;
content->mError = QNetworkReply::OperationCanceledError;
content->mFilePath = QString();
}
else
{
QNetworkReply *reply = content->mFetchingTask->reply();
if ( reply->error() == QNetworkReply::NoError )
{
QTemporaryFile *tf = new QTemporaryFile( QStringLiteral( "XXXXXX" ) );
content->mFile = tf;
tf->open();
content->mFile->write( reply->readAll() );
// Qt docs notes that on some system if fileName is not called before close, file might get deleted
content->mFilePath = tf->fileName();
tf->close();
content->mStatus = QgsFetchedContent::Finished;
}
else
{
content->mStatus = QgsFetchedContent::Failed;
content->mError = reply->error();
content->mFilePath = QString();
}
}
content->emitFetched();
} );
QgsFetchedContent *content = new QgsFetchedContent( url, nullptr, QgsFetchedContent::NotStarted );

mFileRegistry.insert( url, content );

if ( fetchingMode == DownloadImmediately )
content->download();


return content;
}

const QFile *QgsNetworkContentFetcherRegistry::localFile( const QString &filePathOrUrl )
QFile *QgsNetworkContentFetcherRegistry::localFile( const QString &filePathOrUrl )
{
QFile *file = nullptr;
QString path = filePathOrUrl;

if ( !QUrl::fromUserInput( filePathOrUrl ).isLocalFile() )
{
QMutexLocker locker( &mMutex );
if ( mFileRegistry.contains( QUrl( path ) ) )
if ( mFileRegistry.contains( path ) )
{
const QgsFetchedContent *content = mFileRegistry.value( QUrl( path ) );
if ( content->status() == QgsFetchedContent::Finished && !content->file() )
const QgsFetchedContent *content = mFileRegistry.value( path );
if ( content && content->status() == QgsFetchedContent::Finished && content->file() )
{
file = content->file();
}
@@ -166,10 +91,9 @@ QString QgsNetworkContentFetcherRegistry::localPath( const QString &filePathOrUr

if ( !QUrl::fromUserInput( filePathOrUrl ).isLocalFile() )
{
QMutexLocker locker( &mMutex );
if ( mFileRegistry.contains( QUrl( path ) ) )
if ( mFileRegistry.contains( path ) )
{
const QgsFetchedContent *content = mFileRegistry.value( QUrl( path ) );
const QgsFetchedContent *content = mFileRegistry.value( path );
if ( content->status() == QgsFetchedContent::Finished && !content->filePath().isEmpty() )
{
path = content->filePath();
@@ -190,3 +114,75 @@ QString QgsNetworkContentFetcherRegistry::localPath( const QString &filePathOrUr




void QgsFetchedContent::download( bool redownload )
{

if ( redownload && status() == QgsFetchedContent::Downloading )
{
{
if ( mFetchingTask )
disconnect( mFetchingTask, &QgsNetworkContentFetcherTask::taskCompleted, this, &QgsFetchedContent::taskCompleted );
}
cancel();
}
if ( redownload ||
status() == QgsFetchedContent::NotStarted ||
status() == QgsFetchedContent::Failed )
{
mFetchingTask = new QgsNetworkContentFetcherTask( mUrl );
// use taskCompleted which is main thread rather than fetched signal in worker thread
connect( mFetchingTask, &QgsNetworkContentFetcherTask::taskCompleted, this, &QgsFetchedContent::taskCompleted );
QgsApplication::instance()->taskManager()->addTask( mFetchingTask );
mStatus = QgsFetchedContent::Downloading;
}

}

void QgsFetchedContent::cancel()
{
if ( mFetchingTask && mFetchingTask->canCancel() )
{
mFetchingTask->cancel();
}
if ( mFile )
{
mFile->deleteLater();
mFilePath = QString();
}
}


void QgsFetchedContent::taskCompleted()
{
if ( !mFetchingTask || !mFetchingTask->reply() )
{
// if no reply, it has been canceled
mStatus = QgsFetchedContent::Failed;
mError = QNetworkReply::OperationCanceledError;
mFilePath = QString();
}
else
{
QNetworkReply *reply = mFetchingTask->reply();
if ( reply->error() == QNetworkReply::NoError )
{
QTemporaryFile *tf = new QTemporaryFile( QStringLiteral( "XXXXXX" ) );
mFile = tf;
tf->open();
mFile->write( reply->readAll() );
// Qt docs notes that on some system if fileName is not called before close, file might get deleted
mFilePath = tf->fileName();
tf->close();
mStatus = QgsFetchedContent::Finished;
}
else
{
mStatus = QgsFetchedContent::Failed;
mError = reply->error();
mFilePath = QString();
}
}

emit fetched();
}

0 comments on commit 4c92de9

Please sign in to comment.
You can’t perform that action at this time.