[TH] List the serial ports connected to the RPi with pySerial and PyQt5.

จากบทความก่อหน้านี้เราได้อ่านรายชื่ออุปกรณ์ที่เชื่อมต่อกับพอร์ตอนุกรมของบอร์ด Raspberry Pi หรือ RPi ด้วยไลบรารี pySerial ดังภาพที่ 1 ในแบบโหมดตัวอักษรไปแล้ว ในบทความนี้เป็นการผนวกหลักการทำงานจากก่อนหน้าเข้ากับการใช้ส่วนติดต่อกับผู้ใช้แบบกราฟิกส์ หรือ GUI (Graphics User Interface) ผ่านทางไลบรารี PyQt5 โดยแสดงรายการเอาไว้ใน combobox เพื่อให้ผู้ใช้งานเลือกใช้งานได้ แต่ถ้าไม่พบพอร์ตสื่อสารอนุกรมที่เชื่อมต่อกับบอร์ด RPi จะปิดการใช้งานของ combobox ไม่ให้ผู้ใช้งานเลือกใช้งาน ดังนั้น บทความนี้จึงมีเนื้อหาเกี่ยวกับการประยุกต์ใช้ pySerial กับการใช้งานของ QLabel และ QComboBox ในไลบรารี PyQt5

บทความนี้ประกอบด้วย 3 ตอน คือ

  1. List the serial ports connected to the RPi with pySerial.
  2. List the serial ports connected to the RPi with pySerial and PyQt5.
  3. LEDs on/off via PyQt5 and serial communication.

ปรับปรุงโค้ด

จากโค้ดโปรแกรมตัวอย่างในครั้งก่อน ผู้เขียนโปรแกรต้องระบุชื่อของพอร์ตเอาไว้เพื่อทำการตรวจสอบแต่ละชื่อว่าได้เชื่อมต่อเอาไว้หรือไม่ แต่ถ้าต้องการให้โปรแกรมอ่านรายชื่อทั้งหมดที่เกี่ยวข้องขึ้นมา หลังจากนั้นทำการตรวจสอบทั้งหมด ถ้าอุปกรณ์ใดเชื่อมต่อไว้จะจดจำชื่อเอาไว้ในรายการลิสต์ของพอร์ตที่เชื่อมต่อ หลังจากนั้นค่อยรายงานว่าพบอุปกรณ์ที่พอร์ตใดบ้าง ดังตัวอย่างโปรแกรมต่อไปนี้

# -*- coding: UTF-8 -*-
import sys
import glob
import serial

ports = glob.glob('/dev/tty[A-Za-z]*')
result = []
for port in ports:
    try:
        s = serial.Serial(port)
        s.close()
        result.append(port)
    except (OSError, serial.SerialException):
        pass
if (len(result) == 0):
    print("ไม่พบพอร์ตอนุกรม")
else:
    print("พบพอร์ตอนุกรมดังรายการต่อไปนี้:")
    for p in result:
        print("{}".format(p))

ตัวอย่างผลลัพธ์ของโปรแกรมที่ปรับแล้วเป็นดังภาพที่ 2

PyQt5

จากในบทความ PyQt5 เราสาามารถใช้ PyQt5 กับระบบปฏิบัติการ Windows, macOS หรือ Linux พร้อมทั้งตัวอย่างการเปิดหน้าต่าง และการใช้ปุ่มไปเป็นที่เรียบร้อยแล้ว ในบทความนี้เพิ่มเติมการใช้งานกับบอร์ด Raspberry Pi ในเรื่องของการติดตั้ง PyQt5 ผ่านทาง apt โดยตัวอย่างโปรแกรมในบทความนี้สามารถนำไปประยุกต์ใช้กับระบบปฏิบัติการอื่น ๆ ได้เช่นกัน

ติดตั้งไลบรารี PyQt5

การติดตั้งไลบรารี PyQt5 กับ Raspbian ซึ่งเป็นระบบปฏิบัติการของบอร์ด Raspberry Pi สามารถทำได้ผ่านทาง apt install ดังนี้

sudo apt install python3-pyqt5

QLabel

QLabel เป็นวิดเจ็ตสำหรับแสดงข้อความที่สืบทอดมาจาก QtWidgets.QFrame ซึ่งถ฿กสืบทอดมาจาก QtWidgets.QWidget อีกทอดหนึ่ง โดยต้องนำเข้าไลบรารี QLabel โดยการสร้างวัตถุประเภท QLabel ต้องสั่งงานดังนี้ ส่วนพารามิเตอร์ที่ต้องส่งให้นั้นประกอบด้วย ข้อความที่แสดงในเลเบล และค่าตำแหน่งของหน้าต่างหลักที่ต้องการให้เลเบลนี้ไปแสดงบนหน้าต่าง โดยส่วนของข้อความสามารถกำหนดเป็นค่าใด ๆ และสามารถเปลี่ยนได้ในภายหลัง ส่วนหน้าต่างหลักในตัวอย่างโปรแกรมจะใช้เป็น self เพื่อระบุว่า เลเบลอยู่ภายใต้หน้าต่างที่ถูกสร้างขึ้น

