Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/309.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Gtk.ProgressBar在Python中不工作_Python_Progress Bar_Gtk3_Pygobject - Fatal编程技术网

Gtk.ProgressBar在Python中不工作

Gtk.ProgressBar在Python中不工作,python,progress-bar,gtk3,pygobject,Python,Progress Bar,Gtk3,Pygobject,我试图在Python和Gtk3中使用进度条,但它没有得到更新。我在这个论坛上读了一些关于pygtk的文档和问题,但我真的不明白 我编写了一个代码,只用于测试目的。当您单击一个按钮时,它会递归地读取目录的内容。我的意图是在读取所有这些文件时使用进度条 这是全部代码: import os from gi.repository import Gtk class MyWindow(Gtk.Window): """Progress Bar""" def __init__(self):

我试图在Python和Gtk3中使用进度条,但它没有得到更新。我在这个论坛上读了一些关于pygtk的文档和问题,但我真的不明白

我编写了一个代码,只用于测试目的。当您单击一个按钮时,它会递归地读取目录的内容。我的意图是在读取所有这些文件时使用进度条

这是全部代码:

import os
from gi.repository import Gtk

class MyWindow(Gtk.Window):
    """Progress Bar"""

    def __init__(self):
        Gtk.Window.__init__(self, title='Progress Bar')
        self.set_default_size(300, 75)
        self.set_position(Gtk.WindowPosition.CENTER)
        self.set_border_width(10)

        # read dir contents
        mydir = 'Documents'
        home_path = os.environ.get('HOME')
        dir_path = os.path.join(home_path, mydir)
        self.dir_files_list(dir_path)

        # create a grid
        self.grid = Gtk.Grid(column_homogeneous=True, row_homogeneous=True,
                             column_spacing=10, row_spacing=10)
        self.add(self.grid)

        # create a progress bar
        self.progressbar = Gtk.ProgressBar()
        self.grid.add(self.progressbar)

        # create a button
        self.button = Gtk.Button(stock=Gtk.STOCK_APPLY)
        self.button.connect('clicked', self.on_button_pressed)
        self.grid.attach(self.button, 0, 1, 1, 1)

    # function to read the dir contents
    def dir_files_list(self, dir_path):
        self.dir_list = []
        for root, dirs, files in os.walk(dir_path):
            for fn in files:
                f = os.path.join(root, fn)
                self.dir_list.append(f)

    # function to update the progressbar
    def on_button_pressed(self, widget):
        self.progressbar.set_fraction(0.0)
        frac = 1.0 / len(self.dir_list)
        for f in self.dir_list:
            new_val = self.progressbar.get_fraction() + frac
            print new_val, f
            self.progressbar.set_fraction(new_val)
        return True

def main():
    """Show the window"""
    win = MyWindow()
    win.connect('delete-event', Gtk.main_quit)
    win.show_all()
    Gtk.main()
    return 0

if __name__ == '__main__':
    main()

任何来自更有经验的程序员的帮助都将不胜感激

首先,问题是上面的代码运行得太快,因此您会看到进度条更新得太快,似乎没有得到更新。这样做是因为您没有在搜索目录的同时显示结果。当类初始化时,您执行搜索,然后单击按钮,列表将以全速读取并显示在进度栏中。我很确定,如果目录很大,那么当你启动脚本时,窗口显示需要几秒钟的时间,而进度条最终也会在几毫秒内完成

基本上,你所面临的问题是,你在同一个线程中做每件事。Gtk在它自己的线程中,监听来自GUI的事件并更新GUI。大多数情况下,您将使用Gtk.ProgressBar完成一些需要一些时间才能完成的任务,您需要同时执行GUI线程和正在执行任务的工作线程,并将更新从工作线程发送到GUI线程,以便它更新进度条。如果像上面的代码一样,在同一个线程中运行所有内容,那么最终会出现此类问题,例如当GUI冻结时,即由于GUI线程突然执行一些与GUI无关的工作而变得无响应

在PyGTK中,您使用了gobject.idle_addfunction方法参数,这样您就可以从工作线程与GUI线程通信,因此当GUI线程空闲时,它将使用这些参数执行该函数,例如,更新Gtk.ProgressBar

我解决该问题的方法在这里实现,请注意,是针对PyGTK的:

基本上,它是整个应用程序共享的加载窗口。当您想要开始加载某些内容或执行一些繁重的工作时,您必须对WorkingRead类进行子类化。然后,您只需使用WorkingRead子类实例作为参数调用show方法,并完成。在WorkingRead子类中,必须实现有效负载函数,即执行繁重工作的函数。您可以直接从WorkingRead调用加载窗口中的pulse方法来更新ProgressBar,并且不应该关心线程通信,因为该逻辑在那里实现

