Найти - Пользователи
Полная версия: Работа с потоками, синхронизация потоков.
Начало » Python для новичков » Работа с потоками, синхронизация потоков.
1
vim
Пытаюсь учить питон и разобраться с библиотеками. Написал вот такой класс для загрузки файла:
# -*- coding: utf-8 -*-
#
# downloader.py

import os
import sys
import urllib
import urllib2


class Downloader(object):
def __init__(self, link):
self.url = link
self.progress = 0
self.fileSize = None

def getFile(self):
#print "load..."
localFile = urllib.unquote(os.path.basename(self.url))
fileHadler = open(localFile , "wb")
urlHandler = urllib2.urlopen(self.url)
self.fileSize = urlHandler.headers.get('content-length')
chunk = 8192
cur = 0
while 1:
data = urlHandler.read(chunk)
if not data:
#print "...done."
fileHadler.close()
break
fileHadler.write(data)
cur = cur + 8192
self.progress = (cur*100)/int(self.fileSize)
#print "Read %s bytes"%len(data)

if __name__ == '__main__':
#dwnldr = Downloader('http://slav0nic.org.ua/static/books/python/diveintopython-pdf-5.4.zip')
#dwnldr = Downloader('ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz')
#dwnldr.getFile()
pass
Проверил, файлы качает, теперь написал, что-то типа gui к этому, правда сильно урезанный, но мне пока важно именно разобраться с библиотеками и понять как применять:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# fileget.py

import sys
from PyQt4 import QtCore, QtGui
from downloader import Downloader
from threading import Thread


class Window(QtGui.QWidget):
def __init__(self,*args):
QtGui.QWidget.__init__(self,*args)
self.setWindowTitle(u"FileGet")
self.setMinimumSize(500, 100)
self.vboxlay = QtGui.QVBoxLayout(self)
self.bar = QtGui.QProgressBar(self)
self.bar.setOrientation(QtCore.Qt.Horizontal)
self.bar.setFixedSize(500, 20)
self.bar.setMinimum(0)
self.bar.setMaximum(100)
self.bar.setTextVisible(True)
self.vboxlay.addWidget(self.bar)
self.btnGet = QtGui.QPushButton(self)
self.btnGet.setText(u"Загрузить")
self.vboxlay.addWidget(self.btnGet)
#self.bar.setValue(10)
self.dl = Downloader('http://slav0nic.org.ua/static/books/python/diveintopython-pdf-5.4.zip')
self.connect(self.btnGet, QtCore.SIGNAL("clicked()"), self.trStart)

def changeBar(self):
while self.bar.value() != 100:
self.bar.setValue(self.dl.progress)

def trStart(self):
t = Thread(target=self.dl.getFile)
t1 = Thread(target=self.changeBar)
t.start()
t1.start()

def main():
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
return 0

if __name__ == '__main__':
main()
Программа запускается и пытается работать, но вылетает с ошибками. Я так понимаю нужно как-то синхронизировать потоки. Вобщем вопрос как правильно нужно делать, буду признателен если кто-то объяснит мне на данном примере, а также подскажет что не так делаю и как нужно правильно сделать. Буду благодарен за любую полезную информацию.
alexx11
Самый простой вариант через очередь: создаёшь очередь Queue() и о прочитанных кусках перед енй отчитываешься методом put, а в прогресс-баре её в цикле опрашиваешь методом get. Когда всё считаешь не забудь синхронизировать выход, task_done() и join(). Вроде ничего не забыл.
vim
Файл downloader.py изменил вот так:
# -*- coding: utf-8 -*-
#
# downloader.py

import os
import sys
import urllib
import urllib2
from Queue import Queue


class Downloader(object):
def __init__(self, link):
self.url = link
self.progress = 0
self.fileSize = None
self.q = Queue()

def getFile(self):
#print "load..."
localFile = urllib.unquote(os.path.basename(self.url))
fileHadler = open(localFile , "wb")
urlHandler = urllib2.urlopen(self.url)
self.fileSize = urlHandler.headers.get('content-length')
chunk = 8192
cur = 0
while 1:
data = urlHandler.read(chunk)
if not data:
#print "...done."
fileHadler.close()
break
fileHadler.write(data)
cur = cur + 8192
self.progress = (cur*100)/int(self.fileSize)
self.q.put(self.progress)
#print "Read %s bytes"%len(data)

