ในการพัฒนาซอฟต์แวร์ที่เป็น Graphic User Interface (GUI) เกือบทั้่งหมดจะใช้สถาปัตยกรรมแบบ Event Driven คือการตรวจจับเหตุการณ์ที่เกิดขึ้นใน Application หรืออาจเรียกว่า State Change แล้วก็ตอบสนองต่อเหตุการณ์ตามที่ผู้พัฒนาออกแบบไว้ และโปรแกรม Calculator ในตัวอย่างก็เช่นเดียวกัน
การสร้าง Event Driven Application โดยทั่วไปเราต้องคำนึงถึง 3 เรื่องด้วยกันคือ
- ผู้สร้าง Event
- ตัว Event เอง
- ผู้ตอบสนองต่อ Event
แต่ใน PyQt จะมีการเรียกชื่อเหล่านี้ต่างออกไป คือ
- เมื่อมีเหตุการณ์เกิดขึ้น ผู้สร้างเหตุการณ์จะสร้าง Signal ขึ้นมา
- ผู้ตอบสนองต่อเหตุการณ์จะเรียกว่า Slot
- ผู้สร้างเหตุการณ์จะต้องทำการ Emit ก่อน ผู้ตอบสนองจึงจะรับรู้ได้
การผูก Signal เข้ากับ Slot ใน PyQt
PyQt รุ่นที่เก่ากว่า 4.5 มีการใช้รูปแบบคำสั่งแบบข้างล่างนี้
ต่อมามีการเปลี่ยนแปลงรูปแบบไปให้ง่ายขึ้น ดังนี้
Calculator Application
ในตอนนี้ผมได้เปลี่ยนหน้าตาของ Calculator ไปนิดหน่อย ดังภาพ
ตัวที่ทำหน้าที่เป็นผู้สร้าง Signal หรือ Event ก็คือปุ่มต่าง ๆ บนหน้าจอ ส่วน Slot นั้นผมสร้างเป็น Python Script ดังนี้
def append_text(self):
sender = self.sender()
val = sender.text()
if val in ['/','+','-','(',')','x'] :
val = " "+val+" "
disp = self.disp_text.text()
disp += val
self.disp_text.clear()
self.disp_text.setText(disp)
def backspace(self):
self.disp_text.backspace()
def clear_text(self):
self.disp_text.clear()
def calculate(self):
# need to change to pythonic string
txt = str(self.disp_text.text())
if len(txt) > 0 :
result = self.calculator.calculate(txt)
self.disp_text.clear()
self.disp_text.setText(str(result))
คำอธิบาย
ใน def append_text()sender = self.sender()
เป็นการตรวจหาผู้ส่ง Signal ว่ามาจากปุ่มไหน เพราะมีหลายปุ่มที่สามารถส่ง Signal ได้
val = sender.text()
if val in ['/','+','-','(',')','x'] :
val = " "+val+"
รับค่า text ของ sender หากค่านั้นเป็นหนึ่งใน operator ให้ทำการเพิ่มช่องว่างไว้ข้างหน้าและข้างหลัง (เพื่อประโยชน์ในการตัดข้อความต่อไป)
self.disp_text.clear()
self.disp_text.setText(disp)
นำข้อความจาก sender ไปแสดง
ใน def calculate() จะมีการอ้างถึง self.calculator ซึ่งเป็น class ที่ถูกสร้างขึ้นมาเพื่อใช้ประมวลผล และจะกล่าวถึงต่อไป
ต่อไปเป็น script ในการผูก Signal เข้ากับ Slot หรือ การเชื่อมระหว่างปุ่มต่างเข้ากับ slot หรือ Python script ที่สร้างข้างบน
self.num_btn_1.clicked.connect(self.append_text)
self.num_btn_2.clicked.connect(self.append_text)
self.num_btn_3.clicked.connect(self.append_text)
self.num_btn_4.clicked.connect(self.append_text)
self.num_btn_5.clicked.connect(self.append_text)
self.num_btn_6.clicked.connect(self.append_text)
self.num_btn_7.clicked.connect(self.append_text)
self.num_btn_8.clicked.connect(self.append_text)
self.num_btn_9.clicked.connect(self.append_text)
self.num_btn_0.clicked.connect(self.append_text)
self.left_paren_btn.clicked.connect(self.append_text)
self.right_paren_btn.clicked.connect(self.append_text)
self.plus_btn.clicked.connect(self.append_text)
self.minus_btn.clicked.connect(self.append_text)
self.mult_btn.clicked.connect(self.append_text)
self.div_btn.clicked.connect(self.append_text)
self.dot_btn.clicked.connect(self.append_text)
self.bsp_btn.clicked.connect(self.backspace)
self.clear_btn.clicked.connect(self.clear_text)
self.exe_btn.clicked.connect(self.calculate)
ถึงตอนนี้เราก็มีองค์ประกอบของ Application เกือบครบละครับ ตอนนี้เรามี User Interface, Signal และ Slot แล้วก็เอาทั้งหมดมารวมกันจะได้ั PyQt script ตามนี้
import sys
from PyQt4 import QtCore, QtGui, uic
Ui_MainWindow, QtBaseClass = uic.loadUiType("calculator_demo.ui")
class MyApp(QtGui.QMainWindow, Ui_MainWindow):
#on_click = pyqtSignal(str)
def __init__(self):
QtGui.QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self.init_ui()
self.calculator = Calculator()
def init_ui(self):
self.num_btn_1.clicked.connect(self.append_text)
self.num_btn_2.clicked.connect(self.append_text)
self.num_btn_3.clicked.connect(self.append_text)
self.num_btn_4.clicked.connect(self.append_text)
self.num_btn_5.clicked.connect(self.append_text)
self.num_btn_6.clicked.connect(self.append_text)
self.num_btn_7.clicked.connect(self.append_text)
self.num_btn_8.clicked.connect(self.append_text)
self.num_btn_9.clicked.connect(self.append_text)
self.num_btn_0.clicked.connect(self.append_text)
self.left_paren_btn.clicked.connect(self.append_text)
self.right_paren_btn.clicked.connect(self.append_text)
self.plus_btn.clicked.connect(self.append_text)
self.minus_btn.clicked.connect(self.append_text)
self.mult_btn.clicked.connect(self.append_text)
self.div_btn.clicked.connect(self.append_text)
self.dot_btn.clicked.connect(self.append_text)
self.bsp_btn.clicked.connect(self.backspace)
self.clear_btn.clicked.connect(self.clear_text)
self.exe_btn.clicked.connect(self.calculate)
def append_text(self):
sender = self.sender()
val = sender.text()
if val in ['/','+','-','(',')','x'] :
val = " "+val+" "
disp = self.disp_text.text()
disp += val
self.disp_text.clear()
self.disp_text.setText(disp)
def backspace(self):
self.disp_text.backspace()
def clear_text(self):
self.disp_text.clear()
def calculate(self):
# need to change to pythonic string
txt = str(self.disp_text.text())
if len(txt) > 0 :
result = self.calculator.calculate(txt)
self.disp_text.clear()
self.disp_text.setText(str(result))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
window = MyApp()
window.show()
sys.exit(app.exec_())
ปล. ตอนนี้หากนำ script นี้ไปเรียกใช้งานอาจจะมีปัญหาอยู่ เพราะ class Calculator ยังไม่ได้สร้างครับ แล้วจะเอาเล่าต่อตอน 3
Sign up here with your email
ConversionConversion EmoticonEmoticon