Qt自定义按钮弹窗控件

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a844651990/article/details/83242159

  一直以为做一个按钮弹窗控件很简单,可做起来发现并不是那么顺利,折腾了挺长时间的,先看下效果:
在这里插入图片描述

前言

  尝试过两种方案,方案一:使用QToolButton控件,我们可以自定义一个widget,然后setMenu设置为该widget;方案二:点击一个QPushButton然后show一个自定义widget。使用方案一的话各种鼠标事件不用我们管了,但是如果我们想要做的漂亮一些就会有很多局限性,不容易实现。上面的效果图采用的方案二实现的。

需要注意的几点

1.点击按钮弹出一个窗口。再次点击按钮窗口消失。
2.同时只能显示一个窗口。
3.窗口弹出时,点击主窗口其它位置,窗口消失(因为阴影效果的原因会有些小瑕疵,下面会介绍到)。
4.弹出窗口圆角并具有阴影效果。

源码分析

  下面分析下源码中比较重要的几个点。
1.弹出的窗口需要设置下面这些属性:

setWindowFlags((Qt::Dialog | Qt::FramelessWindowHint | this->windowFlags()));
setAttribute(Qt::WA_TranslucentBackground);
setMouseTracking(true);

2.阴影效果:

m_pShadow = new QGraphicsDropShadowEffect(this);
m_pShadow->setOffset(0, 0);
m_pShadow->setColor(QColor("#cccccc"));
m_pShadow->setBlurRadius(10);
m_pMainWidget = new QWidget(this);
m_pCentralWidget = new QWidget(m_pMainWidget);
m_pCentralWidget->setStyleSheet("QWidget { border: none; }");
m_pCentralLayout = new QHBoxLayout(m_pMainWidget);
m_pCentralLayout->addWidget(m_pCentralWidget);
m_pMainWidget->setStyleSheet(" border: 1px solid lightgray; background-color: #fafafa; border-radius: 10px;");
m_pMainWidget->setGraphicsEffect(m_pShadow);

  小瑕疵:这里实现阴影效果采用了一个QWidget嵌入到另一个QWidget(该widget是透明的),所以如果点击弹窗阴影附近(范围和被嵌入的widget的布局的margin大小有关)的时候弹窗是不会消失的。
3.按钮点击时需要将弹出widget的坐标映射到屏幕的坐标:

connect(this, &PopupWidgetButton::ButtonClicked, [=]() {
	if (m_pMainWidget->isHidden()) {
	     QPoint pos;
	     if (m_orien == PWB::Horizontal) {
	         pos.setX(m_pButton->mapToGlobal(QPoint(0, 0)).x() + m_pButton->width());
	         pos.setY(m_pButton->mapToGlobal(QPoint(0, 0)).y() - 40);
	     } else if (m_orien == PWB::Vertical) {
	         pos.setX(m_pButton->mapToGlobal(QPoint(0, 0)).x() + m_pButton->width()/2 - m_pMainWidget->width()/2);
	         pos.setY(m_pButton->mapToGlobal(QPoint(0, 0)).y() + m_pButton->height());
	     }
	     // 同时只能显示一个 popupwidget
	     foreach (auto widget, m_pWidgets) {
	         if (widget != m_pMainWidget) {
	             widget->hide();
	         }
	     }
	     m_pMainWidget->move(pos);
	     m_pMainWidget->show();
	 } else {
	     m_pMainWidget->hide();
	 }
	});
	connect(this, &PopupWidgetButton::OthersClicked, m_pMainWidget, &PopupWidget::hide);

4.事件处理:

bool PopupWidgetButton::eventFilter(QObject *watched, QEvent *event)
{
    if (event->type() == QEvent::MouseButtonRelease) {
        QMouseEvent *e = static_cast<QMouseEvent *>(event);
        QPoint buttonPoint = m_pButton->mapToGlobal(QPoint(0, 0));
        // 需要获取主窗口的坐标
        QPoint ePoint = e->pos() + MainWidget::getMainWidgetPos();

        /**
          * \warning
          *  Qt5中的事件传递的第一层watched并不是我们自己的主窗体Widget类或者ButtonPopupWidget类,
          *  而是它们的一个私有实现类QWidgetWindow,该类的objectName会自动加上“Window”
          */
        if (watched->objectName() == "MainWidgetWindow") {
            m_mainWidgetClicked = false;
            // 点击按钮
            if (ePoint.x() >= buttonPoint.x() && ePoint.x() <= buttonPoint.x() + m_pButton->width()
                    && ePoint.y() >= buttonPoint.y() && ePoint.y() <= buttonPoint.y() + m_pButton->height()) {
                emit ButtonClicked();
                return true;
            }
        }
        // 点击弹出的widget
        else if (watched->objectName() == "PopupMainWidgetWindow") {
            m_mainWidgetClicked =  true;
            return QWidget::eventFilter(watched, event);
        }

        if (!m_pMainWidget->isHidden() && !m_mainWidgetClicked) {
            emit OthersClicked();
        }

    }
    // 点击标题栏
    else if (event->type() == QEvent::NonClientAreaMouseButtonRelease ||
               event->type() == QEvent::NonClientAreaMouseButtonPress ||
               event->type() == QEvent::NonClientAreaMouseButtonDblClick) {
        emit OthersClicked();
    }
    return QObject::eventFilter(watched, event);
}

  关于事件的处理多说两句。Qt的事件传递是从父->子的方向传递的。也就是说如果一个QPushButton指定了parent为QWidget,如何鼠标点击按钮,那么是QWidget一个私有实现类QWidgetWindow类先捕获到该事件的然后再传递给QWidget再传递给QPushButton。QWidgetWindow会有一个objectname,就是在QWidget的objectname的基础上加上window字符串。这些信息在我们写事件过滤器的时候会很有用。

代码下载

CSDN: https://download.csdn.net/download/a844651990/10735129
github: https://github.com/FlyWM/PopupWidgetButton

猜你喜欢

转载自blog.csdn.net/a844651990/article/details/83242159