首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python PyQt4线程,共享变量

Python PyQt4线程,共享变量
EN

Stack Overflow用户
提问于 2017-04-14 02:41:27
回答 1查看 2.4K关注 0票数 0

在过去的几天里,我一直在研究这个话题,但没有找到一个适合我的申请的答案。我正在用PyQt4在python2.7中构建一个GUI来与机器人交互。我需要一个线程(Main)来运行用QT Designer创建的GUI,需要另一个线程(Worker)来不断地向机器人传输数据。我想使用无限工作线程中GUI(主线程)中输入的数据。在我看到的所有教程中,数据只从工作线程发送到主线程;就像通常的“加载条”示例一样。

这就是我到目前为止所拥有的。此程序从导入GUI输出,并在按下它们时将值打印到控制台。我还设置了一个无限线程,将"hello“打印到控制台上,这样就可以工作了。为了让我指向正确的方向,你能告诉我如何在点击“按钮”按钮时,将无限打印的"hello“更改为其他的东西吗?我对C/C++很熟悉,但这是我的第一个Python项目,任何帮助都很感激。

代码语言:javascript
复制
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

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

代码语言:javascript
复制
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这里

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-04-14 05:30:21

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

代码语言:javascript
复制
def pushbutton(self):
    self.threadclass.got_a_click()

向ThreadClass添加相应的方法和实例变量,并更改run方法:

代码语言:javascript
复制
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将只是坐在那里什么都不做,直到一个信号被发送到它的一个插槽。当这种情况发生时,插槽将在辅助线程中执行。代码如下所示:

代码语言:javascript
复制
class ThreadClass(QtCore.QThread): 
    def __init__(self, parent = None):
        super(ThreadClass, self).__init__(parent)
        self.moveToThread(self)

    def onButtonPress(self):
        pass  # Your code here

将这一行添加到GUIself.connectbutton.clicked.connect(self.threadclass.onButtonPress)的构造函数中

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

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

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

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

代码语言:javascript
复制
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,但它们大多是兼容的(或者至少它们应该兼容)。我必须更改第一个import语句;您必须将它更改回。
  2. 为了消除对GUI工具的依赖,我将主窗口替换为一个只有一个SpinBox的窗口。这说明了多线程问题的基本特性,并将其与其他应用程序需求分离开来。
  3. 辅助线程无法访问SpinBox,因为它没有引用它的变量。我猜这是你遇到的大问题。通过将主窗口对象传递给ThreadClass构造函数并将其存储在实例变量中,它是固定的。

从任何意义上说,任何东西都不能从一个线程转移到另一个线程。这段代码简单地使用了一个事实,即Python程序中的所有变量都在同一个内存空间中,即使程序是多线程的。如果程序使用多个进程,那就不是这样了。

这个小程序没有提供任何方式来优雅地关闭。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43404472

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档