GTK中如何防止线程多实例运行解决TreeView重复条目问题
解决GTK多线程操作ListStore导致重复条目的问题
针对快速点击按钮触发多线程并行操作ListStore,导致TreeView出现重复条目的问题,以下是两种可行的解决方案:
方案1:当前线程运行时禁止启动新线程
给窗口类添加线程状态标记,每次点击按钮时先检查是否已有活跃线程,仅当无活跃线程时才启动新线程,线程执行完毕后重置状态标记。
修改后的代码示例:
import threading import time import Gtk class Main(Gtk.Window): def __init__(self): Gtk.Window.__init__(self) self.connect("destroy", Gtk.main_quit) self.liststore = Gtk.ListStore(str) self.treeview = Gtk.TreeView() self.treeview.set_model(self.liststore) cellrenderertext = Gtk.CellRendererText() treeviewcolumn = Gtk.TreeViewColumn("Items", cellrenderertext, text=0) self.treeview.append_column(treeviewcolumn) self.button = Gtk.Button(label='Button') box = Gtk.Box() box.set_orientation(Gtk.Orientation.VERTICAL) box.add(self.button) box.add(self.treeview) self.add(box) self.button.connect('clicked', self.button_clicked) # 初始化线程状态标记 self.active_thread = None def button_clicked(self, widget): # 检查是否已有活跃线程 if self.active_thread and self.active_thread.is_alive(): return def threaded(): self.liststore.clear() self.liststore.append(['first_item']) time.sleep(0.1) self.liststore.append(['second_item']) time.sleep(0.1) self.liststore.append(['third_item']) # 线程结束后重置标记 self.active_thread = None self.active_thread = threading.Thread(target=threaded, daemon=True) self.active_thread.start() if __name__ == '__main__': window = Main() window.show_all() Gtk.main()
方案2:终止已运行的线程再启动新线程
Python原生线程无法强制终止,因此可以通过threading.Event实现线程的优雅退出。每次点击按钮时,先触发停止事件让旧线程退出,再启动新线程。
修改后的代码示例:
import threading import time import Gtk class Main(Gtk.Window): def __init__(self): Gtk.Window.__init__(self) self.connect("destroy", Gtk.main_quit) self.liststore = Gtk.ListStore(str) self.treeview = Gtk.TreeView() self.treeview.set_model(self.liststore) cellrenderertext = Gtk.CellRendererText() treeviewcolumn = Gtk.TreeViewColumn("Items", cellrenderertext, text=0) self.treeview.append_column(treeviewcolumn) self.button = Gtk.Button(label='Button') box = Gtk.Box() box.set_orientation(Gtk.Orientation.VERTICAL) box.add(self.button) box.add(self.treeview) self.add(box) self.button.connect('clicked', self.button_clicked) # 初始化停止事件和线程对象 self.stop_event = threading.Event() self.active_thread = None def button_clicked(self, widget): # 如果有活跃线程,触发停止事件并等待其退出 if self.active_thread and self.active_thread.is_alive(): self.stop_event.set() self.active_thread.join() self.stop_event.clear() def threaded(): self.liststore.clear() # 每次操作前检查是否需要停止 if self.stop_event.is_set(): return self.liststore.append(['first_item']) time.sleep(0.1) if self.stop_event.is_set(): return self.liststore.append(['second_item']) time.sleep(0.1) if self.stop_event.is_set(): return self.liststore.append(['third_item']) self.active_thread = threading.Thread(target=threaded, daemon=True) self.active_thread.start() if __name__ == '__main__': window = Main() window.show_all() Gtk.main()
补充说明:GTK的UI操作规范上应在主线程执行,虽然ListStore多线程操作有时能正常工作,但更严谨的做法是用
Gtk.idle_add或GObject信号机制将UI更新逻辑放到主线程中执行。
内容的提问来源于stack exchange,提问作者user_968563
相关产品推荐
相关产品推荐

