3

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

main.py 文件

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

app=Gui()
Gtk.main()

更新.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 启动时启动。

4

3 回答 3

7

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

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 函数中。如前所述,我的 JF Sebastian 这是不正确的,因为这会立即调用该线程。这冻结了我的整个计算机。

然后我补充说:

Thread(target=update).start()

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

通过添加: GObject.threads_init()

这允许线程串行运行到 python 解释器: Gtk 中的线程

于 2012-08-15T05:23:02.957 回答
4

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

您可以调用threading.current_thread()以找出执行哪个线程update()

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

基于GTK+ 3 教程中的扩展示例

#!/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 相关的函数。

另请参阅多线程 GTK 应用程序 - 第 1 部分:误解

于 2012-08-12T22:14:26.747 回答
0

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

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

于 2012-08-12T15:08:45.323 回答