如何使用 USB 串行(来自 Arduino)的数据刷新/更新 QLabel?

Posted

技术标签:

【中文标题】如何使用 USB 串行(来自 Arduino)的数据刷新/更新 QLabel?【英文标题】:How do I refresh/update QLabel with data from USB serial (from Arduino)? 【发布时间】:2018-01-20 18:34:19 【问题描述】:

我知道这是一个 NOOB 问题;对不起。我为 Cub Scouts 构建了一个 Pinewood Derby 比赛计时器(小型汽车赛道),使用 Arduino 控制时间和 RasPi 的组合,用于车道位置(第一、第二、第三等)的 GUI 显示和以秒为单位的运行时间(例如“ 2.1234")。我在 QT Creator 中有基本的 shell GUI 设置,并通过 USB 成功连接了 Arduino 和 RasPi。我还成功地将 Arduino 串行数据拉入 RasPi(使用单独的小型 Python 代码进行了测试)。

一旦 Arduino 通过 USB 触发比赛结果字符串,我遇到的问题是 GUI 中 QLabels 的动态更新。换句话说,当比赛开始时,Arduino 会发送“B”作为开始(需要 GUI 来空白之前的结果);比赛结束后,Arduino 发送“F”,然后发送位置“1”和时间“2.1234”,我需要 Python 更新/更改每个车道位置和车道时间的相应 QLabels。我确信有一种简单的方法可以做到这一点,但我无法通过搜索这个很棒的网站和其他网站找到方法。

在此先感谢您,对于接下来可能出现的 FUBAR 编码,我深表歉意!下面的示例仅显示了一个通道与我将在弄清楚如何执行此操作后使用的 4 个通道。

我正在使用 两个 Python 代码

与 QT Creator 生成的 XML 代码交互的 Python 代码

import sys
import time
import serial

from PyQt4 import QtCore, QtGui, uic

global ser

ser=serial.Serial("/dev/ttyUSB1",115200)
ser.flushInput()

qtCreatorFile = "PinewoodDisplay.ui" # Enter file here.

Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)