希望解释有帮助

编辑:


我刚刚将上面的代码移植到PyGObject,您可以在这里找到示例:

首先,问题是上面的代码运行得太快,所以您看到进度条更新得太快,似乎没有得到更新。这样做是因为您没有在搜索目录的同时显示结果。当类初始化时,您执行搜索,然后单击按钮,列表将以全速读取并显示在进度栏中。我很确定,如果目录很大,那么当你启动脚本时,窗口显示需要几秒钟的时间,而进度条最终也会在几毫秒内完成

基本上,你所面临的问题是,你在同一个线程中做每件事。Gtk在它自己的线程中,监听来自GUI的事件并更新GUI。大多数情况下,您将使用Gtk.ProgressBar完成一些需要一些时间才能完成的任务,您需要同时执行GUI线程和正在执行任务的工作线程,并将更新从工作线程发送到GUI线程,以便它更新进度条。如果像上面的代码一样,在同一个线程中运行所有内容,那么最终会出现此类问题,例如当GUI冻结时,即由于GUI线程突然执行一些与GUI无关的工作而变得无响应

在PyGTK中,您使用了gobject.idle_addfunction方法参数,这样您就可以从工作线程与GUI线程通信,因此当GUI线程空闲时,它将使用这些参数执行该函数,例如,更新Gtk.ProgressBar

我解决该问题的方法在这里实现,请注意,是针对PyGTK的:

基本上,它是整个应用程序共享的加载窗口。当您想要开始加载某些内容或执行一些繁重的工作时,您必须对WorkingRead类进行子类化。然后,您只需使用WorkingRead子类实例作为参数调用show方法,并完成。在WorkingRead子类中,必须实现有效负载函数,即doe 这是一项繁重的工作。您可以直接从WorkingRead调用加载窗口中的pulse方法来更新ProgressBar,并且不应该关心线程通信,因为该逻辑在那里实现

希望解释有帮助

编辑:


我刚刚将上述内容移植到PyGObject,您可以在这里找到示例:

问题是,在创建窗口时,您正在处理整个目录。甚至在您将其显示在self.dir\u files\u listdir\u path行之前。在我看来,你似乎想在按下“应用”按钮后调用它

至少有两种可能的解决方案:使用线程或使用迭代器。对于您的特定用例,我认为迭代器就足够了,而且比pythonic更简单。我只会在你真正需要的时候推荐线程

您可以一次处理每个目录中的每个文件,而不是事先遍历整个目录,然后再进行处理

在您的示例中,我会将方法dir_files_list重命名为walk并按下按钮,如下所示:

从gc导入收集 从gi.repository导入Gtk,GObject 我们现在还需要导入GObject以使用idle_add,它在没有更高优先级的事件挂起时调用回调。此外,一旦任务完成,我们需要删除回调以不再调用它。为此,我们需要source_remove

我将您的方法dir_files_list重命名为walk,因为它在语义上更接近迭代器。当我们遍历每个目录和文件时,我们将使用yield临时返回。记住,yield True意味着有待处理的项。yield False意味着我们停止迭代

因此,方法是:

def walkself,dir_路径: self.dir_list=[] 对于os.walkdir\u路径中的根目录、目录和文件: i=0.0 self.set_titleos.path.dirnameroot 对于文件中的fn: i=i+1.0 f=os.path.joinroot,fn self.dir\u list.appendf self.progressbar.set_细分/lenfiles 收集 屈服于真实 屈服于真实 GObject.source\u removeself.id 假屈服 现在,我们在这里更新progressbar。在这一部分中,我将更新每个目录中的进度条。也就是说,它将在每个子目录中重新启动,并且进度条将在每个子目录中重新启动。要了解正在访问哪个目录,请使用当前目录设置窗口标题。这里要理解的重要部分是产量。你可以让它适应你想做的任何事情

遍历整个目录后,我们必须返回yield False,但在此之前,我们将删除回调GObject.source\u removeself.id

我认为Self.DriLIST在这里不再有用,但是你可能有不同的想法。

你可能会想知道步行是什么时候,怎么叫的?按下按钮时可以设置:

def on_button_pressed(self, button, *args):
    homedir = os.path.expanduser('~')
    rootdir = os.path.join(homedir, 'Documents')

    self.task = self.walk(rootdir)
    self.id = GObject.idle_add(self.task.next)
