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



pyqt เบื้องต้น บทที่ ๔: การทำให้คำสั่งทำงานเมื่อกดปุ่ม
เขียนเมื่อ 2021/08/06 11:06
แก้ไขล่าสุด 2021/09/28 16:42

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

ในบทที่ บทที่ ๒ ได้พูดถึงการสร้างปุ่มไปแล้ว แต่ว่าปุ่มที่สร้างขึ้นมานั้นเป็นแค่ปุ่มเปล่าๆที่กดไปก็เท่านั้น

ดังนั้นในบทนี้จะพูดถึงการทำให้ปุ่ม QPushButton ที่สร้างขึ้นนั้นเกิดการทำตามคำสั่งที่ต้องการขึ้นมาเมื่อกดปุ่ม




การตั้งฟังก์ชันที่ให้ทำงานเมื่อกดปุ่ม {.clicked, .connect}

เวลาที่กดปุ่มนั้นจะเป็นการทำให้เกิดสัญญาณบอกว่าเราได้ทำการคลิกแล้ว การตั้งคำสั่งให้ทำงานเมื่อมีสัญญาณบางอย่างเข้ามานั้นทำได้โดย
ตัว_widget.ชื่อสัญญาณ.connect(ฟังก์ชัน)

ในกรณีของสัญญาณการกดปุ่มนั้นก็คือคำว่า clicked สัญญาณนี้จะเกิดขึ้นเมื่อกดปุ่มแล้วก็ปล่อยปุ่ม

ตัวอย่างเช่น ถ้าต้องการให้มีข้อความ print ขึ้นมาเมื่อกดปุ่ม
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

def kotlaeo():
    print('กดหาพระแสงอะไร')

qAp = QApplication(sys.argv)
natang = QWidget()
pumkot = QPushButton('กดเลย',natang)
pumkot.resize(200,100)
pumkot.clicked.connect(kotlaeo)
natang.show()
qAp.exec_()

จะได้หน้าต่างที่มีปุ่มแบบนี้ ซึ่งพอกดแล้วก็จะมีข้อความขึ้นมา



กรณีที่ทำเป็นคลาสอาจเขียนได้แบบนี้ โดยตัวฟังก์ชันนั้นให้ใส่ไว้เป็นเมธอดภายในคลาสได้เลย
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

class Natang(QWidget):
    def __init__(self):
        super().__init__()
        pumkot = QPushButton('กดเลย',self)
        pumkot.resize(200,100)
        pumkot.clicked.connect(self.kotlaeo)
        self.show()
    
    def kotlaeo(self):
        print('กดหาพระแสงอะไร')

qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()




การตั้งฟังก์ชันที่ให้ทำงานเมื่อกดค้างและปล่อย {.pressed, .released}

.clicked นั้นจะทำงานเมื่อทำการคลิก นั่นหมายถึงว่ากดเมาส์เสร็จแล้วก็ปล่อยมือออก แต่ถ้าต้องการให้แค่ปุ่มเริ่มถูกกดค้างไว้ก็ทำงานแล้วก็อาจใช้ .pressed และถ้าต้องการให้ทำงานเมื่อปุ่มถูกเลิกกด .released

ตัวอย่างเช่น
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

# ฟังก์ชันที่ให้ print ข้อความเมื่อเริ่มกดปุ่ม
def kotlaeo():
    print('กดแล้ว')

# ฟังก์ชันที่ให้ print ข้อความเมื่อปล่อยปุ่ม 
def ploilaeo():
    print('ปล่อยแล้ว')
    
qAp = QApplication(sys.argv)
natang = QWidget()
pumkot = QPushButton('เอาเลย กดลงมาเลย',natang)
pumkot.resize(220,90)
pumkot.pressed.connect(kotlaeo) # ตั้งฟังก์ชันที่ให้ทำงานเมื่อเริ่มกดปุ่ม
pumkot.released.connect(ploilaeo) # ตั้งฟังก์ชันที่ให้ทำงานเมื่อปล่อยปุ่ม
natang.show()
qAp.exec_()

แล้วก็จะได้ปุ่มที่พอกดแล้วก็มีข้อความขึ้นมาว่า "กดแล้ว" แล้วพอปล่อยก็ขึ้นมาว่า "ปล่อยแล้ว" แบบนี้



ลองดูอีกตัวอย่างที่อาจใช้งานได้ นั่นคือเช่นอาจใช้วิธีนี้เพื่อสร้างปุ่มกดให้จับเวลาที่กดลงไป
import sys,time
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

# ให้เริ่มจับเวลาเมื่อกดปุ่ม
def roemton():
    global wela_roem # จำเป็นต้องตั้งให้ค่าเวลาเป็นตัวแปรสากล
    wela_roem = time.time()