วัตถุ = QLabel( ข้อความ, หน้าต่างหลัก )

การกำหนดข้อความให้กับวัตถุประเภท QLabel ใช้เมธอดตามรูปแบบต่อไปนี้

วัตถุ.setText( ข้อความ )

กรณีที่ต้องการให้ปรับขนาดของ QLabel ใหม่ให้เหมาะกับข้อความที่กำหนดให้เรียกใช้ฟังก์ชันต่อไปนี้

วัตถุ.adjustSize()

สำหรับการระบุตำแหน่งแส้งข้อความประเภทเลเบิลสามารถกำหนดได้คำสั่ง move() ดังรูปแบบของการใช้งานต่อไปนี้

วัตถุ.move( x, y)

ตัวอย่างการสร้างเลเบิลเป็นดังนี้

        self.lbl = QLabel("ข้อความ", self)
        self.lbl.move( 100, 20 )
        self.lbl.setText("ข้อความ2")
        self.lbl.adjustSize()

นอกจากนี้ กรณีที่ต้องการแสดงรูปภาพแทนข้อความนั้น สามารถทำได้เช่นกันแต่จะขอกล่าวถึงในบทความถัดไป

QComboBox

วิดเจ็ต (widget) แบบ QComboBox มีลักษณะเหมือนลิสต์แต่ผู้ใช้สามารถเลือกได้เพียง 1 รายการจากรายการที่ผู้เขียนโปรแกรมเตรียมไว้ให้ ดังภาพที่ 3 โดยต้องเรียกนำเข้าไลบรารี QComboBox ซึ่งเป็นคลาสที่สืบทอดมาจาก QtWidgets.QtWidget อีกทอดหนึ่ง

การสร้างวัตถุประเภทคอมโบบ็อกซ์ (combo box) เพื่อสร้างวิดเจ็ตประเภท QComboBox เขียนได้ดังนี้ โดยหน้าต่างหลักหมายถึงค่าตำแหน่งของหน้าต่างที่วิดเจ็ตคอมโบบ็อกซ์ของเราอยู่ภายใต้หน้าต่างนั้น เช่นในตัวอย่างโปรแกรมจะเป็น self เนื่องจากใช้หน้าต่างที่สร้างเป็นหน้าต่างหลักและให้คอมโบบ็อกซ์อยู่ภายในหน้าต่างนั้น

วัตถุ = QComboBox( หน้าต่างหลัก )

การเพิ่มรายการเข้าไปในรายการของคอมโบบ็อกสามารถใช้คำสั่ง addItem() ตามรูปแบบต่อไปนี้

วัตถุ.addItem( ข้อมูลที่เพิ่ม )

การกำหนดตำแหน่งแสดงผลของตอมโบบ็อกใช้คำสั่ง move() เพื่อระบุพิกัด (x,y) ที่มุมซ้ายบนของคอมโบบ็อกซ์ ดังรูปแบบต่อไปนี้

วัตถุ.move( x, y )

ถ้าผู้เขียนโปรแกรมต้องการทราบจำนวนรายการในคอมโบบ็อกซ์สามารถใช้ฟังก์ชัน count() เป็นคำสั่งสำหรับนับจำนวนตามรูปแบบการใช้งานดังนี้

จำนวนรายการ = วัตถุ.count()

การอ่านค่าลำดับที่ถูกผู้ใช้เลือกใช้คำสั่ง currentIndex() ดังนี้

ลำดับรายการที่ถูกเลือก = วัตถุ.currentIndex()

สำหรับกรณีที่ต้องการอ่านชื่อของการรายการที่ถูกเลือกให้ใช้คำสั่ง currentText() ตามรูปแบบการใช้งานต่อไปนี้

ข้อความ = วัตถุ.currentText()

ตัวอย่างโปรแกรม

ตัวอย่างโปรแกรมต่อไปนี้เป็นการนำโค้ดอ่านรายชื่อพอร์ตอนุกรมที่เชื่อมต่อกับบอร์ด Raspberry Pi ดังภาพที่ 1 มาแสดงผลผ่านทาง PyQt5 โดยนำรายชื่อพอร์ตที่พบนำใส่เข้่ไปในวัตถุประเภท QComboBox ดังนั้น ถ้าพบว่ามี 3 อุปกรณ์เชื่อมต่อพอร์ตอนุกรมจะแสดงรายการเลือกในคอมโบบ็อกซ์เป็น 3 รายการ เป็นต้น

# -*- coding: UTF-8 -*-
import glob
import serial
from PyQt5.QtWidgets import QApplication, QLabel, QComboBox, QWidget, QMainWindow, QPushButton
from PyQt5.QtGui import QIcon, QColor
 
