Qt模仿多标签页窗口拖拽操作

本功能的实现主要依托于Qt的拖拽操作。从本文可以学到Qt的拖拽机制,自定义QMimeData的数据类型,和自定义的QGraphicsEffect效果。本文的视觉特效是应用于拖拽的时候指示当前鼠标的位置和拖拽结果新标签页会放置在当前窗口的第几个标签页之后。以下是窗口的效果图片,为了方便标签是用按钮做的。第一张图是主窗口截图:

第二张图是拖拽“第二页”拍摄的图(因为截屏截不到光标效果)。可以看到在“第四页”右侧有个倒三角形指示拖拽的新标签页将放在“第四页”右侧:

第三张图是拖拽“第二页”到任意地方拍摄的图片。此时松开鼠标“第二页”将变成一个独立的窗口:

第四张图是把“第四页”拖拽成独立窗口之后的效果图:

下面将给出拖拽部分的代码,本代码是基于VS2015和Qt5.9写的。之所以拖拽操作在eventFilter(...)函数里,是因为鼠标在按钮上面操作时父Widget是收不到鼠标消息的,因此用事件过滤器拦截按钮的消息。完整的项目在Github里开源了,链接是https://github.com/mengxiangdu/QtMultiPageWnd.git

bool MDragableCtrl::eventFilter(QObject* src, QEvent* event)
{
    if (event->type() == QEvent::MouseButtonPress)
    {
        lastPt = dynamic_cast<QMouseEvent*>(event)->pos();
    }
    else if (event->type() == QEvent::MouseMove)
    {
        QMouseEvent* tempEvent = dynamic_cast<QMouseEvent*>(event);
        QWidget* thisWidget = dynamic_cast<QWidget*>(src);
        if ((lastPt - tempEvent->pos()).manhattanLength() < QApplication::startDragDistance() && 
            lastPt == QPoint(-1, -1))
        {
            return false;
        }
        int which = findHitButton(thisWidget->mapToParent(tempEvent->pos()));
        if (which >= 0)
        {
            dynamic_cast<QHBoxLayout*>(layout())->removeWidget(saveBtns[which]);
            delete saveBtns[which];
            saveBtns.removeOne(saveBtns[which]);

            QtMultiPageWnd* myself = dynamic_cast<QtMultiPageWnd*>(topLevelWidget());
            QWidget* movePage = myself->takePage(which);
            QtMultiPageWnd* newWnd = new QtMultiPageWnd(movePage);
            newWnd->move(2048, 2048); /* 不显示无法获取正确的frameGeometry()值 */
            newWnd->show();
            /* 空了先隐藏自己 */
            /* 拖拽结束再关闭自己,见下方代码 */
            if (empty())
            {
                myself->hide();
            }

            QDrag drag(this);
            QMimeData *mimeData = new QMimeData;
            mimeData->setData(u8R"(application/x-qt-windows-mime;value="QtMimeType")", QtMimeType(newWnd).toByteArray());
            drag.setMimeData(mimeData);
            drag.setPixmap(createWndSnapshot(newWnd));
            drag.setHotSpot(newWnd->mapToFrame(tempEvent->pos()));

            Qt::DropAction dropAction = drag.exec();
            if (dropAction == Qt::IgnoreAction)
            {
                newWnd->move(QCursor::pos() - drag.hotSpot());
            }
            /* 如果本窗口已经空了,则关闭自己 */
            /* 注意不能直接delete,万一本窗口是主窗口内存在栈上删除会报错 */
            if (empty())
            {
                myself->close();
            }
            return true;
        }
    }
    else if (event->type() == QEvent::MouseButtonRelease)
    {
        lastPt = QPoint(-1, -1);
    }
    return false;
}

 

posted @ 2023-08-24 19:02  兜尼完  阅读(87)  评论(0编辑  收藏  举报