Платформо-зависимые проблемы с производительностью при выборе большого количества файлов с помощью gtk.FileChooserDialog - PullRequest
5 голосов
/ 14 февраля 2012

У меня есть программа pygtk, предназначенная для работы как в Windows, так и в Ubuntu. Это Python 2.7 и gtk2 со статическими привязками (т.е. без самоанализа gobject). Проблема, с которой я столкнулся, существует в Ubuntu, но не в Windows.

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

Проблема в том, что после выбора файлов с помощью gtk.FileChooserDialog (control-A - ваш друг) программа зависает, и события gtk не обрабатываются в течение достаточно долгого времени - даже если моя функция обратного вызова вернулась. За это время загрузка процессора всеми ядрами зависает примерно на 80%, iotop показывает, что мой процесс записывает на диск со скоростью около 20 МБ в секунду, а другие приложения периодически перестают отвечать на запросы - Chrome, Xorg, compiz, banshee и gedit имеют высокую загрузку ЦП (при низкой загрузке до выбора файлов).

Вот пример кода. Чтобы воспроизвести, нажмите кнопку, выберите где-то около 200 файлов (примерно десять экранов, удерживая нажатой клавишу shift и down) и нажмите OK. Не имеет значения, какие файлы - с ними ничего не делается.

import gtk,gobject,time

def print_how_long_it_was_frozen():
    print time.time() - start_time

def button_clicked(button):
    dialog = gtk.FileChooserDialog(
                'Select files to add', w, gtk.FILE_CHOOSER_ACTION_OPEN,
                buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                         gtk.STOCK_OPEN, gtk.RESPONSE_OK))
    dialog.set_select_multiple(True)
    dialog.set_default_response(gtk.RESPONSE_OK)
    response = dialog.run()
    files = dialog.get_filenames()
    dialog.destroy()
    for i, f in enumerate(files):
        print i

    global start_time
    start_time = time.time()
    gobject.idle_add(print_how_long_it_was_frozen)


w = gtk.Window() 
b = gtk.Button('Select files')
w.add(b)
b.connect('clicked', button_clicked)
w.show_all()
gtk.main()

Это приводит к ~ 60-секундному зависанию после обратного вызова, завершенного, в течение которого ничего не должно происходить, кроме обработки уничтожения диалога (что происходит на полпути через зависание).

Это на Ubuntu 11.10. В Windows меньше секунды зависания.

У меня есть подозрения, что это связано с тем, что в Gnome или Unity есть функция 'недавние файлы', или с другой активностью отслеживания. процесс zeitgeist-daemon также сильно загружает процессор во время зависания, хотя его устранение не решает проблему. Также не отключено ведение журнала с помощью Zeitgeist Activity Log Manager. Даже если Zeitgeist можно отключить, я не могу ожидать, что мои пользователи отключат его.

Кто-нибудь знает, как отключить отчеты о последних файлах в приложении gtk, или знает что-нибудь еще, что может быть причиной этого?

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

(тестирование проводилось на 32-битных Windows 7 и 64, но на Ubuntu 11.10. Python 2.7 и pygtk 2.24 на обоих)

Ответы [ 2 ]

6 голосов
/ 15 февраля 2012

Замедление связано с тем, что виджет gtk.FileChooser автоматически помещает все выбранные файлы в список недавно использованных файлов (gtk.RecentManager.add_item()).

Добавление этой функции выполняетсяв отдельном потоке (и, по-видимому, не испытывающим проблем с получением блокировки gtk даже во время зависания) в примере кода:

def log_n_recent_files():
    manager = gtk.recent_manager_get_default()
    manager.purge_items()
    while True:
        time.sleep(1)
        with gtk.gdk.lock:
            items = manager.get_items()
        with open('log.log','a') as f:
            f.write('%f %d\n'%(time.time(), len(items)))

показывает (после оставления работающим на ночь), что задержка на файл увеличивается с увеличением числаиз последних файлов:

Number of files added over time Rate of file adding

Поскольку нет способа добавить несколько файлов в RecentManager, они добавляются по одному за раз.

Каждый раз, когда один из них добавляется, другие приложения gtk получают уведомление об изменении списка последних файлов (сохраненного в ~/.local/share/recently-used.xbel).Затем они анализируют файл и перебирают элементы, ища n самых последних элементов (где n относится к конкретному приложению), чтобы отобразить их.Чтобы определить, какие файлы являются самыми последними, для каждого элемента выполняется системный вызов .

Проблема усугубляется тем фактом, что recently-used.xbel может расти без ограничений..Таким образом, если у вас есть 5000 элементов в recently-used.xbel, и вы выбираете 200 файлов с gtk.FileChooser, вы получите (сумма от n = 1 до 200) (5000 + n) ~ 1 миллион вызовов системного времени для каждогоПриложение GTK запущено.

В gtk.Settings есть свойства, которые заставляют ваше приложение искать меньше файлов в истории, gtk-recent-files-limit и gtk-recent-files-max-age, но они не препятствуют записи ~/.local/share/recently-used.xbel в.

Чтобы запретить запись recently-used.xbel, можно защитить ее от записи или заменить на папку.В этом случае GTK по-прежнему пытается добавить все файлы, но каждая попытка не удалась.Задержка составляет около 1 секунды на 200 файлов - я полагаю, что затраты на попытку все еще значительны.

Поскольку, похоже, нет способа отключить это поведение gtk.FileChooser, единственный другой способ - использоватьдругой виджет для выбора файлов.Даже при использовании 30000 файлов ощутимая задержка при использовании устаревшего виджета gtk.FileSelection отсутствует.

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

2 голосов
/ 14 февраля 2012

Это может не считаться ответом, но это может помочь.

Посмотрев, почему диалоги выбора файлов в gtk2 открывались так медленно, я обнаружил, что gtk.FileChooserDialog s не являются легкимиобъекты.

Вы не должны создавать один для одного использования, а затем уничтожить его.Вместо этого вы должны использовать их повторно, так как вы можете просто .hide() их, и они появятся снова при повторном вызове .run().

обратите внимание, что использование dialog.set_current_folder(dialog.get_current_folder()) заставляет листинг обновляться.

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


Если я изменю ваш код на следующийчто становится:

import gtk,gobject,time

def print_how_long_it_was_frozen():
    print time.time() - start_time

def button_clicked(button):
    response = dialog.run()
    files = dialog.get_filenames()
    dialog.hide()
    for i, f in enumerate(files):
        print i

    global start_time
    start_time = time.time()
    gobject.idle_add(print_how_long_it_was_frozen)


w = gtk.Window() 
b = gtk.Button('Select files')
w.add(b)
b.connect('clicked', button_clicked)
w.show_all()

dialog = gtk.FileChooserDialog(
            'Select files to add', w, gtk.FILE_CHOOSER_ACTION_OPEN,
            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
                     gtk.STOCK_OPEN, gtk.RESPONSE_OK))
dialog.set_select_multiple(True)
dialog.set_default_response(gtk.RESPONSE_OK)

gtk.main()
dialog.destroy()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...