3.无边框窗口设计

3 无边框窗口设计

无边框窗口的基本实现

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMaximizeButtonHint);
}

Qt::FramelessWindowHint:这个标志表示窗口没有边框,也就是一个无边框窗口。这种窗口通常在用户进行拖动时不会受到窗口边框的限制。
Qt::WindowMaximizeButtonHint:这个标志表示窗口应该有一个最大化和最小化按钮。这两个按钮可以让窗口在全屏和最小化状态之间切换。
img
img

void MainWidget::mousePressEvent(QMouseEvent* event)
{
    mouse_pos = event->globalPos();//相对于桌面原点的位置
    window_pos = this->pos();//窗口左上角相对于桌面左上角
    diff_pos = mouse_pos - window_pos;//了鼠标位置与窗口位置之间的差异
}

void MainWidget::mouseMoveEvent(QMouseEvent* event)
{
    QPoint pos = event->globalPos();
    //this->move(pos);
    this->move(pos - diff_pos);
}

就是通过鼠标点击来确定鼠标位置与窗口左上角的差值,然后通过这个鼠标对于桌面左上角的距离减去这个差值

给无边窗窗口自定义标题并实现

无边窗口更好的解决办法

只要改写这个鼠标按下的函数就可以了,但是需要这个库#include <qt_windows.h>应该是要发送给windows告诉这个窗口的移动

void CTitleBar::mousePressEvent(QMouseEvent* event)
{
	if (ReleaseCapture())
	{
		QWidget* pWindow = this->window();
		if (pWindow->isTopLevel())
		{
			SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
		}
	}
}

窗口拉伸实现

bool CFrameLessWidgetBase::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
	MSG* param = static_cast<MSG*>(message);

	switch (param->message)
	{
	case WM_NCHITTEST:
	{
		int nX = GET_X_LPARAM(param->lParam) - this->geometry().x();
		int nY = GET_Y_LPARAM(param->lParam) - this->geometry().y();

		/*if (childAt(nX, nY) != nullptr)
			return QWidget::nativeEvent(eventType, message, result);*/
/*如果鼠标点击的位置在指定的边界宽度范围内(即大于边界宽度,小于窗口宽度减去边界宽度,并且大于边界宽度,小于窗口高度减去边界宽度),那么执行下一步判断。

在这个范围内,再判断是否存在子部件(childAt(nX, nY) != nullptr)。childAt函数会返回指定坐标位置下的最上层子部件,如果存在子部件,则返回的指针不为空。

如果以上两个条件都满足,表示鼠标点击的位置既在窗口边界内,又没有子部件存在,那么就返回QWidget::nativeEvent(eventType, message, result)来交给默认的处理方式。*/
		if (nX > m_nBorderWidth && nX < this->width() - m_nBorderWidth &&
			nY > m_nBorderWidth && nY < this->height() - m_nBorderWidth)
		{
			if (childAt(nX, nY) != nullptr)
				return QWidget::nativeEvent(eventType, message, result);
		}

		if ((nX > 0) && (nX < m_nBorderWidth))
			*result = HTLEFT;

		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width()))
			*result = HTRIGHT;

		if ((nY > 0) && (nY < m_nBorderWidth))
			*result = HTTOP;

		if ((nY > this->height() - m_nBorderWidth) && (nY < this->height()))
			*result = HTBOTTOM;

		if ((nX > 0) && (nX < m_nBorderWidth) && (nY > 0)
			&& (nY < m_nBorderWidth))
			*result = HTTOPLEFT;

		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width())
			&& (nY > 0) && (nY < m_nBorderWidth))
			*result = HTTOPRIGHT;

		if ((nX > 0) && (nX < m_nBorderWidth)
			&& (nY > this->height() - m_nBorderWidth) && (nY < this->height()))
			*result = HTBOTTOMLEFT;

		if ((nX > this->width() - m_nBorderWidth) && (nX < this->width())
			&& (nY > this->height() - m_nBorderWidth) && (nY < this->height()))
			*result = HTBOTTOMRIGHT;

		return true;
	}
	}

	return false;
}

这段代码是一个重写了QWidget的nativeEvent函数的实现。该函数用于处理原生窗口事件,以便对窗口进行一些自定义操作。

在这段代码中,主要是处理了WM_NCHITTEST消息,该消息用于确定鼠标点击位置的窗口部件。在函数中,首先通过param->lParam获取鼠标点击位置的坐标(相对于窗口),然后通过几个条件判断来确定鼠标点击位置属于哪个部件。

首先,判断如果鼠标点击位置在指定的边界宽度范围内,并且该位置处存在子部件,那么将该事件交给默认的处理方式。这样可以确保鼠标点击在子部件上时,能够正常处理子部件的事件。

