Qt无边框窗口拖拽和阴影的实现
作者:_S_Q
先看下效果:
说明
自定义窗口控件的无边框,窗口事件由于没有系统自带边框,无法实现拖拽拉伸等事件的处理,一种方法就是重新重写主窗口的鼠标事件,一种时通过nativeEvent事件处理。重写事件相对繁琐,我们这里推荐nativeEvent处理。注意后续我们在做win平台的进程通信,也会用到它!
我们这里使用的是:nativeEvent
软件用到的样式表,这里就不展示了,大家可以自行调整!
关键点说明
QPainterPath
QPainterPath类提供一个容器,可以用来创建图形并且重复使用。绘制器路径是由许多图形构建基块(如矩形、椭圆形、直线和曲线)组成的对象。构建基块可以连接在封闭的子路径中,例如作为矩形或椭圆。封闭路径具有重合的起点和终点。或者它们可以作为未闭合的子路径独立存在,例如直线和曲线。
抗锯齿
抗锯齿是一种常见的图形处理技术,用于减少在显示器上呈现的图像中出现的锯齿状边缘。
抗锯齿技术通过在边缘周围添加额外的像素来平滑边缘,从而减少锯齿状边缘。这种技术基于亚像素级别的渲染,它将颜色逐渐混合到边缘像素的周围像素中,使得边缘更加平滑。
打开抗锯齿可以使图像更加平滑,尤其是在呈现锐利直线或曲线时。这种技术可以减少锯齿状边缘,使得图像更加清晰,更加真实。特别是在高分辨率屏幕上,抗锯齿可以使得字体更加易读,图像更加细腻。
虽然抗锯齿可以使图像更加平滑,但在某些情况下,关闭抗锯齿可能更加合适。关闭抗锯齿可以提高图像处理速度。
这里我们基于Qt绘图框架用的是:
- setRenderHint(QPainter::Antialiasing, true); //打开抗锯齿
- setRenderHint(QPainter::Antialiasing, false); //关闭抗锯齿
具体实现
CDlgComBase,无边框窗口,带阴影,支持拖拽,注意:
- 该实现方案不支持存在多个显示屏的情况!
- 该实现方案仅支持win平台!
实现无边框带阴影的窗口代码,下面的代码供大家参考:
DlgComBase.h
#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
#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
#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
#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); }
到此这篇关于Qt无边框窗口拖拽和阴影的实现的文章就介绍到这了,更多相关Qt无边框窗口拖拽和阴影内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!