2016-09-22 88 views
3

我更喜欢在不考虑图形用户界面的情况下编写我的应用程序。一旦应用程序代码正常工作,我喜欢在它之上粘贴一个GUI层 - 两者之间有一个干净的界面。应用程序线程和Qt线程之间的清晰分离(Python-PyQt)

我首先尝试使GUI从应用程序运行在不同的进程中。但是我很快就后悔了这个实验。在两个流程之间建立通信链接并不是微不足道的。所以我决定现在,多个线程都没问题(尽管Python全局解释器锁使它们在单个内核上运行)。

MainThread完全掌握在Qt GUI中。显然这是标准做法。因此,让我们假设软件的整体结构应该是这样的(注意,qtThread是同义MainThread):

enter image description here

我的应用程序代码在appThread运行 - 干净与GUI分离。但在某个时候,必须有互动。

我已经阅读了很多关于如何组织这个文章的文章,但许多资料相互矛盾。即使官方的Qt应用程序也是错误的(官方文档鼓励将QThread的子类化)。最具启发意义的文章中,我能找到的是这些:

http://ilearnstuff.blogspot.be/2012/08/when-qthread-isnt-thread.html http://ilearnstuff.blogspot.be/2012/09/qthread-best-practices-when-qthread.html

即使考虑所有后,我仍然在怀疑几件事情。


问题1.什么是启动appThread最合适的方法是什么?


什么是启动appThread最合适的方法是什么?纠正我,如果我错了,但我相信,有两种选择:

选择1:启动一个标准的Python线程
Python提供了threading库,一个可以导入到产生新的线程:

import threading 

if __name__ == '__main__': 
    # 1. Create the qt thread (is MainThread in fact) 
    qtApp = QApplication(sys.argv) 
    QApplication.setStyle(QStyleFactory.create('Fusion')) 

    # 2. Create the appThread 
    appThread = threading.Thread(name='appThread', target=appThreadFunc, args=(p1,p2,)) 
    appThread.start() 

    # 3. Start the qt event loop 
    qtApp.exec_() 
    print('Exiting program') 

这个选择对我来说看起来最清洁。您甚至可以在不考虑GUI的情况下真正编写您的代码appThread代码。毕竟,您使用的是标准Python threading库。那里没有Qt的东西。
但我找不到有关在appThreadMainThread之间建立通信链接的明确文档。第二个问题更多关于这个问题..

选择2:启动一个线程的QThread
这种选择看起来不那么干净,因为你惹Qt的东西写你的应用程序代码。无论如何,它看起来像一个可行的选择,因为两个线程之间的通信链接 - appThreadMainThread - 可能更好地支持。
有无数的方法来启动QThread线程。官方的Qt文档鼓励子类QThread并重新实​​现run()方法。但我读到这种做法其实很糟糕。请参阅我在问题开始时发布的两个链接的更多信息。


问题2.什么是两个线程之间的最佳通信链路?


什么是两个线程之间的最佳通信链路?很明显,这个问题的答案很大程度上取决于问题1中的选择。我可以想象,将标准Python线程链接到GUI与连接QThread非常不同。
我会离开它给你提出建议,但在我的脑海里弹出了几个机制:

  • 队列:使用标准的Python队列或Qt的队列?
  • 信号/槽机制:...
  • 插座:应该工作,但看起来有点笨重
  • 管:...
  • 临时文件:繁琐


备注:


请提一下,如果你的答案适用于Python 2.x或3.x.另外请记住,在谈论线程,队列等时可能会很快出现混淆。请提及您是否参考标准Python线程或QThread,标准Python队列或QQueue,...

+0

为什么你认为有必要为前端和后端使用单独的线程/进程?后端是否阻止了gui?如果不是,这似乎是一个毫无意义的问题。但即使它阻止了,你提出问题的方式似乎也引发了很多基于意见的答案。 SO是关于*特定的编程问题*,而不是通用的软件设计方法。 – ekhumoro

回答

1

我的建议是做大多数人做的事。等到代码需要在一个单独的线程中运行,然后只有把这段代码放到一个线程中。您的代码不需要在单独的线程中具有良好的代码分离。我会这样做的方式如下:

在只知道非GUI库的基类中,有你的appThread代码(没有GUI知识的代码)。这使得以后也可以轻松地支持代码的命令行版本。将需要在此基类的常规Python线程内异步执行的代码。确保你想异步执行的代码只是一个函数调用,以使我的下一个点更容易。

然后,在一个单独的文件中创建一个子类,它从您刚刚编写的基类和QMainWindow类继承。任何需要异步运行的代码都可以通过QThread类调用。如上所述,如果您想在一个函数调用中使异步运行的代码可用,那么可以很容易地让您的QThread子类的这一步工作。

为什么上述?

它使更容易管理状态和通信。当你不需要时,为什么让自己在竞争条件和线程沟通方面疯狂?在应用程序代码与GUI代码的GUI中,实际上没有性能原因,因为大多数情况下用户实际上并没有就CPU而言输入太多的信息。只有那些速度慢的部分应该放在线程中,这样既可以节省时间,又可以使代码管理变得更加简单。另外,在Python中,由于GIL,你不会从单独的线程中获得任何东西。