Индикатор выполнения не обновляется во время работы - PullRequest
7 голосов
/ 30 января 2009

в моей программе на python для загрузки файла в Интернет, я использую индикатор выполнения GTK, чтобы показать прогресс загрузки. Но проблема, с которой я сталкиваюсь, состоит в том, что индикатор выполнения не показывает какую-либо активность, пока загрузка не завершена, а затем он внезапно указывает на завершение загрузки. я использую pycurl, чтобы сделать http-запросы ... мой вопрос - мне нужно иметь многопоточное приложение для загрузки файла и одновременного обновления графического интерфейса? или есть какая-то другая ошибка, которую я делаю?

Заранее спасибо!

Ответы [ 5 ]

12 голосов
/ 31 января 2009

Я собираюсь процитировать PyGTK FAQ :

Вы создали индикатор выполнения внутри окна, затем запускаете цикл, который выполняет некоторую работу:

while work_left:
    ...do something...
    progressbar.set_fraction(...)

Вы заметите, что окно даже не отображается, или если оно это делает, индикатор выполнения остается замороженным до конца задачи. Объяснение простое: gtk управляется событиями, и вы отбираете управление у основного цикла gtk, тем самым не позволяя ему обрабатывать обычные события обновления графического интерфейса.

Самое простое решение состоит в том, чтобы временно возвращать контроль gtk каждый раз, когда изменяется ход:

while work_left:
    ...do something...
    progressbar.set_fraction(...)
    while gtk.events_pending():
        gtk.main_iteration()

Обратите внимание, что с этим решением пользователь не может выйти из приложения (gtk.main_quit не будет работать из-за нового цикла [gtk.main_iteration ()]), пока ваша тяжелая работа не будет завершена.

Другое решение состоит в использовании функций простоя gtk, которые вызываются главным циклом gtk всякий раз, когда ему нечего делать. Таким образом, gtk находится под контролем, и функция простоя должна сделать немного работы. Он должен вернуть True, если есть еще работа, в противном случае False.

Лучшее решение (у него нет недостатков) было указано Джеймсом Хенстриджем. Генераторы python используют функции простоя, чтобы Python автоматически сохранял для нас состояние. Это выглядит так:

def my_task(data):
    ...some work...
    while heavy_work_needed:
        ...do heavy work here...
        progress_label.set_text(data) # here we update parts of UI
        # there's more work, return True
        yield True
    # no more work, return False
    yield False

def on_start_my_task_button_click(data):
    task = my_task(data)
    gobject.idle_add(task.next)

Выше 'while' - просто пример. Единственные правила заключаются в том, что он должен выдавать True после выполнения небольшой работы, и есть еще много работы, и он должен давать False, когда задача выполнена.

1 голос
/ 30 января 2009

Скорее всего, проблема в том, что в вашем обратном вызове прогресса, где, как я полагаю, вы обновляете индикатор выполнения, вы не выполняете вызов для ручного обновления дисплея, то есть запускаете цикл обработки событий GUI. Это всего лишь предположение, хотя, если вы можете предоставить больше кода, может быть проще сузить его.

Причина, по которой вам необходимо вручную обновить отображение, заключается в том, что ваш основной поток также выполняет загрузку, в которой он блокируется.

0 голосов
/ 19 марта 2009

Один из вариантов, если вы не состоите в браке с pycurl, это использовать наблюдателей GObject IO.

http://pygtk.org/pygtk2reference/gobject-functions.html#function-gobject--io-add-watch

Используя это, вы можете чередовать загрузку файла с обычным циклом событий PyGTK и даже выполнять вызов set_progress в обратном вызове IO watch. Если вы выгружаете всю работу по загрузке на pycurl, это не очень возможно, но если вы просто загружаете файл по HTTP, io_add_watch сделает использование сокета для этого гораздо менее болезненным.

0 голосов
/ 03 февраля 2009

Да, вам, вероятно, нужен параллелизм, и да, потоки - это один из подходов, но если вы используете потоки, используйте такой метод: http://unpythonic.blogspot.com/2007/08/using-threads-in-pygtk.html, который отвлечет от боли и позволит вам сосредоточиться по важным аспектам.

(я не повторял все в этом посте из-за лени, отсюда и вики-сообщество).

0 голосов
/ 31 января 2009

В Python 2.x целочисленные операнды приводят к целочисленному делению. Попробуйте это:

#Callback function invoked when download/upload has progress
def progress(download_t, download_d, upload_t, upload_d):
    print 'in fileupload progress'
    mainwin.mainw.prog_bar.set_fraction(float(upload_d) / upload_t)
...