So I had another look at this problem. I started from scratch and had success, then went back to look at the code above and still can't fix it.
In the interests of providing a working example for anyone researching this subject I will provide functional code here:
from PyQt4.QtCore import *
from PyQt4.QtGui import *
class ThreadManagerDialog(QDialog):
def __init__( self, iface, title="Worker Thread"):
QDialog.__init__( self, iface.mainWindow() )
self.iface = iface
self.setWindowTitle(title)
self.setLayout(QVBoxLayout())
self.primaryLabel = QLabel(self)
self.layout().addWidget(self.primaryLabel)
self.primaryBar = QProgressBar(self)
self.layout().addWidget(self.primaryBar)
self.secondaryLabel = QLabel(self)
self.layout().addWidget(self.secondaryLabel)
self.secondaryBar = QProgressBar(self)
self.layout().addWidget(self.secondaryBar)
self.closeButton = QPushButton("Close")
self.closeButton.setEnabled(False)
self.layout().addWidget(self.closeButton)
self.closeButton.clicked.connect(self.reject)
def run(self):
self.runThread()
self.exec_()
def runThread( self):
QObject.connect( self.workerThread, SIGNAL( "jobFinished( PyQt_PyObject )" ), self.jobFinishedFromThread )
QObject.connect( self.workerThread, SIGNAL( "primaryValue( PyQt_PyObject )" ), self.primaryValueFromThread )
QObject.connect( self.workerThread, SIGNAL( "primaryRange( PyQt_PyObject )" ), self.primaryRangeFromThread )
QObject.connect( self.workerThread, SIGNAL( "primaryText( PyQt_PyObject )" ), self.primaryTextFromThread )
QObject.connect( self.workerThread, SIGNAL( "secondaryValue( PyQt_PyObject )" ), self.secondaryValueFromThread )
QObject.connect( self.workerThread, SIGNAL( "secondaryRange( PyQt_PyObject )" ), self.secondaryRangeFromThread )
QObject.connect( self.workerThread, SIGNAL( "secondaryText( PyQt_PyObject )" ), self.secondaryTextFromThread )
self.workerThread.start()
def cancelThread( self ):
self.workerThread.stop()
def jobFinishedFromThread( self, success ):
self.workerThread.stop()
self.primaryBar.setValue(self.primaryBar.maximum())
self.secondaryBar.setValue(self.secondaryBar.maximum())
self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), success )
self.closeButton.setEnabled( True )
def primaryValueFromThread( self, value ):
self.primaryBar.setValue(value)
def primaryRangeFromThread( self, range_vals ):
self.primaryBar.setRange( range_vals[ 0 ], range_vals[ 1 ] )
def primaryTextFromThread( self, value ):
self.primaryLabel.setText(value)
def secondaryValueFromThread( self, value ):
self.secondaryBar.setValue(value)
def secondaryRangeFromThread( self, range_vals ):
self.secondaryBar.setRange( range_vals[ 0 ], range_vals[ 1 ] )
def secondaryTextFromThread( self, value ):
self.secondaryLabel.setText(value)
class WorkerThread( QThread ):
def __init__( self, parentThread):
QThread.__init__( self, parentThread )
def run( self ):
self.running = True
success = self.doWork()
self.emit( SIGNAL( "jobFinished( PyQt_PyObject )" ), success )
def stop( self ):
self.running = False
pass
def doWork( self ):
return True
def cleanUp( self):
pass
class CounterThread(WorkerThread):
def __init__(self, parentThread):
WorkerThread.__init__(self, parentThread)
def doWork(self):
target = 100000000
stepP= target/100
stepS=target/10000
self.emit( SIGNAL( "primaryText( PyQt_PyObject )" ), "Primary" )
self.emit( SIGNAL( "secondaryText( PyQt_PyObject )" ), "Secondary" )
self.emit( SIGNAL( "primaryRange( PyQt_PyObject )" ), ( 0, 100 ) )
self.emit( SIGNAL( "secondaryRange( PyQt_PyObject )" ), ( 0, 100 ) )
count = 0
while count < target:
if count % stepP == 0:
self.emit( SIGNAL( "primaryValue( PyQt_PyObject )" ), int(count / stepP) )
if count % stepS == 0:
self.emit( SIGNAL( "secondaryValue( PyQt_PyObject )" ), count % stepP / stepS )
if not self.running:
return False
count += 1
return True
d = ThreadManagerDialog(qgis.utils.iface, "CounterThread Demo")
d.workerThread = CounterThread(qgis.utils.iface.mainWindow())
d.run()
The structure of this sample is a ThreadManagerDialog class that than can be assigned a WorkerThread (or subclass). When the dialog's run method is called it will in turn call the doWork method on the worker. The result is that any code in doWork will run in a seperate thread, leaving the GUI free to respond to user input.
In this sample an instance of CounterThread is assigned as the worker and a couple of progress bars will be kept busy for a minute or so.
Note: this is formatted so that it is ready to paste into the python console. The last three lines will need to be removed before saving to a .py file.
If you are fine with a solution that uses QNetworkAccessManager
from Qt (instead of the manager from QGIS libs), you can do it this way:
from datetime import datetime
from PyQt4.QtCore import QUrl
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest
def request_download(manager):
url = QUrl("http://www.geotux.tuxfamily.org")
request = QNetworkRequest(url)
print "Download start time: {}".format(datetime.now())
manager.get( request )
def handle_download(reply):
print "Download finish time: {}".format(datetime.now())
print "Finished: {}".format(reply.isFinished())
print "Bytes received: {}".format(len(reply.readAll()))
manager = QNetworkAccessManager()
manager.finished.connect( handle_download )
request_download(manager)
As you can see, you don't need to use global variables.
BTW, I couldn't use your code from a QGIS Python console, it threw this error: RuntimeError: no access to protected functions or signals for objects not created from Python
Best Answer
Right now, the 100 limit is hard coded in the WMS provider. But QGIS is a wonderful open source project and you can submit a feature request to turn this limit into a configurable parameter.
Any developer can take this feature request and submit a new pull request to QGIS. If the solution is accepted, core developers will be happy to apply the changes both for the upcoming version 3 and for the current 2.14.x and 2.18.x versions.
So, the answer your question is a new feature request submission to QGIS.