windows系统下,QT的Widget窗口,使用的是操作系统的窗口Style,很多老板不喜欢这个窗口的标题栏和系统按键,程序员自己无法更改这个窗口标题栏,而大伤脑筋。网上有很多介绍用QT实现无框架窗口的的案例都很好,但都是没有完整的案例可以使用。

今天,用ui拉控件的方式,实现了一个自定义窗口的框架,运行起来还算是稳定。分享出来让初学者学习研究。

一. 无边框窗口实现的难点

1、鼠标拖动标题栏,实现拖动窗口移动。

2、鼠标拖动窗口的边框、四角,实现窗口拖动改变窗口的大小。

二. 无边框窗口实现的技术要点

1、设置无边框

setWindowFlags(Qt::FramelessWindowHint);//无边框

2、调用 paintEvent 重绘窗口的边框

3、捕捉标题栏的鼠标拖动事件,实现拖动窗口移动。

4、过滤鼠标点击、移动、释放事件,拖动窗口边框,改变窗口大小。

三. 运行界面示例图

四. 运行界面组成

 

边框的设计结构如下图:

根据图的内容,很容易在UI中,通过 GridLayout 布局,对窗口的边框和窗口内容进行布局。

本例中,我使用了8个 Label 来作为窗口的边框,其中 4 个作为上下左右的边框,四个作为四个角的边框。 用了一个widget作为窗口内容的框架。总共9个元素形成一个窗口的整体框架,符合九宫布局格式,用 GridLayout进行布局。

上下边框Label设置固定高度 5pix, 左右边框label设置固定宽度5pix。 四个角设置固定5pix高,5pix宽。窗口内容widget根据布局自动拉伸宽高。

肯定有人问,我为什么用 Label作为边框,解释一下,

(1)节省资源:拉伸窗口时,必须捕捉鼠标的位置,如果整个窗口都开启鼠标跟踪,当鼠标在窗口上时,无论你是否想拉伸窗口,系统都在捕捉鼠标的位置,耗用资源太多。如果用label作为边框,只需要开启这九个Label的鼠标跟踪就可以了,只有鼠标在这9个Label上,系统才开始捕捉鼠标的位置,节省cpu资源。

(2)因为Label上可以用来显示图片,如果你不想画边框的话,在Label上贴图,可以做成好看的边框,比如圆角边框,或者带阴影的窗口。

(3) 当然你可以不用Label, 一样可以完成这个功能,这不是本文讨论的内容。

五. 窗口布局设计方案

1, 打开 QT Creator , 创建一个 widget 应用工程,选择 带上ui, 窗口的类型为 QMainWindow 或者 QDialog。工程的名字为 MainWindow, 当然你喜欢的话,工程名随你喜欢。

2, 打开 MainWindow ui,进行编辑。

3、删除MainWindow的菜单、工具栏、状态栏(根据需要是否保存状态栏)

4,拖进三个 Label 放在ui窗口的第一行,分别命名为:leftTopLabel, topLabel,rightTopLabel, 分别代表 左上角边框,顶部边框、右上角边框,清除 Label上的文字。

5、分别拖进去 一个 Label 一个 widget(frame也行)和一个 Label,放在第二行 命名为 :leftLabel, mainWidger, rightLabel.分别代表 左边框,主框架,右边框,清除Label上的文字。

6、拖进去三个Label,放在第三行,命名为:leftBottomLabel, bottomLabel,rightBottomLabel, 分别代表左下角边框,底部边框、右下角边框。清除Lael上的文字。

7、右键点击ui的mainWindow 空余地带,在菜单中选择布局,选择 Grid 布局, 9个元素平均分配大小。

8、 选择9个Label,在属性栏里, 开启鼠标跟踪。

9、选择顶部和底部两个Label, 在属性栏里,设置最大、最小高度为 5,选择鼠标样式为上下移动的鼠标样式。

10、 选择左右两个Label,在属性栏里,设置最大、最小宽度为5,选择鼠标样式为左右移动的鼠标样式。

11、选择左上、右下两个Label,在属性栏里,设置最大、最小宽度为5,最大最小高度为5, 设置鼠标样式为 左上右下的移动鼠标样式。

12、选择 右上、左下两个Label,在属性栏里,设置最大、最小宽度为5,最大最小高度为5,设置鼠标样式为右上左下的移动鼠标样式。

这样设置完后,整个窗口布局中, mainWidget就占据了基本全部的窗口区域,每个边框Label占据5pix的区域,基本上完成了整个窗口的布局设计。

13、 编辑 mainWindow.cpp , 在构造函数里,添加下面的代码:

// 设置无边框setWindowFlags(Qt::FramelessWindowHint);//无边框// 设置透明度setWindowOpacity(0.99);

14、运行程序,一个无边框的窗口就出现了,背景颜色是 QT默认的灰白色,设置透明度,可以设置窗口的透明效果。把鼠标分别放在窗口的上下左右边框上,看鼠标的样式是否是 左右、上下移动的鼠标样式,四个边角,是否是斜上、斜下移动的鼠标样式。

