ต่อจาก
บทที่ ๓
ในบทที่
บทที่ ๒ ได้พูดถึงการสร้างปุ่มไปแล้ว
แต่ว่าปุ่มที่สร้างขึ้นมานั้นเป็นแค่ปุ่มเปล่าๆที่กดไปก็เท่านั้น
ดังนั้นในบทนี้จะพูดถึงการทำให้ปุ่ม 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
ส่วนเมื่อใช้ตัวอื่นก็ต้องดูว่าตัวนั้นมีสัญญาณอะไรบ้าง และตั้งให้ตรงกับความต้องการในการใช้งาน
อ่านบทถัดไป >>
บทที่ ๕