Skip to main content
 首页 » 编程设计

python gtk 3.0 是否不支持从辅助线程访问 GUI

2024年08月25日34mq0036

我开发了一个基于python Gtk3的GUI程序。在主窗口中有一个按钮和一个进度条,我的目的是当按下按钮时有另一个线程运行做一些工作最后另一个 GTK 窗口显示结果,同时 preogress 可以更新进度主窗口正确。

但我总是得到如下错误:

[xcb] Unknown sequence number while processing queue 
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called 
[xcb] Aborting, sorry about that. 
python: xcb_io.c:274: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed. 
Aborted (core dumped) 

我真的不知道那里出了什么问题,对此一无所知,任何人都可以帮忙看一下并在这里提供一个例子吗?

非常感谢!

我在这里开发了一个测试程序如下,在Linux平台下总是报如下错误: (fileChooser.py:40834): Gtk-CRITICAL **: gtk_text_attributes_ref: 断言 'values!= NULL' 失败

(fileChooser.py:40834):Gtk-CRITICAL **:gtk_text_attributes_ref:断言“值!= NULL”失败

(fileChooser.py:40834): Pango-CRITICAL **: pango_layout_new: 断言 'context!= NULL' 失败 段错误(核心转储)

#!/usr/bin/env python 
 
import os 
import re 
import multiprocessing 
import threading 
import platform 
from gi.repository import Gtk, GLib, Gdk, GObject 
 
class ANA(Gtk.Window): 
    def __init__(self): 
        Gtk.Window.__init__(self)                                                                                                                                                               
        self.tree_store = dict() 
        self.COL_NAME = 0  
        self.COL_COLOR = 1  
 
        self.window = Gtk.Window() 
        self.window.set_title("Analysing Results") 
        self.window.set_size_request(1000, 750) 
        self.connect('destroy', lambda *w: Gtk.main_quit()) 
 
        self.main_vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=1) 
        self.window.add(self.main_vbox) 
 
        self.hpaned = Gtk.HPaned() 
        self.hpaned.set_position(295) 
        self.main_vbox.pack_start(self.hpaned, True, True, 0) 
 
        self.notebook = Gtk.Notebook() 
        for tab in ['A', 'B']: 
            scrolled_window = Gtk.ScrolledWindow() 
            scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) 
            scrolled_window.set_shadow_type(Gtk.ShadowType.IN) 
 
            self._new_notebook_page(self.notebook, scrolled_window, tab) 
 
            treeview = self.__create_treeview(tab) 
            scrolled_window.add(treeview) 
 
        self.hpaned.add(self.notebook) 
 
        self.notebook1 = Gtk.Notebook() 
        self.hpaned.add(self.notebook1) 
     
        self.scrolled_window2, self.info_buffer, self.info_text_view = self.__create_text(True) 
        self._new_notebook_page(self.notebook1, self.scrolled_window2, '_Info') 
 
        info_text_view = self.info_text_view 
 
        self.window.show_all() 
 
    def _new_notebook_page(self, notebook, widget, label): 
        l = Gtk.Label(label='') 
        l.set_text_with_mnemonic(label) 
        notebook.append_page(widget, l) 
 
    def __create_text(self, is_source=False): 
        scrolled_window = Gtk.ScrolledWindow() 
        scrolled_window.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC) 
        scrolled_window.set_shadow_type(Gtk.ShadowType.IN) 
 
        text_view = Gtk.TextView() 
        scrolled_window.add(text_view) 
 
        buffer = Gtk.TextBuffer() 
        text_view.set_buffer(buffer) 
        text_view.set_editable(False) 
        text_view.set_cursor_visible(True) 
 
        return scrolled_window, buffer, text_view 
         
    def __create_treeview(self, tab_name): 
        treestore = Gtk.TreeStore(str, str) 
        self.tree_store[tab_name] = treestore 
        treeview = Gtk.TreeView(treestore) 
        selection = treeview.get_selection() 
        selection.set_mode(Gtk.SelectionMode.BROWSE) 
        treeview.set_size_request(200, -1)  
 
        cell = Gtk.CellRendererText() 
        column = Gtk.TreeViewColumn(tab_name, cell, foreground=self.COL_COLOR) 
        column.add_attribute(cell, "text", self.COL_NAME) 
 
        treeview.append_column(column) 
 
        if tab_name == "A": 
            selection.connect('changed', self.selection_changed_A) 
        elif tab_name == "B": 
            selection.connect('changed', self.selection_changed_B) 
 
        treeview.expand_all() 
        return treeview 
 
    def selection_changed_A(self): 
        print "A"  
 
    def selection_changed_B(self): 
        print "B"  
 
class ANALYSING_PROCESS(multiprocessing.Process): 
    def __init__(self): 
        super(ANALYSING_PROCESS, self).__init__() 
 
    def run(self): 
        import time 
        time.sleep(5) 
        ANA() 
 
class ANALYSING_THREAD(threading.Thread): 
    def __init__(self, pbar, timer): 
        super(ANALYSING_THREAD, self).__init__() 
        self.pbar = pbar 
        self.timer = timer 
 
    def run(self): 
        Gdk.threads_init() 
        Gdk.threads_enter() 
        import time 
        time.sleep(5) 
        ANA() 
        Gdk.threads_leave() 
 
        self.pbar.set_text("Done") 
        self.pbar.set_show_text(True) 
        GObject.source_remove(self.timer) 
 
class File_Chooser(Gtk.Window): 
    def __init__(self): 
        Gtk.Window.__init__(self) 
        self.connect('destroy', lambda *w: Gtk.main_quit()) 
 
        self.set_title("Test") 
        self.set_border_width(8) 
 
        frame = Gtk.Frame() 
        self.add(frame) 
 
        self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=8) 
        self.vbox.set_border_width(8) 
        frame.add(self.vbox) 
 
        label = Gtk.Label() 
        label.set_markup("<span font_desc=\"Serif 25\" foreground=\"#015F85\" size=\"x-large\"> Test</span>") 
        self.vbox.pack_start(label, False, False, 0) 
 
        self.button_entry = dict() 
        self.entry_name = dict() 
 
        hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8) 
 
        button = Gtk.Button("Browse Files") 
        button.connect('clicked', self.browse_files) 
        hbox.pack_start(button, False, False, 0) 
 
        label = Gtk.Label() 
        label.set_markup("<span foreground=\"#015F85\"> files </span>") 
        label.set_use_underline(True) 
        hbox.pack_start(label, False, False, 0) 
 
        self.entry1 = Gtk.Entry() 
        hbox.pack_start(self.entry1, True, True, 0) 
        label.set_mnemonic_widget(self.entry1) 
        self.button_entry[button] = self.entry1 
        self.entry_name['files'] = self.entry1 
 
        self.vbox.pack_start(hbox, False, False, 0) 
 
        separator = Gtk.HSeparator() 
        self.vbox.pack_start(separator, False, False, 1) 
 
        alignment1 = Gtk.Alignment() 
        alignment1.set_halign(Gtk.Align.CENTER) 
        self.vbox.pack_start(alignment1, False, False, 1) 
 
        self.pbar = Gtk.ProgressBar() 
        alignment1.add(self.pbar) 
        self.pbar.set_text("Not Run") 
        self.pbar.set_show_text(True) 
 
 
        hbox2 = Gtk.HBox(False, 2) 
        self.vbox.pack_start(hbox2, False, False, 1) 
 
        button3 = Gtk.Button("Analyze") 
        button3.connect('clicked', self.tar_File_analyze) 
        hbox2.pack_end(button3, False, False, 1) 
 
        button4 = Gtk.Button("close") 
        button4.connect('clicked', self.__quit) 
        hbox2.pack_end(button4, False, False, 1) 
 
        self.show_all() 
 
    def browse_files(self, button): 
        dialog = Gtk.FileChooserDialog("Please choose a file", self, 
            Gtk.FileChooserAction.OPEN, 
            (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, 
             Gtk.STOCK_OPEN, Gtk.ResponseType.OK)) 
 
        filter = Gtk.FileFilter() 
        filter.set_name("Tar files") 
        filter.add_pattern("*.tar") 
        filter.add_pattern("*.rar") 
        filter.add_pattern("*.tar.gz") 
        dialog.add_filter(filter) 
 
        filter = Gtk.FileFilter() 
        filter.set_name("All files") 
        filter.add_pattern("*") 
        dialog.add_filter(filter) 
 
        filter = Gtk.FileFilter() 
        filter.set_name("Images") 
        filter.add_mime_type("image/png") 
        filter.add_mime_type("image/jpeg") 
        filter.add_mime_type("image/gif") 
        filter.add_pattern("*.png") 
        filter.add_pattern("*.jpg") 
        filter.add_pattern("*.gif") 
        filter.add_pattern("*.tif") 
        filter.add_pattern("*.xpm") 
        dialog.add_filter(filter) 
 
        response = dialog.run() 
        if response == Gtk.ResponseType.OK: 
            print dialog.get_filename(), 'selected' 
            self.button_entry[button].set_text(dialog.get_filename()) 
            #entry.set_text(dialog.get_filename()) 
        elif response == Gtk.ResponseType.CANCEL: 
            print 'Closed, no files selected' 
        dialog.destroy() 
 
    def tar_File_analyze(self, button): 
             
        #PROGRESS_THREAD(self.pbar).start() 
        self.pbar.set_text("Running") 
        self.pbar.set_show_text(True) 
        ####self.timer = GObject.timeout_add (100, self.progress_timeout, self) 
        self.timer = GLib.timeout_add(100, self.progress_timeout) 
             
        t = ANALYSING_THREAD(self.pbar, self.timer) 
        #t = ANALYSING_PROCESS() 
        t.start() 
 
    def progress_timeout(self): 
        self.pbar.pulse() 
        return True 
 
    def warning(self, warnings): 
        dialog = Gtk.MessageDialog(self, 0, Gtk.MessageType.WARNING, 
                Gtk.ButtonsType.OK, "Warning!") 
        dialog.format_secondary_text(warnings) 
 
        response = dialog.run() 
        if response == Gtk.ResponseType.OK: 
            dialog.destroy() 
 
    def __quit(self, button): 
        self.destroy() 
 
    def run(self): 
        Gtk.main() 
 
def main(): 
    File_Chooser().run() 
 
if __name__ == '__main__': 
    main()                                                            

谁能看一下?

请您参考如下方法:

一般来说,GUI 工具包不支持多线程进行工具包调用。通常只允许初始线程执行此操作。使 GUI 工具包真正线程安全是一个难题,而这是避免它的一种方法。

一个可能的解决方案是让主线程使用空闲函数或定时器回调来检查工作线程中的进度。

另一种解决方案是在外部进程 而不是线程 中工作。这有很多优点;

  • 它使沟通明确且可追踪。 GUI 工具包通常可以观看例如文件描述符或套接字,在超时或空闲回调中处理来自它的信息。
  • 保持 GUI 单线程使得几乎不需要锁定。
  • 在 CPython 上,它实际上可以通过绕过 GIL 来利用多核机器。