赞
踩
文章转自博客园(Qt小罗):Qt实战6.万能的无边框窗口(FramelessWindow) - Qt小罗 - 博客园
最初实现无边框的目标只有一个,即简单好用。所有实现基于Qt本身,现将窗口分为三层,如图:
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
外层和内容层均使用垂直布局,使窗口拉伸时能够自动适应大小。下面对每一层做个简单说明。
作为窗口的最外层,设置为透明,为内层ContentWidget边框设置、阴影显示提供支持。同时根据位置设置光标形状(CursorShape),实现窗口的任意拉伸。
作为内容包含层,可设置边框颜色、宽度、圆角、阴影等效果,同时增加最大化、最小化、关闭按钮,以及logo、软件名称显示部件。
作为外部嵌入层,XWidget提供一个接口void setCentralWidget(QWidget *widget),将其它窗口集成到ContentWidget内部形成一个整体,这个与QMainWindow类似。
- setWindowFlags(Qt::FramelessWindowHint); //隐藏标题栏(无边框)
- setAttribute(Qt::WA_StyledBackground); //启用样式背景绘制
- setAttribute(Qt::WA_TranslucentBackground); //背景透明
setAttribute(Qt::WA_Hover);
- bool XWidget::event(QEvent *event)
- {
- if (event->type() == QEvent::HoverMove) {
- QHoverEvent *hoverEvent = static_cast<QHoverEvent *>(event);
- QMouseEvent mouseEvent(QEvent::MouseMove, hoverEvent->pos(),
- Qt::NoButton, Qt::NoButton, Qt::NoModifier);
- mouseMoveEvent(&mouseEvent);
- }
-
- return QWidget::event(event);
- }
m_bIsPressed 是否按下鼠标
m_bIsResizing 是否正在调整窗口,调整窗口大小时不移动窗口
- void XWidget::mousePressEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- m_bIsPressed = true;
- m_pressPoint = event->globalPos();
- }
-
- return QWidget::mousePressEvent(event);
- }
- void XWidget::mouseMoveEvent(QMouseEvent *event)
- {
- if (m_bIsPressed) {
- if (m_bIsResizing) {
- m_movePoint = event->globalPos() - m_pressPoint;
- m_pressPoint += m_movePoint;
- } else {
- if (!m_bIsDoublePressed && windowState() == Qt::WindowMaximized) {
- restoreWidget();
- QPointF point(width() * ((double)(event->globalPos().x())/QApplication::desktop()->width()),
- height() * ((double)(event->globalPos().y())/QApplication::desktop()->height()));
-
- move(event->globalPos() - point.toPoint());
-
- m_pressPoint = event->globalPos();
- }
-
- QPoint point = event->globalPos() - m_pressPoint;
- move(pos() + point);
-
- m_pressPoint = event->globalPos();
- }
- }
-
- if (windowState() != Qt::WindowMaximized) {
- updateRegion(event);
- }
-
- QWidget::mouseMoveEvent(event);
- }

