จากบทความก่อหน้านี้เราได้อ่านรายชื่ออุปกรณ์ที่เชื่อมต่อกับพอร์ตอนุกรมของบอร์ด Raspberry Pi หรือ RPi ด้วยไลบรารี pySerial ดังภาพที่ 1 ในแบบโหมดตัวอักษรไปแล้ว ในบทความนี้เป็นการผนวกหลักการทำงานจากก่อนหน้าเข้ากับการใช้ส่วนติดต่อกับผู้ใช้แบบกราฟิกส์ หรือ GUI (Graphics User Interface) ผ่านทางไลบรารี PyQt5 โดยแสดงรายการเอาไว้ใน combobox เพื่อให้ผู้ใช้งานเลือกใช้งานได้ แต่ถ้าไม่พบพอร์ตสื่อสารอนุกรมที่เชื่อมต่อกับบอร์ด RPi จะปิดการใช้งานของ combobox ไม่ให้ผู้ใช้งานเลือกใช้งาน ดังนั้น บทความนี้จึงมีเนื้อหาเกี่ยวกับการประยุกต์ใช้ pySerial กับการใช้งานของ QLabel และ QComboBox ในไลบรารี PyQt5
บทความนี้ประกอบด้วย 3 ตอน คือ
- List the serial ports connected to the RPi with pySerial.
- List the serial ports connected to the RPi with pySerial and PyQt5.
- 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
สรุป
จากบทความนี้ผู้อ่านได้นำการอ่านรายชื่อพอร์ตที่ถูกเชื่อมกับพอร์ตอนุกรมของบอร์ด Raspberry Pi มาทำงานในแบบกราฟิกส์โหมดผ่านทาง PtQt5 โดยนำค่าที่อ่านได้ไปใส่ใน Combobox เพื่อให้ผู้ใช้เป็นผู้เลือกรายการ ซึ่งจะพบว่า การที่ได้เขียนโค้ดต้นแบบในแบบโหมดตัวอักษรมาก่อนจะทำให้ง่ายต่อการปรับปรุงโค้ดให้เป็นกราฟิกส์โหมด เนื่องจากในโหมดกราฟิกส์หรือ GUI นั้นต้องอาศัยการเรียกใช้วิดเจ็ตและหน้าต่าง (Windows หรือ Widgets) อื่น ๆ มาประกอบกันเพื่อใช้สำหรับติดต่อกับผู้ใช้ และที่ยากที่สุกคงเป็นเรื่องของการจัดวางอย่างไรแล้วรูปลักษณ์สวยงาม น่าใช้ และใช้งานง่ายตามหลักของ UX Design หรือ การออกแบบโดยหลักผู้ใช้เป็นสำคัญ ซึ่งเป็นอีกประเด็นที่สำคัญมากเนื่องจาก ต่อให้โปรแกรมดีเพียงใด แต่การออกแบบให้ใช้งานได้ง่าย หรือน่าใช้ไม่ได้ ก็ไม่เกิดแรงจูงใจให้ผู้ใช้หันมาใช้โปรแกรมของเรา สุดท้ายนี้ หวังว่าบทความนี้คงมีประโยชน์บ้างไม่มากก็น้อย และขอให้สนุกกับการเขียนโปรแกรมครับ
แหล่งอ้างอิง
(C) 2020-2021, โดย อ.ดนัย เจษฎาฐิติกุล/อ.จารุต บุศราทิจ
ปรับปรุงเมื่อ 2021-10-27, 2022-01-10