15、重写 paintEvent , 给没边框的窗口画一个边框,给窗口设定一个背景色。

void MainWindow::paintEvent(QPaintEvent* event){// 绘图处理// 重绘窗口的背景颜色QPalette palette = this->palette();qDebug() << "palette color: " << palette.color(QPalette::Window);QColor color(100,100,100);   palette.setBrush(QPalette::Window, QBrush(color));this->setPalette(palette);// 重绘窗口的边框大小QPainter painter(this);QRect rect = this->rect();QPen pen;pen.setWidth(5);pen.setColor(Qt::black);painter.setPen(pen);painter.drawRect(rect);QMainWindow::paintEvent(event);}

重新运行程序,一个崭新的窗口就出来了。

六. 拖动窗口的边框,改变窗口的大小的实现。

1,  在mainWindow.cpp 的 MainWindow 构造函数中,给上面做好的边框Label添加事件过滤器,

//设置过滤器,8个Label作为边框,过滤的鼠标事件ui->leftTopLabel->installEventFilter(this);ui->topLabel->installEventFilter(this);ui->rightTopLabel->installEventFilter(this);ui->leftLabel->installEventFilter(this);ui->rightLabel->installEventFilter(this);ui->leftBottomLabel->installEventFilter(this);ui->bottomLabel->installEventFilter(this);ui->rightBottomLabel->installEventFilter(this);//设置拉伸边框改变大小的参数mWp = unKnown; //设置拉伸边框初始标志为 unKnow, 这是一个枚举型变量m_bPressed = false;  // 设置鼠标按下的状态,如果鼠标在边框Label上拖动,则改参数为false.mouseEvent = nullptr;  //设置鼠标事件的初始值,默认为null

2. 在 mainWindow.h 头文件中, 定义 mWp, m_bPressed,, mouseEvent ,pLast参数。

enum LabelBorderStatus {unKnown = 0,toTop = 1,toBottom = 2,toLeft = 3,toRight = 4,toLeftTop = 5,toRightTop = 6,toLeftBottom = 7,toRightBottom = 8};LabelBorderStatus mWp;QMouseEvent* mouseEvent;bool m_bPressed;QPoint pLast; 

(1)定义了一个枚举类型, 用来确定是哪一条边框被拖动,后者哪一个边角被拖动。默认为 unKnow。系统中的变量名为 mWp.

(2)定义了一个布尔型 m_bPressed. 当鼠标在边框Label上被按压时,设定标志位 true, 如果有鼠标被释放,则 false

(3)定义了一个位置 pLast, 每次拖动后,保存拖动后的坐标点,再次触发拖动后,新的位置与该变量的差值,就是窗口应该增加或减少的数量。

3、 实现拖动,重写事件过滤器。

// 过滤 8 个 Label 边框的点击、移动、改变窗口大小bool MainWindow::eventFilter(QObject *target, QEvent *event){// 如果过滤事件是鼠标按下,设置改变窗口大小的标志为鼠标按下状态。if(event->type() == QEvent::MouseButtonPress){//检测是哪条边框按下mWp = testLabelBorder(target->objectName());if(mWp == LabelBorderStatus::unKnown){m_bPressed = false;}else {m_bPressed = true;mouseEvent = nullptr;mouseEvent = static_cast<QMouseEvent*>(event);if(mouseEvent ){// 获取鼠标按下的定位。pLast = mouseEvent->globalPos();event->ignore();}}}// 如果是鼠标释放事件,设置拖动标志位初始状态。else if(event->type() == QEvent::MouseButtonRelease){m_bPressed = false;mWp = LabelBorderStatus::unKnown;}// 如果过滤事件是鼠标移动事件,处理是否拖动边框改变窗口大小else if(event->type() == QEvent::MouseMove){//检测鼠标是否按下,并且拖动的边框类型if(m_bPressed && mWp != LabelBorderStatus::unKnown){mouseEvent = nullptr;mouseEvent = static_cast<QMouseEvent*>(event);if(mouseEvent){//获取鼠标拖动的位置,QPoint ptemp = mouseEvent->globalPos();// 计算鼠标按下并拖动的的位移ptemp = ptemp - pLast ;//保存当前鼠标拖动的位置,用于下一次计算拖动位移pLast = mouseEvent->globalPos();event->ignore();// 拉伸窗口函数moveAndResizeWindow(ptemp);}}}return QMainWindow::eventFilter(target,event);}

由于我们设置了8个边框Label的事件过滤器, 那么鼠标在这八个边框Label上发生的鼠标事件,都被时间过滤器进行监控。

(1)当过滤器被触发,首先我们要判定被触发的事件类型, 目前我们感兴趣的时 鼠标按压事件,鼠标移动事件,鼠标释放事件,除了这三种事件需要我们处理外,其他的事件交给父类去处理。 因此我们过滤出鼠标的三种事件。

(2)首先,如果我们过滤到鼠标被按压的事件,就必须先检测鼠标按压到哪个边框上,如果不是边框Label, 则就不是我们要的事件。这里用一个函数testLabelBorder 检测,如果事件的目标是8个边框Label之一,则返回边框的枚举值,否则返回 unKnow。 如果是是边框,设定 m_bPreesed 为true, 否则设定为 false。 后面都不再处理鼠标事件。

(3) 处理鼠标释放事件。 如果鼠标被释放, 设定标志为 m_bPressed,  不用处理鼠标的事件。

(4)鼠标移动事件过滤处理:如果鼠标移动触发的事件,首先我们要确认鼠标是否被按下,并且鼠标移动的目标是我们定义的8个边框Label, 我们才处理鼠标移动事件。

拖动边框计算鼠标移动的偏移量设计思想:

当鼠标移动事件被触发,此时鼠标的位置ptmp 与 上次保存的 pLast 位置进行相减,计算出鼠标每一次移动的偏移量。

testLabelBorder 检测边框函数的如下:

// 根据边框Label的objectName, 确定拉伸的是哪个边框MainWindow::LabelBorderStatus MainWindow::testLabelBorder(QString _objectName){if(_objectName == "leftTopLabel") return LabelBorderStatus::toLeftTop;if(_objectName == "topLabel") return LabelBorderStatus::toTop;if(_objectName == "rightTopLabel") return LabelBorderStatus::toRightTop;if(_objectName == "leftLabel") return LabelBorderStatus::toLeft;if(_objectName == "rightLabel") return LabelBorderStatus::toRight;if(_objectName == "leftBottomLabel") return LabelBorderStatus::toLeftBottom;if(_objectName == "bottomLabel") return LabelBorderStatus::toBottom;if(_objectName == "rightBottomLabel") return LabelBorderStatus::toRightBottom;return LabelBorderStatus::unKnown;}

(4) 通过鼠标过滤器,我们计算出来鼠标每次移动的偏移量和那一条边框上鼠标发生位移, 则我们就可以对窗口的大小进行拖动处理。

拖动处理的函数如下:

// 移动并改变窗口大小void MainWindow::moveAndResizeWindow(QPoint& pos){if(this->isMaximized()) return;int x=0,y=0,w=0,h=0;// 根据拖动的那一条边框,确定拉伸还是缩小窗口。switch (mWp) {// 左边框被拉伸case LabelBorderStatus::toLeft:x = this->pos().x() + pos.x();y = this->pos().y();w = this->size().width() - pos.x();h = this->size().height();break;// 右边框被拉伸case LabelBorderStatus::toRight:x = this->pos().x();y = this->pos().y();w = this->size().width() + pos.x();h = this->size().height();break;// 上边框被拉伸case LabelBorderStatus::toTop:x = this->pos().x();y = this->pos().y() + pos.y();w = this->size().width() ;h = this->size().height() - pos.y();break;// 下边框被拉伸case LabelBorderStatus::toBottom:x = this->pos().x();y = this->pos().y();w = this->size().width() ;h = this->size().height() + pos.y();break;//右上角被拉伸case LabelBorderStatus::toRightTop:x = this->pos().x();y = this->pos().y() + pos.y();w = this->size().width() + pos.x() ;h = this->size().height() - pos.y();break;//左上角被拉伸case LabelBorderStatus::toLeftTop:x = this->pos().x() + pos.x();y = this->pos().y() + pos.y();w = this->size().width() - pos.x() ;h = this->size().height() - pos.y();break;// 右下角被拉伸case LabelBorderStatus::toRightBottom:x = this->pos().x();y = this->pos().y();w = this->size().width() + pos.x() ;h = this->size().height() + pos.y();break;// 左下角被拉伸case LabelBorderStatus::toLeftBottom:x = this->pos().x() + pos.x();y = this->pos().y();w = this->size().width() - pos.x() ;h = this->size().height() + pos.y();break;default:return;}// 改变窗口的大小和位置。if(w < this->minimumWidth()){x = this->pos().x();w = this->size().width();}if(h < this->minimumHeight()){y = this->pos().y();h = this->size().height();}this->setGeometry(x,y,w,h);}

再次运行程序,把鼠标放到新窗口的四条边、四个角上,拖动窗口的变、角,就可以改变窗口的大小了。一个完整的可拖动改变窗口大小的程序就完成了。

七. 制作可拖动的标题栏 和 最大化最小化系统按钮

前面已经做好了无边框可拖动改变大小的窗口,还没有标题栏, 按照我的思路,由于我们使用了事件过滤器,同样可以过滤标题栏的鼠标事件,来拖动窗口标题,实现移动窗口的位置功能。考虑到事件过滤器代码不能太大,和模块化设计思想,我们把标题栏单独作为一个组件进行封装开发。

1、创建一个标题功能栏UI

(1) 新建一个 ui类, 命名为 TitleFrame, 父类为 widget ,当然也可以选择 Frame。

(2)水平方向,拖一个 Label, 设定固定大小 为35,作为标题栏的 logo, 清除文字,增加 icon 图片,作为logo图片,命名为 logoLabel。

(3) 水平方向,拖一个Label,设定文本为“窗口标题”,设定最小高度值为35.

(4) 水平方向 拉三个 PushButton, 分别命名为 :pb_close, pb_mid_max, pb_mix 分别代表 关闭、最大恢复、最小化按钮。设置固定大小为35,分别给三个按钮设定图标图片。我的示例中,添加了6个PushButton, 分别用来表示 个人信息、设置、帮助按钮,你可以根据需要添加功能按钮。

(5) 右键UI窗口的空白处,选择布局 -》水平布局。一个标题栏就做好了。

2、 给标题栏添加系统按钮添加事件

(1)右键最大化、最小化、恢复按钮,添加 click 槽。

(2)定义信号,把功能按钮的信号,交给 主窗口 mainWindow来处理。根据设计思想,最大化最小化应该由主窗口来实现具体功能。

signals:void quitEvent();void mid_maxEvent();void minEvent();void moveWindow(QPoint& pos); // 通知主窗口移动的信号

(3)实现 最大化最小化按钮的槽函数。


void TitleFrame::on_pb_close_clicked()
{emit quitEvent();
}void TitleFrame::on_pb_mid_max_clicked()
{emit mid_maxEvent();
}void TitleFrame::on_pb_min_clicked()
{emit minEvent();
}

(4) 在主窗口 卖弄Window中,实现最大化最小化的窗口功能。在 mainWindow 的构造函数中,链接槽和信号。

 // titleFrame 的信号处理connect(ui->titleFrame,SIGNAL(quitEvent()),this,SLOT(quitEvent()));connect(ui->titleFrame,SIGNAL(mid_maxEvent()),this,SLOT(mid_maxEvent()));connect(ui->titleFrame,SIGNAL(minEvent()),this,SLOT(minEvent()));

(5)在主窗口 中实现槽函数的最小化,最大化,恢复复窗口的功能。

// 退出按钮处理事件
void MainWindow::quitEvent()
{qApp->quit();
}
// 最大化回复窗口按钮事件
void MainWindow::mid_maxEvent()
{if(this->isMaximized()){showNormal();ui->titleFrame->setMaxIcon(false);}else {showMaximized();ui->titleFrame->setMaxIcon(true);}
}
// 最小化窗口事件
void MainWindow::minEvent()
{showMinimized();
}

(6)把标题栏框架放到主窗口中去。

打开 mainWindow ui进行编辑, 在mainWidget中,命名为 titleFrame 拖一个widget,提升为 TitleFrame. 移动到mainWidget的顶部,修改titleFrame固定高度 36。

(7) 运行程序, 点击最大化,最小化、恢复 按钮,实现窗口的最大化和最小化窗口功能能。

3、实现拖拽窗口标题,拖动窗口移动

拖拽窗口标题移动窗口,使用了与主窗口的事件过滤器不同的方法,本例中使用 重写 鼠标事件的方式来实现。

(1) 当捕捉到鼠标按下的事件时, 首先要判断是否是 titleLabe , 如果是 则处理移动,否则交给父类处理,在标题框架 titleFrame.cpp 代码实现了 mouse Press Event。代码原理和上面介绍的拖动窗口的处理思想完全相同。


void TitleFrame::mousePressEvent(QMouseEvent *event)
{this->setFocus();if(Qt::LeftButton == event->button()){QPoint pos = event->pos();if(pos.x() > ui->windowTitle->pos().x() && pos.x() < ui->windowTitle->pos().x() + ui->windowTitle->rect().width()){if(pos.y() > ui->windowTitle->pos().y() && pos.y() < ui->windowTitle->pos().y() + ui->windowTitle->rect().height()){pLast=event->globalPos();// event->ignore();m_bPressed = true;}}}
}

(2) 当捕捉到鼠标释放的事件,处理代码和改变窗口大小完全一致。

void TitleFrame::mouseReleaseEvent(QMouseEvent * event){m_bPressed = false;}

(3) 当捕捉到鼠标移动时, 计算鼠标移动的位移,发送鼠标移动的信号,交给主窗口来处理窗口移动。


void TitleFrame::mouseMoveEvent(QMouseEvent* event)
{if( m_bPressed){QPoint ptemp = event->globalPos();ptemp = ptemp - pLast ;pLast = event->globalPos();emit moveWindow(ptemp);}
}

(4) 主窗口处理窗口移动的代码


// 拖动窗口标题,移动窗口
void MainWindow::moveWindowEvent(QPoint& pos)
{if(this->isMaximized()) return;QPoint tmp = this->pos() + pos;this->move(tmp);
}

(5) 处理双击标题栏的事件处理, 双击标题栏, 判断最大化和标准状态,是窗口最大化和最小化。代码如下。

void TitleFrame::mouseDoubleClickEvent(QMouseEvent *event){if(Qt::LeftButton == event->button()){QPoint pos = event->pos();if(pos.x() > ui->windowTitle->pos().x() && pos.x() < ui->windowTitle->pos().x() + ui->windowTitle->rect().width()){if(pos.y() > ui->windowTitle->pos().y() && pos.y() < ui->windowTitle->pos().y() + ui->windowTitle->rect().height()){emit mid_maxEvent();}}}}

(6) 运行程序,拖动标题栏,可以任意移动窗口。

八. 程序完成代码。

1. mainwindow.cpp

/****************************************************************************
**
** Copyright (C) 2019 The Qt Project.
** 版权声明/
**
** 作者: 乔玉东.
** 电子邮件: newnbo@hotmail.com  whatwhy@foxmail.com
** 本程序代码为开放代码,你可以使用本代码中的所有内容。
** 当您使用本框架时,请保留版权声明
**
** *******************************************************************************/#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);//设置拉伸边框改变大小的参数mWp = unKnown; //设置拉伸变宽初始标志为 unKnowm_bPressed = false;  // 设置鼠标按下的状态mouseEvent = nullptr;  //设置鼠标事件的初始值// 设置无边框setWindowFlags(Qt::FramelessWindowHint);//无边框// 设置透明度setWindowOpacity(0.99);// 设置窗口的标题ui->titleFrame->setWindowTitle("设置窗口标题,拖动移动窗口");//设置过滤器,8个Label作为边框,过滤的鼠标事件ui->leftTopLabel->installEventFilter(this);ui->topLabel->installEventFilter(this);ui->rightTopLabel->installEventFilter(this);ui->leftLabel->installEventFilter(this);ui->rightLabel->installEventFilter(this);ui->leftBottomLabel->installEventFilter(this);ui->bottomLabel->installEventFilter(this);ui->rightBottomLabel->installEventFilter(this);// titleFrame 的信号处理connect(ui->titleFrame,SIGNAL(quitEvent()),this,SLOT(quitEvent()));connect(ui->titleFrame,SIGNAL(mid_maxEvent()),this,SLOT(mid_maxEvent()));connect(ui->titleFrame,SIGNAL(minEvent()),this,SLOT(minEvent()));connect(ui->titleFrame,SIGNAL(aboutEvent()),this,SLOT(aboutEvent()));connect(ui->titleFrame,SIGNAL(configEvent()),this,SLOT(configEvent()));connect(ui->titleFrame,SIGNAL(personEvent()),this,SLOT(personEvent()));connect(ui->titleFrame,SIGNAL(moveWindow(QPoint&)),this,SLOT(moveWindowEvent(QPoint&)));}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::paintEvent(QPaintEvent* event){// 绘图处理// 重绘窗口的背景颜色QPalette palette = this->palette();qDebug() << "palette color: " << palette.color(QPalette::Window);QColor color(100,100,100);   palette.setBrush(QPalette::Window, QBrush(color));this->setPalette(palette);// 重绘窗口的边框大小QPainter painter(this);QRect rect = this->rect();QPen pen;pen.setWidth(5);pen.setColor(Qt::black);painter.setPen(pen);painter.drawRect(rect);QMainWindow::paintEvent(event);}
// 退出按钮处理事件
void MainWindow::quitEvent()
{qApp->quit();
}
// 最大化最小化窗口按钮事件
void MainWindow::mid_maxEvent()
{if(this->isMaximized()){showNormal();ui->titleFrame->setMaxIcon(false);}else {showMaximized();ui->titleFrame->setMaxIcon(true);}
}
// 最小化窗口事件
void MainWindow::minEvent()
{showMinimized();
}
// 关于按钮处理事件
void MainWindow::aboutEvent()
{}
// 配置事件
void MainWindow::configEvent()
{}
// 个人信息事件
void MainWindow::personEvent()
{}// 拖动窗口标题,移动窗口
void MainWindow::moveWindowEvent(QPoint& pos)
{if(this->isMaximized()) return;QPoint tmp = this->pos() + pos;this->move(tmp);
}// 过滤 8 个 Label 边框的点击、移动、改变窗口大小bool MainWindow::eventFilter(QObject *target, QEvent *event){// 如果过滤事件是鼠标按下,设置改变窗口大小的标志为鼠标按下状态。if(event->type() == QEvent::MouseButtonPress){//检测是哪条边框按下mWp = testLabelBorder(target->objectName());if(mWp == LabelBorderStatus::unKnown){m_bPressed = false;}else {m_bPressed = true;mouseEvent = nullptr;mouseEvent = static_cast<QMouseEvent*>(event);if(mouseEvent ){// 获取鼠标按下的定位。pLast = mouseEvent->globalPos();event->ignore();}}}// 如果是鼠标释放事件,设置拖动标志位初始状态。else if(event->type() == QEvent::MouseButtonRelease){m_bPressed = false;mWp = LabelBorderStatus::unKnown;}// 如果过滤事件是鼠标移动事件,处理是否拖动边框改变窗口大小else if(event->type() == QEvent::MouseMove){//检测鼠标是否按下,并且拖动的边框类型if(m_bPressed && mWp != LabelBorderStatus::unKnown){mouseEvent = nullptr;mouseEvent = static_cast<QMouseEvent*>(event);if(mouseEvent){//获取鼠标拖动的位置,QPoint ptemp = mouseEvent->globalPos();// 计算鼠标按下并拖动的的位移ptemp = ptemp - pLast ;//保存当前鼠标拖动的位置,用于下一次计算拖动位移pLast = mouseEvent->globalPos();event->ignore();// 拉伸窗口函数moveAndResizeWindow(ptemp);}}}return QMainWindow::eventFilter(target,event);}
// 移动并改变窗口大小void MainWindow::moveAndResizeWindow(QPoint& pos){if(this->isMaximized()) return;int x=0,y=0,w=0,h=0;// 根据拖动的那一条边框,确定拉伸还是缩小窗口。switch (mWp) {// 左边框被拉伸case LabelBorderStatus::toLeft:x = this->pos().x() + pos.x();y = this->pos().y();w = this->size().width() - pos.x();h = this->size().height();break;// 右边框被拉伸case LabelBorderStatus::toRight:x = this->pos().x();y = this->pos().y();w = this->size().width() + pos.x();h = this->size().height();break;// 上边框被拉伸case LabelBorderStatus::toTop:x = this->pos().x();y = this->pos().y() + pos.y();w = this->size().width() ;h = this->size().height() - pos.y();break;// 下边框被拉伸case LabelBorderStatus::toBottom:x = this->pos().x();y = this->pos().y();w = this->size().width() ;h = this->size().height() + pos.y();break;//右上角被拉伸case LabelBorderStatus::toRightTop:x = this->pos().x();y = this->pos().y() + pos.y();w = this->size().width() + pos.x() ;h = this->size().height() - pos.y();break;//左上角被拉伸case LabelBorderStatus::toLeftTop:x = this->pos().x() + pos.x();y = this->pos().y() + pos.y();w = this->size().width() - pos.x() ;h = this->size().height() - pos.y();break;// 右下角被拉伸case LabelBorderStatus::toRightBottom:x = this->pos().x();y = this->pos().y();w = this->size().width() + pos.x() ;h = this->size().height() + pos.y();break;// 左下角被拉伸case LabelBorderStatus::toLeftBottom:x = this->pos().x() + pos.x();y = this->pos().y();w = this->size().width() - pos.x() ;h = this->size().height() + pos.y();break;default:return;}
// 资源中,缺少这一段判断代码,拖动左上、左、左下边框到最小窗口时,窗口会跟着移动,添加判断则可以解决。if(w < this->minimumWidth()){x = this->pos().x();w = this->size().width();}if(h < this->minimumHeight()){y = this->pos().y();h = this->size().height();}// 改变窗口的大小和位置。this->setGeometry(x,y,w,h);}
// 根据边框Label的objectName, 确定拉伸的是哪个边框MainWindow::LabelBorderStatus MainWindow::testLabelBorder(QString _objectName){if(_objectName == "leftTopLabel") return LabelBorderStatus::toLeftTop;if(_objectName == "topLabel") return LabelBorderStatus::toTop;if(_objectName == "rightTopLabel") return LabelBorderStatus::toRightTop;if(_objectName == "leftLabel") return LabelBorderStatus::toLeft;if(_objectName == "rightLabel") return LabelBorderStatus::toRight;if(_objectName == "leftBottomLabel") return LabelBorderStatus::toLeftBottom;if(_objectName == "bottomLabel") return LabelBorderStatus::toBottom;if(_objectName == "rightBottomLabel") return LabelBorderStatus::toRightBottom;return LabelBorderStatus::unKnown;}

2.mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QPainter>
#include <QPalette>
//#include <QGraphicsOpacityEffect>namespace Ui {
class MainWindow;
}class MainWindow : public QMainWindow
{Q_OBJECTpublic:enum LabelBorderStatus {unKnown = 0,toTop = 1,toBottom = 2,toLeft = 3,toRight = 4,toLeftTop = 5,toRightTop = 6,toLeftBottom = 7,toRightBottom = 8};explicit MainWindow(QWidget *parent = nullptr);~MainWindow();LabelBorderStatus mWp;QMouseEvent* mouseEvent;bool m_bPressed;QPoint pLast;LabelBorderStatus testLabelBorder(QString _objectName);void moveAndResizeWindow(QPoint& pos);private slots:void quitEvent();void mid_maxEvent();void minEvent();void aboutEvent();void configEvent();void personEvent();void moveWindowEvent(QPoint& pos);private:Ui::MainWindow *ui;void paintEvent(QPaintEvent* event);    bool eventFilter(QObject *target, QEvent *event);};#endif // MAINWINDOW_H

3.titleframe.h

#ifndef TITLEFRAME_H
#define TITLEFRAME_H#include <QWidget>
#include <QDebug>
#include <QMouseEvent>namespace Ui {
class TitleFrame;
}class TitleFrame : public QWidget
{Q_OBJECTpublic:explicit TitleFrame(QWidget *parent = nullptr);~TitleFrame();void setMaxIcon(bool arg);void setWindowTitle(QString _title);bool m_bPressed;QPoint pLast;// QRect titleRect;signals:void quitEvent();void mid_maxEvent();void minEvent();void aboutEvent();void configEvent();void personEvent();void moveWindow(QPoint& pos);private slots:void on_pb_close_clicked();void on_pb_mid_max_clicked();void on_pb_min_clicked();void on_pb_about_clicked();void on_pb_config_clicked();void on_pb_person_clicked();private:Ui::TitleFrame *ui;void mousePressEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent * event);void mouseReleaseEvent(QMouseEvent * event);void  mouseDoubleClickEvent(QMouseEvent *event);
};#endif // TITLEFRAME_H

4.titleframe.cpp

/****************************************************************************
**
** Copyright (C) 2019 The Qt Project.
** 版权声明/
**
** 作者: 乔玉东.
** 电子邮件: newnbo@hotmail.com  whatwhy@foxmail.com
** 本程序代码为开放代码,你可以使用本代码中的所有内容。
** 当您使用本框架时,请保留版权声明
**
** *******************************************************************************/#include "titleframe.h"
#include "ui_titleframe.h"TitleFrame::TitleFrame(QWidget *parent) :QWidget(parent),ui(new Ui::TitleFrame)
{m_bPressed = false;ui->setupUi(this);  }TitleFrame::~TitleFrame()
{delete ui;
}void TitleFrame::on_pb_close_clicked()
{emit quitEvent();
}void TitleFrame::on_pb_mid_max_clicked()
{emit mid_maxEvent();
}void TitleFrame::on_pb_min_clicked()
{emit minEvent();
}void TitleFrame::on_pb_about_clicked()
{emit aboutEvent();
}void TitleFrame::on_pb_config_clicked()
{emit configEvent();
}void TitleFrame::on_pb_person_clicked()
{emit personEvent();
}void TitleFrame::setMaxIcon(bool arg)
{QIcon icon;if(arg){icon.addFile(":/images/btn_mid.png");}else {icon.addFile(":/images/btn_max.png");}ui->pb_mid_max->setIcon(icon);
}void TitleFrame::setWindowTitle(QString _title)
{ui->windowTitle->setText(_title);
}void TitleFrame::mousePressEvent(QMouseEvent *event)
{this->setFocus();if(Qt::LeftButton == event->button()){QPoint pos = event->pos();if(pos.x() > ui->windowTitle->pos().x() && pos.x() < ui->windowTitle->pos().x() + ui->windowTitle->rect().width()){if(pos.y() > ui->windowTitle->pos().y() && pos.y() < ui->windowTitle->pos().y() + ui->windowTitle->rect().height()){pLast=event->globalPos();// event->ignore();m_bPressed = true;}}}
}void TitleFrame::mouseMoveEvent(QMouseEvent* event)
{if( m_bPressed){QPoint ptemp = event->globalPos();ptemp = ptemp - pLast ;pLast = event->globalPos();emit moveWindow(ptemp);}
}void TitleFrame::mouseReleaseEvent(QMouseEvent * event){m_bPressed = false;}void TitleFrame::mouseDoubleClickEvent(QMouseEvent *event){if(Qt::LeftButton == event->button()){QPoint pos = event->pos();if(pos.x() > ui->windowTitle->pos().x() && pos.x() < ui->windowTitle->pos().x() + ui->windowTitle->rect().width()){if(pos.y() > ui->windowTitle->pos().y() && pos.y() < ui->windowTitle->pos().y() + ui->windowTitle->rect().height()){emit mid_maxEvent();}}}}

5. main.cpp

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

6.mainwindow.pro

#-------------------------------------------------
#
# Project created by QtCreator 2019-03-29T14:46:38
#
#-------------------------------------------------QT       += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsTARGET = MainWindow
TEMPLATE = app# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0CONFIG += c++11SOURCES += \main.cpp \mainwindow.cpp \titleframe.cppHEADERS += \mainwindow.h \titleframe.h \compnent/minibutton.hFORMS += \mainwindow.ui \titleframe.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += targetRESOURCES += \qrc.qrc

整个自定义窗口的代码完成了。写了整个下午的文档,很累。 如果你觉得本文章对你有帮助,扫描下面二维码,微信打赏一下。1块不嫌少,100块不嫌多。

本项目资源可以下载, 下载地址:QT实现的自定义窗口框架

QT5 自定义窗口的详细设计方案相关推荐

  1. 3、Qt5 主窗口点击按钮 弹出另一个自定义窗口

    一.目的 实现点击主窗口按钮,弹出一个指定的自定义窗口. 二.效果图 三.源代码 1.widget.cpp 主窗口程序 #include "widget.h" #include & ...

  2. qt5 linux 窗口不能置顶_Qt 5.15 LTS发布,Qt 6要来了

    Qt 5.15 LTS 已经发布了,这是 Qt 5 系列的最后一个功能版本,为下一个主要版本 Qt 6 做了大量准备工作.此版本将为所有商业许可证持有者提供三年的长期支持,在三年的支持期后,可以选择提 ...

  3. WPF绘制自定义窗口

    原文:WPF绘制自定义窗口 WPF是制作界面的一大利器,下面就用WPF模拟一下360的软件管理界面,360软件管理界面如下: 界面不难,主要有如下几个要素: 窗体的圆角 自定义标题栏及按钮 自定义状态 ...

  4. Qt4_在Qt设计师中集成自定义窗口部件

    在Qt设计师中集成自定义窗口部件 在Qt设计师中使用自定义窗口部件之前,我们必须让Qt设计师先察觉到它们的存在.有两种方法可以完成这一任务:改进法(promotion)和插件法(pluigin). 改 ...

  5. QT自定义窗口插件在QT Creator的应用

    根据<C++ GUI Programming with Qt 4,Second Edition>中第5章的"在Qt设计师中集成自定义窗口部件"小节,使用插件法生成的窗口 ...

  6. PySide2 基础入门-创建实例窗口(详细解释)

    PySide2 基础入门-创建实例窗口(详细解释) python 3.7 / Pyside2 (如果使用pyQt5,将Pyside2 直接替换PyQt5即可)首先我们在Qt Designer中画好界面 ...

  7. flink 自定义 窗口_《从0到1学习Flink》—— Flink Data transformation(转换)

    前言 在第一篇介绍 Flink 的文章 <<从0到1学习Flink>-- Apache Flink 介绍> 中就说过 Flink 程序的结构 Flink 应用程序结构就是如上图 ...

  8. Windows消息:如何自定义窗口消息与线程消息

    自定义消息 一.自定义窗口消息 #define WM_MY_MSG WM_USER + 0x100 afx_msg LRESULT OnMyMsg(WPARAM, LPARAM); LRESULT C ...

  9. [WPF疑难] 继承自定义窗口

    [WPF疑难] 继承自定义窗口 原文 [WPF疑难] 继承自定义窗口 [WPF疑难] 继承自定义窗口 周银辉 项目中有不少的弹出窗口,按照美工的设计其外边框(包括最大化,最小化,关闭等按钮)自然不同于 ...

  10. QT学习笔记(六):Qt5主窗口框架示例

    QT学习笔记(五):Qt5主窗口框架代码示例 一.添加编辑菜单:并在下拉菜单和工具栏中添加"打开文件"动作菜单 #include <QToolButton> #incl ...

最新文章

  1. 分布式系统中节点之间的同步形成区块链
  2. python训练数据集_python – 如何训练大型数据集进行分类
  3. 【django轻量级框架】Django框架介绍与安装
  4. linux中运行.sql文件
  5. 大一java图书馆管理系统课程设计
  6. java 崩溃日志_Android收集程序崩溃日志的方法
  7. Chrome开发者工具一个有用的快捷键组合:Ctrl+Shift+E
  8. 电子设计竞赛(三)-SPWM与PID
  9. MySQL 全文搜索支持, mysql 5.6.4支持Innodb的全文检索和类memcache的nosql支持
  10. 图解Transformer-一篇文章看懂transformer
  11. 自定义注解完成数据库切库
  12. bluez 设置绑定pin码_「RT-Thread笔记」IO设备模型及PIN设备
  13. SLF4J: Failed to load class “org.slf4j.impl.StaticLoggerBinder”
  14. java入门第二季 答答租车系统
  15. [转载]Android开发网上的一些重要知识点
  16. 电子签约存证及印章管理整体化解决方案
  17. 论文复审意见及实验规划
  18. python 读取txt函数总结
  19. 2019.12.18
  20. Shell中ls -l 与 ll 的区别

热门文章

  1. qq邮件exchange服务器,解决Exchange邮件系统无法接收QQ邮件的问题
  2. 石家庄地铁线路查询系统(补)
  3. 快速下载github上部分文件夹或文件
  4. Stata:GMM-简介及实现范例
  5. 【探究服务】——服务的更多技巧
  6. 1000年出现了哪些闰年C语言,c语言:判断1000年---2000年之间的闰年
  7. 程序员学习资料分享---爱分享的程序员(新浪微博)
  8. HDLBits—Exams/ece241 2014 q7a
  9. 天线基础知识(三)天线增益
  10. Elasticsearch的关键词搜索