多线程Python程序最适合哪些GUI框架?
我正在写一个多线程的Python程序,这个程序有一个图形用户界面(GUI),里面有几个模块会通过改变文本和背景颜色来“触碰”这个界面。目前我在使用PyGTK,但我发现这个界面有时候会“悄悄地”崩溃(没有错误信息,程序就直接结束了),有时候还会出现段错误。
这个网站提到GTK并不是完全线程安全的,使用PyGTK进行多线程编程是比较棘手的。有没有更好的Python GUI框架,适合多线程程序,能减少出现问题的可能性呢?
2 个回答
如果你想从一个线程更新图形界面(GUI),你可能需要用到 gobject.idle_add()
这个方法,这样可以让更新界面的函数在主循环稍后被调用。大多数图形界面框架(比如Qt)都要求你添加一个回调函数,这个函数会在主循环空闲的时候被调用。GTK也支持从线程中调用图形界面函数,你可以使用 gtk.gdk.lock
这个上下文管理器,或者在你的图形界面调用前后使用 gtk.gdk.threads_enter
和 gtk.gdk.threads_leave
。
所以你可以选择这样做:
gobject.idle_add(lambda: window.whatever(arg1, arg2))
或者你可以这样做:
with gtk.gdk.lock:
window.whatever(arg1, arg2)
哦,我绝对推荐 PyQt4。刚开始的时候,我对那些 SIGNAL
和 EMIT
的东西搞不太懂,但现在我用它做了一个程序,发现 QThread
模块真的是非常有用。
至于稳定性,我从来没有遇到过崩溃。即使在调试那些半成品的代码时,QT也没有出现任何问题。每当我点击一个信号槽不正确的按钮时,它只是把错误信息抛到控制台窗口。
而GTK有时候就会突然崩溃,完全没有任何错误提示。就只会出现一个非常不友好的 Segmentation Fault
。这也是我觉得使用PyQt很愉快的原因之一。当你遇到错误时,你实际上知道哪里出问题了。
之后我觉得这主要是个人喜好,但还有一个优点就是在Mac、Linux和Windows上看起来都很原生的界面。GTK+在Windows上(别误会,我用的是Ubuntu)总给我一种X-org的感觉,这让我觉得不太舒服。
祝你好运!
为了让PyQt看起来更吸引人,这里有我书籍绑定应用程序的一小段代码(有点乱):
class Binder(QtCore.QThread):
'''
Class for binding the actual book
'''
def __init__(self, parent = None):
super(Binder, self).__init__(parent)
def initialize(self, pages, options, outfile):
self.pages = pages
self.options = options
self.outFile = outfile
self.book = organizer.Book()
self.enc = Encoder(self.options)
self.ocr = ocr.OCR(self.options)
self.connect(self.enc, QtCore.SIGNAL('updateProgress(int, int)'), self.updateProgress)
def updateProgress(self, percent, item):
self.emit(QtCore.SIGNAL('updateProgress(int, QString)'), int(percent), 'Binding the book...')
self.emit(QtCore.SIGNAL('updateBackground(int, QColor)'), int(item), QtGui.QColor(170, 255, 170, 120))
if int(percent) == 100:
time.sleep(0.5)
self.emit(QtCore.SIGNAL('finishedBinding'))
def run(self):
self.die = False
for page in self.pages:
self.add_file(page, 'page')
if not self.die:
self.analyze()
if not self.die:
self.book.get_dpi()
if self.options['ocr'] and not self.die:
self.get_ocr()
if not self.die:
self.enc.initialize(self.book, self.outFile)
self.enc.start()