Wielowątkowe aplikacje z QThread
Aplikacja GUI oparta o PyQt4 (czy inne biblioteki) może wykonywać jedną operację na raz. Jeżeli np. zaczniemy pobierać plik z internetu to do czasu jego pobrania aplikacja będzie "zablokowana". Interfejs nie będzie odświeżany i nie będzie reagował na zdarzenia. Tego typu problemy rozwiązuje się wykonując czasochłonne operacje w oddzielnych wątkach. Nawet niezbyt rozbudowana aplikacja może wykorzystywać wiele pobocznych wątków. W PyQt4 poboczne wątki tworzy się za pomocą QtCore.QThread.
Poboczny wątek stworzony za pomocą QThread to klasa dziedzicząca QtCore.QThread i posiadająca metodę run. Oto przykład:
class DoCommandThread(QtCore.QThread):
def __init__(self, parent, cmd):
super(DoCommandThread, self).__init__(parent)
self.ui = parent.ui
self.parent = parent
self.cmd = cmd
def run(self):
p = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE, env=os.environ, shell=True)
#print str(p.communicate()[1])
self.du = DoCommandThread(self, 'ls -a')
self.du.start()
QtCore.QObject.connect(self.du,QtCore.SIGNAL("finished()"), self.po_wykonaniu_watku)
Poniżej przykład zastosowania pobocznego wątku w aplikacji QYolk do pobrania informacji o aktualizacjach z internetu. W głównej klasie mam takie sloty:
def stop_check_update_all(self):
"""
Przerwanie wątku pobierania aktualizacji
"""
self.cua.terminate()
# change the cursor back to normal form
q = self.cursor()
q.setShape(QtCore.Qt.ArrowCursor)
self.setCursor(q)
self.ui.pkgTree.resizeColumnToContents(1)
def check_update_all(self):
"""
Kliknięto w przycisk "Sprawdź aktualizacje"
Odpalamy wątek
"""
# change the cursor untill data gathered:
q = self.cursor()
q.setShape(QtCore.Qt.WaitCursor)
self.setCursor(q)
self.ui.pkgTree.setColumnWidth(1, 300)
# WĄTEK
self.cua = CheckUpdateAllThread(self)
self.cua.start()
QtCore.QObject.connect(self.cua,QtCore.SIGNAL("finished()"), self.check_update_all_finish)
def check_update_all_finish(self):
"""
Wątek skończył działanie
"""
q = self.cursor()
q.setShape(QtCore.Qt.ArrowCursor)
self.setCursor(q)
self.ui.pkgTree.resizeColumnToContents(1)
class CheckUpdateAllThread(QtCore.QThread):
def __init__(self, parent):
super(CheckUpdateAllThread, self).__init__(parent)
self.ui = parent.ui
self.parent = parent
def run(self):
items = self.ui.pkgTree.findItems('*', QtCore.Qt.MatchWildcard)
if len(items) > 0:
ch = pypi.CheeseShop()
for itm in items:
pkg = unicode(itm.text(0))
ver = unicode(itm.text(1))
try:
# TO TRWA KILKA SEKUND
ret = ch.query_versions_pypi(pkg)
curver = ret[1][0]
except:
curver = False
if ver != curver and curver:
ms = self.tr('available release')
msg = u'%s (%s: %s)' % (ver, unicode(ms), curver)
else:
ms = self.tr('no updates')
msg = u'%s (%s)' % (ver, unicode(ms))
itm.setText(1, msg)
Comment article