簡體   English   中英

Python PyQt4線程,共享變量

[英]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

我對您的代碼所做的更改:

  1. 我使用PySide而不是PyQt,但是它們大多數兼容(或者至少應該是)。 我不得不更改第一個導入語句; 您將不得不將其改回。
  2. 為了消除對GUI工具的依賴,我將主窗口替換為具有單個SpinBox的主窗口。 這說明了多線程問題的基本功能,並將其與其他應用程序需求分離了。
  3. 您的輔助線程無法訪問SpinBox,因為它沒有引用它的變量。 我懷疑這是您遇到的最大問題。 通過將主窗口對象傳遞給ThreadClass構造函數並將其存儲在實例變量中,可以修復此問題。

從任何意義上講,任何事物都不會從一個線程轉移到另一線程。 此代碼僅使用以下事實:即使程序是多線程的,Python程序中的所有變量都位於同一內存空間中。 [如果程序使用多個進程,那將不是真的。]

這個小程序沒有提供任何正常關閉的方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM