Qt實現拖拽功能圖文教程(支持拖放文件、拖放操作)
拖放
拖放是在一個應用程序內或者多個應用程序之間傳遞信息的一種直觀的現代操作方式。除了為剪貼板提供支持外,通常它還提供數據移動和復制的功能。
拖放操作包括兩個截然不同的動作:拖動和放下。Qt窗口部件可以作為拖動點(darg site)、放下點(drop site)或者同時作為拖動點和放下點。
Qt程序接受其他程序的拖拽
我們經常將文本文件推拽到notepate++等類型文本編輯器軟件中。那么如何讓Qt程序也能夠支持這種操作呢?
我們需要在主窗口重新實現了來自父類的dragEnterEvent()
和 dropEvent()
函數。
protected: virtual void dragEnterEvent(QDragEnterEvent* event) override; virtual void dropEvent(QDropEvent* event) override;
在構造函數中,創(chuàng)建了一個QTextEdit
并且把它設置為中央窗口部件。默認情況下,QTextEdit
可以接受來自其他應用程序文本的拖動,并且如果用戶在它上面放下一個文件,它將會把這個文件的內容填充到QTextEdit部件中。
由于拖放事件是從子窗口部件傳遞給父窗口部件的,所以通過禁用QTextEdit上的放下操作以及啟用主窗口上的放下操作,就可以在整個MainWindow窗口中獲得放下事件。
#include <QMimeData> #include <QTextStream> MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui->setupUi(this); textEdit = new QTextEdit(this); setCentralWidget(textEdit); textEdit->setAcceptDrops(false); setAcceptDrops(true); setWindowTitle("Text Editor"); }
當用戶把一個對象拖動到這個窗口部件上時,就會調用dragEnterEvent()
。如果對這個事件調用acceptProposedAction()
,就表明用戶可以在這個窗口部件上拖放對象。默認情況下,窗口部件是不接受拖動的。Qt會自動改變光標來向用戶說明這個窗口部件是不是有效的放下點。
這里,我們希望用戶拖動的只能是文件,而非其他類型的東西。為了實現這一點,我們可以檢查拖動的MIME
類型。
MIME
類型中的text/uri-list
用于存儲一系列的統(tǒng)一資源標識符(Universal Re-source Identifier ,URI),它們可以是文件名、統(tǒng)―資源定位器(Uniform Resource Locator , URL,如 HTTP或者FTP路徑),或者其他全局資源標識符。標準的MIME類型是由國際因特網地址分配委員會(Internet Assigned Numbers Authority , IANA)定義的,它們由類型、子類型信息以及分隔兩者的斜線組·成。MME類通常由剪貼板和拖放系統(tǒng)使用,以識別不同類型的數據??梢詮南旅娴木W站得到正式的 MIME類型列表: http://www.iana.org/assignments/media-types/
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { if(event->mimeData()->hasFormat("text/uri-list")) { event->acceptProposedAction(); } }
當用戶在窗口部件上放下一個對象時,就會調用dropEvent()
。.我們調用函數QMimeData::urls()
來獲得QUrl
列表。通常,用戶一次只拖動一個文件,但是通過拖動一個選擇區(qū)域來同時拖動多個文件也是可能的。如果要拖放的URL不止一個,或者要拖放的URL,不是一個本地文件名,則會立即返回到原調用處。
QWidget也提供 dragMoveEvent()和 dragLeaveEvent()函數,但是在絕大多數應用程序中并不需要重新實現它們。
void MainWindow::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); if(urls.empty()) return; QString fileName = urls.first().toLocalFile(); if(fileName.isEmpty()) return; if(ReadFile(fileName)) { setWindowTitle(QString("%1-%2").arg(fileName).arg("Drag File")); } } bool MainWindow::ReadFile(const QString &filename) { QFile file(filename); file.open(QIODevice::ReadOnly | QIODevice::Text | QIODevice::Truncate); if(false == file.isOpen()) { return false; } QTextStream stream(&file); textEdit->insertPlainText(stream.readAll()); file.flush(); file.close(); return true; }
看看效果吧
部件/控件之間相互拖放
我們將實現類似于一個下圖的效果,但不需要向左向右的按鍵,通過拖拽目標實現移動。
思路:創(chuàng)建一個支持拖拽的QListWidget
子類ProjectListWidget
,并且作為該界面的一個部件。
在ProjectListWidget類中需要重寫父類的五個事件,和一個私有方法以及一個坐標記錄
protected: virtual void mousePressEvent(QMouseEvent* event) override; virtual void mouseMoveEvent(QMouseEvent* event) override; virtual void dragEnterEvent(QDragEnterEvent* event) override; virtual void dragMoveEvent(QDragMoveEvent* event) override; virtual void dropEvent(QDropEvent* event) override; private: void performDrag(); private: QPoint startPos;
在構造函數中,我們使列表框上的放下生效。
#include <QApplication> #include <QDrag> #include <QMimeData> ProjectListWidget::ProjectListWidget(QWidget* parent) :QListWidget{parent} { setAcceptDrops(true); }
當用戶按下鼠標左鍵,就把鼠標位置保存到statPos
私有變量中。然后我們正常調用QListWidget 中mousePressEvent()
。
void ProjectListWidget::mousePressEvent(QMouseEvent *event) { if(event->button() == Qt::LeftButton) { startPos = event->pos(); } QListWidget::mousePressEvent(event); }
當用戶按住鼠標左鍵并移動鼠標光標時,就認為這是一個拖動的開始。我們計算當前鼠標位置和原來鼠標左鍵按下的點之間的距離-—這個“曼哈頓長度”(Manhattan Length)其實是從坐標原點到該矢量長度快速計算的近似值。如果這個距離大于或等于QApplication推薦的拖動起始距離值(通常是4個像素),那么就調用私有函數performDrag()
以啟動拖動操作。這可以避免用戶因為手握鼠標抖動而產生拖動。
void ProjectListWidget::mouseMoveEvent(QMouseEvent *event) { if(event->buttons() & Qt::LeftButton) { if(int distance = (event->pos() - startPos).manhattanLength(); distance >= QApplication::startDragDistance()) { performDrag(); } } QListWidget::mouseMoveEvent(event); }
在perfomDrag()
中,創(chuàng)建了一個類型為QDrag的對象,并且把this作為它的父對象。這個QDrag對象將數據存儲在QMimeData
對象中。在這個實例中,我們利用QMineData::setText()
提供了作為text/plain
字符串的數據。QMimeData 提供了一些可用于處理最常用拖放類型(諸如圖像、URL、顏色,等等)的函數,同時也可以處理任意由QByteArrays表宗的MiME類型。QDrag::setPiximap()
調用則可以在拖放發(fā)生時使圖標隨光標移動。
QDrag::exec()
調用啟動并執(zhí)行拖動操作;直到用戶放下或取消此次拖動操作才會停止。它把所有支持的“拖放動作"(如 Qi: : CopyAction, Qt : : MoveAction和 Qt: : LinkAction)的組合作為其參數,并且返回被執(zhí)行的拖放動作(如果沒有執(zhí)行任何動作,則返回Qt: IgnoreAction)。至于執(zhí)行的是哪`個動作,取決于放下發(fā)生時源窗口部件是否允許、目標是否支持及按下了哪些組合鍵。在 exec()調用后,Qt擁有拖動對象的所有權并且可以在不需要它的時候刪除它。
void ProjectListWidget::performDrag() { if(QListWidgetItem* item = currentItem(); nullptr != item) { QMimeData* mineData = new QMimeData(); mineData->setText(item->text()); QDrag* drag = new QDrag(this); drag->setMimeData(mineData); drag->setPixmap(QPixmap(":/icon.jpg")); if(drag->exec(Qt::MoveAction) == Qt::MoveAction) { delete item; item = nullptr; } } }
ProjectListWidget窗口部件不僅能發(fā)起拖動,還可以接收同一個應用程序中來自另外一個ProjectListWidget部件的拖動。如果窗口部件是同個應用程序的一部分,QDragEnterEvent::source()
返回一個啟動這個拖動的窗口部件的指針;否則,返回一個空指針值。
void ProjectListWidget::dragEnterEvent(QDragEnterEvent *event) { ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source()); if(nullptr != source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } }
dragMoveEvent()中的代碼與dragEnterEvent()中編寫的代碼基本相同。因為需要重寫QListWidget的函數實現(實際上是 QAbstractItemView的函數實現)。
void ProjectListWidget::dragMoveEvent(QDragMoveEvent *event) { ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source()); if(nullptr != source && source != this) { event->setDropAction(Qt::MoveAction); event->accept(); } }
在dropEvent()中,我們使用QMimeData::text()
重新找回拖動的文本并隨文本創(chuàng)建一個拖動項。還需要將事件作為“移動動作”來接受,從而告訴源窗口部件現在可以刪除原來的拖動項了。
void ProjectListWidget::dropEvent(QDropEvent *event) { ProjectListWidget* source = reinterpret_cast<ProjectListWidget*>(event->source()); if(nullptr != source && source != this) { addItem(event->mimeData()->text()); event->setDropAction(Qt::MoveAction); event->accept(); } }
效果如下
總結
拖放是在應用程序之間傳遞數據的有力機制。但是在某些情況下;,有可能在執(zhí)行拖放時并未使用Qt的拖放工具。如果只是想在一個應用程序的窗口部件中移動數據,通常只要重新實現mousePressEvent()和 mouseReleaseEvent()函數就可以了。
到此這篇關于Qt實現拖拽功能(支持拖放文件、拖放操作)的文章就介紹到這了,更多相關Qt實現拖拽功能內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Visual Studio Code配置C/C++開發(fā)環(huán)境的教程圖解
這篇文章主要介紹了Visual Studio Code配置C/C++開發(fā)環(huán)境的教程,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06C++中靜態(tài)成員函數訪問非靜態(tài)成員的實例
這篇文章主要介紹了C++中靜態(tài)成員函數訪問非靜態(tài)成員的實例的相關資料,需要的朋友可以參考下2017-07-07