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



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

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

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




การลากข้อความเข้าหน้าต่าง {.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
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
maya
javascript
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
เรียนภาษาจีน
qiita
บทความอื่นๆ

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

ไทย

日本語

中文