Gtk小部件出现延迟

0 投票
2 回答
1262 浏览
提问于 2025-04-18 10:08

我想用python3和gi.repository来创建一个Gtk.HeaderBar,上面有一个Gtk.Button。当我点击这个按钮时,它会被一个Gtk.Spinner替代。计算完成后,按钮应该再次出现。

这里有一个我认为应该如何工作的例子,但Gtk.Spinner只在计算(在这个例子中是睡眠)后短暂显示了一下。我该如何让这个旋转的加载图标在整个计算(或睡眠)过程中都能显示出来呢?

from gi.repository import Gtk
import time

class window:
    def __init__(self):
        self.w = Gtk.Window()
        self.button = Gtk.Button('x')
        self.button.connect('clicked', self.on_button_clicked)
        self.spinner = Gtk.Spinner()
        self.hb = Gtk.HeaderBar()
        self.hb.props.show_close_button = True
        self.hb.pack_start(self.button)
        self.w.set_titlebar(self.hb)
        self.w.connect('delete-event', Gtk.main_quit)
        self.w.show_all()

    def on_button_clicked(self, widget):
        self.button.hide()
        self.hb.pack_start(self.spinner)
        self.spinner.show()
        self.spinner.start()
        time.sleep(5)
        self.spinner.stop()
        self.hb.remove(self.spinner)
        self.button.show()

if __name__ == '__main__':
    w = window()
    Gtk.main()

2 个回答

1

问题在于time.sleep()这个函数:它是一个阻塞函数。

def on_button_clicked(self, widget):
    self.button.hide()
    self.hb.pack_start(self.spinner)
    self.spinner.show()
    self.spinner.start()
    t = time.time()
    while time.time() - t < 5:
        Gtk.main_iteration()
    self.spinner.stop()
    self.hb.remove(self.spinner)
    self.button.show()

我想这就是你所期待的效果。

补充一下:你可以在while循环里面加上time.sleep(.1),这样可以节省CPU的使用,但别忘了Gtk.main_iteration()这个函数:它是用来让你从while循环退出,回到主循环的(比如显示加载动画、进度条等等)。

1

GTK+ 是一个事件驱动的系统,意思是它会根据事件来运行,比如用户点击按钮或输入内容。这里的主循环(mainloop)需要保持空闲,以便随时更新用户界面。而那些需要时间的操作,比如读取文件、建立网络连接或者进行复杂计算,应该是异步进行的,也就是说它们可以在后台运行,不会阻塞主循环。

在你的情况中,代码大概是这样的:

def on_button_clicked(self, widget):
    self.button.hide()
    self.spinner.show()
    self.spinner.start()
    GLib.timeout_add_seconds (5, self.processing_finished)

def processing_finished(self):
    self.spinner.stop()
    self.spinner.hide()
    self.button.show()

注意,我去掉了 pack 和 remove 的调用:这些操作应该放在 __init__() 方法里。同时,你也需要在里面加上 from gi.repository import GLib

这样一来,主循环就可以随时更新用户界面。如果你真的想使用像 sleep() 这样的阻塞调用,那就需要在另一个线程中进行。不过,我的建议是使用一些异步的库,比如 timeout_add_seconds() 这个调用。

撰写回答