所以我正忙着编写一个应用程序,需要在一定时间后检查网站的更新,我正在使用带有 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() 在后台运行。 #thread 2
我的想法是否正确还是我错过了什么?
编辑:还行
on_update_click功能:
Thread(target=update).start().
K,计算机不再冻结:D
所以现在发生的事情是,只有当我关闭 Gtk.main() 时,更新线程才会启动。当 UI 关闭时继续更新是件好事,但我也希望在 UI 启动时启动它。
所以我终于设法让它工作了。我需要说:
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 塞巴斯蒂安这是不正确的,因为这会立即调用此线程。这冻结了我的整个计算机。
然后我只是补充说:
Thread(target=update).start()
on_update_clicked函数仅在主线程 Gtk.main() 关闭后才起作用。所以线程没有同时运行。
加入: GObject.threads_init()
这允许线程串行运行到 Python 解释器:GTK 中的线程!
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 部分:误解。
线程是解决问题的第一种方法。您可以创建线程并在该线程内运行长时间运行的阻塞函数(并且您的 GUI 不会挂起)。
另一种方法是使用异步网络,例如使用python-gio(GObject-IO)或其他可以与GLib的主循环一起使用的库(就像他们对Twisted所做的那样)。此方法略有不同,使用非阻塞套接字操作。当套接字(您正在轮询的站点)中的数据可供读取时,您的主循环将进行回调。不幸的是,GIO没有高级HTTP API,因此您可以使用GSocketClient
并手动创建HTTP请求结构。