Why can't I have a loop in the onclick functio

2019-08-16 19:01发布

问题:

I am creating a user interface using PyGObject and I want the program to "run" a loop when a button is clicked and refresh the window accordingly. For example, in the following code, I want the program - on the click of the button - to update the label with a number, sleep for 1 second, increment the number and update the label again with the number, etc. The program currently, however, just shows the changes at the very end (after the loop is finished). How do I make the changes to the label show every time I iterate over the loop. Note that this is a simplified version of my question so I would appreciate a general solution to how to doing showing every update to the window inside a loop of the onclick function of a button. I have attached my (faulty) code below:

import sys
from time import sleep
import gi
gi.require_version("Gtk", "3.0")
from gi.repository import Gtk

class SpecialButton(Gtk.Box):

    def __init__(self, GUI):
        Gtk.Box.__init__(self)

        self.GUI = GUI
        self.set_border_width(10)
        self.message = "1"

        button = Gtk.Button.new_with_label("Change label")
        button.connect("clicked", self.on_click)
        self.pack_start(button, True, True, 0)

    def on_click(self, widget):
        msg = int(self.message)
        for i in range(5):
            self.GUI.restart_window(str(msg))
            msg += 1
            sleep(1)

class GUI:

    def __init__(self):
        self.win = Gtk.Window()
        self.window_grid = Gtk.Grid()
        self.box = Gtk.Box(spacing=10)
        self.label = Gtk.Label("Default label")
        self.win.connect("delete-event", Gtk.main_quit)
        self.start_window()

    def start_window(self):
        self.box.pack_start(SpecialButton(self), True, True, 0)
        self.window_grid.add(self.box)
        self.window_grid.add(self.label)
        self.win.add(self.window_grid)
        self.win.show_all()

    def restart_window(self, label="Default label"):
        self.window_grid.destroy()
        self.window_grid = Gtk.Grid()
        self.box = Gtk.Box(spacing=10)
        self.label = Gtk.Label(label)
        self.start_window()

def main():
    app = GUI()
    Gtk.main()

if __name__ == "__main__":
    sys.exit(main())

回答1:

It's important not to run any kind of blocking code on the main thread because it takes away the opportunity of whatever GUI library you're using to perform the vital work of keeping the UI looking clean and crisp. In this case, it's not really the loop that's the problem, but the call to time.sleep, which blocks. Perhaps try using the the click handler to start a worker thread to run the loop, or begin an asynchronous process involving non-blocking calls to asyncio.sleep.



标签: gtk pygobject