class MyApp(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

while True:
    if ser.inWaiting()>0:
        inputValue = (ser.readline().strip())
        self.Lane1_Place.setText(inputValue)

从 QT Creator 生成的 XML(不确定我是否发布正确)- 文件名是“PinewoodDisplay.ui”:

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>619</width>
    <height>1051</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <property name="styleSheet">
   <string notr="true">background-color: rgb(102, 102, 108)</string>
  </property>
  <widget class="QWidget" name="centralWidget">
   <widget class="QGroupBox" name="Lane1_Group">
    <property name="geometry">
     <rect>
      <x>50</x>
      <y>20</y>
      <width>481</width>
      <height>741</height>
     </rect>
    </property>
    <property name="cursor">
     <cursorShape>CrossCursor</cursorShape>
    </property>
    <property name="autoFillBackground">
     <bool>false</bool>
    </property>
    <property name="styleSheet">
     <string notr="true">background-color : rgb(0, 75, 0)</string>
    </property>
    <property name="title">
     <string/>
    </property>
    <property name="alignment">
     <set>Qt::AlignHCenter|Qt::AlignTop</set>
    </property>
    <layout class="QGridLayout" name="gridLayout">
     <item row="2" column="0">
      <widget class="QLabel" name="Lane1_Place">
       <property name="sizePolicy">
        <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
         <horstretch>0</horstretch>
         <verstretch>0</verstretch>
        </sizepolicy>
       </property>
       <property name="palette">
        <palette>
         <active>
          <colorrole role="WindowText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Button">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Text">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="ButtonText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Base">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Window">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
         </active>
         <inactive>
          <colorrole role="WindowText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Button">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Text">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="ButtonText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Base">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Window">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
         </inactive>
         <disabled>
          <colorrole role="WindowText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Button">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Text">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="ButtonText">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>255</red>
             <green>255</green>
             <blue>255</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Base">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
          <colorrole role="Window">
           <brush brushstyle="SolidPattern">
            <color alpha="255">
             <red>0</red>
             <green>75</green>
             <blue>0</blue>
            </color>
           </brush>
          </colorrole>
         </disabled>
        </palette>
       </property>
       <property name="font">
        <font>
         <family>Gentium Book Basic</family>
         <pointsize>350</pointsize>
         <weight>75</weight>
         <bold>true</bold>
        </font>
       </property>
       <property name="autoFillBackground">
        <bool>false</bool>
       </property>
       <property name="styleSheet">
        <string notr="true">color: rgb(255, 255, 255)</string>
       </property>
       <property name="lineWidth">
        <number>0</number>
       </property>
       <property name="text">
        <string>4</string>
       </property>
       <property name="scaledContents">
        <bool>true</bool>
       </property>
       <property name="alignment">
        <set>Qt::AlignCenter</set>
       </property>
      </widget>
     </item>
     <item row="3" column="0">
      <widget class="QLabel" name="Lane1_Time">
       <property name="font">
        <font>
         <pointsize>90</pointsize>
         <weight>75</weight>
         <bold>true</bold>
        </font>
       </property>
       <property name="styleSheet">
        <string notr="true">color: rgb(255, 255, 255)</string>
       </property>
       <property name="text">
        <string>4.5678</string>
       </property>
       <property name="alignment">
        <set>Qt::AlignCenter</set>
       </property>
      </widget>
     </item>
     <item row="1" column="0">
      <widget class="QLabel" name="lblLane1">
       <property name="font">
        <font>
         <pointsize>36</pointsize>
         <weight>75</weight>
         <bold>true</bold>
        </font>
       </property>
       <property name="styleSheet">
        <string notr="true">color: rgb(255, 255, 255)</string>
       </property>
       <property name="text">
        <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; color:#e9e9e9;&quot;&gt;Lane 1&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
       </property>
       <property name="alignment">
        <set>Qt::AlignCenter</set>
       </property>
      </widget>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menuBar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>619</width>
     <height>27</height>
    </rect>
   </property>
  </widget>
  <widget class="QToolBar" name="mainToolBar">
   <attribute name="toolBarArea">
    <enum>TopToolBarArea</enum>
   </attribute>
   <attribute name="toolBarBreak">
    <bool>false</bool>
   </attribute>
  </widget>
  <widget class="QStatusBar" name="statusBar"/>
  <action name="actionTest">
   <property name="text">
    <string>test</string>
   </property>
  </action>
 </widget>
 <layoutdefault spacing="6" margin="11"/>
 <resources/>
 <connections/>
</ui>

【问题讨论】:

【参考方案1】:

需要读取串行数据的while True等阻塞任务对GUI不友好,因此不应在同一个线程上运行,适当的选择是在另一个线程上执行它们,并发送必要的信息通过信号传递给主线程。

import sys
import serial

from PyQt4 import QtCore, QtGui, uic

qtCreatorFile = "PinewoodDisplay.ui"

Ui_MainWindow, QtBaseClass = uic.loadUiType(qtCreatorFile)

class SerialThread(QtCore.QThread):
    dataChanged = QtCore.pyqtSignal(str)
    def __init__(self, *args, **kwargs):
        QtCore.QThread.__init__(self, *args, **kwargs)
        self.ser = serial.Serial("/dev/ttyUSB1",115200)

    def run(self):
        while True:
            if self.ser.inWaiting()>0:
                inputValue = self.ser.readline().strip()
                self.dataChanged.emit(inputValue)

class MyApp(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.setupUi(self)
        thread = SerialThread(self)
        thread.dataChanged.connect(self.Lane1_Place.setText, QtCore.Qt.QueuedConnection)
        thread.start()

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    window = MyApp()
    window.show()
    sys.exit(app.exec_())

QRunnableQThreadPool 可以完成相同的任务,也可以发送您可以使用QMetaObject::invokeMethod() 的数据

【讨论】:

OMG --- 它有效!嗯,你知道的。太感谢了;我无法告诉你我花了多少时间试图弄清楚这一点。真的很感激!

以上是关于如何使用 USB 串行(来自 Arduino)的数据刷新/更新 QLabel?的主要内容,如果未能解决你的问题,请参考以下文章

通过 QSerialPort 访问来自 Arduino 的串行数据时遇到问题

Arduino:禁用USB时传入的USB串行数据软管连接

Arduino Uno Raspberry Pi 串行通信双读数

串行 Mac OS X 不断冻结/锁定/消失 USB 到 Arduino

将串行从 Raspberry 传递到 Arduino USB HID

Microsoft Visual C++ 2010 和 Arduino UNO 之间通过 USB 进行串行通信