一直以为做一个按钮弹窗控件很简单,可做起来发现并不是那么顺利,折腾了挺长时间的,先看下效果:
尝试过两种方案,方案一:使用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());}// 同时只能显示一个 popupwidgetforeach (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* 主窗口的事件先传递给它一个私有实现类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;}}// 点击弹出的widgetelse 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);
}
关于事件的处理多说两句。QWidget有一个私有实现类QWidgetWindow类先捕获到事件,然后再传递给QWidget。QWidgetWindow会有一个objectname,就是在QWidget的objectname的基础上加上window字符串。这些信息在我们写事件过滤器的时候会很有用。
CSDN:
github:
2018.12.28更新说明:
本文发布于:2024-02-02 06:01:49,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170682491141839.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |