Qt----拖拽

最近比較忙,今天此纔有時間來繼續學習下Qt。Qt的拖拽能夠按字面意思分爲兩部分。通常來講咱們常見的拖拽分別由兩個程序合做完成。例如咱們常常把桌面的文件拖拽進其餘目錄:html

desktop-drag

這個拖拽在Qt中由兩方合做完成,一個是桌面窗口另外一個是目錄窗口,桌面發起「拖」動做,目錄窗口接受「放」動做。若是你細心觀察還能夠發現,在不一樣的狀況下拖拽還能夠產生不一樣的結果:git

drag-copy drag-move drag-link

拖拽方能夠發起多個拖拽類型(複製、連接、移動等),接收方能夠選擇接受其中某個類型或者直接拒絕。除此以外,拖拽支持多種數據格式,而且還能夠在同一個應用程序中進行(託和拽操做均由同一個exe發起)。咱們經過一個示例程序來學習,這個程序包含如下功能:github

  • 內部發起拖拽。
  • 切換可接受的拖拽動做:移動、拷貝、連接。
  • 支持多種拖拽數據:image、html、text、color等。
  • 拖拽區域高亮。

1. 內部拖拽

咱們在主窗口內部放置了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);
}

2. 可接受拖拽類型切換

這個功能咱們經過菜單來實現,由於同一時間只接受一種類型,所以這幾個菜單要求必須互斥。互斥菜單這個概念咱們在代碼裏選擇使用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);
}

3. 多種拖拽數據類型

咱們在發起拖拽的時候設置傳遞的數據及類型,經過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

4. 拖拽區域高亮

  • 當拖拽進入窗口之中時,會觸發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();
}

5. 運行結果

result-1 result-2

代碼詳見連接blog

相關文章
相關標籤/搜索