【问题标题】:Threading in Gtk pythonGtk python中的线程
【发布时间】:2012-08-12 14:37:11
【问题描述】:

所以我正忙于编写一个需要在一定时间后检查网站更新的应用程序,我正在使用带有 Gtk +3 的 python

main.py 文件

class Gui:
    ...
    def on_update_click():
        update()

app=Gui()
Gtk.main()

update.py 文件

def update():
    #check site for updates
    time.sleep(21600) #check again in 6hrs

我怀疑我将不得不使用线程。 我的想法是:

Gtk.main() 运行主线程。

当用户点击更新按钮时,update() 在后台运行。 #线程2

我的想法是正确的还是我错过了什么?

编辑: 好的,
on_update_click 函数:

            Thread(target=update).start(). 

K,电脑不再死机了:D

所以现在发生的事情是,只有当我关闭 Gtk.main() 时,更新线程才会启动。关闭 UI 时继续更新很好,但我也希望它在 UI 启动时启动。

【问题讨论】:

  • on_update_click() 错过了self 参数。

标签: python multithreading gtk3


【解决方案1】:

所以我终于设法让它工作了。 我需要说:

from gi.repository import Gtk,GObject

GObject.threads_init()
Class Gui:
    .....
    ......
    def on_update_click():
            Thread(target=update).start()

一开始我用的是:

thread.start_new_thread(update())

在 on_update_click 函数中。如前所述,我的 J.F Sebastian 这是不正确的,因为这会立即调用该线程。这冻结了我的整个计算机。

然后我就补充了:

Thread(target=update).start()

on_update_clicked 函数仅在主线程 Gtk.main() 关闭后才起作用。所以线程没有同时运行。

通过添加: GObject.threads_init()

这允许线程串行运行到 python 解释器: Threads in Gtk!

【讨论】:

    【解决方案2】:

    thread.start_new_thread(update()) 是错误的。它在主线程中立即调用update(),你不应该直接使用thread 模块;请改用threading 模块。

    您可以调用threading.current_thread() 来找出执行update() 的线程。

    为了简化您的代码,您可以在主线程中运行所有 gtk 代码,并使用阻塞操作来检索网页并在后台线程中运行它们。

    基于the extended example from GTK+ 3 tutorial:

    #!/usr/bin/python
    import threading
    import urllib2
    from Queue import Queue
    
    from gi.repository import Gtk, GObject
    
    UPDATE_TIMEOUT = .1 # in seconds
    
    _lock = threading.Lock()
    def info(*args):
        with _lock:
            print("%s %s" % (threading.current_thread(), " ".join(map(str, args))))
    
    class MyWindow(Gtk.Window):
    
        def __init__(self):
            Gtk.Window.__init__(self, title="Hello World")
    
            self.button = Gtk.Button(label="Click Here")
            self.button.connect("clicked", self.on_button_clicked)
            self.add(self.button)
    
            self.updater = Updater()
            self._update_id = None
            self.update()
    
        def on_button_clicked(self, widget):
            info('button_clicked')
            self.update()
    
        def update(self):
            if self._update_id is not None: 
                GObject.source_remove(self._update_id)
    
            self.updater.add_update(self.done_updating) # returns immediately
            # call in UPDATE_TIMEOUT seconds
            self._update_id = GObject.timeout_add(
                int(UPDATE_TIMEOUT*1000), self.update)
    
        def done_updating(self, task_id):
            info('done updating', task_id)
            self.button.set_label("done updating %s" % task_id)
    
    
    class Updater:
        def __init__(self):
            self._task_id = 0
            self._queue = Queue(maxsize=100) #NOTE: GUI blocks if queue is full
            for _ in range(9):
                t = threading.Thread(target=self._work)
                t.daemon = True
                t.start()
    
        def _work(self):
            # executed in background thread
            opener = urllib2.build_opener()
            for task_id, done, args in iter(self._queue.get, None):
                info('received task', task_id)
                try: # do something blocking e.g., urlopen()
                    data = opener.open('http://localhost:5001').read()
                except IOError:
                    pass # ignore errors
    
                # signal task completion; run done() in the main thread
                GObject.idle_add(done, *((task_id,) + args))
    
        def add_update(self, callback, *args):
            # executed in the main thread
            self._task_id += 1
            info('sending task ', self._task_id)
            self._queue.put((self._task_id, callback, args))
    
    GObject.threads_init() # init threads?
    
    win = MyWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    
    Gtk.main()
    

    注意:GObject.idle_add() 是唯一一个从不同线程调用的与 gtk 相关的函数。

    另见Multi-threaded GTK applications – Part 1: Misconceptions

    【讨论】:

    • 好的,所以在on_upate_click函数中添加了
    【解决方案3】:

    线程是解决问题的第一种方法。您可以创建线程并在该线程内运行长时间运行的阻塞函数(并且您的 GUI 不会挂起)。

    另一种方法是使用异步网络,例如使用 python-gio (GObject-IO) 或其他可能与 GLib 的主循环 (like they do with Twisted) 一起工作的库。这种方法有点不同,它使用非阻塞套接字操作。当来自套接字(您正在轮询的站点)的数据可供读取时,您的主循环将进行回调。不幸的是,GIO 没有高级 HTTP API,因此您可以使用 GSocketClient 并手动创建 HTTP 请求结构。

    【讨论】:

    • 我不明白你的意思:运行长时间运行的阻塞函数
    • 连接到站点并获取页面内容可能是一个长时间运行的操作 - 长达几秒钟。如果你用例如urllib,您的 GUI(该应用程序目前是单线程的)将在执行更新功能时冻结。这意味着阻塞:在更新完成之前无法执行 GUI 事件处理程序。这就是为什么我建议在线程中运行 update() 或使用异步(非阻塞)网络操作。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-04
    • 1970-01-01
    相关资源
    最近更新 更多