φυβλαςのβλογ
บล็อกของ 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)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

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

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月

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

ไทย

日本語

中文