如何从线程向Gtk应用传递数据?避免阻塞主循环
解决Gtk主线程与Socket子线程的UI更新问题
你的问题核心是不能在Gtk主循环线程中调用阻塞式的queue.get(),这会卡住整个UI事件循环。下面提供两种可行的解决方案:
方案一:用GLib超时回调轮询队列
利用GLib.timeout_add定期(非阻塞)检查队列中的数据,避免阻塞主循环:
修改后的完整代码:
import socket from queue import Queue import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib import threading HOST = '' # Listen on all available network interfaces PORT = 5000 # Arbitrary port number def socket_server(queue): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) print('Listening for incoming connections...') while True: client_socket, client_address = server_socket.accept() data = client_socket.recv(1024) print(f'Received data from client: {data.decode()}') queue.put(data.decode()) client_socket.close() class MyWindow(Gtk.Window): def __init__(self, queue): self.queue = queue Gtk.Window.__init__(self, title="GTK App") self.set_default_size(400, 300) self.label = Gtk.Label(label="Welcome to the GTK App!") self.add(self.label) # 每隔100ms检查一次队列 GLib.timeout_add(100, self.check_queue) def check_queue(self): # 非阻塞获取队列中所有待处理的数据 while not self.queue.empty(): try: text = self.queue.get_nowait() self.label.set_text(text) except Queue.Empty: break return True # 返回True让超时回调持续执行 if __name__ == '__main__': q = Queue() socket_thread = threading.Thread(target=socket_server, args=(q,)) socket_thread.daemon = True # 设置为守护线程,退出主程序时自动结束 socket_thread.start() win = MyWindow(q) win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
关键说明:
GLib.timeout_add(100, self.check_queue):注册一个每隔100ms执行一次的回调函数queue.get_nowait():非阻塞获取队列数据,避免卡住主循环- 返回
True确保超时回调会持续被调用
方案二:直接用GLib线程安全回调更新UI
Gtk的UI操作必须在主线程执行,GLib.idle_add可以将UI更新操作放入主循环的空闲队列,自动在主线程执行,无需使用Queue:
修改后的完整代码:
import socket import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, GLib import threading HOST = '' # Listen on all available network interfaces PORT = 5000 # Arbitrary port number def socket_server(update_callback): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((HOST, PORT)) server_socket.listen(1) print('Listening for incoming connections...') while True: client_socket, client_address = server_socket.accept() data = client_socket.recv(1024) received_text = data.decode() print(f'Received data from client: {received_text}') # 将UI更新操作提交到主线程执行 GLib.idle_add(update_callback, received_text) client_socket.close() class MyWindow(Gtk.Window): def __init__(self): Gtk.Window.__init__(self, title="GTK App") self.set_default_size(400, 300) self.label = Gtk.Label(label="Welcome to the GTK App!") self.add(self.label) def update_label(self, text): self.label.set_text(text) return False # 执行一次后不再重复调用 if __name__ == '__main__': win = MyWindow() # 将更新标签的方法作为回调传给socket线程 socket_thread = threading.Thread(target=socket_server, args=(win.update_label,)) socket_thread.daemon = True socket_thread.start() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main()
关键说明:
GLib.idle_add(update_callback, received_text):线程安全地将UI更新任务提交给主线程- 无需使用Queue,直接在socket线程中触发UI更新,代码更简洁
update_label返回False表示该回调只执行一次
两种方案都能解决阻塞问题,方案二更高效直接,推荐使用。
内容的提问来源于stack exchange,提问作者Parker
相关产品推荐
相关产品推荐

