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



pyqt เบื้องต้น บทที่ ๒๕: การกดลากข้อมูลเข้ามาวางในหน้าต่าง
เขียนเมื่อ 2021/08/27 11:49
แก้ไขล่าสุด 2022/03/22 19:14

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

ในบทนี้จะเขียนถึงเรื่องของการทำให้สามารถกดลากข้อมูลอะไรต่างๆเข้ามาภายในหน้าต่างได้




การลากข้อความเข้าหน้าต่าง {.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 สำหรับลากขึ้นมาได้ ซึ่งเนื้อหาในส่วนนี้จะยกไปเขียนถึงในบทต่อไป



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





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

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

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

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> 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月

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

ไทย

日本語

中文