I hacked together an demo that more or less reproduces the multiprocessing example, with the addition of the ability to abort uploads. It can only handle six parallel uploads at a time because that is the maximum QNetworkAccessManager will allow. However, this limit could be increased by simply adding another QNetworkAccessManager.
There was one issue I came across while testing the demo. It seems that under some circumstances, the post-data can get sent twice (see here, for example). But I don't know whether this is a Qt bug, or an issue with my test setup (I used a python threaded httpserver). Anyway, it was easy enough to fix by closing the reply-object in the uploadProgress handler (see below).
from PyQt4 import QtCore, QtGui, QtNetwork
class Window(QtGui.QWidget):
def __init__(self, address):
QtGui.QWidget.__init__(self)
self.address = address
self.table = QtGui.QTableWidget(self)
header = self.table.horizontalHeader()
header.setStretchLastSection(True)
header.hide()
self.table.setColumnCount(2)
self.button = QtGui.QPushButton('Add Upload', self)
self.button.clicked.connect(self.handleAddUpload)
layout = QtGui.QVBoxLayout(self)
layout.addWidget(self.table)
layout.addWidget(self.button)
self.netaccess = QtNetwork.QNetworkAccessManager(self)
self._uploaders = {}
def handleAddUpload(self):
stream = QtCore.QFile('files/sample.tar.bz2')
if stream.open(QtCore.QIODevice.ReadOnly):
data = stream.readAll()
stream.close()
row = self.table.rowCount()
button = QtGui.QPushButton('Abort', self.table)
button.clicked.connect(lambda: self.handleAbort(row))
progress = QtGui.QProgressBar(self.table)
progress.setRange(0, len(data))
self.table.setRowCount(row + 1)
self.table.setCellWidget(row, 0, button)
self.table.setCellWidget(row, 1, progress)
uploader = self._uploaders[row] = Uploader(row, self.netaccess)
uploader.uploadProgress.connect(self.handleUploadProgress)
uploader.uploadFinished.connect(self.handleUploadFinished)
uploader.upload(data, self.address)
def handleUploadProgress(self, key, sent, total):
print('upload(%d): %d [%d]' % (key, sent, total))
progress = self.table.cellWidget(key, 1)
progress.setValue(sent)
def handleUploadFinished(self, key):
print('upload(%d) finished' % key)
button = self.table.cellWidget(key, 0)
button.setDisabled(True)
uploader = self._uploaders.pop(key)
uploader.deleteLater()
def handleAbort(self, key):
try:
self._uploaders[key].abort()
except (KeyError, AttributeError):
pass
class Uploader(QtCore.QObject):
uploadProgress = QtCore.pyqtSignal(object, int, int)
uploadFinished = QtCore.pyqtSignal(object)
def __init__(self, key, parent):
QtCore.QObject.__init__(self, parent)
self._key = key
self._reply = None
def abort(self):
if self._reply is not None:
self._reply.abort()
def upload(self, data, url):
if self._reply is None:
request = QtNetwork.QNetworkRequest(QtCore.QUrl(url))
request.setHeader(
QtNetwork.QNetworkRequest.ContentTypeHeader,
'application/x-www-form-urlencoded')
self._reply = self.parent().post(request, data)
self._reply.uploadProgress.connect(self.handleUploadProgress)
self._reply.finished.connect(self.handleFinished)
def handleUploadProgress(self, sent, total):
self.uploadProgress.emit(self._key, sent, total)
if sent >= total:
# prevent duplicated uploads
self._reply.close()
def handleFinished(self):
self._reply.deleteLater()
self._reply = None
self.uploadFinished.emit(self._key)
if __name__ == '__main__':
import sys
app = QtGui.QApplication(sys.argv)
window = Window('http://localhost:54321/upload')
window.setGeometry(500, 300, 500, 300)
window.show()
sys.exit(app.exec_())