self.task是一个迭代器,它具有从self.task检索下一项的next方法,只要没有idle_add的挂起事件,就会调用self.task。我们获取id以便在完成后删除回调

您必须从_init__方法中删除self.dir_files_listdir_路径行


还有一件事:我手动调用了垃圾收集器gc.collect,因为它在处理大目录时非常有用,具体取决于您对它们所做的操作。

问题是,在创建窗口时,您正在处理整个目录。甚至在您将其显示在self.dir\u files\u listdir\u path行之前。在我看来,你似乎想在按下“应用”按钮后调用它

至少有两种可能的解决方案:使用线程或使用迭代器。对于您的特定用例,我认为迭代器就足够了,而且比pythonic更简单。我只会在你真正需要的时候推荐线程

您可以一次处理每个目录中的每个文件,而不是事先遍历整个目录,然后再进行处理

在您的示例中,我会将方法dir_files_list重命名为walk并按下按钮,如下所示:

从gc导入收集 从gi.repository导入Gtk,GObject 我们现在还需要导入GObject以使用idle_add,它在没有更高优先级的事件挂起时调用回调。此外,一旦任务完成,我们需要删除回调以不再调用它。为此,我们需要source_remove

我将您的方法dir_files_list重命名为walk,因为它在语义上更接近迭代器。当我们遍历每个目录和文件时,我们将使用yield临时返回。记住,yield True意味着有待处理的项。yield False意味着我们停止迭代

因此,方法是:

def walkself,dir_路径: self.dir_list=[] 对于os.walkdir\u路径中的根目录、目录和文件: i=0.0 self.set_titleos.path。 地黄根 对于文件中的fn: i=i+1.0 f=os.path.joinroot,fn self.dir\u list.appendf self.progressbar.set_细分/lenfiles 收集 屈服于真实 屈服于真实 GObject.source\u removeself.id 假屈服 现在,我们在这里更新progressbar。在这一部分中,我将更新每个目录中的进度条。也就是说,它将在每个子目录中重新启动,并且进度条将在每个子目录中重新启动。要了解正在访问哪个目录,请使用当前目录设置窗口标题。这里要理解的重要部分是产量。你可以让它适应你想做的任何事情

遍历整个目录后,我们必须返回yield False,但在此之前,我们将删除回调GObject.source\u removeself.id

我认为Self.DriLIST在这里不再有用,但是你可能有不同的想法。

你可能会想知道步行是什么时候,怎么叫的?按下按钮时可以设置:

def on_button_pressed(self, button, *args):
    homedir = os.path.expanduser('~')
    rootdir = os.path.join(homedir, 'Documents')

    self.task = self.walk(rootdir)
    self.id = GObject.idle_add(self.task.next)
self.task是一个迭代器,它具有从self.task检索下一项的next方法,只要没有idle_add的挂起事件,就会调用self.task。我们获取id以便在完成后删除回调

您必须从_init__方法中删除self.dir_files_listdir_路径行


还有一件事:我手动调用了垃圾收集器gc.collect,因为它在处理大目录时可能会很有用,具体取决于您对它们所做的操作。

非常感谢。现在有了你的解释,我能够对Gtk+和线程有更多的了解,但我不确定我是否能够在我的程序中实现这一点。。。好吧,至少我要试试。。。我感谢你努力解释我的问题之外隐藏的东西。我更新了答案,检查PyGObject版本:非常感谢你的时间。我要看看这个版本。格雷西亚斯!我放弃了,这对我的技能来说太复杂了。我需要一些更简单的,并在一个文件中,如果可能的话:无论如何,非常感谢!非常感谢。现在有了你的解释,我能够对Gtk+和线程有更多的了解,但我不确定我是否能够在我的程序中实现这一点。。。好吧,至少我要试试。。。我感谢你努力解释我的问题之外隐藏的东西。我更新了答案,检查PyGObject版本:非常感谢你的时间。我要看看这个版本。格雷西亚斯!我放弃了,这对我的技能来说太复杂了。我需要一些更简单的,并在一个文件中,如果可能的话:无论如何,非常感谢!非常感谢@gpoo对您的帮助!我将花一些时间来阅读这篇文章,并尝试理解和吸收一切:-请随意询问任何看起来模糊或需要更多解释的问题。非常感谢@gpoo的帮助!我将花一些时间来阅读这篇文章,并试图理解和吸收所有的东西:-请随意问任何看起来模糊或需要更多解释的问题。