# ให้จับเวลาตอนปล่อยปุ่มแล้วเอาไปลบกับเวลาที่บันทึกเมื่อตอนกดปุ่ม
def sinsut():
    chai_wela = time.time()-wela_roem
    print('กดไปนาน', chai_wela, 'วินาที')
    
qAp = QApplication(sys.argv)
natang = QWidget()
pumkot = QPushButton('กดเวลา',natang)
pumkot.resize(120,70)
pumkot.pressed.connect(roemton)
pumkot.released.connect(sinsut)
natang.show()
qAp.exec_()

ก็จะได้ปุ่มที่กดแล้วบอกเวลาเป็นวินาทีตามที่กดค้างไว้แบบนี้ออกมา






ในกรณีที่ฟังก์ชันต้องรับค่าพารามิเตอร์

ในตัวอย่างที่กล่าวมาข้างต้นนั้นจะเห็นว่าใช้กับฟังก์ชันที่ไม่ต้องรับพารามิเตอร์ทั้งนั้นเลย แต่ในบางครั้งเราอาจต้องการใช้บางฟังก์ชันที่ต้องใส่ค่าพารามิเตอร์

ปัญหาก็คือสิ่งที่ถูกใส่ในวงเล็บหลัง .connect นั้นคือตัวฟังก์ชัน และเราไม่สามารถใส่ค่าอะไรลงไปในฟังก์ชันนั้นได้

แต่ก็มีวิธีการที่ทำได้อยู่ นั่นคือใช้ lambda ช่วย แบบนี้
.connect(lambda: ฟังก์ชัน(ค่าป้อนเข้า, ..., ...))

เกี่ยวกับรายละเอียดเรื่องการใช้ lambda อ่านได้ใน https://phyblas.hinaboshi.com/tsuchinoko21

ตัวอย่าง ลองสร้าง ๒ ปุ่มที่ใช้ฟังก์ชันเดียวกัน แต่เรียกใช้ด้วยค่าต่างกัน
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

def kotlana(khokhwam):
    print('~~'+khokhwam+'~~')

qAp = QApplication(sys.argv)
natang = QWidget()

pumkot1 = QPushButton('กด 1',natang)
pumkot1.setGeometry(0,0,90,90)
pumkot1.clicked.connect(lambda: kotlana('1'))

pumkot2 = QPushButton('กด 2',natang)
pumkot2.setGeometry(90,0,90,90)
pumkot2.clicked.connect(lambda: kotlana('2'))

natang.show()
qAp.exec_()

เท่านี้ถ้ากดปุ่ม 1 ก็จะได้ข้อความว่า ~~1~~ ขึ้นมา ถ้ากด 2 ก็จะได้ ~~2~~



กรณีที่ทำเป็นคลาสก็อาจเขียนได้แบบนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

class Natang(QWidget):
    def __init__(self):
        super().__init__()
        
        pumkot1 = QPushButton('กด 1',self)
        pumkot1.setGeometry(0,0,90,90)
        pumkot1.clicked.connect(lambda: self.kotlana('1'))
        
        pumkot2 = QPushButton('กด 2',self)
        pumkot2.setGeometry(90,0,90,90)
        pumkot2.clicked.connect(lambda: self.kotlana('2'))
        
        self.show()
    
    def kotlana(self,khokhwam):
        print('~~'+khokhwam+'~~')

qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()

เพียงแต่ว่าวิธีการนี้หากใช้โดยที่ไม่เข้าใจดีพอก็อาจทำให้เกิดผลลัพธ์ที่ผิดไปจากที่คาดคิดได้ เช่นลองดูตัวอย่างนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

def kot(s):
    print('#'+str(s)+'#')

qAp = QApplication(sys.argv)
natang = QWidget()

for i in range(1,5):
    pum = QPushButton('กด\n%d'%i,natang)
    pum.setGeometry((i-1)*40+10,10,40,70)
    pum.clicked.connect(lambda: kot(i))

natang.show()
qAp.exec_()

ก็จะได้ปุ่ม ๔ ปุ่ม แบบนี้



ซึ่งเราคงคาดหวังว่ากดปุ่มไหนก็จะออกมาเป็นเลขนั้น แต่ในความเป็นจริงแล้วผลที่ได้ออกมาก็คือ ไม่ว่าจะกดปุ่มไหนก็ได้ 4 หมด

สาเหตุที่เป็นแบบนั้นก็เพราะเมื่อใช้ lambda ทำให้มองยากและสับสนได้ง่าย ความจริงแล้วการเขียนด้วย lambda แบบนี้ให้ผลเทียบเท่ากับการเขียนแบบนี้
    def f():
        kot(i)
    pum.clicked.connect(f)

