zoukankan      html  css  js  c++  java
  • Qt----拖拽

    最近比较忙,今天此才有时间来继续学习下Qt。Qt的拖拽可以按字面意思分为两部分。一般来说我们常见的拖拽分别由两个程序合作完成。例如我们经常把桌面的文件拖拽进其他目录:

    desktop-drag

    这个拖拽在Qt中由两方合作完成,一个是桌面窗口另一个是目录窗口,桌面发起“拖”动作,目录窗口接受“放”动作。如果你细心观察还可以发现,在不同的情况下拖拽还可以产生不同的结果:

    drag-copy drag-move drag-link

    拖拽方可以发起多个拖拽类型(复制、链接、移动等),接收方可以选择接受其中某个类型或者直接拒绝。除此之外,拖拽支持多种数据格式,并且还可以在同一个应用程序中进行(托和拽操作均由同一个exe发起)。我们通过一个示例程序来学习,这个程序包含以下功能:

    • 内部发起拖拽。
    • 切换可接受的拖拽动作:移动、拷贝、链接。
    • 支持多种拖拽数据: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类型是一个粗体的斜箭头:

    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不是很好用,没有获取当前类型的功能,只能约定好或者一个一个去尝试。

    4. 拖拽区域高亮

    • 当拖拽进入窗口之中时,会触发dragEnterEvent()事件,我们在这里高亮窗口背景;
    • 当我们接受拖拽事件(通过QDragEnterEvent::accept()或者QDropEvent::acceptProposedAction())后,窗口会收到dragMoveEvent()事件。QDragMoveEvent事件包含一个answerRect()函数,返回相对于窗口的当前拖拽范围,我们通过判断来选择是否绘制中心的高亮区域。有趣的是这个范围大小始终是1x1,它被当成点来处理。
    • 当拖拽离开窗口时,会触发dragLeaveEvent()事件,我们进行重绘。

    下面是该部分代码:

    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

    代码详见链接

  • 相关阅读:
    第 13 章 外观模式
    第 12 章 桥接模式
    第 10 章 适配器模式
    第 7 章 原型模式
    PHP的预处理语句的使用
    PHP的异常处理
    PHP中PDO函数的使用
    PHP的PDO模式数据操作
    PHP的 Final关键字、类(文件)的加载和命名空间
    PHP的魔术常量和魔术方法
  • 原文地址:https://www.cnblogs.com/lgxZJ/p/7879034.html
Copyright © 2011-2022 走看看