过去几天我一直在研究这个话题,但我的申请没有找到合适的答案。我正在使用PyQt4在python 2.7中构建一个GUI来与机器人进行交互。我需要一个线程(main)来运行用QT Designer创建的GUI和另一个线程(worker)来连续地向机器人传输数据和从机器人传输数据。我想使用在无限工作线程中的GUI(主线程)中输入的数据。在我看到的所有教程中,数据仅从工作者发送到主线程;像通常的“加载栏”示例。
这是我到目前为止所拥有的。该程序从QT Designer导入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中的值,然后将该值传递给工作线程。在这种情况下,我有一个输入框,以便用户可以更改数字。然后,我需要程序将该数字传输到将与机器人通信的工作线程。现在代码不起作用,我尽可能接近它以更好地展示我正在尝试做的事情。
如果有帮助,这是我的GUI here
答案 0 :(得分:0)
有几种方法可以做到这一点。第一个(最简单的)是在方法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
方法位于辅助QThread
上下文中。
更复杂的方法是使用Qt的能力将信号发送到其他线程中的Slots。为此,您必须在辅助线程中运行QEventLoop。 QThread基类已经为此设置了:它有一个run
方法,它只包含一个事件循环。因此,为了拥有一个运行事件循环的QThread子类,只需不要覆盖run方法。在将信号发送到其中一个插槽之前,此QThread将无所事事。当发生这种情况时,Slot将在辅助线程中执行。代码如下所示:
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机制,该对象中的所有Slots都将通过ThreadClass线程中的事件循环来调用。没有它,Slot onButtonPress
将在主线程的上下文中运行,即使它的代码位于ThreadClass
。
需要注意的另一个陷阱是直接致电onButtonPress
。那会的
不使用Signal / Slot机制,onButtonPress将像任何普通的Python函数一样执行。例如,如果您从现有函数onButtonPress
调用pushbutton
,它将在主线程上下文中运行。为了获得您想要的线程行为,您必须将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程序中的所有变量都存在于同一个内存空间中,即使程序是多线程的。 [如果该程序使用了多个进程,则情况并非如此。]
这个小程序没有提供任何优雅的关闭方式。