Skip to content

Commit 8e20996

Browse files
committed
simplify netwotk content fetcher registry
kudos @wonder-sk
1 parent 30b7fd1 commit 8e20996

File tree

5 files changed

+98
-130
lines changed

5 files changed

+98
-130
lines changed

python/core/qgsnetworkcontentfetcherregistry.sip.in

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ FetchedContent holds useful information about a network content being fetched
3535
Failed
3636
};
3737

38-
explicit QgsFetchedContent( QTemporaryFile *file = 0, ContentStatus status = NotStarted );
38+
explicit QgsFetchedContent( const QString &url, QTemporaryFile *file = 0, ContentStatus status = NotStarted );
3939
%Docstring
4040
Constructs a FetchedContent with pointer to the downloaded file and status of the download
4141
%End
@@ -76,21 +76,6 @@ Return the potential error of the download
7676
void fetched();
7777
%Docstring
7878
Emitted when the file is fetched and accessible
79-
%End
80-
81-
void downloadStarted( const bool redownload );
82-
%Docstring
83-
Emitted when the download actually starts
84-
%End
85-
86-
void cancelTriggered();
87-
%Docstring
88-
Emitted when download is canceled.
89-
%End
90-
91-
void taskCompleted();
92-
%Docstring
93-
Emitted when the download is finished (although file not accessible yet)
9479
%End
9580

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

126111
~QgsNetworkContentFetcherRegistry();
127112

128-
const QgsFetchedContent *fetch( const QUrl &url, const FetchingMode fetchingMode = DownloadLater );
113+
const QgsFetchedContent *fetch( const QString &url, const FetchingMode fetchingMode = DownloadLater );
129114
%Docstring
130115
Initialize a download for the given URL
131116

src/core/qgsnetworkcontentfetcherregistry.cpp

Lines changed: 82 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -27,104 +27,30 @@ QgsNetworkContentFetcherRegistry::QgsNetworkContentFetcherRegistry()
2727

2828
QgsNetworkContentFetcherRegistry::~QgsNetworkContentFetcherRegistry()
2929
{
30-
QMap<QUrl, QgsFetchedContent *>::const_iterator it = mFileRegistry.constBegin();
30+
QMap<QString, QgsFetchedContent *>::const_iterator it = mFileRegistry.constBegin();
3131
for ( ; it != mFileRegistry.constEnd(); ++it )
3232
{
3333
delete it.value();
3434
}
3535
mFileRegistry.clear();
3636
}
3737