接下来,根据鼠标点击位置的x和y坐标与窗口的边界宽度进行比较,来确定鼠标点击的位置是在窗口的哪个边界上。根据不同的位置设置不同的鼠标光标类型,例如HTLEFT表示左边界,HTRIGHT表示右边界,HTTOP表示上边界,HTBOTTOM表示下边界,HTTOPLEFT表示左上角,HTTOPRIGHT表示右上角,HTBOTTOMLEFT表示左下角,HTBOTTOMRIGHT表示右下角。

最后,将结果存储在result指针中,并返回true表示该事件已经处理完毕。

总的来说,这段代码是为了实现一个无边框窗口,并对窗口的各个边界进行特殊处理,以便实现改变窗口大小和拖动窗口等功能。

setAttribute(Qt::WA_Hover)

是在使用Qt框架开发窗口应用程序时,可以通过设置该属性来启用鼠标悬停事件。

鼠标悬停事件指的是当鼠标光标进入或离开窗口区域时触发的事件。启用了鼠标悬停事件后,当鼠标光标进入窗口时,你可以通过编写代码来实现一些特殊效果,比如显示提示信息、改变窗口的外观等。

举个例子,你可能在一个按钮上设置了鼠标悬停事件,当鼠标光标移动到按钮上时,按钮会产生一些变化,比如颜色变化、边框高亮、文字变大等,以提醒用户该按钮可以被点击。
以上是gpt解释
img

因为这个自定义窗口使用的是自己的写的窗口去做的标题和最大小化按钮,所以在鼠标放在操作时很容易误识别,按着自己写的窗口要进行移动,而在边缘时要拉伸,如果不加,因为这个自己写的窗口和主窗口上面重合,所以导致在边缘不能拉伸了,单上面的代码注释解决了这一块的问题,就是相当于把里面的控件大小表面没缩小,实际面积缩小了

设计一个无边窗窗口的公共类

放在gitee上,需要留言

关闭 最小化 最大化

img

//三个按钮都用的同一个槽函数
void CTitleBar::onClicked()
{
    //方案二的体现
	
	 QPushButton* pButton = qobject_cast<QPushButton*>(sender());//返回点击按钮控件的变量名

	 QWidget* pWindow = this->window();//这是QWidget类的一个成员函数。当你在一个QWidget的子类中调用这个函数时,它会返回该部件的父窗口的指针。

	 if (pButton == m_pMinBtn)
	 {
		 pWindow->showMinimized();
	 }
	 else if (pButton == m_pMaxBtn)
	 {
		 if (pWindow->isMaximized())
		 {
			 pWindow->showNormal();
			 m_pMaxBtn->setStyleSheet("QPushButton{background-image:url(:/LessWidgetPro/resources/titlebar/normal.svg);border:none}" \
				 "QPushButton:hover{" \
				 "background-color:rgb(99, 99, 99);" \
				 "background-image:url(:/LessWidgetPro/resources/titlebar/normal_hover.svg);border:none;}");
		 }
		 else
		 {
			 pWindow->showMaximized();
			 m_pMaxBtn->setStyleSheet("QPushButton{background-image:url(:/LessWidgetPro/resources/titlebar/max.svg);border:none}" \
				 "QPushButton:hover{" \
				 "background-color:rgb(99, 99, 99);" \
				 "background-image:url(:/LessWidgetPro/resources/titlebar/max_hover.svg);border:none;}");
		 }
	 }
	 else if (pButton == m_pCloseBtn)
	 {
		//方案一的体现,子窗口点击关闭,发送给主窗口关闭信号
		 emit sig_close();
	 }
}

阴影设计

img
img
setOffset就是使阴影朝向坐标系哪边,像这个就是阴影朝向右下,都是以左上角(0,0)为起点
img

圆角窗口

重写paintevent方法

img

void MainWidget::paintEvent(QPaintEvent* event)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);  // 反锯齿;
    painter.setBrush(QBrush(QColor(255, 255, 255)));
    painter.setPen(Qt::transparent);
    QRect rect = this->rect();
    painter.drawRoundedRect(rect, 15, 15);  //设置窗口圆角 15px
}

qss样式表改变

样式表能改变的更多不局限,比如你只想下面两个角变圆,样式表更容易做一些

MainWidget::MainWidget(QWidget *parent)
    : QWidget(parent)
{
    setAttribute(Qt::WA_TranslucentBackground);  //设置窗口背景透明
    setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint);  //去掉窗口边框

    //this->setStyleSheet("QWidget{background-color:#FFFFFF;border-radius:30px;}");
    this->setStyleSheet("QWidget{background-color:#FFFFFF;  \
        border-top-left-radius:15px;   \
        border-bottom-right-radius:15px; \
        }");
}

void MainWidget::paintEvent(QPaintEvent*)
{
    QStyleOption opt;
    opt.init(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
}
posted @   NoAcalculia  阅读(170)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程
点击右上角即可分享
微信分享提示