先看下效果:
说明
自定义窗口控件的无边框,窗口事件由于没有系统自带边框,无法实现拖拽拉伸等事件的处理,一种方法就是重新重写主窗口的鼠标事件,一种时通过nativeEvent事件处理。重写事件相对繁琐,我们这里推荐nativeEvent处理。注意后续我们在做win平台的进程通信,也会用到它!
- 我们这里使用的是:nativeEvent
软件用到的样式表,这里就不展示了,大家可以自行调整!
关键点说明
QPainterPath
QPainterPath类提供一个容器,可以用来创建图形并且重复使用。绘制器路径是由许多图形构建基块(如矩形、椭圆形、直线和曲线)组成的对象。构建基块可以连接在封闭的子路径中,例如作为矩形或椭圆。封闭路径具有重合的起点和终点。或者它们可以作为未闭合的子路径独立存在,例如直线和曲线。
抗锯齿
- 抗锯齿是一种常见的图形处理技术,用于减少在显示器上呈现的图像中出现的锯齿状边缘。
抗锯齿技术通过在边缘周围添加额外的像素来平滑边缘,从而减少锯齿状边缘。这种技术基于亚像素级别的渲染,它将颜色逐渐混合到边缘像素的周围像素中,使得边缘更加平滑。 - 打开抗锯齿可以使图像更加平滑,尤其是在呈现锐利直线或曲线时。这种技术可以减少锯齿状边缘,使得图像更加清晰,更加真实。特别是在高分辨率屏幕上,抗锯齿可以使得字体更加易读,图像更加细腻。
- 虽然抗锯齿可以使图像更加平滑,但在某些情况下,关闭抗锯齿可能更加合适。关闭抗锯齿可以提高图像处理速度。
- 这里我们基于Qt绘图框架用的是:
- setRenderHint(QPainter::Antialiasing, true); //打开抗锯齿
- setRenderHint(QPainter::Antialiasing, false); //关闭抗锯齿
具体实现
CDlgComBase,无边框窗口,带阴影,支持拖拽,注意:
- 该实现方案不支持存在多个显示屏的情况!
- 该实现方案仅支持win平台!
实现无边框带阴影的窗口代码,下面的代码供大家参考:
DlgComBase.h
cpp
#pragma once
#include "DlgShadow.h"
#include "FrameComTitleBar.h"
#include <QVBoxLayout>
class CDlgComBase : public CDlgShadow
{
Q_OBJECT
public:
CDlgComBase(QWidget *parent = 0, bool bCenterDlg = true, bool bHasTitleBar = true);
~CDlgComBase();
void SetWindowsTitle(const QString& strTitle, bool bCheckPos = false);
// 显示隐藏按钮
void ShowMinBtn(bool bShow);
void ShowMaxBtn(bool bShow);
void ShowCloseBtn(bool bShow);
void ShowSettingBtn(bool bShow);
void ShowMaximized();
void SetTitleBarObjectName(QString strObjectName);
void SetHeadBarHeight(int nHeight);
protected:
virtual bool IsCaption(int nXPos, int nYPos);
QWidget* GetCenterWidget() { return &m_frameCenter; }
virtual void OnNcLBtnDbClick(int nXPos, int nYPos);
protected slots:
void OnTimerCenter();
private:
CFrameComTitleBar m_frameComTitleBar;
QVBoxLayout m_vBoxLayout;
QFrame m_frameCenter;
bool m_bHasTitleBar;
};
DlgComBase.cpp
cpp
#include "DlgComBase.h"
#include <QTimer>
CDlgComBase::CDlgComBase(QWidget *parent, bool bCenterDlg, bool bHasTitleBar)
: CDlgShadow(parent), m_frameComTitleBar(this), m_frameCenter(this), m_bHasTitleBar(bHasTitleBar)
{
m_frameComTitleBar.setObjectName("framComTitleBar");
m_frameComTitleBar.setFixedHeight(GetHeadBarHeight());
int nShadowLen = GetShadowLen();
m_vBoxLayout.setContentsMargins(nShadowLen, nShadowLen, nShadowLen, nShadowLen);
m_vBoxLayout.setSpacing(0);
if (m_bHasTitleBar)
{
m_vBoxLayout.addWidget(&m_frameComTitleBar);
}
m_vBoxLayout.addWidget(&m_frameCenter);
m_frameCenter.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setLayout(&m_vBoxLayout);
if (bCenterDlg)
QTimer::singleShot(10, this, SLOT(OnTimerCenter()));
}
CDlgComBase::~CDlgComBase()
{
}
void CDlgComBase::SetWindowsTitle(const QString& strTitle, bool bCheckPos)
{
m_strTitle = strTitle;
m_frameComTitleBar.SetWindowsTitle(strTitle, bCheckPos);
setWindowTitle(strTitle);
}
void CDlgComBase::ShowMinBtn(bool bShow)
{
m_frameComTitleBar.ShowMinBtn(bShow);
}
void CDlgComBase::ShowMaxBtn(bool bShow)
{
SetHasMaxFun(bShow);
m_frameComTitleBar.ShowMaxBtn(bShow);
}
void CDlgComBase::ShowCloseBtn(bool bShow)
{
m_frameComTitleBar.ShowCloseBtn(bShow);
}
void CDlgComBase::ShowSettingBtn(bool bShow)
{
m_frameComTitleBar.ShowSettingBtn(bShow);
}
bool CDlgComBase::IsCaption(int nXPos, int nYPos)
{
QWidget* pChild = childAt(nXPos, nYPos);
if (pChild == NULL)
{
ADD_LOGD("CDlgComBase::IsCaption() return true");
return true;
}
if (pChild == &m_frameComTitleBar || pChild == m_frameComTitleBar.GetTitleLabel())
{
ADD_LOGD("CDlgComBase::IsCaption() return true");
return true;
}
ADD_LOGD("CDlgComBase::IsCaption() return false");
return false;
}
void CDlgComBase::SetTitleBarObjectName(QString strObjectName)
{
m_frameComTitleBar.setObjectName(strObjectName);
}
void CDlgComBase::OnTimerCenter()
{
CenterInParent((QWidget*)parent());
}
void CDlgComBase::SetHeadBarHeight(int nHeight)
{
m_frameComTitleBar.setFixedHeight(nHeight);
CDlgShadow::SetHeadBarHeight(nHeight);
}
void CDlgComBase::ShowMaximized()
{
m_frameComTitleBar.ShowMaximized();
CDlgShadow::ShowMaximized();
}
void CDlgComBase::OnNcLBtnDbClick(int nXPos, int nYPos)
{
if (m_bHasMaxFun)
m_frameComTitleBar.ShowMaxRestoreBtn(m_bMaximized);
CDlgShadow::OnNcLBtnDbClick(nXPos, nYPos);
}
DlgShadow.h
cpp
#ifndef SHADOWDLG_H
#define SHADOWDLG_H
#include <QDialog>
#include <QMouseEvent>
class CDlgShadow : public QDialog
{
Q_OBJECT
public:
CDlgShadow(QWidget *parent = 0);
~CDlgShadow();
void HideDlg();
void ShowDlg();
void SetDlgBkColor(QColor& clrDlgBk);
void CenterInParent(QWidget* pWidget);
void SetResizeable(bool bOn) { m_bResizeable = bOn; }
virtual void OnBtnSettingClicked(QPoint& ptBtnBottom);
virtual void OnBtnMinClicked();
virtual void OnBtnMaxClicked();
virtual void OnBtnRestoreClicked();
virtual void OnBtnCloseClicked();
virtual bool OnProHotKey(int nFsModifiers, int nVk);
virtual void OnMsgEndSession();
void ShowMaximized();
protected:
void paintEvent(QPaintEvent* event);
void keyPressEvent(QKeyEvent* event);
int GetShadowLen() { return m_nShadowLen; }
int GetHeadBarHeight() { return m_nHeadBarHeight; }
void SetHeadBarHeight(int nHeight);
void SetHasMaxFun(bool bHasMaxFun) { m_bHasMaxFun = bHasMaxFun; }
bool nativeEvent(const QByteArray& eventType, void* pMessage, long* pResult);
virtual bool IsCaption(int nXPos, int nYPos);
virtual void OnNcLBtnDbClick(int nXPos, int nYPos);
virtual void OnKeyReturnPress();
virtual void OnKeyEscapePress();
virtual void OnNcLBtnClick();
void closeEvent(QCloseEvent *event);
protected:
int m_nFrameLen; // 边框宽度,单位:像素
int m_nShadowLen; // 阴影宽度,单位:像素
int m_nHeadBarHeight; // 标题栏高度
bool m_bHasMaxFun;
bool m_bMaximized;
bool m_bNcLBtnClk;
bool m_bHideDlg;
QString m_strTitle; // 调试时使用
bool m_bHotKey; // 处理快捷键功能
private:
QRect m_rectDlg;
QColor m_clrDlgBk;
bool m_bResizeable;
};
#endif // SHADOWDLG_H
DlgShadow.cpp
cpp
#include "DlgShadow.h"
#include <QPainter>
#include <qmath.h>
#include <QApplication>
#include <QDesktopWidget>
#include <Windows.h>
CDlgShadow::CDlgShadow(QWidget *parent)
: QDialog(parent)
{
setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog | Qt::WindowMinimizeButtonHint);
setAttribute(Qt::WA_TranslucentBackground);
m_nFrameLen = 10;
m_nShadowLen = 6;
m_nHeadBarHeight = 36;
m_bMaximized = false;
m_bHasMaxFun = true;
m_clrDlgBk = QColor(255, 255, 255);
m_bResizeable = true;
m_bNcLBtnClk = false;
m_bHideDlg = false;
m_bHotKey = false;
}
CDlgShadow::~CDlgShadow()
{
}
void CDlgShadow::paintEvent(QPaintEvent* event)
{
QPainterPath path;
path.setFillRule(Qt::WindingFill);
path.addRoundedRect(m_nShadowLen, m_nShadowLen, width() - 2 * m_nShadowLen, height() - 2 * m_nShadowLen, 2, 2);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.fillPath(path, QBrush(m_clrDlgBk));
QColor color(0, 0, 0, 50);
for (int i = 0; i < m_nShadowLen; i++)
{
QPainterPath pathShadow;
pathShadow.setFillRule(Qt::WindingFill);
pathShadow.addRoundedRect(m_nShadowLen - i, m_nShadowLen - i, width() - (m_nShadowLen - i) * 2, height() - (m_nShadowLen - i) * 2, 2 + i, 2 + i);
int nAlpha = 50 - qSqrt(i) * 25;
if (nAlpha < 0)
nAlpha = 0;
color.setAlpha(nAlpha);
painter.setPen(color);
painter.drawPath(pathShadow);
}
painter.setRenderHint(QPainter::Antialiasing, false);
painter.fillPath(path, QBrush(m_clrDlgBk));
QDialog::paintEvent(event);
}
void CDlgShadow::OnBtnMinClicked()
{
showMinimized();
}
void CDlgShadow::OnBtnMaxClicked()
{
m_bMaximized = true;
m_rectDlg = geometry();
setGeometry(-m_nShadowLen, -m_nShadowLen, QApplication::desktop()->availableGeometry().width() + m_nShadowLen * 2,
QApplication::desktop()->availableGeometry().height() + m_nShadowLen * 2);
}
void CDlgShadow::OnBtnRestoreClicked()
{
m_bMaximized = false;
setFixedHeight(QWIDGETSIZE_MAX);
setGeometry(m_rectDlg);
}
void CDlgShadow::SetDlgBkColor(QColor& clrDlgBk)
{
m_clrDlgBk = clrDlgBk;
}
void CDlgShadow::SetHeadBarHeight(int nHeight)
{
m_nHeadBarHeight = nHeight;
}
bool CDlgShadow::IsCaption(int nXPos, int nYPos)
{
if (childAt(nXPos, nYPos) == 0)
{
ADD_LOGD("CDlgShadow::IsCaption() return true");
return true;
}
else
{
ADD_LOGD("CDlgShadow::IsCaption() return false");
return false;
}
}
bool CDlgShadow::nativeEvent(const QByteArray& eventType, void* pMessage, long* pResult)
{
ADD_LOGD(QString("CDlgShadow::nativeEvent in"));
if (m_bHideDlg)
{
ADD_LOGD(QString("CDlgShadow::nativeEvent out"));
return QDialog::nativeEvent(eventType, pMessage, pResult);
}
const MSG* pMsg = static_cast<MSG*>(pMessage);
if (pMsg->message == WM_NCHITTEST)
{
RECT rect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
int nWin32Width = rect.right - rect.left;
int nWin32Height = rect.bottom - rect.top;
int nQtWidth = QApplication::desktop()->availableGeometry().width();
int nQtHeight = QApplication::desktop()->availableGeometry().height();
int nMsgX = ((int)(short)LOWORD(pMsg->lParam)) * nQtWidth / nWin32Width;
int nMsgY = ((int)(short)HIWORD(pMsg->lParam)) * nQtHeight / nWin32Height;
int xPos = nMsgX - frameGeometry().x();
int yPos = nMsgY - frameGeometry().y();
if (IsCaption(xPos, yPos))
{
*pResult = HTCAPTION;
}
else
{
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_NCHITTEST pResult:%1").arg(*pResult));
return false;
}
if (!m_bResizeable)
{
if (*pResult == HTCAPTION)
{
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_NCHITTEST pResult:%1").arg(*pResult));
return true;
}
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_NCHITTEST pResult:%1").arg(*pResult));
return QDialog::nativeEvent(eventType, pMessage, pResult);
}
if (xPos > 0 && xPos < m_nFrameLen)
*pResult = HTLEFT;
if (xPos >(width() - m_nFrameLen) && xPos < (width() - 0))
*pResult = HTRIGHT;
if (yPos > 0 && yPos < m_nFrameLen)
*pResult = HTTOP;
if (yPos >(height() - m_nFrameLen) && yPos < (height() - 0))
*pResult = HTBOTTOM;
if (xPos > 0 && xPos < m_nFrameLen && yPos > 0 && yPos < m_nFrameLen)
*pResult = HTTOPLEFT;
if (xPos >(width() - m_nFrameLen) && xPos < (width() - 0) && yPos > 0 && yPos < m_nFrameLen)
*pResult = HTTOPRIGHT;
if (xPos > 0 && xPos < m_nFrameLen && yPos >(height() - m_nFrameLen) && yPos < (height() - 0))
*pResult = HTBOTTOMLEFT;
if (xPos >(width() - m_nFrameLen) && xPos < (width() - 0) && yPos >(height() - m_nFrameLen) && yPos < (height() - 0))
*pResult = HTBOTTOMRIGHT;
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_NCHITTEST pResult:%1").arg(*pResult));
return true;
}
else if (pMsg->message == WM_NCLBUTTONDBLCLK)
{
int xPos = ((int)(short)LOWORD(pMsg->lParam)) - frameGeometry().x();
int yPos = ((int)(short)HIWORD(pMsg->lParam)) - frameGeometry().y();
OnNcLBtnDbClick(xPos, yPos);
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_NCLBUTTONDBLCLK"));
return true;
}
else if (pMsg->message == WM_NCLBUTTONDOWN)
{
if (m_bNcLBtnClk)
{
OnNcLBtnClick();
}
}
else if (pMsg->message == WM_HOTKEY)
{
if (m_bHotKey)
{
UINT nFuModifiers = (UINT)LOWORD(pMsg->lParam); // 模式
UINT nVirtKey = (UINT)HIWORD(pMsg->lParam); // 键值
if (OnProHotKey(nFuModifiers, nVirtKey))
{
ADD_LOGD(QString("CDlgShadow::nativeEvent out, WM_HOTKEY"));
return true;
}
}
}
else if (pMsg->message == WM_ENDSESSION)
{
ADD_LOGD(QStringLiteral("截获关机指令1"));
OnMsgEndSession();
}
ADD_LOGD(QString("CDlgShadow::nativeEvent out"));
return QDialog::nativeEvent(eventType, pMessage, pResult);
}
void CDlgShadow::OnNcLBtnDbClick(int nXPos, int nYPos)
{
if (!m_bHasMaxFun)
return;
if (nYPos > m_nFrameLen + m_nHeadBarHeight)
return;
if (m_bMaximized)
{
OnBtnRestoreClicked();
}
else
{
OnBtnMaxClicked();
}
}
void CDlgShadow::CenterInParent(QWidget* pWidget)
{
int nXPos = 0;
int nYPos = 0;
if (pWidget == NULL)
{
nXPos = (QApplication::desktop()->width() - width()) / 2;
nYPos = (QApplication::desktop()->height() - height()) / 2;
}
else
{
QWidget* pParent = (QWidget*)pWidget->parent();
// if (pParent != NULL)
// {
// //QPoint ptGloba = pWidget->mapToGlobal(QPoint(0, 0));
// nXPos = /*ptGloba.x() + */(pWidget->width() - width()) / 2;
// nYPos = /*ptGloba.y() + */(pWidget->height() - height()) / 2;
// }
// else
{
QPoint ptGloba = pWidget->mapToGlobal(QPoint(0, 0));
nXPos = ptGloba.x() + (pWidget->width() - width()) / 2;
nYPos = ptGloba.y() + (pWidget->height() - height()) / 2;
}
}
move(nXPos, nYPos);
}
void CDlgShadow::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return/* || event->key() == Qt::Key_Space*/)
{
OnKeyReturnPress();
event->accept();
}
else if (event->key() == Qt::Key_Escape)
{
OnKeyEscapePress();
event->ignore();
}
}
void CDlgShadow::OnKeyReturnPress()
{
//accept();
}
void CDlgShadow::OnKeyEscapePress()
{
//reject();
}
void CDlgShadow::OnBtnCloseClicked()
{
reject();
}
void CDlgShadow::OnBtnSettingClicked(QPoint& ptBtnBottom)
{
}
void CDlgShadow::OnNcLBtnClick()
{
}
void CDlgShadow::HideDlg()
{
m_bHideDlg = true;
setWindowOpacity(0);
}
void CDlgShadow::ShowDlg()
{
setWindowOpacity(1);
m_bHideDlg = false;
}
void CDlgShadow::closeEvent(QCloseEvent *event)
{
event->ignore();
OnBtnCloseClicked();
}
bool CDlgShadow::OnProHotKey(int nFsModifiers, int nVk)
{
return false;
}
void CDlgShadow::OnMsgEndSession()
{
}
void CDlgShadow::ShowMaximized()
{
m_bMaximized = true;
int nXPos = (QApplication::desktop()->availableGeometry().width() - (1273 + 11)) / 2;
int nYPos = (QApplication::desktop()->availableGeometry().height() - (878 + 11)) / 2;
int nMaxHeight = QApplication::desktop()->availableGeometry().height() + m_nShadowLen * 2;
//setFixedHeight(nMaxHeight);
setFixedHeight(QWIDGETSIZE_MAX);
m_rectDlg = QRect(nXPos, nYPos, (1273 + 11), (878 + 11));
setGeometry(-m_nShadowLen, -m_nShadowLen, QApplication::desktop()->availableGeometry().width() + m_nShadowLen * 2,
QApplication::desktop()->availableGeometry().height() + m_nShadowLen * 2);
}