- void XWidget::updateRegion(QMouseEvent *event)
- {
- QRect mainRect = geometry();
-
- int marginTop = event->globalY() - mainRect.y();
- int marginBottom = mainRect.y() + mainRect.height() - event->globalY();
- int marginLeft = event->globalX() - mainRect.x();
- int marginRight = mainRect.x() + mainRect.width() - event->globalX();
-
- if (!m_bIsResizing) {
- if ( (marginRight >= MARGIN_MIN_SIZE && marginRight <= MARGIN_MAX_SIZE)
- && ((marginBottom <= MARGIN_MAX_SIZE) && marginBottom >= MARGIN_MIN_SIZE) ) {
- m_direction = BOTTOMRIGHT;
- setCursor(Qt::SizeFDiagCursor);
- } else if ( (marginTop >= MARGIN_MIN_SIZE && marginTop <= MARGIN_MAX_SIZE)
- && (marginRight >= MARGIN_MIN_SIZE && marginRight <= MARGIN_MAX_SIZE)) {
- m_direction = TOPRIGHT;
- setCursor(Qt::SizeBDiagCursor);
- } else if ( (marginLeft >= MARGIN_MIN_SIZE && marginLeft <= MARGIN_MAX_SIZE)
- && (marginTop >= MARGIN_MIN_SIZE && marginTop <= MARGIN_MAX_SIZE) ) {
- m_direction = TOPLEFT;
- setCursor(Qt::SizeFDiagCursor);
- } else if ( (marginLeft >= MARGIN_MIN_SIZE && marginLeft <= MARGIN_MAX_SIZE)
- && (marginBottom >= MARGIN_MIN_SIZE && marginBottom <= MARGIN_MAX_SIZE)) {
- m_direction = BOTTOMLEFT;
- setCursor(Qt::SizeBDiagCursor);
- } else if (marginBottom <= MARGIN_MAX_SIZE && marginBottom >= MARGIN_MIN_SIZE) {
- m_direction = DOWN;
- setCursor(Qt::SizeVerCursor);
- } else if (marginLeft <= MARGIN_MAX_SIZE - 1 && marginLeft >= MARGIN_MIN_SIZE - 1) {
- m_direction = LEFT;
- setCursor(Qt::SizeHorCursor);
- } else if (marginRight <= MARGIN_MAX_SIZE && marginRight >= MARGIN_MIN_SIZE) {
- m_direction = RIGHT;
- setCursor(Qt::SizeHorCursor);
- } else if (marginTop <= MARGIN_MAX_SIZE && marginTop >= MARGIN_MIN_SIZE) {
- m_direction = UP;
- setCursor(Qt::SizeVerCursor);
- } else {
- if (!m_bIsPressed) {
- setCursor(Qt::ArrowCursor);
- }
- }
- }
-
- if (NONE != m_direction) {
- m_bIsResizing = true;
- resizeRegion(marginTop, marginBottom, marginLeft, marginRight);
- }
- }

不要看着代码多就感觉复杂,上面其实就干了一件事,判断鼠标是否达到边框限定位置,达到了就把方向记录下来。
- void XWidget::resizeRegion(int marginTop, int marginBottom,
- int marginLeft, int marginRight)
- {
- if (m_bIsPressed) {
- switch (m_direction) {
- case BOTTOMRIGHT:
- {
- QRect rect = geometry();
- rect.setBottomRight(rect.bottomRight() + m_movePoint);
- setGeometry(rect);
- }
- break;
- case TOPRIGHT:
- {
- if (marginLeft > minimumWidth() && marginBottom > minimumHeight()) {
- QRect rect = geometry();
- rect.setTopRight(rect.topRight() + m_movePoint);
- setGeometry(rect);
- }
- }
- break;
- case TOPLEFT:
- {
- if (marginRight > minimumWidth() && marginBottom > minimumHeight()) {
- QRect rect = geometry();
- rect.setTopLeft(rect.topLeft() + m_movePoint);
- setGeometry(rect);
- }
- }
- break;
- case BOTTOMLEFT:
- {
- if (marginRight > minimumWidth() && marginTop> minimumHeight()) {
- QRect rect = geometry();
- rect.setBottomLeft(rect.bottomLeft() + m_movePoint);
- setGeometry(rect);
- }
- }
- break;
- case RIGHT:
- {
- QRect rect = geometry();
- rect.setWidth(rect.width() + m_movePoint.x());
- setGeometry(rect);
- }
- break;
- case DOWN:
- {
- QRect rect = geometry();
- rect.setHeight(rect.height() + m_movePoint.y());
- setGeometry(rect);
- }
- break;
- case LEFT:
- {
- if (marginRight > minimumWidth()) {
- QRect rect = geometry();
- rect.setLeft(rect.x() + m_movePoint.x());
- setGeometry(rect);
- }
- }
- break;
- case UP:
- {
- if (marginBottom > minimumHeight()) {
- QRect rect = geometry();
- rect.setTop(rect.y() + m_movePoint.y());
- setGeometry(rect);
- }
- }
- break;
- default:
- {
- }
- break;
- }
- } else {
- m_bIsResizing = false;
- m_direction = NONE;
- }
- }

