ต่อจาก
บทที่ ๒๒
สำหรับในบทนี้จะเป็นเรื่องของการเขียนทับเมธอดของ event ต่างๆภายในคลาสของ widget ขึ้นใหม่เพื่อให้มีพฤติกรรมในแบบที่เราต้องการขึ้นมาได้
เมธอดของ event ที่สามารถเขียนทับเพื่อปรับ widget
โดยทั่วไปแล้วออบเจ็กต์ในคลาส QWidget จะมีเมธอดของที่ใช้ทำงานเมื่อเวลาเกิด event ต่างๆดังนี้
resizeEvent | เมื่อมีการปรับขนาด widget |
mousePressEvent | เมื่อกดคลิกเมาส์ที่ widget นั้น |
mouseReleaseEvent | เมื่อปล่อยเมาส์จาก widget นั้น |
mouseDoubleClickEvent | เมื่อดับเบิลคลิก widget นั้น |
enterEvent | เมื่อเลื่อนเมาส์เข้าไปใน widget นั้น |
leaveEvent | เมื่อเลื่อนเมาส์ออกจาก widget นั้น |
closeEvent | เมื่อกำลังจะปิด widget นั้น |
keyPressEvent | เมื่อกดคีย์บอร์ด |
mouseMoveEvent | เมื่อเลื่อนเมาส์ขณะที่คลิกค้างอยู่ |
dragEnterEvent | เมื่อคลิกลากเมาส์แล้วเข้ามาใน widget นั้น |
dragLeaveEvent | เมื่อคลิกลากเมาส์แล้วออกไปจาก widget นั้น |
dropEvent | เมื่อวางลากแล้วปล่อยเมาส์ |
changeEvent | เมื่อ widget มีการเปลี่ยนแปลง |
ซึ่งเมธอดเหล่านี้จะมีอยู่ภายในตัว QWidget อยู่แล้ว แม้ว่าเราจะไม่ได้เขียนแก้ขึ้น แต่เราสามารถเขียนคลาสใหม่โดยรับทอดจาก QWidget เดิมเพื่อให้ตัว widget นั้นมีพฤติกรรมแตกต่างออกไปจากเดิมเมื่อเกิด event ต่างๆได้
การเขียนทับเมธอดเหล่านี้อาจทำได้ใน QWidget และ widget ทุกชนิดซึ่งเป็นซับคลาสของ QWidget ด้วย และใช้ได้ทั้งที่ใช้เป็นหน้าต่างหรือใช้เป็นส่วนประกอบย่อย
ที่จริงเมื่อใช้ pyqt นั้นโดยทั่วไปแล้ว widget ที่เป็นตัวหน้าต่างจะถูกสร้างโดยเขียนคลาสโดยรับทอด QWidget หรือ widget ตัวใดๆที่ต้องการใช้เป็นตัวหน้าต่าง เช่น QMainWindow
แต่ในบทที่ผ่านมานั้นจะเห็นว่าเขียนโค้ดโดยที่ไม่ได้มีการสร้างคลาสขึ้นมาใหม่ เพราะไม่ได้มีความจำเป็นจะต้องไปทำอะไรในส่วนเมธอดของคลาส แต่ว่าเนื้อหาในส่วนตรงนี้ไปนั้นจะเป็นส่วนที่เราจำเป็นต้องเขียนคลาสใหม่ขึ้นมาโดยทำการรับทอดจากคลาสของ QWidget ที่ต้องการใช้ ดังนั้นเนื้อหาจากบทนี้ไปจะเปลี่ยนมาเป็นการเขียนเป็นคลาสทั้งหมด
ความแตกต่างระหว่างวิธีการเขียนแบบสร้างคลาสกับไม่สร้างคลาสนั้นได้ยกตัวอย่างและอธิบายถึงไปใน
บทที่ ๑ และ
บทที่ ๒
เมื่อจะเขียนทับเมธอดอาจเขียนได้ในลักษณะนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget
class Natang(QWidget):
def __init__(self):
super().__init__()
'''
ส่วนประกอบและรายละเอียดต่างๆของ GUI
'''
self.show() # สั่งให้แสดงหน้าต่างที่สร้างขึ้นมา
def เมธอดของ_event_ที่จะเขียนทับ(self,e):
'''
เนื้อหาที่ต้องการให้ทำ
'''
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ต่อไปจะแนะนำเมธอดของ event ที่เขียนทับได้ไปทีละตัว
การให้ทำคำสั่งเมื่อเปลี่ยนขนาดหน้าต่าง {resizeEvent}
resizeEvent เป็น event ที่เกิดขึ้นเมื่อทำการเปลี่ยนขนาดของ widget ซึ่งสำหรับ widget ที่เป็นตัวหน้าต่างนั้นปกติเราจะปรับขนาดได้ง่ายโดยการเอาเมาส์ไปวางลากที่ขอบ (ยกเว้นว่าถ้าใช้เมธอด setFixedSize ไปจะทำให้เลื่อนขนาดไม่ได้)
หากมี widget ที่ต้องการให้ทำเมื่อมีการเปลี่ยนขนาดหน้าต่างก็ทำได้โดยเชียนทับ resizeEvent ในคลาสใหม่ที่สร้างขึ้น
ตัวอย่างเช่น หากต้องการหน้าต่างที่เมื่อมีการเปลี่ยนขนาดแล้วมีการพิมพ์แสดงขนาดของหน้าต่างเดิมก่อนเปลี่ยนและขนาดใหม่หลังเปลี่ยนอาจเขียนได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(350,100) # ขนาดหน้าต่างตอนเริ่่มต้น
self.khokhwam = QLabel('',self) # ข้อความแสดงความเปลี่ยนแปลงของขนาดหน้าต่าง
self.khokhwam.setGeometry(10,10,400,70)
self.khokhwam.setStyleSheet('font-family: Tahoma; font-size: 16px;')
self.show()
def resizeEvent(self,e):
khanatdoem = e.oldSize()
khanatmai = e.size()
self.khokhwam.setText('หน้าต่างถูกเปลี่ยนขนาด\nจาก (%d,%d)\nเป็น (%d,%d)'%(khanatdoem.width(),khanatdoem.height(),khanatmai.width(),khanatmai.height()))
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ก็จะได้หน้าต่างแบบนี้ออกมา ซึ่งพอลองเลื่อนปรับขนาดดูก็จะมีข้อความขึ้นบอกขนาดที่เปลี่ยนไปตลอดเวลาที่เลื่อนเปลี่ยนขนาด
พารามิเตอร์ของเมธอด event ที่จะได้นั้นก็คือตัวออบเจ็กต์ที่เก็บข้อมูลต่างๆของ event นั้นๆ (ในที่นี้ขอแทนด้วยตัวแปร e)
สำหรับ resizeEvent นั้นจะมีการเก็บขนาดเก่าและขนาดใหม่ไว้ สามารถใช้เมธอด .oldSize เพื่อดูค่าขนาดเก่า และ .size เพื่อดูค่าขนาดใหม่ได้
การให้ทำคำสั่งเมื่อกดหรือปล่อยเมาส์ {mousePressEvent mouseReleaseEvent mouseDoubleClickEvent}
mousePressEvent, mouseReleaseEvent และ mouseDoubleClickEvent เป็น event ที่เกิดขึ้นเมื่อกดเมาส์ ปล่อยเมาส์ และดับเบิลคลิกเมาส์ หากปรับเมธอดเหล่านี้ก็จะทำให้เกิดอะไรบางอย่างขึ้นตามที่ต้องการเมื่อกดหรือปล่อยหรือดับเบิลคลิกเมาส์ได้
ภายในออบเจ็กต์ตัว event ที่ได้มาเป็นพารามิเตอร์ในเมธอดนั้นจะมีเมธอด .pos() ซึ่งจะให้ออบเจ็กต์เก็บค่าตำแหน่งของเมาส์ในขณะที่เกิด event นั้น ซึ่งในออบเจ็กต์นั้นก็สามารถใช้เมธอด .x() และ .y() เพื่อได้ค่าตัวเลขซึ่งเป็นตำแหน่งของเมาส์ในแนวตั้งและแนวนอนได้
ตัวอย่างเช่น ลองให้พิมพ์ค่าตำแหน่งออกมาเมื่อถูกกดหรือปล่อยเมาส์หรือดับเบิลคลิก
import sys
from PyQt5.QtWidgets import QApplication,QWidget
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(250,200)
self.show()
def mousePressEvent(self,e):
print('เมาส์ถูกกดที่ตำแหน่ง (%d,%d)'%(e.pos().x(),e.pos().y()))
def mouseReleaseEvent(self,e):
print('เมาส์ถูกปล่อยที่ตำแหน่ง (%d,%d)'%(e.pos().x(),e.pos().y()))
def mouseDoubleClickEvent(self,e):
print('เมาส์ถูกดับเบิลคลิกที่ตำแหน่ง %s'%e.pos())
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ลองดับเบิลคลิกเมาส์ในหน้าต่างก็จะได้ข้อความในลักษณะประมาณนี้ขึ้นมา
เมาส์ถูกปล่อยที่ตำแหน่ง (113,57)
เมาส์ถูกดับเบิลคลิกที่ตำแหน่ง PyQt5.QtCore.QPoint(113, 57)
เมาส์ถูกปล่อยที่ตำแหน่ง (113,57)
ปุ่มของเมาส์ที่ถูกกด {.button}
event ของเมาส์นั้นนอกจากเก็บตำแหน่งที่กดแล้วยังเก็บข้อมูลที่บอกว่ากดปุ่มไหนของเมาส์ด้วย โดยจะได้ค่าปุ่มจากเมธอด .button
ตัวอย่างเช่น หากต้องการให้มีการทำอะไรต่างกันในกรณีที่คลิกซ้าย, คลิกขวา หรือคลิกกลาง ก็อาจเขียนได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget
from PyQt5.QtCore import Qt
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(100,80)
self.show()
def mousePressEvent(self,e):
if(e.button()==Qt.LeftButton):
print('คลิกซ้าย')
if(e.button()==Qt.RightButton):
print('คลิกขวา')
if(e.button()==Qt.MiddleButton):
print('คลิกกลาง')
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ลองคลิกโดยใช้ปุ่มต่างกันไป จะเห็นข้อความขึ้นมาตามปุ่มที่กดไปนั้น
ในที่นี้ใช้แฟล็กแทนปุ่มเมาส์ซ้าย ขวา กลาง เป็น Qt.LeftButton Qt.RightButton และ Qt.MiddleButton
การให้ทำคำสั่งเมื่อเมาส์เข้าหรือออกจากหน้าต่าง {enterEvent leaveEvent}
enterEvent จะเกิดขึ้นเมื่อเมาส์เข้ามาใน widget และ leaveEvent จะเกิดขึ้นเมื่อเมาส์ออกจาก widget
ตัวอย่างเช่น ลองสร้างหน้าต่างที่ถ้าเอาเมาส์เข้าไปวางแล้วพื้นกลายเป็นสีแดง
import sys
from PyQt5.QtWidgets import QApplication,QWidget
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(205,60)
self.show()
def enterEvent(self,e): # เมื่อเอาเมาส์วางจะเป็นสีแดง
self.setStyleSheet('background-color: red;')
def leaveEvent(self,e): # เมื่อเอาเมาส์ออกก็เปลี่ยนสีกลับ
self.setStyleSheet('')
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
หน้าต่างเวลาที่เมาส์ไม่อยู่
หน้าต่างเมื่อเอาเมาส์ไปวาง จะเปลี่ยนเป็นสีแดงแบบนี้
การให้ทำคำสั่งเมื่อเมาส์เข้าหรือออกจาก widget
ในตัวอย่างที่ผ่านมาเรามักเขียนคลาสใหม่ขึ้นเฉพาะตัว QWidget ที่ใช้เป็นหน้าต่าง แต่ว่า widget ตัวที่ใช้เป็นส่วนประกอบย่อยก็สามารถเขียนคลาสใหม่โดยเขียนทับเมธอดของ event ได้เช่นกัน
ตัวอย่างเช่น
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton
# สร้างคลาสของปุ่มที่ต้องการ
class Pumkot(QPushButton):
def enterEvent(self,e): # เมื่อเมาส์เข้าปุ่มให้ขยายฟอนต์
self.setStyleSheet('font-size: 50px;')
self.setEnabled(True)
def leaveEvent(self,e=None): # เมื่อเมาส์ออกจากปุ่ม
self.setStyleSheet('font-size: 20px;')
self.setEnabled(False)
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(190,90)
pumkot = Pumkot('กด',self) # สร้างออบเจ็กต์ของคลาสปุ่มที่เราสร้างขึ้นมาเอง
pumkot.setGeometry(10,10,170,70)
pumkot.leaveEvent() # ให้เริ่มต้นมาบังคับให้เกิด leaveEvent ไปเลยทันทีก็ได้
self.show()
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
หน้าต่างเวลาปกติ
หน้าต่างเมื่อเอาเมาส์ไปวาง
การให้ทำคำสั่งเมื่อหน้าต่างกำลังจะถูกปิด {closeEvent .accept .ignore}
event บางตัวนั้นอาจทำให้เกิดผลต่างกันออกไปเมื่อมีการเรียกเมธอด .accept() หรือ .ignore() เช่น closeEvent ซึ่งเป็น event ที่เกิดขึ้นเมื่อ widget กำลังจะถูกปิด แต่ยังไม่ได้ถูกปิดลง
เราสามารถทำให้เมื่อกดปุ่มปิดแล้วมีการสร้างกล่องเด้งให้แสดงคำถามยืนยันว่าจะปิดแน่หรือเปล่าได้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QMessageBox
from PyQt5.QtCore import Qt
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(125,65)
self.setStyleSheet('font-family: Tahoma; font-size: 17px;')
self.show()
def closeEvent(self,e):
lueak = QMessageBox.question(self,'','จะปิดแน่หรือ',QMessageBox.Yes|QMessageBox.No,QMessageBox.No)
if lueak == QMessageBox.Yes:
e.accept() # ถ้ากด yes ก็ accept คือให้ปิดไป
else:
e.ignore() # ถ้ากด no ก็ ignore คือไม่ต้องปิด
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ลองกดปิดหน้าต่างแล้วจะขึ้นกล่องเด้งขึ้นมาถามยืนยันแบบนี้ ต้องกด Yes ไปจึงจะปิดจริงๆ
การให้ทำคำสั่งเมื่อกดปุ่มในคีย์บอร์ด {keyPressEvent keyReleaseEvent}
keyPressEvent จะเกิดขึ้นเมื่อกดปุ่มใดๆในคีย์บอร์ด และ keyReleaseEvent จะเกิดขึ้นเมื่อปล่อยคีย์บอร์ด
ข้อมูลที่ว่าปุ่มไหนถูกกดไปนั้นจะถูกเก็บไว้ในออบเจ็กต์ของ event นี้ ซึ่งสามารถใช้เมธอด .key เพื่อดูค่าได้
ตัวอย่างเช่น ลองสร้างหน้าต่างที่มีปุ่มปุ่มหนึ่งวางอยู่ แล้วทำให้เวลากดปุ่ม a d w x แล้วปุ่มนั้นเคลื่อนไปยังซ้าย ขวา บน ล่าง อาจเขียนได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton
from PyQt5.QtCore import Qt
class Natang(QWidget):
def __init__(self):
super().__init__()
self.resize(180,130)
self.pum = QPushButton('⊕',self)
self.pum.setGeometry(40,30,60,50)
self.show()
def keyPressEvent(self,e): # เมื่อคีย์บอร์ดถูกกด
x = self.pum.x()
y = self.pum.y()
if(e.key()==Qt.Key_A): # เคลื่อนไปทางซ้าย
self.pum.move(x-10,y)
self.pum.setText('←')
elif(e.key()==Qt.Key_D): # เคลื่อนไปทางขวา
self.pum.move(x+10,y)
self.pum.setText('→')
elif(e.key()==Qt.Key_W): # เคลื่อนไปด้านบน
self.pum.move(x,y-10)
self.pum.setText('↑')
elif(e.key()==Qt.Key_X): # เคลื่อนไปด้านล่าง
self.pum.move(x,y+10)
self.pum.setText('↓')
elif(e.key() in [Qt.Key_Escape,Qt.Key_Q]):
self.close()
if(x!=self.pum.x() or y!=self.pum.y()):
print('จาก (%d,%d) ย้ายไป (%d,%d)'%(x,y,self.pum.x(),self.pum.y()))
def keyReleaseEvent(self,e):
self.pum.setText('⊕')
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
จะได้หน้าต่างแบบนี้ออกมา ลองกด a d w x แล้วดูความเปลี่ยนแปลงของปุ่มได้
เช่นเมื่อกด d
ค่าของปุ่มนั้นจะได้มาเป็นตัวเลขซึ่งแสดงถึงปุ่มนั้นๆ โดยอาจแทนด้วยแฟล็กต่างๆ เช่นปุ่ม a ใช้เป็น Qt.Key_A เป็นต้น ลองดูแฟล็กของปุ่มต่างๆเพิ่มเติมได้ใน
https://doc.qt.io/qt-5/qt.html#Key-enum
อ่านบทถัดไป >>
บทที่ ๒๔