if __name__ == '__main__':
#dwnldr = Downloader('http://slav0nic.org.ua/static/books/python/diveintopython-pdf-5.4.zip')
#dwnldr = Downloader('ftp://ftp.freebsd.org/pub/FreeBSD/ports/ports/ports.tar.gz')
#dwnldr.getFile()
pass
,
а файл fileget.py изменил следующим образом:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# fileget.py

import sys
from PyQt4 import QtCore, QtGui
from downloader import Downloader
from threading import Thread


class Window(QtGui.QWidget):
def __init__(self,*args):
QtGui.QWidget.__init__(self,*args)
self.setWindowTitle(u"FileGet")
self.setMinimumSize(500, 100)
self.vboxlay = QtGui.QVBoxLayout(self)
self.bar = QtGui.QProgressBar(self)
self.bar.setOrientation(QtCore.Qt.Horizontal)
self.bar.setFixedSize(500, 20)
self.bar.setMinimum(0)
self.bar.setMaximum(100)
self.bar.setTextVisible(True)
self.vboxlay.addWidget(self.bar)
self.btnGet = QtGui.QPushButton(self)
self.btnGet.setText(u"Загрузить")
self.vboxlay.addWidget(self.btnGet)
#self.bar.setValue(10)
self.dl = Downloader('http://slav0nic.org.ua/static/books/python/diveintopython-pdf-5.4.zip')
self.connect(self.btnGet, QtCore.SIGNAL("clicked()"), self.trStart)

def changeBar(self):
while 1:
self.bar.setValue(self.dl.q.get())
if self.dl.progress == 100:
self.dl.q.task_done()
break
self.dl.q.join()

def trStart(self):
t = Thread(target=self.dl.getFile)
t1 = Thread(target=self.changeBar)
t1.start()
t.start()

def main():
app = QtGui.QApplication(sys.argv)
win = Window()
win.show()
sys.exit(app.exec_())
return 0

if __name__ == '__main__':
main()
.
При запуске получаю следующее:

X Error: BadIDChoice (invalid resource ID chosen for this connection) 14
Major opcode: 55 (X_CreateGC)
Resource id: 0x38000fd
python: ../../src/xcb_io.c:249: process_responses: Проверочное утверждение «(((long) (dpy->last_request_read) - (long) (dpy->request)) <= 0)» не выполнено.
Aborted
, что делаю не так, может кто подскажет на примере, как нужно правильно.
Alex2ndr
Я честно скажу, что QT не знаю. Но мне кажется, что здесь привлекать многопоточность просто не надо. Вот сейчас вы крутите 2 бесконечных цикла - в первом качаете, во втором изменяете значения прогрессбара. И хотите делать их параллельно. А разве нельзя просто выкинуть 2-й цикл и изменять значения прогрессбара в 1-м? Это и проще и не нужно никакой многопоточности.
alexx11
Alex2ndr
Это и проще и не нужно никакой многопоточности.
Факт, в этом примере многопоточность как из пушки по воробьям. Можно сделать как ты говоришь, или если для восприятия автору необходима целостность объектов, ну не хочет он интерфес с чтением файла мешать, можно сделать через callback.
По существу:
1. Очередь в родительском потоке должна быть иначе смысл её пропадает, ты её должен создать в первом запустившемся потоке а для этого нужны другие методы синхронизации.
2. родительский поток надо сразу же после запуска каждого дочернего объявить демоном t.setDaemon(True) и в нём ждать join() завершения.
3. В дочерних отчитываться об окончании task_done().
P.S.: Хоть в мануал загляни, плс.
vim
Спасибо, всем, кто помогал, сделал через callback:
в файле downloader.py изменил функцию getFile следующим образом:
def getFile(self, callback=None):
#print "load..."
localFile = urllib.unquote(os.path.basename(self.url))
fileHadler = open(localFile , "wb")
urlHandler = urllib2.urlopen(self.url)
self.fileSize = urlHandler.headers.get('content-length')
chunk = 8192
cur = 0
while 1:
data = urlHandler.read(chunk)
if not data:
#print "...done."
fileHadler.close()
break
fileHadler.write(data)
cur = cur + 8192
if callback:
self.progress = (cur*100)/int(self.fileSize)
callback(self.progress)
, а в файле fileget.py изменил следующие строки:
self.connect(self.btnGet, QtCore.SIGNAL("clicked()"), self.start)

def changeBar(self, progress):
self.bar.setValue(progress)

def start(self):
self.dl.getFile(callback=self.changeBar)
. Теперь все работает без ошибок и как ожидалось.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB