Python 使用gtk.FileChooserDialog选择大量文件时出现与平台相关的性能问题

Python 使用gtk.FileChooserDialog选择大量文件时出现与平台相关的性能问题,python,ubuntu,filesystems,gtk,Python,Ubuntu,Filesystems,Gtk,我有一个pygtk程序,可以在Windows和Ubuntu上运行。它是Python2.7和带有静态绑定的gtk2(即无gobject内省)。我遇到的问题存在于Ubuntu上,但不存在于Windows上 我的程序应该能够处理大量的文件(这里我测试了大约200个),但是每个文件的实际处理量并不多。我按每个文件对处理进行排队,并向用户显示进度 问题是,在使用gtk.FileChooserDialog(control-a是您的朋友)选择文件后,程序挂起,gtk事件在相当长的一段时间内没有得到处理,即使我

我有一个pygtk程序,可以在Windows和Ubuntu上运行。它是Python2.7和带有静态绑定的gtk2(即无gobject内省)。我遇到的问题存在于Ubuntu上,但不存在于Windows上

我的程序应该能够处理大量的文件(这里我测试了大约200个),但是每个文件的实际处理量并不多。我按每个文件对处理进行排队,并向用户显示进度

问题是,在使用gtk.FileChooserDialog(control-a是您的朋友)选择文件后,程序挂起,gtk事件在相当长的一段时间内没有得到处理,即使我的回调函数已返回。在此期间,所有内核上的CPU使用率约为80%,
iotop
显示我的进程正在以每秒20MB的速度写入磁盘,而其他应用程序会间歇性无响应-Chrome、Xorg、compiz、banshee和gedit都有较高的CPU使用率(在选择文件之前使用率较低)

下面是一些示例代码。若要复制,请单击按钮,从某处选择大约200个文件(大约十个屏幕值按住shift键和down键),然后单击“确定”。什么文件都不重要,什么都不做

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上。窗户上的悬挂时间不到一秒钟

我怀疑这是由于某些Gnome或Unity的“最近的文件”功能或其他活动跟踪造成的。进程
zeitgeist守护进程
在挂起期间也有很高的CPU使用率,尽管杀死它并不能解决问题。使用Zeitgeist活动日志管理器禁用日志记录也是如此。即使时代精神可以被禁用,我也不能期望我的用户禁用它

是否有人知道如何禁用gtk应用程序的最新文件报告,或者知道任何其他可能导致此问题的原因

必须通过“选择文件夹”对话框添加大量文件进行处理,但对于数量较少的文件,每个文件的挂起时间似乎约为半秒,这对于其他响应迅速的应用程序来说是不可接受的


(测试在32位Windows 7和64上进行,但在Ubuntu 11.10.Python 2.7和pygtk 2.24上都进行)

这可能不算答案,但可能会有所帮助

在研究了为什么gtk2中的文件选择器对话框打开得如此缓慢之后,我发现
gtk.FileChooserDialog
s不是轻量级对象

你不应该为一次使用创建一个,然后销毁它。相反,您应该重用它们,因为您只需
.hide()
它们,当再次调用
.run()
时,它们就会重新出现

请注意,使用
dialog.set\u current\u folder(dialog.get\u current\u 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()

减速是因为
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)))
显示(在整夜运行后)每个文件的延迟随着最近文件数的增加而增加:

由于没有方法将多个文件添加到
RecentManager
,因此每次只添加一个文件

每次添加一个时,其他gtk应用程序都会收到通知,说明最近使用的文件列表(存储在
~/.local/share/recently used.xbel
中)已更改。然后,他们解析文件并遍历项目,查找最近的n个项目(其中n是特定于应用程序的),以显示它们。确定每个项目的最新文件时

最近使用的
。xbel
能够使问题更加严重。因此,如果您在最近使用的.xbel
中有5000个项目,并且您正在使用
gtk.FileChooser
选择200个文件,那么对于每个运行的gtk应用程序,您将得到(从n=1到200的总和)(5000+n)~100万次系统调用

gtk.Settings
中有一些属性可以让你的应用程序在历史记录中查找更少的文件,
gtk-recent-files-limit
gtk-recent-files-max-age
,但它们不会阻止写入
~/.local/share/recently-used.xbel

要防止最近使用的.xbel
被写入,可以对其进行写保护,或者用文件夹替换它。在这种情况下,gtk仍然尝试添加所有文件,但每次尝试都失败。延迟大约为每200个文件1秒-我猜尝试的开销仍然很大

由于似乎无法关闭
gtk.FileChooser
的这种行为,因此唯一的其他方法是使用不同的FileChooser小部件。即使有30000个文件,在使用不推荐使用的
gtk.FileSelection
小部件时也没有明显的延迟


这是一个丑陋的小部件,但我想我必须使用它并提交一个bug报告/功能请求,以便能够通过
gtk禁用最近的文件报告。FileChooser

有趣的是,将hide()改为destroy()似乎将挂起时间大约减少了一半。相关bug报告的事实是
gtk.RecentManager.add_item()
速度较慢,看起来它可能已在gtk3中修复,但在gtk2中未修复。