φυβλαςのβλογ
บล็อกของ phyblas



pyqt เบื้องต้น บทที่ ๒๓: การเขียนทับเมธอด event ต่างๆเพื่อให้มีการทำงานตามที่ต้องการเมื่อมีเหตุการณ์ต่างๆเกิดขึ้น
เขียนเมื่อ 2021/08/25 07:24
แก้ไขล่าสุด 2021/09/28 16:42

ต่อจาก บทที่ ๒๒

สำหรับในบทนี้จะเป็นเรื่องของการเขียนทับเมธอดของ 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



อ่านบทถัดไป >> บทที่ ๒๔





-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python >> pyqt

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文