ในบทความนี้จะอธิบายวิธีการเขียนโค้ดใช้งานมอดูล
pyqt ซึ่งเป็นมอดูลหนึ่งในไพธอนที่เอาไว้ใช้สร้าง
GUI (Graphical User Interface = ส่วนต่อประสานกราฟิกกับผู้ใช้)
pyqt คืออะไร
ภาษาไพธอนสามารถสร้าง GUI ได้อย่างไม่ยากนัก มีมอดูลอยู่หลายตัวที่ใช้ทำแบบนี้ได้ เช่น tkinter, kivy, wxpython เป็นต้น
ในจำนวนนั้นตัวหนึ่งที่นิยมใช้กันอย่างกว้างขวางก็คือ pyqt ซึ่งเป็นมอดูลสำหรับเขียนเฟรมเวิร์ก qt ด้วยภาษาไพธอน
ภาพตัวอย่าง GUI ที่ถูกสร้างโดยใช้ pyqt (รายละเอียดอ่านได้ใน
https://phyblas.hinaboshi.com/20180301)
qt คือเฟรมเวิร์กสร้าง GUI ที่ได้รับความนิยมสูงและถูกใช้สร้างโปรแกรมต่างๆมามากมายแล้ว โดยเดิมมีพื้นฐานมาจากภาษา C++ แต่ก็ถูกพัฒนาขึ้นมาให้ใช้ในภาษาต่างๆเช่น java, php, python, ruby, ฯลฯ
รายละเอียดเพิ่มเติมอ่านได้ใน wikipedia
https://th.wikipedia.org/wiki/Qt
ดังนั้นถ้าใช้ pyqt เป็นแล้วหากจะเปลี่ยนไปเขียน GUI โดยใช้ qt ในภาษาอื่นก็ทำได้ไม่ยาก เพราะใช้พื้นฐานร่วมกัน
qt นั้นได้ถูกพัฒนาขึ้นมาเรื่อยๆ ปัจจุบันเวอร์ชัน qt6 เพิ่งจะออกมา โดยมอดูลของ qt6 ในไพธอนนั้นมีชื่อว่า pyqt6
แต่เนื่องจาก qt6 เพิ่งออกและยังมีข้อมูลน้อยอยู่ ในที่นี้จะยังคงสอน qt5 เป็นหลัก จนกว่า qt6 จะเริ่มอยู่ตัวและถูกใช้งานอย่างกว้างขวาง ถึงตอนนั้นก็อาจจะกลับมาแก้เนื้อหาทั้งหมดเป็น qt6
นอกจาก pyqt แล้วก็ยังมี pyside ที่เป็นมอดูลสำหรับใช้ qt ในไพธอนเช่นกัน ซึ่งแท้จริงแล้วสองตัวนี้มีข้อแตกต่างกันตรงที่แค่ถูกพัฒนาขึ้นโดยคนละบริษัท และมีเงื่อนไขด้านลิขสิทธิ์การใช้งานแตกต่างกันเล็กน้อย และคำสั่งภายในนั้นมีความแตกต่างกันอยู่บ้าง แต่โดยรวมแล้วส่วนใหญ่เหมือนกัน
มอดูลของ qt5 ในไพธอนมีชื่อว่า pyqt5 ซึ่งเป็นตัวที่จะใช้สอนในนี้เป็นหลัก ส่วนของ qt6 ใช้ชื่อว่า pyqt6
โค้ด pyqt5 ที่เขียนในบทเรียนนี้ถ้าใครจะเอาไปใช้ใน pyqt6 ก็ไม่ได้ต่างกันมาก อาจต้องปรับโค้ดสักเล็กน้อย แต่โดยรวมแล้วไม่ได้ต่างกันมาก หลักการสามารถนำไปใช้ได้เช่นกัน ถ้าอะไรที่ใช้ใน pyqt5 ได้ก็สามารถใช้ใน pyqt6 ได้เช่นกัน
และนอกจากนี้ยังสามารถนำไปใช้กับ pyside ได้เช่นกัน โดยสำหรับมอดูลฝั่ง pyside ที่ใช้กับ qt5 มีชื่อว่า pyside2 ส่วนของ qt6 ใช้ชื่อว่า pyside6
pyside2 จะคล้ายกับ pyqt5 ในขณะที่ pyside6 จะคล้ายกับ pyqt6
นอกจากนี้อาจยังมีคนใช้รุ่นเก่าคือ qt4 อยู่ซึ่งแม้จะมีความใกล้เคียงกับ qt5 แต่ก็มีความแตกต่างกันพอสมควร แต่ถ้าจะใครจะนำโค้ดไปดัดแปลงใช้กับ qt4 ก็ทำได้ไม่ยากนัก
เรื่องเวอร์ชันของ qt และมอดูลในไพธอนอาจสรุปได้ดังนี้ กล่าวโดยรวมคือ
เวอร์ชันของ qt | มอดูลในไพธอน |
qt4 | pyqt4 ≈ pyside |
qt5 | pyqt5 ≈ pyside2 |
qt6 | pyqt6 ≈ pyside6 |
โดยแม้ว่าในที่นี้จะอธิบาย pyqt5 เป็นหลัก แต่ก็สามารถนำไปใช้กับ pyqt4, pyqt6, pyside, pyside2, pyside6 ได้โดยแค่อาจต้องเปลี่ยนแปลงโค้ดบางส่วน แต่หลักการโดยรวมแล้วไม่ต่างกันนัก
การติดตั้ง pyqt5
pyqt5 ก็เช่นเดียวกับมอดูลอื่นๆในไพธอน สามารถติดตั้งได้ง่ายโดยใช้ pip
pip install pyqt5
หรืออาจใช้ conda ก็ได้
conda install -c anaconda pyqt
เพียงแต่หากใครใช้ anaconda ก็จะมี pyqt5 ติดมาให้อยู่แล้ว ไม่จำเป็นต้องทำอะไรเพิ่ม
หากใครอยากลอง pyqt6 ก็ติดตั้งได้โดย pip เช่นกัน
pip install pyqt6
การเริ่มลองใช้งาน
เพื่อทดสอบว่า pyqt ถูกติดตั้งลงเครื่องอย่างถูกต้องพร้อมใช้งานได้หรือยัง ลองรันโค้ดตามนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget
qAp = QApplication(sys.argv)
natang = QWidget()
natang.show()
qAp.exec_()
อนึ่ง ให้ระวังว่าคำว่า pyqt5 ในโค้ดจะต้องเขียนตัวพิมพ์เล็กพิมพ์ใหญ่ให้ตรงกันเป็น PyQt5 ทั้งหมด ไม่เช่นนั้นจะเกิดข้อผิดพลาด
สำหรับรายละเอียดของโค้ดนี้จะอธิบายอีกทีหลังจากนี้ สำหรับตอนนี้แค่ให้ลองรันดูเพื่อทดสอบก่อน ซึ่งถ้าหากไม่มีข้อผิดพลาดอะไรน่าจะได้หน้าต่างลักษณะนี้ออกมา แสดงว่าใช้งานได้
แต่ถ้าหากใช้ระบบปฏิบัติการอื่นๆก็จะได้ผลต่างกันออกไป
เช่น ภาพนี้ลองใน mac
ส่วนภาพนี้ลองใน linux
จะเห็นว่าลักษณะหน้าต่างจะมีรายละเอียดปลีกย่อยต่างกันออกไป ขึ้นอยู่กับระบบปฏิบัติการที่ใช้ แต่โดยรวมแล้วจะมีลักษณะเหมือนกัน
สำหรับในบทความนี้จะแสดงผลที่ได้จากการใช้ pyqt 5.9.7 รันใน mac โดยไพธอน 3.9 ใน spyder แต่ไม่ว่าจะใช้ระบบปฏิบัติการไหนหรือใช้ไพธอนเวอร์ชันไหนหรือ pyqt เวอร์ชันไหน โดยรวมแล้ววิธีการใช้งานก็จะไม่ต่างกันมาก
อนึ่ง หากต้องการลองตรวจดูว่า pyqt ที่ใช้อยู่เป็นเวอร์ชันเท่าไหร่ได้ก็อาจทำได้โดยโค้ดนี้
import PyQt5
print(PyQt5.QtCore.QT_VERSION_STR)
widget คืออะไร
คำว่า widget คงเป็นศัพท์คำหนึ่งที่ต้องเจออยู่ตลอดเมื่อใช้ qt หรือแม้แต่เมื่อใช้งานคอมพิวเตอร์ จึงต้องขออธิบายไว้ตรงนี้เพื่อทำความคุ้นเคยไว้สักหน่อย
widget เป็นคำศัพท์ภาษาอังกฤษคำหนึ่งที่แปลเป็นไทยได้ยาก หากเปิดดิกชันนารีก็อาจะเจอคำอธิบายว่า "เครื่องจักรกลเล็กๆ ที่ไม่รู้จักชื่อ" ซึ่งก็ดูจะเป็นคำอธิบายที่ไม่ได้ทำให้เข้าใจขึ้นมาสักเท่าไหร่นัก ดังนั้นไม่จำเป็นต้องไปสนใจก็ได้
อย่างไรก็ตาม ความหมายของ widget ที่ใช้ในทางด้านคอมพิวเตอร์นั้นมาจากคำว่า window (หน้าต่าง) + gadget (อุปกรณ์ขนาดเล็กๆสำหรับทำอะไรบางอย่าง)
ดังนั้นในที่นี้ widget ก็คือ "อะไรบางอย่างที่มีลักษณะเป็นหน้าต่าง ซึ่งเอาไว้ใช้เป็นอุปกรณ์ทำอะไรบางอย่าง"
ในภาษาจีนเองก็แปลเป็น
小工具 ซึ่งแปลตรงๆก็คือ "อุปกรณ์เล็ก"
แต่อุปกรณ์ที่ว่านี้อาจไม่ใช่อะไรที่เป็นรูปธรรมจับต้องได้ แต่อาจเป็นพวกชุดคำสั่งหรือโปรแกรมบางอย่างที่ถูกสร้างขึ้นมา
สำหรับใน qt นั้น การสร้าง GUI จะทำโดยการเอา widget ต่างๆมาประกอบเข้าด้วยกัน
นั่นคือใน qt จะเรียกทุกสิ่งทุกอย่างที่เป็นส่วนประกอบว่า widget ดังนั้นให้เข้าใจว่า widget คือส่วนประกอบที่จะใช้ ซึ่งมีอยู่หลากหลายชนิด
สำหรับใน pyqt นั้น widget ชนิดต่างๆนั้นอยู่ในรูปของ
คลาส ของไพธอน ซึ่งทั้งหมดถูกบรรจุอยู่ในมอดูลย่อย PyQt5.QtWidgets เรียกใช้ได้โดย
from PyQt5.QtWidgets import ชื่อ_widget
ภาพแสดงส่วนประกอบของ GUI ที่สร้างจาก pyqt ซึ่งประกอบด้วย widget ชนิดต่างๆ
ในที่นี้ widget ที่ชื่อ QWidget คือตัวหลักที่คลุมทั้งหมด ซึ่งทำหน้าที่เป็นหน้าต่างใหญ่
ส่วน QLabel, QLineEdit, QDateEdit, QButtonGroup, QRadioButton, QCheckBox, QComboBox ต่างก็เป็น widget ซึ่งถูกนำมาใช้ประกอบเป็นส่วนย่อยของ widget ที่เป็นหน้าต่างใหญ่อีกที
จากภาพจะเห็นได้ว่า GUI ของ qt นั้นได้จากการเอา widget มาประกอบกันในลักษณะเช่นนี้
ใน pyqt นั้น QWidget คือคลาสของ widget พื้นฐาน และคลาสอื่นๆเล่น QLabel, QLineEdit, ฯลฯ นั้นล้วนเป็นซับคลาสของ QWidget ซึ่งถูกใส่หน้าที่ให้ทำงานเฉพาะในแบบต่างๆ
ชื่อคลาสของ widget ต่างๆของ qt นั้นจะขึ้นต้นด้วย Q ทั้งหมด เป็นเอกลักษณ์อย่างหนึ่งที่ช่วยให้จำง่าย
ส่วนประกอบของมอดูล pyqt5 และการเรียกใช้งาน
ต่อไปจะเริ่มอธิบายส่วนประกอบของ pyqt5 ในเบื้องต้น
pyqt5 จะประกอบไปด้วยมอดูลย่อยที่งานเป็นหลักอยู่ ๓ ตัวคือ QtCore, QtGui, QtWidgets และนอกจากนี้ก็ยังมีตัวอื่นๆอีกที่เอาไว้ใช้ทำงานในด้านต่างๆที่เจาะจงลงไปอีกเช่น QtOpenGL, QtWebEngine, QtSql, QtXml, ฯลฯ
QtWidgets จะเก็บพวกคลาสของ widget ชนิดต่างๆซึ่งจำเป็นต้องใช้เป็นส่วนประกอบในการสร้าง GUI
QtGui เก็บพวกคลาสของส่วนประกอบอื่นๆที่จำเป็นนอกเหนือจากพวก widget
ซึ่งที่จริงแล้วแค่ import PyQt5 ทีเดียวก็สามารถใช้คำสั่งต่างๆทั้งใน QtCore, QtGui, QtWidgets ได้
import PyQt5
print(PyQt5.QtWidgets.QApplication) # ได้ <class 'spydercustomize.SpyderQApplication'>
print(PyQt5.QtCore.Qt) # ได้ <class 'PyQt5.QtCore.Qt'>
print(PyQt5.QtGui.QFont()) # ได้ <PyQt5.QtGui.QFont object at 0x7feefb01c660>
เพียงแต่เนื่องจากต้องพิมพ์ยาว วิธีการเช่นนี้จึงไม่นิยมใช้นัก
วิธีการที่แนะนำให้ใช้คือการใช้ from import เอาเฉพาะส่วนประกอบที่ต้องการมาทีละอย่างแบบนี้แทน
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QFont
print(QApplication) # ได้ <class 'spydercustomize.SpyderQApplication'>
print(Qt) # ได้ <class 'PyQt5.QtCore.Qt'>
print(QFont()) # ได้ <PyQt5.QtGui.QFont object at 0x7feefb0714a0>
แต่ก็มีคนจำนวนมากที่ใช้ from import * เพื่อ import ทุกสิ่งทุกอย่างมาเตรียมไว้ทีเดียวแบบนี้
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *
from PyQt5.QtGui import *
แต่วิธีนี้ไม่ค่อยแนะนำ โดยเฉพาะสำหรับผู้ฝึกหัดใช้งานใหม่ ดังนั้นในที่นี้จะใช้วิธีการ from import เฉพาะตัวที่ต้องใช้ในเท่านั้น อีกทั้งแบบนี้จะดูแล้วเป็นระเบียบเข้าใจได้ง่ายกว่า
ภาพรวมโดยทั่วไปในการใช้งาน
โดยทั่วไปแล้วเวลาเขียนโค้ด pyqt5 เพื่อสร้าง GUI นั้น โดยภาพรวมแล้วจะออกมาในลักษณะแบบนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget # เรียกใช้คลาสต่างๆที่ต้องการ
qAp = QApplication(sys.argv) # สร้างอินสแตนซ์ของ QApplication
natang = QWidget() # สร้างอินสแตนซ์ของ QWidget ขึ้นมาเป็นหน้าต่าง
'''
ส่วนประกอบและรายละเอียดต่างๆของ GUI
'''
natang.show() # สั่งให้แสดงหน้าต่างที่สร้างขึ้นมา
qAp.exec_() # ใช้เมธอด .exec_ ที่ตัวอินสแตนซ์ของ QApplication ที่สร้างขึ้นมาเพื่อเริ่มทำงาน
โดยเริ่มจากเรียกใช้โดย import คลาสหรือส่วนประกอบต่างๆที่ต้องการใช้ใน pyqt5 เตรียมไว้
ในตัวอย่างนี้แสดงแค่ ๒ ตัวคือ QApplication กับ QWidget แต่ในการใช้งานจริงหากต้องการใช้อะไรก็ให้ import ตัวนั้นมาด้วย
(หรือถ้าขี้เกียจก็อาจใช้
from PyQt5.QtWidgets import *
เรียกใช้ทั้งหมดทีเดียวไปเลย แม้ว่านี่อาจจะไม่ใช่วิธีที่น่าแนะนำนัก)
นอกจากนี้ยังรวมถึงมอดูลอื่นๆที่จะเอามาใช้ด้วย ซึ่งโดยปกติจะต้องมี sys เพราะจำเป็นต้องใช้ sys.argv
จากนั้นก็เริ่มทำการสร้างอินสแตนซ์ของ QApplication ซึ่งเป็นส่วนประกอบหลักสำหรับตัวโปรแกรม โดยเมื่อสร้าง ให้ใส่ sys.argv ไปเป็นอาร์กิวเมนต์
เกี่ยวกับเรื่องของ sys.argv นั้นอ่านรายละเอียดได้ใน
https://phyblas.hinaboshi.com/20190705
ในที่นี้อาจทำความเข้าใจแบบง่ายๆแค่ว่า
qAp = QApplication(sys.argv)
คือโค้ดเริ่มต้นสร้าง GUI ของ qt ในไพธอน ซึ่งมักจะต้องเขียนเสมอเวลาที่ใช้ pyqt
จากนั้นในตอนส่วนท้ายที่สุดของโค้ดคือ
qAp._exec()
นั้นเป็นการสั่งให้เริ่มต้นการทำงาน
โค้ดที่อยู่ในบรรทัดระหว่าง
qAp = QApplication(sys.argv)
กับ
qAp._exec()
นี้คือส่วนของการสร้างหน้าต่าง
โดยมักจะเริ่มต้นด้วย
natang = QWidget()
เพื่อสร้างอินสแตนซ์ของ widget ที่เป็นหน้าต่างหลักขึ้นมา แล้วก็จบลงด้วย
natang.show()
เพื่อสั่งให้แสดงหน้าต่างนั้น
ทีนี้โค้ดส่วนที่อยู่ระหว่าง
natang = QWidget()
กับ
natang.show()
ก็คือรายละเอียดต่างๆของ GUI ที่เราต้องการสร้างขึ้น เช่นการสร้างและจัดวาง widget ต่างๆและกำหนดการทำงาน
อนึ่ง ทั้ง
qAp
และ
natang
นั้นเป็นชื่อตัวแปรที่กำหนดขึ้นเองในที่นี้ ซึ่งตลอดบทเรียนนี้ก็จะใช้แบบนี้ทั้งหมด
ภาพรวมในการเขียนนั้นก็เป็นดังที่กล่าวมานี้ อย่างไรก็ตามในทางปฏิบัติแล้วถ้าไปดูในตัวอย่างการใช้ส่วนใหญ่แล้วจะใช้วิธีการสร้างเป็นคลาสขึ้นโดยทำเป็นซับคลาสของ QWidget ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget
# สร้างคลาสของหน้าต่างที่เราต้องการขึ้นมาเป็นซับคลาสของ QWidget
class Natang(QWidget):
def __init__(self):
super().__init__()
'''
ส่วนประกอบและรายละเอียดต่างๆของ GUI
'''
self.show() # สั่งให้แสดงหน้าต่างที่สร้างขึ้นมา
qAp = QApplication(sys.argv)
natang = Natang() # สร้างอินสแตนซ์ของคลาสหน้าต่างที่เราสร้างขึ้นมา
qAp.exec_()
ซึ่งเมื่อเขียนแบบนี้ก็จะทำให้ดูเป็นระเบียบขึ้นมา เพราะโค้ดทั้งหมดที่กำหนดลักษณะและการทำงานของ GUI จะอยู่ภายในคลาสที่สร้างขึ้นทั้งหมดเลย
อย่างไรก็ตาม ถึงจะไม่สร้างเป็นคลาสก็ทำงานได้เช่นเดียวกัน จึงอาจแล้วแต่ความสะดวกและความต้องการในการใช้ในแต่ละงาน
สำหรับใน
เนื้อหาบทเรียนนี้จะเขียนโค้ดแบบ
ไม่สร้างคลาสเป็นหลัก เพื่อความเข้าใจง่าย แต่ในการใช้งานจริงก็แนะนำให้สร้างเป็นคลาส ซึ่งวิธีการเขียนในภาพรวมก็เหมือนกัน ถ้าเข้าใจหลักการดีแล้ว ก็สามารถปรับไปเขียนแบบสร้างคลาสได้
สรุปท้ายบท
ในบทนี้ได้แนะนำให้รู้จักกับ pyqt และภาพรวมของการเขียนโค้ดในเบื้องต้นแล้ว
สำหรับในบทถัดๆไปจะค่อยเริ่มอธิบายการสร้างและปรับแต่ง widget ต่างๆเพื่อประกอบกันเป็น GUI ขึ้นมา
อ่านบทถัดไป >>
บทที่ ๒