class MyApp(QWidget):
    def __init__(self, title, w=640, h=480):
        super().__init__()
        self.setWindowTitle(title)
        self.setGeometry(0, 0, w, h)
        self.status = False

        self.lbl = QLabel("รายการพอร์ตอนุกรม", self)
        self.lbl.move( 100, 20 )
        self.lbl.adjustSize()

        self.cbb = QComboBox(self)
        self.cbb.move( 280, 20 )
        
        ## serial port
        ports = glob.glob('/dev/tty[A-Za-z]*')
        for port in ports:
            try:
                s = serial.Serial(port)
                s.close()
                self.cbb.addItem(port)
            except (OSError, serial.SerialException):
                pass

        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    my_app = MyApp("PyQt5 Serial")
    sys.exit(app.exec_())

หลักการทำงานของโปรแกรมตัวอย่างเป็นดังนี้

  • สร้างหน้าต่างหลักจาก QWidget
  • กำหนดให้มีขนาดกว้าง 640 และสูง 480 หรือแล้วแต่ผู้ใช้กำหนดจากโปรแกรมหลัก
  • มีการสร้างวิดเจ็ตย่อย 2 ตัว
    • สร้างเลเบลสำหรับแสดง “รายการพอร์ตอนุกรม”
    • สร้างคอมโบบ็อกซ์สำหรับเก็บรายการพอร์ตที่ถูกเชื่อมต่อ เพื่อให้ผู้ใช้เลือก
  • ทำการค้นหารายชื่อพอร์ตที่เชื่อมต่อกับอุปกรณ์ภายนอกผ่านทางพอร์ตสื่อสารอนุกรม
    • สร้างตัวแปรสำหรับเก็บลิสต์รายชื่อพอร์ตอนุกรมทั้งหมดที่ระบบรู้จัก ซึ่งมีรูปแบบขึ้นต้นเป็น /dev/tty
    • ทำการวนรอบเพื่อตรวจว่าแต่ละชื่อนั้นใช้งานได้หรือไม่
      • ถ้าใช้งานได้ให้นำชื่อไปเก็บในรายการของคอมโบบ็อก
      • ถ้าไม่ได้ซึ่งรู้ได้จากการเกิด exception ก็จะข้ามการเกิดความผิดพลาดนั้นไป
  • แสดงหน้าต่าง

ส่วนโปรแกรมหลักนั้นทำการสร้างหน้าต่างโดยกำหนดข้อความในชื่อหน้าต่างเป็น “PyQt5 Serial” และการออกจากโปรแกรมทำโดยการปดปุ่มปิดหน้าต่าง และตัวอย่างผลลัพธ์ของการทำงานของโปรแกรมเป็นดังภาพที่ 4

ภาพที่ 4 ตัวอย่างผลลัพธ์ของโปรแกรมตัวอย่าง

สรุป

จากบทความนี้ผู้อ่านได้นำการอ่านรายชื่อพอร์ตที่ถูกเชื่อมกับพอร์ตอนุกรมของบอร์ด Raspberry Pi มาทำงานในแบบกราฟิกส์โหมดผ่านทาง PtQt5 โดยนำค่าที่อ่านได้ไปใส่ใน Combobox เพื่อให้ผู้ใช้เป็นผู้เลือกรายการ ซึ่งจะพบว่า การที่ได้เขียนโค้ดต้นแบบในแบบโหมดตัวอักษรมาก่อนจะทำให้ง่ายต่อการปรับปรุงโค้ดให้เป็นกราฟิกส์โหมด เนื่องจากในโหมดกราฟิกส์หรือ GUI นั้นต้องอาศัยการเรียกใช้วิดเจ็ตและหน้าต่าง (Windows หรือ Widgets) อื่น ๆ มาประกอบกันเพื่อใช้สำหรับติดต่อกับผู้ใช้ และที่ยากที่สุกคงเป็นเรื่องของการจัดวางอย่างไรแล้วรูปลักษณ์สวยงาม น่าใช้ และใช้งานง่ายตามหลักของ UX Design หรือ การออกแบบโดยหลักผู้ใช้เป็นสำคัญ ซึ่งเป็นอีกประเด็นที่สำคัญมากเนื่องจาก ต่อให้โปรแกรมดีเพียงใด แต่การออกแบบให้ใช้งานได้ง่าย หรือน่าใช้ไม่ได้ ก็ไม่เกิดแรงจูงใจให้ผู้ใช้หันมาใช้โปรแกรมของเรา สุดท้ายนี้ หวังว่าบทความนี้คงมีประโยชน์บ้างไม่มากก็น้อย และขอให้สนุกกับการเขียนโปรแกรมครับ

แหล่งอ้างอิง

  1. QComboBox

(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-10-27, 2022-01-10