ต่อจาก
บทที่ ๒๔
ในบทนี้จะเขียนถึงเรื่องของการทำให้สามารถกดลากข้อมูลอะไรต่างๆเข้ามาภายในหน้าต่างได้
การลากข้อความเข้าหน้าต่าง {.setAcceptDrops dragEnterEvent .mimeData}
ปกติเวลาเข้าเว็บต่างๆ จะเห็นว่าเราสามารถกดคลุมลากข้อความ หรือกดลากรูปไปวางตามที่ต่างๆได้อย่างอิสระ หรือเมื่อเปิดหน้าต่างค้นไฟล์ (file explorer ใน windows หรือ inder ใน mac) เราสามารถลากไฟล์ไปวางยังที่ต่างๆได้
ข้อมูลที่ถูกลากมาได้เหล่านั้นสามารถเอาเข้ามาใส่ในหน้าต่าง GUI หรือ widget ภายใน GUI ที่เราสร้างขึ้นมาด้วย pyqt ได้เช่นกัน เพียงแต่เราจำเป็นต้องตั้งให้ widget นั้นสามารถรับข้อมูลที่ลากเข้ามาได้ ซึ่งทำได้โดยใช้เมธอด .setAcceptDrops ที่ตัว widget นั้น ตั้งค่าให้เป็น True เท่านี้ก็จะสามารถลากอะไรๆเข้าไปใน widget นั้นได้
เมื่อมีข้อมูลลากเข้ามาแล้วจะทำยังไง จัดการกับมันยังไง ให้กำหนดได้โดยการเขียนทับเมธอด dragEnterEvent ซึ่งเป็น event ที่จะเกิดขึ้นเมื่อมีการลากอะไรอยู่แล้วเมาส์เข้ามาในพื้นที่ของ widgetนั้น
ข้อมูลที่ลากมานั้นเรียกว่า mime data ซึ่งจะได้จากการใช้เมธอด .mimeData ที่ตัวออบเจ็กต์ของ event นั้น
ข้อมูลนั้นถ้าเป็นตัวหนังสือ สามารถเอาข้อมูลตัวหนังสือที่ถูกลากเข้ามาได้โดยใช้เมธอด .text ที่ตัว mime data นั้น
ตัวอย่างเช่น ลองสร้าง GUI ให้พอลากอะไรเข้ามาแล้วพิมพ์ข้อความนั้นออกมา
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True) # ตั้งให้สามารถลากอะไรเข้ามาได้
self.khokhwam = QLabel('',self) # QLabel ที่จะใช้แสดงข้อความที่ถูกลากเข้ามา
self.khokhwam.setGeometry(10,10,200,150)
self.khokhwam.setStyleSheet('font-family: Courier New; font-size: 18px;')
self.show()
def dragEnterEvent(self,e):
text = e.mimeData().text() # เอาข้อความจาก mimedata ที่ถูกลากเข้ามา
self.khokhwam.setText(text) # นำข้อความนั้นมาใส่ให้ QLabel ภายในหน้าต่าง
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ก็จะได้หน้าต่างขึ้นมา ลองเปิดเว็บแล้วลากข้อความอะไรเข้าไปดู
พอเมาส์เริ่มเข้าสู่หน้าต่าง ข้อความนั้นก็จะมาปรากฏในหน้าต่างแบบนี้
การให้ทำคำสั่งเมื่อปล่อยวางเมาส์ {dropEvent}
ในตัวอย่างที่แล้วนั้นเราใช้แค่ dragEnterEvent นั่นคือเมื่อมีอะไรบางถูกลากอย่างเข้ามาในหน้าต่างก็จะทำงานทันที ต่อให้ยังไม่ปล่อยเมาส์ก็ตาม
แต่ที่จริงโดยทั่วไปแล้วเรามักจะต้องการให้ของที่ลากนั้นเข้าไปได้ก็ต่อเมื่อมีการปล่อยเมาส์ แบบนี้ถึงจะเป็นการ "ลากและวาง" (drag & drop)
การลากและวางนั้นทำได้โดยเขียนทับเมธอดทั้ง dragEnterEvent และ dropEvent โดยให้ภายใน dragEnterEvent มีการเรียกเมธอด .accept จากออบเจ็กต์ตัว event นั้นขึ้นมา จากนั้นจึงกำหนดสิ่งที่ต้องการให้ทำใน dropEvent อีกที
ตัวอย่างเช่น
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel,QVBoxLayout
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('font-family: Courier New; font-size: 18px;')
self.setAcceptDrops(True) # ตั้งให้ลากอะไรเข้ามาได้
self.vbl = QVBoxLayout() # โครงสำหรับวางข้อความที่ใส่เข้ามา
self.setLayout(self.vbl)
self.show()
def dragEnterEvent(self,e):
e.accept() # เมื่อลากอะไรเข้ามาก็ให้รับไว้
def dropEvent(self,e): # เมื่อปล่อยเมาส์หลังจากลากเข้ามา
label = QLabel(e.mimeData().text()) # สร้าง QLabel ที่ใส่ข้อความที่ลากเข้ามา
self.vbl.addWidget(label) # ใส่ลงในโครง
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
จะได้หน้าต่างเปล่ามา ลองเปิดเว็บแล้วลากข้อความเข้ามา แล้วก็ปล่อยเมาส์
ข้อความนั้นก็จะถูกปล่อยลงในหน้าต่าง
การกำหนดตำแหน่งที่วาง {.pos}
ออบเจ็กต์ตัว event ที่ได้มาเป็นพารามิเตอร์ของ dropEvent นั้นจะมีข้อมูลตำแหน่งของจุดที่ถูกปล่อยด้วย ซึ่งสามารถใช้เมธอด .pos เพื่อเอาค่าตำแหน่งนั้นได้
เช่นคราวนี้ถ้าอยากให้ข้อความที่ลากเข้ามาถูกวางลงในตำแหน่งที่เราวางเมาส์ ก็สามารถทำได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel
from PyQt5.QtCore import Qt
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('font-family: Tahoma; color: #649; font-size: 17px;')
self.setAcceptDrops(True)
self.resize(400,300)
self.show()
def dragEnterEvent(self,e):
e.accept()
def dropEvent(self,e):
self.label = QLabel(e.mimeData().text(),self) # สร้าง QLabel ใหม่ขึ้นมาสำหรับใส่ข้อความที่ลากเข้ามา
self.label.resize(800,600)
self.label.setAlignment(Qt.AlignCenter) # ตั้งให้ข้อความจัดอยู่ตรงกลาง
pos = e.pos()
self.label.move(pos.x()-400,pos.y()-300) # เลื่อนข้อความไปวางในตำแหน่งที่ปล่อยเมาส์
self.label.show()
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ได้หน้าต่างแบบนี้ออกมา ลองลากข้อความเข้าไป
ข้อความนั้นก็จะถูกวางในตำแหน่งที่ปล่อยเมาส์
การลากไฟล์เข้าหน้าต่าง {.hasUrls .urls}
นอกจากลากข้อมูลต่างๆจากหน้าเว็บได้แล้ว เรายังสามารถลากไฟล์เข้ามาในหน้าต่างของเราได้ด้วย โดยลองเปิดหน้าต่างค้นไฟล์แล้วลากเข้ามาดู
เมื่อลากไฟล์เข้ามา ข้อมูลชื่อและพาธของไฟล์นั้นจะถูกเก็บอยู่ใน mimedata ถ้าใช้เมธอด .urls ก็จะได้ลิสต์ของออบเจ็กต์ที่เก็บชื่อและพาธของทุกไฟล์ที่ลากเข้ามา
หากใช้เมธอด .toLocalFile() ในนั้นจะได้ชื่อและพาธไฟล์ออกมา
ตัวอย่าง ลองสร้าง GUI ให้พอลากไฟล์เข้ามาในหน้าต่างแล้วมีการเปิดไฟล์ทั้งหมดที่ลากเข้ามา แล้วนับจำนวนอักษรและบรรทัดในไฟล์นั้น
import sys,os
from PyQt5.QtWidgets import QApplication,QWidget,QLabel,QGridLayout
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setStyleSheet('QLabel{background-color: #dfd; font-family: Tahoma; font-size: 19px; border: 1px solid #411;}')
self.setAcceptDrops(True)
self.gbl = QGridLayout() # ใช้โครงแบบตาราง
self.setLayout(self.gbl)
self.gbl.addWidget(QLabel('ชื่อไฟล์'),0,0)
self.gbl.addWidget(QLabel('จำนวนอักษร'),0,1)
self.gbl.addWidget(QLabel('จำนวนบรรทัด'),0,2)
self.n_file = 0 # จำนวนไฟล์ที่มีอยู่
self.show()
def dragEnterEvent(self,e):
mida = e.mimeData()
if(mida.hasUrls()): # ให้รับเฉพาะกรณีที่มีข้อมูล url เท่านั้น
e.accept()
def dropEvent(self,e):
mida = e.mimeData()
for url in mida.urls():
self.n_file += 1
url = url.toLocalFile() # เอาสายอักขระชื่อและพาธไฟล์
print(url)
chuefile = os.path.basename(url)
label = QLabel(chuefile)
self.gbl.addWidget(label,self.n_file,0) # ชื่อไฟล์
with open(url,encoding='utf-8') as f: # เปิดไฟล์ขึ้นมา
nueaha = f.read() # อ่านข้อความในไฟล์
self.gbl.addWidget(QLabel('%d'%(len(nueaha))),self.n_file,1) # จำนวนอักษร
self.gbl.addWidget(QLabel('%d'%(len(nueaha.split('\n')))),self.n_file,2) # จำนวนบรรทัด
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ก็จะได้หน้าต่างแบบนี้ ลองเปิดไฟล์แล้วลากอะไรเข้าไปดู
ชื่อไฟล์เหล่านั้นพร้อมกับจำนวนอักษรกับจำนวนบรรทัดก็จะมาปรากฏในหน้าต่างแบบนี้
เมธอด .hasUrls() ที่ใช้กับออบเจ็กต์ mime data ในที่นี้มีไว้ตรวจดูว่าข้อมูลที่ลากเข้ามานั้นมี url หรือไม่ ถ้าหากแค่ลากตัวอักษรจากเว็บเข้ามาก็จะไม่มี url จึงจะได้ False แต่ถ้าลากไฟล์เข้ามา หรือลากลิงก์จากเว็บก็จะได้ True
ในที่นี้เราจะให้รับข้อมูลที่ลากเข้ามาในกรณ๊ที่มี url เท่านั้น จึงจะนำมาเปิดไฟล์อ่านได้
การลากภาพเข้าหน้าต่าง {.imageData .hasImage}
ข้อมูลรูปภาพก็สามารถลากเข้ามาในหน้าต่างได้เช่นกัน โดยสามารถเอาข้อมูลรูปภาพได้จากโดยเมธอด .imageData ที่ตัวออบเจ็กต์ meta data
ตัวอย่างเช่น ลองสร้างหน้าต่างที่ถ้าลากภาพเข้ามาแล้วภาพจะถูกนำมาวางในหน้าต่าง
import sys
from PyQt5.QtWidgets import QApplication,QWidget,QLabel,QVBoxLayout
from PyQt5.QtGui import QPixmap
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.vbl = QVBoxLayout()
self.setLayout(self.vbl)
self.show()
def dragEnterEvent(self,e):
mida = e.mimeData()
if(mida.hasImage()): # ตรวจสอบว่ามีข้อมูลภาพ จึงจะรับ
e.accept()
def dropEvent(self,e):
mida = e.mimeData()
label = QLabel() # สร้าง QLabel ที่จะใช้ใส่วางรูปภาพ
self.vbl.addWidget(label)
pix = QPixmap(mida.imageData()) # นำข้อมูลรูปภาพที่ลากเข้ามาสร้างเป็น QPixmap
label.setPixmap(pix) # ใส่รูปภาพลงใน QLabel ที่เตรียมไว้
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
ก็จะได้หน้าต่างแบบนี้ ลองเปิดเว็บแล้วลากภาพเข้าไป
ภาพก็จะถูกใส่ลงไปในหน้าต่างแบบนี้ แล้วถ้าลองลากลงไปอีก
ภาพก็จะถูกใส่เพิ่มลงไปอีก
ในที่นี้ .hasImage() เอาไว้ตรวจสอบว่าข้อมูลที่ลากเข้ามานั้นมีข้อมูลรูปภาพหรือไม่ ถ้ามีจึงจะ .accept() ให้สามารถวางลงในหน้าต่างได้
สรุปเรื่องชนิดข้อมูลที่ลากมาได้
สรุปโดยรวมแล้ว ข้อมูลชนิดต่างๆที่สามารถลากเข้ามาได้มีอยู่ ๕ ชนิด ซึ่งในการลากครั้งหนึ่งอาจมีข้อมูลหลายชนิดอยู่พร้อมกัน
ข้อมูลแต่ละชนิดจะมีเมธอดที่เอาไว้ในดูค่า หรือตรวจดูว่ามีหรือไม่ หรือรวมไปถึงตั้งค่าใหม่ใส่เข้าไปได้ด้วย
ข้อมูลชนิดต่างๆและเมธอดที่ใช้นั้นอาจสรุปเป็นตารางได้ดังนี้
ความหมาย | ดูค่า | ตรวจดูว่ามีหรือไม่ | ตั้งค่า |
ข้อความทั่วไป | .text() | .hasText() | .setText() |
ข้อความ html | .html() | .hasHtml() | .setHtml() |
ข้อความพวก url หรือที่อยู่ไฟล์ | .urls() | .hasUrls() | .setUrls() |
รูปภาพ | .imageData() | .hasImage() | .setImageData() |
สี | .colorData() | .hasColor() | .setColorData() |
ลองสร้างหน้าต่างสำหรับทดสอบชนิดข้อมูลที่ลากเข้ามาดูได้ดังนี้
import sys
from PyQt5.QtWidgets import QApplication,QWidget
class Natang(QWidget):
def __init__(self):
super().__init__()
self.setAcceptDrops(True)
self.show()
def dragEnterEvent(self,e):
mida = e.mimeData()
if(mida.hasText):
print('มีข้อความ')
if(mida.hasHtml):
print('มี html')
if(mida.hasUrl):
print('มี url')
if(mida.hasImage):
print('มีรูปภาพ')
if(mida.hasColor):
print('มีสี')
qAp = QApplication(sys.argv)
natang = Natang()
qAp.exec_()
จะได้หน้าต่างเปล่าๆออกมองลากอะไรต่างๆที่ลากได้เข้าไปแล้วดูผลที่ได้
สรุปท้ายบท
ในบทนี้ได้แนะนำถึงการนำข้อมูลต่างๆมาลากเข้าไปในหน้าต่างหรือ widget ที่เราสร้างขึ้นแล้ว รวมถึงอธิบายถึงข้อมูลชนิดต่างๆไป
แต่นอกจากนี้แล้วเราสามารถตั้งข้อมูลใน mime data สำหรับลากขึ้นมาได้ ซึ่งเนื้อหาในส่วนนี้จะยกไปเขียนถึงในบทต่อไป
อ่านบทถัดไป >>
บทที่ ๒๖