38-
const QgsFetchedContent *QgsNetworkContentFetcherRegistry::fetch( const QUrl &url, const FetchingMode fetchingMode )
38+
const QgsFetchedContent *QgsNetworkContentFetcherRegistry::fetch( const QString &url, const FetchingMode fetchingMode )
3939
{
40-
QMutexLocker locker( &mMutex );
40+
4141
if ( mFileRegistry.contains( url ) )
4242
{
4343
return mFileRegistry.value( url );
4444
}
4545

46-
QgsFetchedContent *content = new QgsFetchedContent( nullptr, QgsFetchedContent::NotStarted );
47-
48-
// start
49-
QObject::connect( content, &QgsFetchedContent::downloadStarted, this, [ = ]( const bool redownload )
50-
{
51-
if ( redownload && content->status() == QgsFetchedContent::Downloading )
52-
{
53-
{
54-
QMutexLocker locker( &mMutex );
55-
if ( content->mFetchingTask )
56-
disconnect( content->mFetchingTask, &QgsNetworkContentFetcherTask::fetched, content, &QgsFetchedContent::taskCompleted );
57-
}
58-
// no locker when calling cancel!
59-
content->cancel();
60-
}
61-
QMutexLocker locker( &mMutex );
62-
if ( redownload ||
63-
content->status() == QgsFetchedContent::NotStarted ||
64-
content->status() == QgsFetchedContent::Failed )
65-
{
66-
content->mFetchingTask = new QgsNetworkContentFetcherTask( url );
67-
connect( content->mFetchingTask, &QgsNetworkContentFetcherTask::fetched, content, &QgsFetchedContent::taskCompleted );
68-
QgsApplication::instance()->taskManager()->addTask( content->mFetchingTask );
69-
content->mStatus = QgsFetchedContent::Downloading;
70-
}
71-
} );
72-
73-
// cancel
74-
QObject::connect( content, &QgsFetchedContent::cancelTriggered, this, [ = ]()
75-
{
76-
QMutexLocker locker( &mMutex );
77-
if ( content->mFetchingTask && content->mFetchingTask->canCancel() )
78-
{
79-
content->mFetchingTask->cancel();
80-
}
81-
if ( content->mFile )
82-
{
83-
content->mFile->deleteLater();
84-
content->mFilePath = QString();
85-
}
86-
} );
87-
88-
// finished
89-
connect( content, &QgsFetchedContent::taskCompleted, this, [ = ]()
90-
{
91-
QMutexLocker locker( &mMutex );
92-
if ( !content->mFetchingTask || !content->mFetchingTask->reply() )
93-
{
94-
// if no reply, it has been canceled
95-
content->mStatus = QgsFetchedContent::Failed;
96-
content->mError = QNetworkReply::OperationCanceledError;
97-
content->mFilePath = QString();
98-
}
99-
else
100-
{
101-
QNetworkReply *reply = content->mFetchingTask->reply();
102-
if ( reply->error() == QNetworkReply::NoError )
103-
{
104-
QTemporaryFile *tf = new QTemporaryFile( QStringLiteral( "XXXXXX" ) );
105-
content->mFile = tf;
106-
tf->open();
107-
content->mFile->write( reply->readAll() );
108-
// Qt docs notes that on some system if fileName is not called before close, file might get deleted
109-
content->mFilePath = tf->fileName();
110-
tf->close();
111-
content->mStatus = QgsFetchedContent::Finished;
112-
}
113-
else
114-
{
115-
content->mStatus = QgsFetchedContent::Failed;
116-
content->mError = reply->error();
117-
content->mFilePath = QString();
118-
}
119-
}
120-
content->emitFetched();
121-
} );
46+
QgsFetchedContent *content = new QgsFetchedContent( url, nullptr, QgsFetchedContent::NotStarted );
12247

12348
mFileRegistry.insert( url, content );
12449

12550
if ( fetchingMode == DownloadImmediately )
12651
content->download();
12752

53+
12854
return content;
12955
}
13056

