700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 1 自定义无边框窗体

1 自定义无边框窗体

时间:2020-08-08 13:54:07

相关推荐

1 自定义无边框窗体

1 开发测试环境

IDE:VS

Qt:5.14.2 x64

操作系统:Win10、Win11

实现效果

2 实现

网上搜索Qt窗口的无边框化实现方案有很多,但从实现的难易程度,以及效果上来说,最优

的还是通过Windows消息来实现,即重新实现Qt窗口的nativeEvent方法。该方案不仅实现了无边

框窗口,还保留了窗口的拖动、贴边等系统特性,以下为实现代码(以QWidget为例,

QMainWindow、QDialog可以平移过去):

#include "ui/frameless_widget.h"#include <windows.h>#include <windowsx.h>#pragma comment (lib,"user32.lib")FramelessWidget::FramelessWidget(QWidget *parent) : QWidget(parent), movale_(true), resizable_(true), border_width_(5), title_bar_(Q_NULLPTR), just_maximized_(false){this->setWindowFlags(this->windowFlags() | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinimizeButtonHint);this->installEventFilter(this);HWND hwnd = (HWND)this->winId();DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);}FramelessWidget::~FramelessWidget(){}void FramelessWidget::setTitleBar(QWidget *title_bar){title_bar_ = title_bar;title_bar_->installEventFilter(this);}void FramelessWidget::showEvent(QShowEvent *event){this->setAttribute(Qt::WA_Mapped);QWidget::showEvent(event);}bool FramelessWidget::eventFilter(QObject *watched, QEvent *event){if (watched == this){if (event->type() == QEvent::WindowStateChange){if (this->windowState() == Qt::WindowNoState){movale_ = true;resizable_ = true;}else{movale_ = false;resizable_ = false;}emit signalWindowStateChanged(!movale_);}}return QWidget::eventFilter(watched, event);}bool FramelessWidget::nativeEvent(const QByteArray &event_type, void *message, long *result){MSG *msg = (MSG *)message;switch (msg->message){case WM_NCCALCSIZE:{//this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION*result = 0;return true;}case WM_NCHITTEST:{*result = 0;const LONG border_width = border_width_;RECT winrect;GetWindowRect(HWND(winId()), &winrect);long x = GET_X_LPARAM(msg->lParam);long y = GET_Y_LPARAM(msg->lParam);bool resize_width = minimumWidth() != maximumWidth();bool resize_height = minimumHeight() != maximumHeight();if (resize_width){//left borderif (x >= winrect.left && x < winrect.left + border_width){*result = HTLEFT;}//right borderif (x < winrect.right && x >= winrect.right - border_width){*result = HTRIGHT;}}if (resize_height){//bottom borderif (y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOM;}//top borderif (y >= winrect.top && y < winrect.top + border_width){*result = HTTOP;}}if (resize_width && resize_height){//bottom left cornerif (x >= winrect.left && x < winrect.left + border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMLEFT;}//bottom right cornerif (x < winrect.right && x >= winrect.right - border_width &&y < winrect.bottom && y >= winrect.bottom - border_width){*result = HTBOTTOMRIGHT;}//top left cornerif (x >= winrect.left && x < winrect.left + border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPLEFT;}//top right cornerif (x < winrect.right && x >= winrect.right - border_width &&y >= winrect.top && y < winrect.top + border_width){*result = HTTOPRIGHT;}}//*result still equals 0, that means the cursor locate OUTSIDE the frame area//but it may locate in titlebar areaif (*result == 0){if (!title_bar_) return false;QPoint pos = title_bar_->mapFromGlobal(QPoint(x, y));if (!title_bar_->rect().contains(pos)) return false;QWidget* child = title_bar_->childAt(pos);if (!child){*result = HTCAPTION;return true;}else {/*if (_lpWhiteList.contains(child)){*result = HTCAPTION;return true;}*/return false;}}return true;} //end case WM_NCHITTESTcase WM_GETMINMAXINFO:{if (::IsZoomed(msg->hwnd)) {RECT frame = { 0, 0, 0, 0 };AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);//record frame area dataframes_.setLeft(abs(frame.left));frames_.setTop(abs(frame.bottom));frames_.setRight(abs(frame.right));frames_.setBottom(abs(frame.bottom));QWidget::setContentsMargins(frames_.left() + margins_.left(), \frames_.top() + margins_.top(), \frames_.right() + margins_.right(), \frames_.bottom() + margins_.bottom());just_maximized_ = true;}else {if (just_maximized_){QWidget::setContentsMargins(margins_);//after window back to normal size from maximized state//a twinkle will happen, to avoid this twinkle//repaint() is important used just before the window back to normalrepaint();frames_ = QMargins();just_maximized_ = false;}}return false;}default:return QWidget::nativeEvent(event_type, message, result);}}

