You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从线程向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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 22:32:37