QT自定义窗口标题栏(自定义最大化和还原功能,无边框无布局,附带标题栏拖动和双击效果)

网上找了很多资料,但是都没有想要的,基本上都是要布局,然后用this->showFullScreen。但是我已经做了很多控件,现在再来布局根本不太现实,并且布局后的位置不可以设置到自己想要的位置。

然后我参照了一些别人写的resize的,但别人是有边框的,然后重写resizeEvent。另外就是字体大小无法更改。今天,我做的这个就完全是不需要布局,不需要重写resizeEvent函数,可以更改字体大小。

1.先设置窗口无边框,设置无边框会隐藏掉QT窗口自带的标题栏,在ui界面拖出一个widget作为标题栏,加入三个按钮分别为最小化,最大化,关闭(没有图片,暂时就这么丑吧)

2.在ui界面右键转到click函数,最小化和关闭比较简单

利用QT功能自带的showMinimized()和close()函数即可。

3.重点是最大化和还原功能(这里我遇到很多问题,希望其中的思路对你有帮助)

思路:全局定义一个bool变量来判断当前是否为最大化的状态,默认为初始状态。我们打开软件是希望默认打开我们已经设定好的尺寸,但是我们在开发的时候使用的分辨率和用户使用的分辨率就未必相同,这样可能会导致没有布局的界面显示不全。因此我们也需要适应当前屏幕的分辨率。定义变量来判断是否已经适应了当前的分辨率。另外,还需要有一个bool来判断是否已经适应了分辨率。

如何适应分辨率,在网上找的代码也不太符合效果,就自己学习他们的代码改版后的一个,通过获取当前屏幕的大小(例如1920*1080),计算软件的长宽分别与之的比例,通过resize和setGeometry来修改全部控件。并且通过宽之比来对字体进行变换。(注:在控件设置stylesheet之后无法通过setFont来改变字体大小,所以就需要对当前的样式进行重新修改,但是又不想修改掉其他已经设置好的样式,通过找到font设置大小的地方,用新的大小来替换掉,重新设置样式即可)。

而实现最大化和还原,最大化就是将整个界面按照当前分辨率除以当前界面的尺寸为放大比例(分x,y方向);还原就是将放大之前的所有控件的尺寸大小和位置保存下来,将其一一还原。

4.具体代码:

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QMouseEvent>
 
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
 
    void AdjustRES();//适应分辨率
 
private slots:
    void on_close_clicked();//关闭
 
    void on_mini_clicked();//最小化
 
    void on_max_reset_clicked();//最大化 还原
 
    bool eventFilter(QObject *, QEvent *);//事件重写,拖动标题栏可移动窗口
private:
    Ui::MainWindow *ui;
 
    bool MoveFlag;  //标题框鼠标事件移动标志
    QPoint L;       //用于计算坐标
    QPoint P2;
    QPoint P3;
 
    int SCREENX;  //当前分辨率的宽
    int SCREENY;  //当前分辨率的高
    double screenX;//当前屏幕可用宽
    double screenY;//当前屏幕可用高
    double min_scaleX;//横向缩小倍数
    double min_scaleY;//纵向缩小倍数
    double max_scaleX;//横向放大倍数
    double max_scaleY;//纵向放大倍数
    QList<QWidget *> m_pushAllWidget;//存入全部控件
    QList<QRect> m_lastGeometry;//记录位置和大小
    QList<QString> m_lastStyleSheet;//记录字体的大小
};
#endif // MAINWINDOW_H
 

main.cpp

#include "mainwindow.h"
#include <QApplication>
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    w.AdjustRES();
 
    return a.exec();
}
 

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QRect>
#include <QDesktopWidget>
#include <QDebug>
 
static bool My_WindowIsMax = false;//判断是否最大化
static bool My_WindowIsAdjust = false;//判断是否已经适应分辨率
static bool My_WindowRestoreisFirst = true;//判断是否是第一次进行适应
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setAttribute(Qt::WA_TranslucentBackground, true);//设置窗体透明
    this->setWindowFlags(Qt::Window | Qt::FramelessWindowHint|Qt::WindowSystemMenuHint|Qt::WindowMinimizeButtonHint|Qt::WindowMaximizeButtonHint);//设置无边框,点击任务栏图标可最小化
    ui->widget->installEventFilter(this);//设置event事件,标题栏
    m_pushAllWidget = ui->centralwidget->findChildren<QWidget*>();//将界面内所有的控件都存入
 
 
    QDesktopWidget *desktopWidget = QApplication::desktop();//获取桌面
    QRect screenRect = desktopWidget->availableGeometry();//获取桌面屏幕有效矩形
    SCREENX = desktopWidget->screenGeometry().width();//获取当前分辨率
    SCREENY = desktopWidget->screenGeometry().height();
    screenX = screenRect.width();//屏幕的宽度
    screenY = screenRect.height();//屏幕的高度
    max_scaleX = screenX/ui->widget->width();
    max_scaleY = (screenY+8)/ui->widget->height();
    min_scaleX = 0.6;//自定义缩小比例
    min_scaleY = 0.8;//自定义缩小比例
}
 
