最近比較忙,今天此纔有時間來繼續學習下Qt。Qt的拖拽能夠按字面意思分爲拖和拽兩部分。通常來講咱們常見的拖拽分別由兩個程序合做完成。例如咱們常常把桌面的文件拖拽進其餘目錄:html
這個拖拽在Qt中由兩方合做完成,一個是桌面窗口另外一個是目錄窗口,桌面發起「拖」動做,目錄窗口接受「放」動做。若是你細心觀察還能夠發現,在不一樣的狀況下拖拽還能夠產生不一樣的結果:git
拖拽方能夠發起多個拖拽類型(複製、連接、移動等),接收方能夠選擇接受其中某個類型或者直接拒絕。除此以外,拖拽支持多種數據格式,而且還能夠在同一個應用程序中進行(託和拽操做均由同一個exe發起)。咱們經過一個示例程序來學習,這個程序包含如下功能:github
咱們在主窗口內部放置了3個Label,分別能夠用來演示3種不一樣數據類型的拖拽效果。若是鼠標左鍵在這幾個標籤區域內按下而且移動距離超過5個像素,咱們就發起拖拽動做,可能的拖拽類型爲:Copy、Move、Link。經過QDrag類咱們就能發起一個拖拽動做,這個類還提供了setPixmap()
和setHotSpot()
函數類供咱們自定義拖拽時的圖像和位置,這裏咱們就不設置了。函數
調用QDrag::exec()
就能進入拖拽的事件處理循環了,注意在Linux和Mac上,這個exec並不會阻塞全局的消息循環;在Windows上,它是阻塞的:學習
void MainWindow::mousePressEvent(QMouseEvent *event) { if (event->button() & Qt::LeftButton) { m_dragStartPos = event->pos(); if (m_imageLabel->geometry().contains(event->pos())) m_dragLabel = m_imageLabel; else if (m_colorLabel->geometry().contains(event->pos())) m_dragLabel = m_colorLabel; else if (m_htmlLabel->geometry().contains(event->pos())) m_dragLabel = m_htmlLabel; else m_dragStartPos = QPoint(); } QMainWindow::mousePressEvent(event); } void MainWindow::mouseMoveEvent(QMouseEvent *event) { QDrag drag(this); if (m_dragStartPos.isNull()) goto end; if ((event->pos() - m_dragStartPos).manhattanLength() < 5) goto end; ... drag.exec(Qt::CopyAction | Qt::MoveAction | Qt::LinkAction); end: QMainWindow::mouseMoveEvent(event); }
這個功能咱們經過菜單來實現,由於同一時間只接受一種類型,所以這幾個菜單要求必須互斥。互斥菜單這個概念咱們在代碼裏選擇使用QActionGroup,雖然本身實現切換也不麻煩,可是這樣更快更方便。每一個子菜單觸發時會切換可接受拖拽類型,Qt提供了3種基本類型:Move、Copy、Link。拖拽顯示時,Copy類型是一個+
號,Move類型是一個→
,Link類型是一個粗體的斜箭頭:this
void MainWindow::createMenus() { auto menu = this->menuBar()->addMenu(tr("Acceptable Drags")); auto copyAction = menu->addAction("Only Copy", this, SLOT(onlyAcceptCopyDrag()), QKeySequence(Qt::CTRL + Qt::Key_C)); copyAction->setCheckable(true); copyAction->setChecked(true); m_actionGroup.addAction(copyAction); auto moveAction = menu->addAction("Only Move", this, SLOT(onlyAcceptMoveDrag()), QKeySequence(Qt::CTRL + Qt::Key_M)); moveAction->setCheckable(true); moveAction->setChecked(false); m_actionGroup.addAction(moveAction); auto linkAction = menu->addAction("Only Link", this, SLOT(onlyAcceptLinkDrag()), QKeySequence(Qt::CTRL + Qt::Key_L)); linkAction->setCheckable(true); linkAction->setChecked(false); m_actionGroup.addAction(linkAction); }
咱們在發起拖拽的時候設置傳遞的數據及類型,經過QDrag::setMimeData()
函數來設置傳入的數據及類型。這個函數接受一個QMimeData類型的指針,這個類用MIME來區分數據類型。對於常見的類型它提供了方便的函數setHtml()
、setColorData()
等,咱們只須要傳入數據就能夠了:html和texx類型的數據以字符串表示,Image和Color類型數據以QVariant表示;對於自定義類型,咱們須要調用setData()
函數,同時傳入MIME類型和對應的數據(由於使用簡單,代碼中沒有演示)。數據獲取則經過對應的html()
、imageData()
、data()
等函數:指針
void MainWindow::mouseMoveEvent(QMouseEvent *event) { ... QMimeData* mimeData = new QMimeData(); if (m_dragLabel == m_htmlLabel) mimeData->setHtml("<html>This is a html page</html>"); if (m_dragLabel == m_colorLabel) mimeData->setColorData(QVariant(QColor(Qt::cyan))); if (m_dragLabel == m_imageLabel) mimeData->setImageData(QVariant(QPixmap(":/img/image.jpg"))); drag.setMimeData(mimeData); ... }
其實我的以爲QMimeData不是很好用,沒有獲取當前類型的功能,只能約定好或者一個一個去嘗試。code
dragEnterEvent()
事件,咱們在這裏高亮窗口背景;QDragEnterEvent::accept()
或者QDropEvent::acceptProposedAction()
)後,窗口會收到dragMoveEvent()
事件。QDragMoveEvent事件包含一個answerRect()
函數,返回相對於窗口的當前拖拽範圍,咱們經過判斷來選擇是否繪製中心的高亮區域。有趣的是這個範圍大小始終是1x1
,它被當成點來處理。dragLeaveEvent()
事件,咱們進行重繪。下面是該部分代碼:htm
void MainWindow::dragEnterEvent(QDragEnterEvent *event) { event->setDropAction(m_acceptableDropAction); event->accept(); m_currentBkColor = m_dragEnteredColor; QWidget::dragEnterEvent(event); } void MainWindow::dragLeaveEvent(QDragLeaveEvent *event) { m_currentBkColor = m_dragLeavedColor; repaint(); QWidget::dragLeaveEvent(event); } void MainWindow::dragMoveEvent(QDragMoveEvent *event) { if (m_acceptableRect.contains(event->answerRect())) m_currentDragableAreaColor = m_highlightColor; else m_currentDragableAreaColor = m_unhighlightColor; repaint(); }
代碼詳見連接。blog