@@ -135,11 +61,10 @@ QFile *QgsNetworkContentFetcherRegistry::localFile( const QString &filePathOrUrl
13561

13662
if ( !QUrl::fromUserInput( filePathOrUrl ).isLocalFile() )
13763
{
138-
QMutexLocker locker( &mMutex );
139-
if ( mFileRegistry.contains( QUrl( path ) ) )
64+
if ( mFileRegistry.contains( path ) )
14065
{
141-
const QgsFetchedContent *content = mFileRegistry.value( QUrl( path ) );
142-
if ( content->status() == QgsFetchedContent::Finished && !content->file() )
66+
const QgsFetchedContent *content = mFileRegistry.value( path );
67+
if ( content && content->status() == QgsFetchedContent::Finished && content->file() )
14368
{
14469
file = content->file();
14570
}
@@ -166,10 +91,9 @@ QString QgsNetworkContentFetcherRegistry::localPath( const QString &filePathOrUr
16691

16792
if ( !QUrl::fromUserInput( filePathOrUrl ).isLocalFile() )
16893
{
169-
QMutexLocker locker( &mMutex );
170-
if ( mFileRegistry.contains( QUrl( path ) ) )
94+
if ( mFileRegistry.contains( path ) )
17195
{
172-
const QgsFetchedContent *content = mFileRegistry.value( QUrl( path ) );
96+
const QgsFetchedContent *content = mFileRegistry.value( path );
17397
if ( content->status() == QgsFetchedContent::Finished && !content->filePath().isEmpty() )
17498
{
17599
path = content->filePath();
@@ -190,3 +114,75 @@ QString QgsNetworkContentFetcherRegistry::localPath( const QString &filePathOrUr
190114

191115

192116

117+
118+
void QgsFetchedContent::download( bool redownload )
119+
{
120+
121+
if ( redownload && status() == QgsFetchedContent::Downloading )
122+
{
123+
{
124+
if ( mFetchingTask )
125+
disconnect( mFetchingTask, &QgsNetworkContentFetcherTask::taskCompleted, this, &QgsFetchedContent::taskCompleted );
126+
}
127+
cancel();
128+
}
129+
if ( redownload ||
130+
status() == QgsFetchedContent::NotStarted ||
131+
status() == QgsFetchedContent::Failed )
132+
{
133+
mFetchingTask = new QgsNetworkContentFetcherTask( mUrl );
134+
// use taskCompleted which is main thread rather than fetched signal in worker thread
135+
connect( mFetchingTask, &QgsNetworkContentFetcherTask::taskCompleted, this, &QgsFetchedContent::taskCompleted );
136+
QgsApplication::instance()->taskManager()->addTask( mFetchingTask );
137+
mStatus = QgsFetchedContent::Downloading;
138+
}
139+
140+
}
141+
142+
void QgsFetchedContent::cancel()
143+
{
144+
if ( mFetchingTask && mFetchingTask->canCancel() )
145+
{
146+
mFetchingTask->cancel();
147+
}
148+
if ( mFile )
149+
{
150+
mFile->deleteLater();
151+
mFilePath = QString();
152+
}
153+
}
154+
155+
156+
void QgsFetchedContent::taskCompleted()
157+
{
158+
if ( !mFetchingTask || !mFetchingTask->reply() )
159+
{
160+
// if no reply, it has been canceled
161+
mStatus = QgsFetchedContent::Failed;
162+
mError = QNetworkReply::OperationCanceledError;
163+
mFilePath = QString();
164+
}
165+
else
166+
{
167+
QNetworkReply *reply = mFetchingTask->reply();
168+
if ( reply->error() == QNetworkReply::NoError )
169+
{
170+
QTemporaryFile *tf = new QTemporaryFile( QStringLiteral( "XXXXXX" ) );
171+
mFile = tf;
172+
tf->open();
173+
mFile->write( reply->readAll() );
174+
// Qt docs notes that on some system if fileName is not called before close, file might get deleted
175+
mFilePath = tf->fileName();
176+
tf->close();
177+
mStatus = QgsFetchedContent::Finished;
178+
}
179+
else
180+
{
181+
mStatus = QgsFetchedContent::Failed;
182+
mError = reply->error();
183+
mFilePath = QString();
184+
}
185+
}
186+
187+
emit fetched();
188+
}

src/core/qgsnetworkcontentfetcherregistry.h

Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ class CORE_EXPORT QgsFetchedContent : public QObject
5454
};
5555

5656
//! Constructs a FetchedContent with pointer to the downloaded file and status of the download
57-
explicit QgsFetchedContent( QTemporaryFile *file = nullptr, ContentStatus status = NotStarted )
58-
: QObject(), mFile( file ), mStatus( status ) {}
57+
explicit QgsFetchedContent( const QString &url, QTemporaryFile *file = nullptr, ContentStatus status = NotStarted )
58+
: QObject(), mUrl( url ), mFile( file ), mStatus( status ) {}
5959

6060
~QgsFetchedContent()
6161
{
@@ -84,36 +84,27 @@ class CORE_EXPORT QgsFetchedContent : public QObject
8484
* \brief Start the download
8585
* \param redownload if set to true, it will restart any achieved or pending download.
8686
*/
87-
void download( bool redownload = false ) {emit downloadStarted( redownload );}
87+
void download( bool redownload = false );
8888

8989
/**
9090
* @brief Cancel the download operation
9191
*/
92-
void cancel() {emit cancelTriggered();}
92+
void cancel();
9393

9494
signals:
9595
//! Emitted when the file is fetched and accessible
9696
void fetched();
9797

98-
//! Emitted when the download actually starts
99-
void downloadStarted( const bool redownload );
100-
101-
//! Emitted when download is canceled.
102-
void cancelTriggered();
103-
104-
//! Emitted when the download is finished (although file not accessible yet)
98+
private slots:
10599
void taskCompleted();
106100

107101
private:
108-
void emitFetched() {emit fetched();}
102+
QString mUrl;
109103
QTemporaryFile *mFile = nullptr;
110104
QString mFilePath;
111105
QgsNetworkContentFetcherTask *mFetchingTask = nullptr;
112106
ContentStatus mStatus = NotStarted;
113107
QNetworkReply::NetworkError mError = QNetworkReply::NoError;
114-
115-
// allow modification of task and file from main class
116-
friend class QgsNetworkContentFetcherRegistry;
117108
};
118109

119110
/**
@@ -151,7 +142,7 @@ class CORE_EXPORT QgsNetworkContentFetcherRegistry : public QObject
151142
* \param fetchingMode defines if the download will start immediately or shall be manually triggered
152143
* \note If the download starts immediately, it will not redownload any already fetched or currently fetching file.
153144
*/
154-
const QgsFetchedContent *fetch( const QUrl &url, const FetchingMode fetchingMode = DownloadLater );
145+
const QgsFetchedContent *fetch( const QString &url, const FetchingMode fetchingMode = DownloadLater );
155146

156147
#ifndef SIP_RUN
157148

@@ -169,10 +160,7 @@ class CORE_EXPORT QgsNetworkContentFetcherRegistry : public QObject
169160
QString localPath( const QString &filePathOrUrl );
170161

171162
private:
172-
QMap<QUrl, QgsFetchedContent *> mFileRegistry;
173-
174-
//! Mutex to prevent concurrent access to the class from multiple threads at once (may corrupt the entries otherwise).
175-
mutable QMutex mMutex;
163+
QMap<QString, QgsFetchedContent *> mFileRegistry;
176164

177165
};
178166

src/gui/qgsattributeform.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ void QgsAttributeForm::init()
11211121
QgsDebugMsg( QString( "loading form: %1" ).arg( mLayer->editFormConfig().uiForm() ) );
11221122
const QString path = mLayer->editFormConfig().uiForm();
11231123
QFile *file = QgsApplication::instance()->networkContentFetcherRegistry()->localFile( path );
1124-
if ( file->isReadable() && file->open( QFile::ReadOnly ) )
1124+
if ( file && file->open( QFile::ReadOnly ) )
11251125
{
11261126
QUiLoader loader;
11271127

tests/src/python/test_qgsnetworkcontentfetcherregistry.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
from qgis.testing import unittest, start_app
2323
from qgis.core import QgsNetworkContentFetcherRegistry, QgsFetchedContent, QgsApplication
2424
from utilities import unitTestDataPath
25-
from qgis.PyQt.QtCore import QUrl
2625
from qgis.PyQt.QtNetwork import QNetworkReply, QNetworkRequest
2726
import socketserver
2827
import threading
@@ -55,7 +54,7 @@ def __init__(self, methodName):
5554

5655
def testFetchBadUrl(self):
5756
registry = QgsApplication.networkContentFetcherRegistry()
58-
content = registry.fetch(QUrl('http://x'))
57+
content = registry.fetch('http://x')
5958
self.loaded = False
6059

6160
def check_reply():
@@ -72,7 +71,7 @@ def check_reply():
7271
def testFetchGoodUrl(self):
7372
url = 'http://localhost:' + str(self.port) + '/qgis_local_server/index.html'
7473
registry = QgsApplication.networkContentFetcherRegistry()
75-
content = registry.fetch(QUrl(url))
74+
content = registry.fetch(url)
7675
self.loaded = False
7776

7877
def check_reply():
@@ -89,7 +88,7 @@ def check_reply():
8988
self.assertEqual(registry.localPath(url), content.filePath())
9089

9190
# create new content with same URL
92-
contentV2 = registry.fetch(QUrl(url))
91+
contentV2 = registry.fetch(url)
9392
self.assertEqual(contentV2.status(), QgsFetchedContent.Finished)
9493

9594
def testFetchReloadUrl(self):
@@ -99,7 +98,7 @@ def writeSimpleFile(content):
9998
self.file_content = content
10099

101100
registry = QgsApplication.networkContentFetcherRegistry()
102-
content = registry.fetch(QUrl('http://localhost:' + str(self.port) + '/qgis_local_server/simple_content.txt'))
101+
content = registry.fetch('http://localhost:' + str(self.port) + '/qgis_local_server/simple_content.txt')
103102
self.loaded = False
104103
writeSimpleFile('my initial content')
105104

@@ -136,7 +135,7 @@ def testLocalPath(self):
136135
self.assertEqual(registry.localPath('xxxx'), 'xxxx')
137136

138137
# an existent but unfinished download should return an empty path
139-
content = registry.fetch(QUrl('xxxx'))
138+
content = registry.fetch('xxxx')
140139
self.assertEqual(registry.localPath('xxxx'), '')
141140

142141

0 commit comments

Comments
 (0)