MainWindow::~MainWindow()
{
    delete ui;
}
 
void MainWindow::AdjustRES()//点击最大化再还原,手动适应分辨率
{
    on_max_reset_clicked();
    on_max_reset_clicked();
}
 
bool MainWindow::eventFilter(QObject *watched, QEvent *event)   //标题栏拖动移动窗口
{
    QMouseEvent *e = static_cast<QMouseEvent *>(event);
    if(watched == ui->widget)//时时监视标题栏是否有进行鼠标操作
    {
        if(event->type() == QEvent::MouseButtonDblClick)
        {
            on_max_reset_clicked();//最大化/还原
            //如果你加载了最大化和还原的资源图片,这里可以加一个反转按钮check的状态。
        }
        if(event->type() == QEvent::MouseButtonPress)
        {
            L = e->globalPos();//保存第一次按住的全局坐标
            MoveFlag = true;
        }
        if(event->type() == QEvent::MouseMove)
        {
            QPoint temp = e->globalPos();
            if(abs(temp.x() - L.x())<5 && abs(temp.y() - L.y())<5) return false;
            if(MoveFlag)
            {
                if(L.isNull()) return false;
                P3 = e->globalPos();
                P2 = P3 - L;
                if(!My_WindowIsMax) this->move(this->pos()+P2);
                if(My_WindowIsMax)
                {
                    on_max_reset_clicked();//如果你加载了最大化和还原的资源图片,这里可以加一个反转按钮check的状态。
                    this->move(P2+L/3);//根据自己实际情况重新调整移动位置,这里我就不多深入
                }
                L = e->globalPos();
            }
        }
        if(event->type() == QEvent::MouseButtonRelease)
        {
            MoveFlag = false;
        }
    }
    return false;
}
 
void MainWindow::on_mini_clicked()
{
    this->showMinimized();
}
 
