[英]Python PyQt4 Threading, sharing variables
過去幾天,我一直在研究此主題,但找不到適合我的應用程序的答案。 我正在使用PyQt4在python 2.7中構建GUI以與機器人進行交互。 我需要一個線程(主線程)來運行使用QT Designer創建的GUI,而另一個線程(工作人員)則需要不斷地與機器人傳輸數據。 我想在無限工作線程中使用在GUI(主線程)中輸入的數據。 在我所看到的所有教程中,數據僅從工作線程發送到主線程。 就像通常的“加載欄”示例一樣。
到目前為止,這就是我所擁有的。 該程序從QT Designer導入GUI輸出,並在按下時將值打印到控制台。 我還設置了一個無限線程,該線程將“ hello”打印到可以正常工作的控制台。 只是為了使我指向正確的方向,您能告訴我如何在單擊名為“ pushbutton”的按鈕時將無限打印“ 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中更新值,然后將該值傳遞給工作線程。 在這種情況下,我有一個輸入框,以便用戶可以更改號碼。 然后,我需要程序將該數字傳輸到將與機器人通信的工作線程。 現在代碼不起作用,我只是盡可能地接近它,以更好地顯示我要執行的操作。
如果有幫助,這是我的GUI 這里
有兩種方法可以做到這一點。 第一個(最簡單的)方法是在方法pushbutton
放入一行代碼:
def pushbutton(self):
self.threadclass.got_a_click()
將相應的方法和實例變量添加到ThreadClass,然后更改run方法:
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
,而run
方法在輔助QThread
上下文中。
一種更復雜的方法是利用Qt的功能將信號發送到其他線程中的插槽。 為此,必須在輔助線程中運行QEventLoop。 QThread基類已經為此設置:它具有一個run
方法,該方法僅包含一個事件循環。 因此,為了使QThread子類運行事件循環,只需不要覆蓋run方法。 這個QThread只會坐在那里什么也不做,直到一個Signal被發送到它的一個插槽中。 發生這種情況時,插槽將在輔助線程中執行。 代碼如下:
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 Signal / Slot機制,該對象中的所有Slot都將通過ThreadClass線程中的事件循環被調用。 沒有它,Slot onButtonPress
將在主線程的上下文中運行,即使其代碼位於ThreadClass
。
注意的另一個陷阱是直接調用onButtonPress
。 那將不會使用Signal / Slot機制,並且onButtonPress將像任何普通的Python函數一樣執行。 例如,如果您從現有功能pushbutton
調用onButtonPress
,它將在主線程上下文中運行。 為了獲得所需的線程行為,必須使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
我對您的代碼所做的更改:
從任何意義上講,任何事物都不會從一個線程轉移到另一線程。 此代碼僅使用以下事實:即使程序是多線程的,Python程序中的所有變量都位於同一內存空間中。 [如果程序使用多個進程,那將不是真的。]
這個小程序沒有提供任何正常關閉的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.