Python PyQt4 线程,共享变量



在过去的几天里,我一直在研究这个话题,但没有找到适合我的应用程序的答案。我正在使用PyQt4在python 2.7中构建一个GUI,以便与机器人进行交互。我需要一个线程(主线程)来运行使用 QT 设计器创建的 GUI,另一个线程(worker)来连续地将数据传输到机器人和从机器人传输数据。我想在无限工作线程中使用在 GUI(主线程)中输入的数据。在我看过的所有教程中,数据仅从worker发送到主线程;就像通常的"加载栏"示例一样。

这就是我目前所拥有的。该程序从 QT 设计器导入 GUI 输出,并在按下值时将值打印到控制台。我还有一个无限线程设置,可以将"hello"打印到控制台,这确实有效。只是为了让我指向正确的方向,您能否告诉我如何在单击名为"按钮"的按钮时将无限打印"hello"更改为其他内容? 我熟悉C/C++但这是我的第一个Python项目,任何帮助都值得赞赏。

from PyQt4 import QtGui, QtTest, QtCore # Import the PyQt4 module we'll need
import sys # We need sys so that we can pass argv to QApplication
import gui # Import the GUI output from QT Designer

class GUI(QtGui.QMainWindow, gui.Ui_MainWindow):  # This class is to interact         with the GUI
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)  
self.threadclass = ThreadClass()
self.threadclass.start() # start thread class
#########  Binds GUI widgets with functions  #####
self.connectbutton.clicked.connect(self.pushbutton) # Call function "pushbutton" when pressed
self.motor_up.clicked.connect(self.motorup) # Call function motorup when pressed
self.motor_down.clicked.connect(self.motordown) # Call function motorup when pressed
self.motor_speed_slider.valueChanged.connect(self.slider) # Call function slider
self.auto_manual_check.stateChanged.connect(self.checkbox) # Call function checkbox
self.Kp_box.valueChanged.connect(self.kp_entry)
self.Ki_box.valueChanged.connect(self.ki_entry)
self.Kd_box.valueChanged.connect(self.kd_entry)

######### Functions to run when GUI event is encountered
def pushbutton(self): # stuff to do if connect button is pressed                ## THIS BUTTON
self.connectlabel.setText("Connected")
QtTest.QTest.qWait(2000)        
self.connectlabel.setText("Disconnected")
self.emit(SIGNAL("text(Qstring)"),"it worked")
def slider(self): # Stuff to do when slider is changed
print(self.motor_speed_slider.value()) # Print to monitor
def motorup(self): # Run if motor up is clicked
print("motor up")
def motordown(self): # Run if motor down is clicked
print("motor down")
def checkbox(self): # Run if checkbox is changed
if self.auto_manual_check.isChecked():
print("is checked")
else:
print("unchecked")
def kp_entry(self): # Run if kp is changed
print(self.Kp_box.value())
def ki_entry(self): # Run if ki is changed
print(self.Ki_box.value())
def kd_entry(self):# Run if kd is changed
print(self.Kd_box.value())

class ThreadClass(QtCore.QThread): # Infinite thread to communicate with  robot
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:            # Runs continuously 
print "hello"   #                           CHANGE THIS WHEN pushbutton IS PRESSED


def main():  # Function to run the main GUI
app = QtGui.QApplication(sys.argv)  # A new instance of QApplication
form = GUI()                 # We set the form to be our ExampleApp 
form.show()                         # Show the form
app.exec_()                         # and execute the app

if __name__ == '__main__':              # if we're running file directly and not importing it
main()                              # run the main function

编辑:感谢您的快速和详细的回复。在尝试了您的解决方案之后,我想我越来越近了,但是我没有让它们为我的应用程序工作,我想我可能在最初的问题中不清楚。我本质上是在尝试在线程之间传递变量,实际打印只是为了表明变量已成功传输。我认为这个精简的代码会清除它。

from PyQt4 import QtGui, QtTest, QtCore # Import the PyQt4 module we'll need
import sys # We need sys so that we can pass argv to QApplication
import gui # Import the GUI output from QT Designer

class GUI(QtGui.QMainWindow, gui.Ui_MainWindow):            # This class is to interact with the GUI
def __init__(self):
super(self.__class__, self).__init__()
self.setupUi(self)  
self.threadclass = ThreadClass()
self.threadclass.start() 
self.Kp_box.valueChanged.connect(self.kp_entry)     # Call function kp_entry if box value is changed
def kp_entry(self):                                     # Run if kp is changed                  
print(self.Kp_box.value())                          # I WANT TO SEND THE BOX VALUE TO THE WORKER THREAD        
class ThreadClass(QtCore.QThread):                          # Infinite thread to communicate with robot
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
def run(self):
while 1:             
print self.Kp_box.value()                       #CONTINUOUSLY PRINT THE UPDATED BOX VALUE    

def main():                             # Run the main GUI
app = QtGui.QApplication(sys.argv)  # A new instance of QApplication
form = GUI()                        # We set the form to be our ExampleApp 
form.show()                         # Show the form
app.exec_()                         # and execute the app
if __name__ == '__main__':              # if we're running file directly and not importing it
main()                              # run the main function