void MainWindow::on_max_reset_clicked()
{
    int tmpIndex = ui->stackedWidget->currentIndex();
    ui->stackedWidget->setCurrentIndex(1);//这一页面是空白的,之所以跳到这里是因为如果直接重新设置geometry会影响到当前页面的控件变形
    if(!My_WindowIsMax)//如果当前不是最大,最大化
    {
        if(SCREENX == 1920.0 && SCREENY == 1080.0)//如果是你默认开发的分辨率,建议不要更改
        {
            My_WindowIsAdjust = true;
            for(int i=0; i<m_pushAllWidget.size(); i++)
            {
                m_lastGeometry.push_back(m_pushAllWidget.at(i)->geometry());
                m_lastStyleSheet.push_back(m_pushAllWidget.at(i)->styleSheet());
            }
            My_WindowRestoreisFirst = false;
        }
        max_scaleX = screenX/ui->Allbackground->width();//最大化的比例就是当前分辨率的可用长宽除以底层窗口的长宽
        max_scaleY = screenY/ui->Allbackground->height();
        for(int i=0; i<m_pushAllWidget.size(); i++)
        {
            m_pushAllWidget.at(i)->resize(m_pushAllWidget.at(i)->width()*max_scaleX,m_pushAllWidget.at(i)->height()*max_scaleY);
            m_pushAllWidget.at(i)->setGeometry(m_pushAllWidget.at(i)->x()*max_scaleX,m_pushAllWidget.at(i)->y()*max_scaleY,m_pushAllWidget.at(i)->width(),m_pushAllWidget.at(i)->height());
            QString stylesheet = m_pushAllWidget.at(i)->styleSheet();
            QString str = "font:";
            if(stylesheet.contains(str))
            {
                QString get;
                for(int j=stylesheet.indexOf(str)+5; j<stylesheet.indexOf(str)+7; j++)
                {
                    get += stylesheet.at(j);
                }
                int a = get.toInt();
                int b = a*max_scaleY;
                get = QString::number(b);
                stylesheet.replace(stylesheet.indexOf(str)+5,2,get);
                m_pushAllWidget.at(i)->setStyleSheet(stylesheet);
            }
        }
        move(0,0);//最大化后将窗口的位置调整到0,0 , 如果有加边框阴影效果,需要重新调整
        this->setGeometry(this->x(),this->y(),SCREENX+50,SCREENY+50);//任务栏缩略图大小需要重新设置MainWindow的大小,不然MainWindow过大无法,缩略图无法正确显示
        ui->stackedWidget->setCurrentIndex(tmpIndex);
        My_WindowIsMax = true;
        return;
    }
 
    if(My_WindowIsMax)//如果当前不是初始字体,还原
    {
        if(My_WindowIsAdjust)//如果已经适应分辨率了
        {
            for(int i=0; i<m_pushAllWidget.size(); i++)
            {
                m_pushAllWidget.at(i)->setGeometry(m_lastGeometry.at(i));
                m_pushAllWidget.at(i)->setStyleSheet(m_lastStyleSheet.at(i));
            }
            ui->stackedWidget->setCurrentIndex(tmpIndex);
            My_WindowIsMax = false;
            this->setGeometry(this->x(),this->y(),ui->Allbackground->width()+50,ui->Allbackground->height()+50);
            move((SCREENX-ui->Allbackground->width())/2,(SCREENY-ui->Allbackground->height())/2);
            return;
        }
        My_WindowIsAdjust = true;//第一次还原后让为已经适应分辨率
        for(int i=0; i<m_pushAllWidget.size(); i++)//第一次
        {
            m_pushAllWidget.at(i)->resize(m_pushAllWidget.at(i)->width()*min_scaleX,m_pushAllWidget.at(i)->height()*min_scaleY);
            m_pushAllWidget.at(i)->setGeometry(m_pushAllWidget.at(i)->x()*min_scaleX,m_pushAllWidget.at(i)->y()*min_scaleY,m_pushAllWidget.at(i)->width(),m_pushAllWidget.at(i)->height());
            QString stylesheet = m_pushAllWidget.at(i)->styleSheet();
            QString str = "font:";
            if(stylesheet.contains(str))
            {
                QString get;
                for(int j=stylesheet.indexOf(str)+5; j<stylesheet.indexOf(str)+7; j++)
                {
                    get += stylesheet.at(j);
                }
                int a = get.toInt();
                int b = a*min_scaleY;
                get = QString::number(b);              
                stylesheet.replace(stylesheet.indexOf(str)+5,2,get);
                m_pushAllWidget.at(i)->setStyleSheet(stylesheet);
            }
        }
        this->setGeometry(this->x(),this->y(),ui->Allbackground->width()+50,ui->Allbackground->height()+50);
        move((SCREENX-ui->Allbackground->width())/5*2,(SCREENY-ui->Allbackground->height())/5*2);
        ui->stackedWidget->setCurrentIndex(tmpIndex);
        My_WindowIsMax = false;
        if(My_WindowRestoreisFirst)
        {
            for(int i=0; i<m_pushAllWidget.size(); i++)
            {
                m_lastGeometry.push_back(m_pushAllWidget.at(i)->geometry());
                m_lastStyleSheet.push_back(m_pushAllWidget.at(i)->styleSheet());
            }
            My_WindowRestoreisFirst = false;
        }
        return;
    }
}
 
void MainWindow::on_close_clicked()
{
    this->close();
}
 
5.注意

(1)MainWindow的大小要设置为你所需要的最大分辨率的大小,因为在代码中并没有改变MainWindow的大小,如果超过MainWindow的大小,会显示不全。比如MainWindow为1920*1080,在2560*1440的分辨率下窗口没有办法显示完全。建议在你所需的分辨率下进行微调。

(2)重新设置位置后,可能会有个别的控件没有达到你所想要的效果,可以再以当前的x,y上进行具体更改。字体的大小可能超过当前边框,需重新调整。

(3)设置控件字体样式,例如font:10px,再font:和10px之间不要留有空格,否则会因为字符串替换数量不对等而设置错误,再多按几次会出现程序崩溃。

(4)在构造函数中将所有widget存入到容器,方便使用并且不容易弄乱控件顺序。

 (5)获取桌面长宽,注意screenGeometry()和availableGeometry()是不一样的,第一个是获取当前分辨率,第二个是获取可用的桌面长宽,因为有任务栏的存在,这个最大化功能可能会出现盖过任务栏或与任务栏有一段距离,这个自己调整。

 (6)任务栏缩略图大小需要重新设置MainWindow的大小,不然MainWindow过大无法,缩略图无法正确显示。

(7)标题栏的拖动缩小根据个人实际情况重新调整。

(8)如果放大缩小过程出现卡顿,是正常的,毕竟要对多个控件进行重新设置geometry。

  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值