3 扩展

以上,已经实现一个无边框窗口,可以拖动边框进行放大、缩小,以及通过点击任务栏标签

进行最小化和还原。如果想进一步实现窗口的拖动以及贴边特性,则需要实现一个窗口标题栏,并

通过setTitleBar进行设置。为了方便扩展,我们提供一个中间适配窗口,继承自

FramelessWidget,实现基础的标题栏,并对外提供设置标题、隐藏最小化、最大化按钮等接口,

当软件有多个无边框窗口需求时,可以继承适配窗口,并通过接口定制标题栏显示内容。

#include "ui/base_widget.h"BaseWidget::BaseWidget(QString title, QWidget *parent) : FramelessWidget(parent), layout_(Q_NULLPTR), title_bar_(Q_NULLPTR), title_bar_layout_(Q_NULLPTR), sys_icon_(Q_NULLPTR), title_(Q_NULLPTR), right_layout1_(Q_NULLPTR), right_layout2_(Q_NULLPTR), minimize_button_(Q_NULLPTR), switch_button_(Q_NULLPTR), close_button_(Q_NULLPTR), content_widget_(Q_NULLPTR), status_bar_(Q_NULLPTR), message_(Q_NULLPTR){initUI();title_->setText(title);this->setTitleBar(title_bar_);connect(this, &BaseWidget::signalWindowStateChanged, this, &BaseWidget::slotWindowStateChanged);connect(minimize_button_, &QToolButton::clicked, this, &BaseWidget::slotMinimizeButtonClicked);connect(switch_button_, &QToolButton::clicked, this, &BaseWidget::slotMaximizeButtonClicked);connect(close_button_, &QToolButton::clicked, this, &BaseWidget::slotCloseButtonClicked);}BaseWidget::~BaseWidget(){}void BaseWidget::initUI(){// 0layout_ = new QVBoxLayout;layout_->setContentsMargins(0, 0, 0, 0);layout_->setSpacing(0);// 1title_bar_ = new QWidget(this);title_bar_->setObjectName(QString("objTitleBar"));title_bar_->setFixedHeight(42);title_bar_layout_ = new QHBoxLayout;title_bar_layout_->setContentsMargins(5, 0, 0, 0);title_bar_layout_->setSpacing(0);sys_icon_ = new QLabel(title_bar_);title_ = new QLabel(title_bar_);title_->setObjectName(QString("objTitle"));right_layout1_ = new QVBoxLayout;right_layout1_->setContentsMargins(1, 1, 1, 1);right_layout2_ = new QHBoxLayout;right_layout2_->setContentsMargins(0, 0, 0, 0);right_layout2_->setSpacing(5);minimize_button_ = new QToolButton(title_bar_);minimize_button_->setObjectName(QString("objMinimizeButton32x32"));switch_button_ = new QToolButton(title_bar_);switch_button_->setObjectName(QString("objRestoreButton32x32"));close_button_ = new QToolButton(title_bar_);close_button_->setObjectName(QString("objCloseButton32x32"));right_layout2_->addWidget(minimize_button_);right_layout2_->addWidget(switch_button_);right_layout2_->addWidget(close_button_);right_layout1_->addLayout(right_layout2_);right_layout1_->addStretch();title_bar_layout_->addWidget(sys_icon_);title_bar_layout_->addStretch();title_bar_layout_->addWidget(title_);title_bar_layout_->addStretch();title_bar_layout_->addLayout(right_layout1_);title_bar_->setLayout(title_bar_layout_);content_widget_ = new QWidget(this);content_widget_->setObjectName("objContentWidget");status_bar_ = new QStatusBar(this);message_ = new QLabel(status_bar_);message_->setMinimumSize(message_->sizeHint());message_->setAlignment(Qt::AlignVCenter);status_bar_->addWidget(message_);layout_->addWidget(title_bar_);layout_->addWidget(content_widget_);layout_->addWidget(status_bar_);status_bar_->setMaximumHeight(30);//status_bar_->showMessage(QStringLiteral("123"));this->setLayout(layout_);}void BaseWidget::slotWindowStateChanged(bool max){if (max){switch_button_->setObjectName(QString("objRestoreButton24x24"));switch_button_->setStyle(switch_button_->style());}else{switch_button_->setObjectName(QString("objMaximizeButton24x24"));switch_button_->setStyle(switch_button_->style());}}void BaseWidget::slotMinimizeButtonClicked(){this->showMinimized();}void BaseWidget::slotMaximizeButtonClicked(){if (this->isMaximized()){this->showNormal();}else{this->showMaximized();}}void BaseWidget::slotCloseButtonClicked(){this->close();}

完整代码

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。