ซึ่งจะมีความหมายว่าให้ทำฟังก์ชัน kot กับตัวแปร i ซึ่งตัวแปร i ในที่นี้ก็ไม่ใช่ค่า i ในขณะที่เราใช้ pum.clicked.connect แต่เป็นค่า i ตอนสุดท้าย นั่นคือตอนที่มันวนใน for จนครบแล้ว กลายเป็นค่าสุดท้ายคือ 4

ด้วยเหตุนี้จึงทำให้ได้ผลออกมาเหมือนกันทั้งหมด ทั้งๆที่เราคงจะต้องการให้ต่างกันไป

สำหรับวิธีการแก้นั้นให้เปลี่ยนมาเขียนแบบนี้แทน
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

def kot(s):
    def f():
        print('#'+str(s)+'#')
    return f

qAp = QApplication(sys.argv)
natang = QWidget()

for i in range(1,5):
    pum = QPushButton('กด\n%d'%i,natang)
    pum.setGeometry((i-1)*40+10,10,40,70)
    pum.clicked.connect(kot(i))

natang.show()
qAp.exec_()

ผลที่ได้ก็จะได้ปุ่มในลักษณะเหมือนเดิม แต่คราวนี้ลองกดดูจะขึ้นเลขตามที่ต้องการแล้ว

รูปแบบการเขียนอาจดูซับซ้อนเข้าใจยากขึ้นมา ในที่นี้หากอธิบายก็จะยืดยาว เพราะเป็นเรื่องของฟังก์ชันที่ซ้อนอยู่ในฟังก์ชัน มีรายละเอียดน่าสนใจมากมาย ขอแนะนำให้อ่านใน ไพธอนเบื้องต้นบทที่ ๒๙




การทำให้กดปุ่มแล้วหน้าต่างถูกปิดลง {.close}

ด้วยวิธีการตั้งคำสั่งให้ปุ่มดังที่อธิบายไปข้างต้นนี้ เราอาจนำมาใช้เพื่อทำปุ่มสำหรับปิดหน้าต่างได้

การปิดหน้าต่างอาจทำได้โดยเมธอด .close ซึ่งเราสามารถสั่งให้มันถูกเรียกใช้เมื่อกดปุ่มได้ ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

qAp = QApplication(sys.argv)
natang = QWidget()
pumkot = QPushButton('จบเลย',natang)
pumkot.resize(140,70)
pumkot.clicked.connect(natang.close)
natang.show()
qAp.exec_()






การทำให้ widget ไม่ทำงาน {.setEnabled}

เมธอด .setEnabled ใช้กำหนดสถานะว่าจะให้ widget ตัวนั้นทำงานได้หรือไม่ ซึ่งถ้าใส่เป็น True ก็จะทำงาน ถ้าเป็น False ก็จะไม่ทำงาน

widget ปุ่ม QPushButton นั้นโดยปกติถ้าสร้างขึ้นมาไม่ได้ตั้งก็จะกดได้อยู่แล้ว แต่ถ้าตั้ง .setEnabled(False) ก็จะทำให้ปุ่มนั้นกดไม่ได้

เช่น
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QPushButton

qAp = QApplication(sys.argv)
natang = QWidget()
pumkot = QPushButton('หมดแรงกด',natang)
pumkot.resize(140,70)
pumkot.setEnabled(False)
natang.show()
qAp.exec_()

จะได้ปุ่มที่กดไม่ลง อย่างที่เห็น



เมธอด .setEnabled นั้นนอกจากปุ่มกดแล้ว ก็ยังเอาไปใช้กับ widget ชนิดอื่นๆได้เช่นกัน เพื่อทำให้ widget นั้นไม่ทำงาน




สรุปท้ายบท

ในบทนี้ได้อธิบายถึงการตั้งให้ฟังก์ชันทำงานเมื่อกดปุ่มได้แล้ว แต่แน่นอนว่า widget ที่ใช้ใน GUI ไม่ได้มีแค่ปุ่มกด หลักการเดียวกันนี้ยังเอาไปใช้กับ widget ชนิดอื่นๆได้อีก ซึ่งจะกล่าวถึงในบทถัดๆไป

ใน widget แต่ละชนิดจะมีสัญญาณที่รับได้ต่างกันออกไป สำหรับกรณี QPushButton นั้นจะมี .clicked .pressed .released ส่วนเมื่อใช้ตัวอื่นก็ต้องดูว่าตัวนั้นมีสัญญาณอะไรบ้าง และตั้งให้ตรงกับความต้องการในการใช้งาน



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





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

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

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

หมวดหมู่

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

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

สารบัญ

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

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文