Qt無邊框窗口拖拽和陰影的實(shí)現(xiàn)
先看下效果:

說明
自定義窗口控件的無邊框,窗口事件由于沒有系統(tǒng)自帶邊框,無法實(shí)現(xiàn)拖拽拉伸等事件的處理,一種方法就是重新重寫主窗口的鼠標(biāo)事件,一種時(shí)通過nativeEvent事件處理。重寫事件相對(duì)繁瑣,我們這里推薦nativeEvent處理。注意后續(xù)我們?cè)谧鰓in平臺(tái)的進(jìn)程通信,也會(huì)用到它!
我們這里使用的是:nativeEvent
軟件用到的樣式表,這里就不展示了,大家可以自行調(diào)整!
關(guān)鍵點(diǎn)說明
QPainterPath
QPainterPath類提供一個(gè)容器,可以用來創(chuàng)建圖形并且重復(fù)使用。繪制器路徑是由許多圖形構(gòu)建基塊(如矩形、橢圓形、直線和曲線)組成的對(duì)象。構(gòu)建基塊可以連接在封閉的子路徑中,例如作為矩形或橢圓。封閉路徑具有重合的起點(diǎn)和終點(diǎn)。或者它們可以作為未閉合的子路徑獨(dú)立存在,例如直線和曲線。
抗鋸齒
抗鋸齒是一種常見的圖形處理技術(shù),用于減少在顯示器上呈現(xiàn)的圖像中出現(xiàn)的鋸齒狀邊緣。
抗鋸齒技術(shù)通過在邊緣周圍添加額外的像素來平滑邊緣,從而減少鋸齒狀邊緣。這種技術(shù)基于亞像素級(jí)別的渲染,它將顏色逐漸混合到邊緣像素的周圍像素中,使得邊緣更加平滑。
打開抗鋸齒可以使圖像更加平滑,尤其是在呈現(xiàn)銳利直線或曲線時(shí)。這種技術(shù)可以減少鋸齒狀邊緣,使得圖像更加清晰,更加真實(shí)。特別是在高分辨率屏幕上,抗鋸齒可以使得字體更加易讀,圖像更加細(xì)膩。
雖然抗鋸齒可以使圖像更加平滑,但在某些情況下,關(guān)閉抗鋸齒可能更加合適。關(guān)閉抗鋸齒可以提高圖像處理速度。
這里我們基于Qt繪圖框架用的是:
- setRenderHint(QPainter::Antialiasing, true); //打開抗鋸齒
- setRenderHint(QPainter::Antialiasing, false); //關(guān)閉抗鋸齒
具體實(shí)現(xiàn)
CDlgComBase,無邊框窗口,帶陰影,支持拖拽,注意:
- 該實(shí)現(xiàn)方案不支持存在多個(gè)顯示屏的情況!
- 該實(shí)現(xiàn)方案僅支持win平臺(tái)!
實(shí)現(xiàn)無邊框帶陰影的窗口代碼,下面的代碼供大家參考:
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; // 邊框?qū)挾?,單位:像?
int m_nShadowLen; // 陰影寬度,單位:像素
int m_nHeadBarHeight; // 標(biāo)題欄高度
bool m_bHasMaxFun;
bool m_bMaximized;
bool m_bNcLBtnClk;
bool m_bHideDlg;
QString m_strTitle; // 調(diào)試時(shí)使用
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("截獲關(guān)機(jī)指令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);
}
到此這篇關(guān)于Qt無邊框窗口拖拽和陰影的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt無邊框窗口拖拽和陰影內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++中默認(rèn)無參構(gòu)造函數(shù)的工作機(jī)制淺析
構(gòu)造函數(shù)主要作用在于創(chuàng)建對(duì)象時(shí)為對(duì)象的成員屬性賦值,構(gòu)造函數(shù)由編譯器自動(dòng)調(diào)用,無須手動(dòng)調(diào)用;析構(gòu)函數(shù)主要作用在于對(duì)象銷毀前系統(tǒng)自動(dòng)調(diào)用,執(zhí)行一些清理工作2023-02-02
Qt各種字符轉(zhuǎn)換的實(shí)現(xiàn)示例
本文主要介紹了Qt各種字符轉(zhuǎn)換的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05
C++?MiniZip實(shí)現(xiàn)目錄壓縮與解壓的示例詳解
Zlib是一個(gè)開源的數(shù)據(jù)壓縮庫,提供了一種通用的數(shù)據(jù)壓縮和解壓縮算法,本文主要為大家詳細(xì)介紹了如何利用Zlib實(shí)現(xiàn)目錄壓縮與解壓,需要的小伙伴可以參考下2023-11-11
C++語言實(shí)現(xiàn)線性表之鏈表實(shí)例
這篇文章主要介紹了C++語言實(shí)現(xiàn)線性表之鏈表,實(shí)例分析了C++實(shí)現(xiàn)線性表中鏈表的原理與相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
C語言實(shí)現(xiàn)學(xué)生籍貫信息記錄簿
這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)學(xué)生籍貫信息記錄簿,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06