同样的,上面这段代码也只干了一件事,如果鼠标达到了边框限定位置,并且按下了鼠标按键,就跟着改变窗口大小。
- void XWidget::mouseReleaseEvent(QMouseEvent *event)
- {
- if (event->button() == Qt::LeftButton) {
- m_bIsPressed = false;
- m_bIsResizing = false;
- m_bIsDoublePressed = false;
- }
-
- QWidget::mouseReleaseEvent(event);
- }
- void XWidget::leaveEvent(QEvent *event)
- {
- m_bIsPressed = false;
- m_bIsDoublePressed = false;
- m_bIsResizing = false;
-
- QWidget::leaveEvent(event);
- }
- void XWidget::createShadow()
- {
- QGraphicsDropShadowEffect *shadowEffect = new QGraphicsDropShadowEffect(this);
- shadowEffect->setColor(Qt::black);
- shadowEffect->setOffset(0, 0);
- shadowEffect->setBlurRadius(13);
- ui->widgetContent->setGraphicsEffect(shadowEffect);
- }
此方法虽有效,会损耗性能,复杂界面不建议使用。
- void XWidget::maximizeWidget()
- {
- ui->pushButtonRestore->show();
- ui->pushButtonMax->hide();
-
- ui->verticalLayoutShadow->setContentsMargins(0, 0, 0, 0);
-
- showMaximized();
- }
- void XWidget::restoreWidget()
- {
- ui->pushButtonRestore->hide();
- ui->pushButtonMax->show();
-
- ui->verticalLayoutShadow->setContentsMargins(9, 9, 9, 9);
-
- showNormal();
- }
- #widgetContent {
- background-color: white;
- border: 1px solid lightgray;
- border-radius: 3px;
- }
-
-
- #widgetContent QTreeWidget {
- border: 1px solid lightgray;
- }
-
-
- #titleBarWidget QPushButton {
- min-width: 25px;
- max-width: 25px;
- min-height: 25px;
- max-height: 25px;
- qproperty-flat: true;
- border: none;
- }
- #titleBarWidget QPushButton:hover {
- background-color: #D5E1F2;
- }
- #titleBarWidget QPushButton:pressed {
- background-color: #A3BDE3;
- }
- #titleBarWidget QPushButton#pushButtonClose {
- border-image: url(:/img/titleBar/close.png) 0 0 0 0 stretch stretch;
- }
- #titleBarWidget QPushButton#pushButtonRestore {
- border-image: url(:/img/titleBar/restore.png) 0 0 0 0 stretch stretch;
- }
- #titleBarWidget QPushButton#pushButtonMax {
- border-image: url(:/img/titleBar/max.png) 0 0 0 0 stretch stretch;
- }
- #titleBarWidget QPushButton#pushButtonMin {
- border-image: url(:/img/titleBar/min.png) 0 0 0 0 stretch stretch;
- }
- #titleBarWidget QPushButton#pushButtonMenu {
- border-image: url(:/img/titleBar/menu.png) 0 0 0 0 stretch stretch;
- }
-
-
- #menuBarTabWidget::tab-bar {
- left: 65px;
- }
- #menuBarTabWidget {
- border: 1px;
- }
- #menuBarTabWidget {
- background-color: #2B579A;
- }
- #menuBarTabWidget::pane {
- border: 1px solid lightgray;
- border-left: 0px;
- border-right: 0px;
- }
- #menuBarTabWidget QTabBar::tab{
- min-width: 55px;
- max-width: 55px;
- min-height: 23px;
- max-height: 23px;
- }
- #menuBarTabWidget QTabBar::tab {
- background: transparent;
- margin-left: 4px;
- margin-right: 4px;
- }
- #menuBarTabWidget QTabBar::tab:hover {
- color: #2B579A;
- }
- #menuBarTabWidget QTabBar::tab:selected {
- border: 1px solid #BAC9DB;
- background: white;
- border-bottom-color: #FFFFFF;
- }
- #menuBarTabWidget QTabBar::tab:!selected {
- margin-top: 1px;
- }
-
-
- QMenu {
- background-color: #FCFCFC;
- border: 1px solid #8492A6;
- }
- QMenu::item {
- background-color: transparent;
- }
- QMenu::item:selected {
- color: black;
- background-color: #D5E1F2;
- }
-
-
- #pushBtnFileMenu {
- min-width: 58px;
- max-width: 58px;
- min-height: 23px;
- max-height: 23px;
- color: white;
- border: 1px solid #2B579A;
- background-color: #2B579A;
- }
- #pushBtnFileMenu::menu-indicator {
- image: none;
- }
-

之前也看了不少Qt实现FramelessWindow的例子,不是很复杂就是不通用。通过上面的实现,现在已完成了一个通用的版本,只要将自己的窗口设置到ContentWidget即可。本次实践关键地方有以下三点:
可能算不上最佳实践,但是已经能够满足绝大多数使用场景了,往里面套就行,使用起来非常之简单,还是很nice的。
本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。