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

时间:2015-05-25 09:31:30

标签: python gtk3

我开发了一个基于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:断言'值!= 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()                                                            

有人可以看一下吗?

1 个答案:

答案 0 :(得分:1)

通常,GUI工具包支持多个线程进行工具包调用。通常只允许初始线程这样做。使GUI工具包真正是线程安全的是一个难题,这是一种避免它的方法。

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

另一个解决方案是使用外部进程而不是线程进行工作。这有许多优点;

  • 它使通信变得明确和可追踪。 GUI工具包通常可以观看例如文件描述符或套接字,在超时或空闲回调中处理信息。
  • 保持GUI单线程使锁定大多不必要。
  • 在CPython上,它实际上可以通过侧面步进GIL来使用多核机器。