我想要做的只是使用户能够更新 GUI 中的值,然后将该值传递给工作线程。在这种情况下,我有一个输入框,以便用户可以更改数字。然后,我需要程序将该数字传输到将与机器人通信的工作线程。现在代码不起作用,我只是尽可能接近它以更好地展示我正在尝试做什么。

如果有帮助,这是我的 GUIhere

有几种方法可以做到这一点。 第一个(最简单的)方法是在方法pushbutton中放入一行代码:

def pushbutton(self):
self.threadclass.got_a_click()

将相应的方法和实例变量添加到 ThreadClass,并更改运行方法:

def __init__(self):
super(ThreadClass, self).__init__(parent)
self.flag = False
def got_a_click(self):
self.flag = True
def run(self):
while not self.flag:
print "hello"
do_something_useful()

这可能已经足够好了。 请注意,该方法got_a_click在主循环上下文中运行,run方法在辅助QThread上下文中运行。

更复杂的方法是使用Qt将信号发送到其他线程中的插槽的能力。 为此,您必须在辅助线程中运行QEventLoop。 QThread 基类已经为此设置好了:它有一个仅包含一个事件循环的run方法。 因此,为了拥有一个运行事件循环的 QThread 子类,只需不要重写 run 方法。 这个QThread将坐在那里什么都不做,直到信号被发送到它的一个插槽。 发生这种情况时,插槽将在辅助线程中执行。 代码如下所示:

class ThreadClass(QtCore.QThread): 
def __init__(self, parent = None):
super(ThreadClass, self).__init__(parent)
self.moveToThread(self)
def onButtonPress(self):
pass  # Your code here

将此行添加到GUI的构造函数中:self.connectbutton.clicked.connect(self.threadclass.onButtonPress)

每次单击该按钮时,onButtonPress都会在辅助线程上下文中运行。

请注意 ThreadClass 构造函数中self.moveToThread(self)语句。 这一点非常重要。 它告诉Qt信号/插槽机制,此对象中的所有插槽都将通过ThreadClass线程中的事件循环调用。 没有它,SlotonButtonPress将在主线程的上下文中运行,即使其代码位于ThreadClass中。

另一个需要注意的陷阱是直接打电话给onButtonPress。 那将 不使用信号/插槽机制,onButtonPress 将像任何普通的 Python 函数一样执行。 例如,如果从现有函数调用onButtonPresspushbutton它将在主线程上下文中运行。 为了获得您想要的线程行为,您必须使Qt信号发送到onButtonPress插槽。

这是您重新措辞的问题的答案。 以下代码片段按预期工作:

from PySide import QtGui, QtTest, QtCore # Import the PySide module we'll need
import sys
class GUI(QtGui.QMainWindow):            # This class is to interact with the GUI
def __init__(self):
super(self.__class__, self).__init__()
self.Kp_box = QtGui.QSpinBox(self)
self.threadclass = ThreadClass(self)
self.threadclass.start() 
self.Kp_box.valueChanged.connect(self.kp_entry)     # Call function kp_entry if box value is changed
def kp_entry(self):                                     # Run if kp is changed                  
print("Main", self.Kp_box.value())                          # I WANT TO SEND THE BOX VALUE TO THE WORKER THREAD        
class ThreadClass(QtCore.QThread):                          # Infinite thread to communicate with robot
def __init__(self, main_window):
self.main_window = main_window
super(ThreadClass, self).__init__(main_window)
def run(self):
while 1:             
print(self.main_window.Kp_box.value())                       #CONTINUOUSLY PRINT THE UPDATED BOX VALUE    

def main():                             # Run the main GUI
app = QtGui.QApplication(sys.argv)  # A new instance of QApplication
form = GUI()                        # We set the form to be our ExampleApp 
form.show()                         # Show the form
app.exec_()                         # and execute the app
if __name__ == '__main__':              # if we're running file directly and not importing it
main()                              # run the main function

我从您的代码中更改了什么:

  1. 我使用PySide而不是PyQt,但它们大多兼容(或者至少兼容) 他们应该是)。 我不得不更改第一个导入 陈述;你将不得不把它改回来。
  2. 为了消除对 GUI 工具的依赖,我替换了您的主要 窗口,其中包含一个具有单个旋转框的窗口。 这证明了 多线程问题的基本特征并将其解耦 从您的其他应用程序要求。
  3. 您的辅助线程无法访问旋转框,因为它没有 引用它的变量。 我怀疑这是你的大问题 有。 通过将主窗口对象传递给 ThreadClass 构造函数并将其存储在实例变量中。

绝不是任何东西从一个线程转移到另一个线程。 此代码仅使用以下事实:Python 程序中的所有变量都位于相同的内存空间中,即使该程序是多线程的。 [如果程序使用多个进程,则不是这样。

这个小程序不提供任何优雅关闭的方法。

最新更新