转载:Qt之界面(自定义标题栏、无边框、可移动、缩放)_我不是萧海哇~~~~的博客-优快云博客
参考了两篇文章处理,还有半屏没处理,就是左右边框的时候半屏。
重点:
1.拖动需要重载标题栏的 mousePressEvent方法。
2.边框拉伸的是采用的window的消息通信nativeEvent。需要注意的是这里面windows鼠标点获取的点和qt中获取的鼠标点位置不同,这里涉及到分辨下等问题,需要做转换处理,上面的文章没处理。
3.还有设置了自定义标题栏,需要对边框四周留缝隙,特别是顶部位置,就是在设置布局的时候,要给顶部留空隙,这时候边框拖动才能捕捉到。
效果:
代码:
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QMouseEvent>
#include <QLabel>
#include <QPushButton>
#include <QPoint>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
bool nativeEvent(const QByteArray &eventType, void *message, qintptr *result);
private:
Ui::Widget *ui;
int m_nBorderWidth; //m_nBorder表示鼠标位于边框缩放范围的宽度
};
class TitleBar : public QWidget
{
Q_OBJECT
public:
explicit TitleBar(QWidget *parent = 0);
protected:
// 双击标题栏进行界面的最大化/还原
virtual void mouseDoubleClickEvent(QMouseEvent *event);
protected:
virtual void mousePressEvent(QMouseEvent *event);
signals:
public slots:
//the slots function of min\max\close bt
void onChangeClicked();
private:
QLabel *m_luLabel; //left-upper label
QLabel *m_midLabel; //mid title label
QPushButton *m_minBt; //min bt
QPushButton *m_maxBt; //max bt
QPushButton *m_closeBt; //close bt
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include "titlebar.h"
#include <QVBoxLayout>
#include <QPalette>
#include <QWindow>
//调用WIN API需要用到的头文件与库 [实现缩放]
#ifdef Q_OS_WIN
#include <qt_windows.h>
#include <Windowsx.h>
#endif
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setMouseTracking(true);
//Qt::FramelessWindowHint设置窗口标志为无边框,而Qt::WindowStaysOnTopHint使窗口位于所有界面之上
this->setWindowFlags(Qt::FramelessWindowHint);
//定义自定义标题栏对象
TitleBar *pTitleBar = new TitleBar();
pTitleBar->setParent(this);
installEventFilter(pTitleBar);
//使用调色板设置窗口的背景色
QPalette pal(palette());
pal.setColor(QPalette::Window, QColor(0, 255, 255));
setAutoFillBackground(true);
setPalette(pal);
//窗口布局中加入标题栏
QVBoxLayout *pLayout = new QVBoxLayout();
pLayout->addWidget(pTitleBar);
pLayout->addStretch();
pLayout->setSpacing(0);
//自定义标题的时候需要留一些边框空隙给拉伸的时候使用
pLayout->setContentsMargins(5, 5, 5, 5);
setLayout(pLayout);
//m_nBorder表示鼠标位于边框缩放范围的宽度,可以设置为5
m_nBorderWidth=5;
}
Widget::~Widget()
{
delete ui;
}
//nativeEvent主要用于进程间通信-消息传递,使用这种方式后来实现窗体的缩放 [加上了这函数,窗口也能移动了]
bool Widget::nativeEvent(const QByteArray &eventType, void *message, qintptr *result)
{
Q_UNUSED(eventType)
MSG *param = static_cast<MSG *>(message);
switch (param->message)
{
case WM_NCHITTEST:
{
//由于windows的分辨率不一样,需要我们做转化
int xPt = GET_X_LPARAM(param->lParam);
int yPt = GET_Y_LPARAM(param->lParam);
QPoint gpos(xPt,yPt);
QWindow * handle = this->window()->windowHandle();
if(handle && handle->screen())
{
QScreen * screen = handle->screen();
QPoint offset = screen->geometry().topLeft();
gpos = (gpos - offset) / screen->devicePixelRatio() + offset;
}
int nX = gpos.x() - this->geometry().x();
int nY = gpos.y()- this->geometry().y();
//如果是子控件
if (childAt(nX, nY) != nullptr)
return QWidget::nativeEvent(eventType, message, result);
*result = HTCAPTION;//在标题栏 2
int width=this->width();
int height=this->height();
// 鼠标区域位于窗体边框,进行缩放
if ((nX > 0) && (nX < m_nBorderWidth))
*result = HTLEFT;//在窗口的左边框 10
if ((nX > (width - m_nBorderWidth)) && (nX < width))
*result = HTRIGHT;//在窗口的右边框 11
if ((nY > 0) && (nY < m_nBorderWidth))
*result = HTTOP;//在窗口框的上面的水平线上 12
if ((nY > (height - m_nBorderWidth)) && (nY < height))
*result = HTBOTTOM;//窗口框的下面的水平线上 15
if ((nX > 0) && (nX < m_nBorderWidth) && (nY > 0)
&& (nY < m_nBorderWidth))
*result = HTTOPLEFT;//在窗口框的左上角 13
if ((nX > (width - m_nBorderWidth)) && (nX < width)
&& (nY > 0) && (nY < m_nBorderWidth))
*result = HTTOPRIGHT;//在窗口框的右上角 14
if ((nX > 0) && (nX < m_nBorderWidth)
&& (nY > (height - m_nBorderWidth)) && (nY < height))
*result = HTBOTTOMLEFT;//在窗口框的左下角 16
if ((nX > (width - m_nBorderWidth)) && (nX < width)
&& (nY > (height - m_nBorderWidth)) && (nY < height))
*result = HTBOTTOMRIGHT;//在窗口框的右下角 17
return true;
}
}
return QWidget::nativeEvent(eventType, message, result);
}
TitleBar::TitleBar(QWidget *parent)
: QWidget{parent}
{
m_luLabel = new QLabel;
m_midLabel = new QLabel;
m_minBt = new QPushButton;
m_maxBt = new QPushButton;
m_closeBt = new QPushButton;
//set size
m_luLabel->setFixedSize(20, 20);
m_luLabel->setScaledContents(true);
//save window default layout
m_midLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
m_midLabel->setFixedHeight(32);
m_minBt->setFixedSize(20, 20);
m_maxBt->setFixedSize(20, 20);
m_closeBt->setFixedSize(20, 20);
//detele button frame
m_minBt->setFlat(true);
m_maxBt->setFlat(true);
m_closeBt->setFlat(true);
//set icon":/new/prefix1/rc/minimize.png"
m_minBt->setIcon(QIcon(":/new/prefix1/rc/minimize.png"));
m_minBt->setIconSize(m_minBt->size());
m_maxBt->setIcon(QIcon(":/new/prefix1/rc/maximize.png"));
m_maxBt->setIconSize(m_maxBt->size());
m_closeBt->setIcon(QIcon(":/new/prefix1/rc/close-small.png"));
m_closeBt->setIconSize(m_closeBt->size());
QPixmap icon(":/new/prefix1/rc/avatar.png");
m_luLabel->setPixmap(icon);
//set name
m_midLabel->setObjectName("titleLabel");
m_minBt->setObjectName("minimizeButton");
m_maxBt->setObjectName("maximizeButton");
m_closeBt->setObjectName("closeButton");
//set label text
m_midLabel->setText("这里是项目名称字段");
//prompt name when Mouse over the button
m_minBt->setToolTip("最小化");
m_maxBt->setToolTip("放大/缩小");
m_closeBt->setToolTip("关闭");
QHBoxLayout *pLayout = new QHBoxLayout(this);
pLayout->addWidget(m_luLabel);
pLayout->addWidget(m_midLabel,0,Qt::AlignCenter);
pLayout->addWidget(m_minBt);
pLayout->addWidget(m_maxBt);
pLayout->addWidget(m_closeBt);
pLayout->setSpacing(10);//set distance between controls
pLayout->setContentsMargins(0, 0, 0, 0);//set Surrounding margin
this->setLayout(pLayout);
connect(m_minBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
connect(m_maxBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
connect(m_closeBt,SIGNAL(clicked()),this,SLOT(onChangeClicked()));
}
void TitleBar::onChangeClicked()
{
//if called by slot,return pointer,else nullptr
QPushButton *pBtn = qobject_cast<QPushButton *>(sender());
QWidget *pWindow = this->window();
if (!pBtn||!pWindow->isWindow())
{
return;
}
if (pBtn == m_minBt)
{
pWindow->showMinimized();//min window。
}
else if (pBtn == m_maxBt)
{
pWindow->isMaximized() ? pWindow->showNormal() : pWindow->showMaximized();//scaled window
}
else if (pBtn == m_closeBt)
{
pWindow->close();//close window
}
}
void TitleBar::mouseDoubleClickEvent(QMouseEvent *event)
{
Q_UNUSED(event);
emit m_maxBt->clicked();
}
void TitleBar::mousePressEvent(QMouseEvent *event)
{
if (ReleaseCapture())
{
QWidget *pWindow = this->window();
//if mouse press is top window
if (pWindow->isTopLevel())
{
SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
}
}
event->ignore();
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}