QT_Day01_应用框架_项目文件_父窗口和子窗口的区别_信号和槽_坐标系统_内存回收机制_菜单栏窗口_对话框
QT_Day01
1.应用框架
1.1main.cpp
```
#include "mywidget.h"
//应用程序类QApplication
//QT头文件木有.h
//头文件和类名一致
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);//有且只有一个应用程序类的对象
MyWidget w;//MyWidget继承QWidget,QWidget是一个窗口基类
//MyWidget也是一个窗口类,w就是一个窗口;
w.show();//窗口创建默认隐藏,需要人为显示;
//a.exec();让程序一致执行,等待用户的操作;也就是等待事件的发生;
return a.exec();//return a.exec();与分开两句是一样的效果,a.exec();和return 0;
}
1.2mywidget.h文件
```
#ifndef MYWIDGET_H
#define MYWIDGET_H
#include <QWidget>
class MyWidget : public QWidget
{
Q_OBJECT//信号与槽的时候需要
public:
MyWidget(QWidget *parent = 0);//构造函数。可以按F4跳过去看;
~MyWidget();//析构函数;
};
#endif // MYWIDGET_H
2.项目文件
```
#注释是需要用#的;
#模块--到.h文件,光标到QWidget,按F1跳到对应的帮助文档;
#包含头文件需要的模块,需要进行添加
QT += core gui
#高于4版本,需要添加QT+=widgets,为了兼容QT4;
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
#应用程序exe的名字;
TARGET = 01_QtTest
#指定makefile的类型,app,可以生成库文件lib
TEMPLATE = app
DEFINES += QT_DEPRECATED_WARNINGS
#源文件.cpp文件
SOURCES += \
main.cpp \
mywidget.cpp
#头文件.h文件
HEADERS += \
mywidget.h
2.1QT5的模块
3.父窗口和子窗口
- 如何指定子类对象?
- 通过setParent来指定父窗口: b.setParent(&w);
- 通过构造函数传参来指定父窗口:b2 = new QPushButton(this);
- 指定父对象的好处?
- 子窗口在父窗口上
- 运行只需要运行父窗口就可以显示,子窗口不用.show();
3.1main.cpp
```
#include<QApplication>
#include<QWidget>
#include<QPushButton>
int main(int argc,char *argv[])
{
QApplication app(argc,argv);
QWidget w;
w.setWindowTitle("主要看气质");//设置标题
// w.show();
QPushButton b;
b.setText("(●'◡'●)");//给按钮设置内容;
b.setParent(&w);
b.move(100,100);//移动坐标;位置;
QPushButton b1(&w);//通过构造函数传参
b1.setText("abc");
w.show();
/*
*如果不指定父对象,窗口和窗口是独立的,木有关系的。
* a指定b作为它的父对象,a放在b上,
* 指定父对象:有2种方式:
* (1)setParent(2)通过构造函数传参
* 指定父对象,只需要父对象显示,上面的子对象自动显示
*/
app.exec();
return 0;
}
- 运行截图:
3.信号和槽signals and slots
在mainWidgets.cpp进行编写代码,类似于分层编写函数;
-
1.在对应得.h文件,写所需添加的头文件,以及一些成员的声明
-
2.在mainWidgets.cpp中进行定义,在构造函数或者新建一个函数中定义内容;
-
3.main()函数中调用函数;
-
举个点击按钮进行关闭窗口操作:
-
.h文件
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include<QPushButton> class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); private: QPushButton b1; QPushButton *b2; }; #endif // MAINWIDGET_H
-
mainWidgets.cpp文件
#include "mainwidget.h" #include<QPushButton> MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { b1.setParent(this); b1.setText("close"); b1.move(100,100); b2 = new QPushButton(this); b2->setText("HelloWorld"); connect(&b1,&QPushButton::pressed,this,&MainWidget::close); /*&b1:信号发出者,指针类型 * &QPushButton::pressed:处理的信号,&发送者的类名::信号名 * this:信号接收者 * &MainWidget::close:槽函数,信号处理函数,&接收的类名::槽函数名字 */ } MainWidget::~MainWidget() { }
3.1标准信号和槽
- 信号和槽用于两个对象之间的通信。是QT的核心,也是不同于其他开发框架的最突出特征;
- 在GUI中,一个信号发生变化,总希望其他部件也能了解到该变化;
- 为实现对象间的通信,一些工具包中使用回调callback机制,而在QT中方使用了信号和槽之间来进行对象间的通信。
- 当一个特殊的事件发生时便可以发射一个信号,比如按钮被单击就发射cliced()信号,而槽就是一个函数,它在信号发射后被调用来相应这个信号。
- 一个信号一个槽,其实,一个信号可以关联到多个槽上,多个信号也可以关联到同一个槽上,甚至,另一个信号还可以关联到另一个信号上。
- 如果存在多个槽与某个信号关联,那么,这个信号被发射时,这些槽将会一个接一个地执行,执行顺序与关联顺序相同。
- 标志信号和槽函数的例子编写:
#include "mainwidget.h" #include<QPushButton> MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { b1.setParent(this); b1.setText("close"); b1.move(100,100); b2 = new QPushButton(this); b2->setText("HelloWorld"); connect(&b1,&QPushButton::pressed,this,&MainWidget::close); /*&b1:信号发出者,指针类型 * &QPushButton::pressed:处理的信号,&发送者的类名::信号名 * this:信号接收者 * &MainWidget::close:槽函数,信号处理函数,&接收的类名::槽函数名字 */ }
3.2自定义槽函数
- mainWidgets.cpp代码:
MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { b1.setParent(this); b1.setText("close"); b1.move(100,100); b2 = new QPushButton(this); b2->setText("HelloWorld"); connect(&b1,&QPushButton::pressed,this,&MainWidget::close); /*&b1:信号发出者,指针类型 * &QPushButton::pressed:处理的信号,&发送者的类名::信号名 * this:信号接收者 * &MainWidget::close:槽函数,信号处理函数,&接收的类名::槽函数名字 */ /* *自定义槽,普通函数的用法 * Qt5:任意的成员函数,普通的全局函数,静态函数 * 槽函数需要和信号一致(参数,返回值) * 由于信号都是木有返回值的,所以,槽函数一定木有返回值; */ connect(b2,&QPushButton::released,this,&MainWidget::mySlot); //功能:按下b2按键,换成123 connect(b2,&QPushButton::released,&b1,&QPushButton::hide); //抬起按钮时,b1接收信号隐藏按钮; /* * 信号:短信 * 槽函数:接收短信的手机 */ } void MainWidget::mySlot() { b2->setText("123"); }
- .h代码:
class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); void mySlot(); private: QPushButton b1; QPushButton *b2; };
3.3两个独立的窗口
-
信号发送是 emit released();;connect作为绑定一个信号发送者以及信号的地方,从来都不是它负责发送信号的
-
子窗体是父窗体的一个成员对象。两个独立的窗体之间不可能建立信息的传递,除非是像这样子在这个主窗体的项目上新建一个窗口。
-
子窗口是在父窗口内运行的,在子窗口发送信号,父窗口可以收到信号。
-
为什么把所有的代码都放构造函数?因为程序运行会第一时间执行构造函数,这样子我们的代码就可以运行;
-
mainwidget.h代码:
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include<QPushButton> #include"subwidget.h"//包含子窗口的头文件 class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); void mySlot(); void changeWin(); void dealSub();//处理子窗口 private: QPushButton b1; QPushButton b3; SubWidget w; }; #endif // MAINWIDGET_H
-
subwidget.h代码:
#ifndef SUBWIDGET_H #define SUBWIDGET_H #include <QWidget> #include<QPushButton> class SubWidget : public QWidget { Q_OBJECT public: explicit SubWidget(QWidget *parent = nullptr); void sendSlot(); signals: //声明一个信号; /* * 信号必须有signals关键字来声明 * 信号木有返回值,但可以有参数 * 信号就是函数的声明,只需要声明,无需定义 * 使用:emit mySignal(); */ void mySignal(); public slots: private: QPushButton b; }; #endif // SUBWIDGET_H
-
main.cpp代码:
#include "mainwidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWidget w; w.show(); return a.exec(); }
-
mainwidget.cpp代码:
#include "mainwidget.h" #include<QPushButton> MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { b1.setParent(this); b1.setText("close"); b1.move(100,100); setWindowTitle("老大");//等价于this->setWindowTitle("老大"); b3.setParent(this); b3.setText("切换到子窗口"); b3.move(50,50); //显示子窗口; // w.show(); connect(&b3,&QPushButton::released,this,&MainWidget::changeWin); //处理子窗口的信号 connect(&w,&SubWidget::mySignal,this,&MainWidget::dealSub); resize(400,300);//如果不设置宽度,高度。则窗口跳转过程会窗口缩小; } void MainWidget::changeWin() { //子窗口显示 w.show(); //本窗口隐藏 this->hide(); } void MainWidget::dealSub() { //子窗口隐藏 w.hide(); //本窗口显示 this->show(); } MainWidget::~MainWidget() { }
-
subwidget.cpp代码:
#include "subwidget.h" SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { this->setWindowTitle("小弟"); b.setParent(this); b.setText("切换到主窗口"); connect(&b,&QPushButton::clicked,this,&SubWidget::sendSlot); //这里connect只是作为绑定一个信号发送者以及信号的地方, resize(400,300);//如果不设置宽度,高度。则窗口跳转过程会窗口缩小; } void SubWidget::sendSlot() { emit mySignal(); }
如何查看标志的信号函数?
- 点中控件按钮--QPushButton后按F1,--找到父类QAbstractButton后点击--signals--跳转页面后可以找到标准函数。
3.4带参数的信号
-
信号也可以重载,重载需要考虑的是二义性的问题;
-
程序是day01——03_SignalAndSolt:
-
mainWidget.h的代码:
class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); //public slots: //QT4所需要访问权限和slots来修饰槽函数; void mySlot(); void changeWin(); void dealSub();//处理子窗口 void dealSlot(int,QString); private: QPushButton b1; QPushButton b3; SubWidget sub; };
-
subwidget.h的代码:
class SubWidget : public QWidget { Q_OBJECT public: explicit SubWidget(QWidget *parent = nullptr); void sendSlot(); signals: //声明一个信号; /* * 信号必须有signals关键字来声明 * 信号木有返回值,但可以有参数 * 信号就是函数的声明,只需要声明,无需定义 * 使用:emit mySignal(); */ void mySignal(); void mySignal(int,QString); public slots: private: QPushButton b; };
-
mainWidget.cpp函数:
connect(&b3,&QPushButton::released,this,&MainWidget::changeWin); //处理子窗口的信号 void (SubWidget::*funSignal)() = &SubWidget::mySignal; connect(&sub,funSignal,this,&MainWidget::dealSub); //如果重载信号,而且重载的时候会产生二义性; void (SubWidget::*testSignal)(int,QString) = &SubWidget::mySignal;//处理二义性语句 connect(&sub,testSignal,this,&MainWidget::dealSlot); //QT4的信号链接比较简单; //QT4槽函数必须有slots关键字来修饰 //connect(&sub,SIGNAL(mySignal()),this,SLOT(dealSub())); //connect(&sub,SIGNAL(mySignal(int,QString)),this,SLOT(dealSlot(int,QString))); //QT4会容易出错,这是为什么不建议使用的原因:SIGNAL SLOT将函数名字->字符串 不进行错误检查;
-
subwidget.cpp的代码:
SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { this->setWindowTitle("小弟"); b.setParent(this); b.setText("切换到主窗口"); connect(&b,&QPushButton::clicked,this,&SubWidget::sendSlot); //这里connect只是作为绑定一个信号发送者以及信号的地方, resize(400,300);//如果不设置宽度,高度。则窗口跳转过程会窗口缩小; } void SubWidget::sendSlot() { emit mySignal(); //信号有参数,则槽函数也必须有int; emit mySignal(250,"我是子窗口");//应用程序输出; }
5.Lambda表达式,是一个匿名函数对象
-
C++11增加的新特性,需要在项目文件添加:CONFIG += C++11
-
QT配合信号一起使用,非常方便;方便在于:不用定义常函数,不用指定接收者;
-
信号无参数的代码:
/* * Lambda表达式,是一个匿名函数对象 * C++11增加的新特性,需要在项目文件添加:CONFIG += C++11 * QT配合信号一起使用,非常方便;方便在于:不用定义常函数,不用指定接收者; */ QPushButton *b4 = new QPushButton(this); b4->setText("Lambda表达式"); b4->move(150,150); int a = 10,b = 100; connect(b4,&QPushButton::released, [=]() mutable //[] =:把外部所有局部变量、类中所有成员以值传递方式传进来;常用 //this:类中所有成员以值传递方式 //& :引用符号的方式传进,把外部所有局部变量传进; //举例子:b4也是一个外部局部变量,如果引用(引用其实就是变量本身)符号,则会出现数据错误,因为在操作b4这个按钮,这个里面的内存还木有释放,则会出现错误; //外部局部变量是一个按钮,是动态内存空间,则不要用引用, { b4->setText("123456");//这样子写(当[]里面什么都木有写的时候)会出现错误,说b4不在这个作用域上, //则如何更正这个方法?则[]括号的作用:把外部变量传进来,可以传很多个;但如果很多个参数,一个一个输入则会很麻烦, //如何更正?则使用=号,=:把外部所有局部变量、类中所有成员以值传递方式传进来; //**但有一个局限性,以值传递的方式默认只修饰为只读,则是不能修改原本值的数据。否则会保错。 //如何将这个局限性去除呢?在[]()的后面加一个mutable。则说明这是可变的,则里面的变量是可以修改的; qDebug()<<"1111111"; b = 200; qDebug()<<a<<b; });//有信号发送后立即调用这个qDebug();函数:void fun(){}; //这个Ladbam是匿名,则木有名字;
-
如果信号带有参数,Ladbam表达式该如何去写?代码:
//如果信号有参数时: //clicked:这个函数默认带有参数,无重载,则点击他就会返回一个false connect(b4,&QPushButton::clicked, [=](bool isCheck) //[] =:把外部所有局部变量、类中所有成员以值传递方式传进来;常用 //this:类中所有成员以值传递方式 //& :引用符号的方式传进,把外部所有局部变量传进; { qDebug()<<isCheck; });
-
注意:不是按钮发送信号,是点击按钮去调用这个发送信号的函数代码;
6.坐标系统
- 代码:(运行结果如下)
move(100,100);//为什么按钮和主窗口移动都是用move; //因为move是基类继承过来的函数,所有的都可以用他; /* * 对于父窗口(主窗口),坐标系统相对于屏幕; * 原点:相对于屏幕左上角 * x:往右递增; * y:往下递增; */ QPushButton *b1 = new QPushButton(this); b1->move(0,0); b1->setText("壳子"); b1->resize(200,200); /* * 子窗口的坐标系统相对于父窗口 * 原点:相对于窗口空白区域(不包括边框) * x:往右递增; * y:往下递增; */ QPushButton *b2 = new QPushButton(b1); b2->move(10,10); b2->setText("/(ㄒoㄒ)/~~");
7.内存回收机制(day01-04_QT_zuobiao)
-
创建使用动态分配空间的变量,按照C++的说法,需要手动去释放空间;delete ;不释放内存空间则会内存泄漏;
-
在QT中,会有一个对象树,但有个条件就是,需要变量或者类都继承于QObject;这个对象树,在程序结束时候自动回收,不需要我们手动去回收;下面是一个QObject树图;
-
当程序结束的时候,会自动回收,则如何回收呢?先从树的地下开始回收。
-
如何验证是否是自动释放?
-
因为那个QPushButton按钮不是我们自己写的,木有办法再析构函数哪里打印验证是否释放了的;
-
所以我们要自己写个按钮,右点新建一个C++类,MyButton
-
按钮是QPushButton,所以我们将基类该为QPushButton;在mybutton.h里面也需要改父类成QPushButton;
```
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include
class MyButton : public QPushButton
{
Q_OBJECT
public:
explicit MyButton(QWidget *parent = nullptr);
~MyButton();
signals:
public slots:
};
#endif // MYBUTTON_H
-
在mybutton.cpp里面也需要将父类改为QPushButton;
```
#ifndef MYWIDGET_H
#define MYWIDGET_H#include <QWidget> class MyWidget : public QWidget { Q_OBJECT public: MyWidget(QWidget *parent = 0); ~MyWidget(); }; #endif // MYWIDGET_H
-
-
在myButton.c函数代码:
#include "mybutton.h" #include <QDebug> MyButton::MyButton(QWidget *parent) : QPushButton(parent) { } MyButton::~MyButton() { qDebug()<<"析构函数被调用"; }
-
在mywidget.cpp中的代码:
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
move(100,100);//为什么按钮和主窗口移动都是用move;
//因为move是基类继承过来的函数,所有的都可以用他;
/*
* 对于父窗口(主窗口),坐标系统相对于屏幕;
* 原点:相对于屏幕左上角
* x:往右递增;
* y:往下递增;
*/
QPushButton *b1 = new QPushButton(this);
b1->move(0,0);
b1->setText("壳子");
b1->resize(200,200);
/*
* 子窗口的坐标系统相对于父窗口
* 原点:相对于窗口空白区域(不包括边框)
* x:往右递增;
* y:往下递增;
*/
QPushButton *b2 = new QPushButton(b1);
b2->move(10,10);
b2->setText("/(ㄒoㄒ)/~~");
MyButton *b3 = new MyButton(this);
b3->setText("123");
b3->move(100,100);
//添加:(1)指定父对象后,(2)直接或间接继承于QObject;
//子对象如果是动态分配空间的new,不需要手动释放delete,系统会自动释放;
8.菜单栏——工具栏(day01_05_QMainWindow)
8.1要素
- 8.1.1基类是QMainWindow
- 8.1.2菜单栏
- 代码:
//菜单栏 QMenuBar *mBar = menuBar(); //添加菜单的内容 QMenu *pFile = mBar->addMenu("文件"); //点击弹出菜单项目(添加菜单项/动作) QAction *pNew = pFile->addAction("新建"); connect(pNew,&QAction::triggered, [=]() { qDebug()<<"新建被按下"; } ); //添加分隔线; pFile->addSeparator(); QAction *pOpen = pFile->addAction("打开");
- 8.1.3工具栏
//工具栏,菜单项的快捷方式; QToolBar *toolBar = addToolBar("toolBar");//新建一个工具栏 //工具栏添加快捷键 toolBar->addAction(pNew); QPushButton *b = new QPushButton(this); b->setText("😊"); toolBar->addWidget(b);//添加小控件 connect(b,&QPushButton::clicked, [=]() { b->setText("/(ㄒoㄒ)/~~"); } );
- 8.1.4核心控件
*文本编辑label//核心控件:文本编辑器 QTextEdit *textEdit = new QTextEdit(this); setCentralWidget(textEdit);//文件编辑器
- 8.1.5状态栏
//状态栏 QStatusBar *sBar = statusBar(); //标签 QLabel *label = new QLabel(this); label->setText("Normal text file"); sBar->addWidget(label); //addwidget--从左往右添加; sBar->addWidget(new QLabel("2",this)); sBar->addPermanentWidget(new QLabel("33",this));//从右往左;
- 8.1.6 浮动窗口
- 代码,:
//浮动窗口 QDockWidget *dock = new QDockWidget(this); addDockWidget(Qt::RightDockWidgetArea,dock);//需要填写东西才可以查看帮助文档,就算数据不对也可; // addDockWidget(放置的位置:查看帮助文档F1查看,浮动窗口对象); //给浮动窗口添加控件; QTextEdit *textEdit1 = new QTextEdit(this); dock->setWidget(textEdit1);//在浮动窗口设置一个文本编辑区;
- 8.1.6代码需要添加的头文件如下:
#include "mainwindow.h" #include<QMenuBar>//菜单栏需要包含的头文件 #include <QMenu>//需要菜单头文件 #include <QAction> #include <QDebug>//打印输出包含头文件; #include <QToolBar>//工具栏需要的头文件 #include <QPushButton> #include <QStatusBar>//状态栏; #include <QLabel>//标签头文件; #include <QTextEdit>//文件编辑器所需要的头文件 #include <QDockWidget>//浮动窗口所需的头文件
9.模态和非模态对话框
- 模态:只能操作该对话框的内容
- 非模态:在能操作当前对话框的同时,也能对其他地方的对话框进行操作;
- 包含的头文件:
#include <QMenu> #include <QMenuBar> #include <QAction> #include <QDialog> #include <QDebug>
- 代码:
QMenuBar *mBar = menuBar(); setMenuBar(mBar);//设定菜单栏,不设置也会有; QMenu *menu = mBar->addMenu("对话框"); //模态对话框 QAction *p1 = menu->addAction("模态对话框"); connect(p1,&QAction::triggered, [=]() { QDialog dlg; dlg.exec();//固定的显示对话框在界面上; qDebug()<<"11111"; } ); //非模态对话框 QAction *p2 = menu->addAction("非模态对话框"); connect(p2,&QAction::triggered, [=]() { /* //QDialog dlg; dlg.show();//一闪而过?为什么?是因为运行之后就不存在了,就释放了内存空间,解决方式:可以将对话框对象设置为一个成员 qDebug()<<"22222"; */ /* //如果非要使用局部变量,则给她动态分配空间; QDialog *p = new QDialog(this); p->show(); 该方法有一个不会的地方,这个窗口会是在程序结束的时候才释放; * 假如持续性的点击按钮,则会分配很多个内存空间;会导致内存占用很多; */ //更正内存占用较多问题: QDialog *p = new QDialog;//不指定父对象,因为指定父对象,可以在程序结束后自动释放;那不指定父对象如何释放? p->setAttribute(Qt::WA_DeleteOnClose);//setAttribute函数,在窗口关闭的时候自动释放内存空间; p->show(); } );
10.标准对话框和文件对话框
10.1标准对话框
- 标准对话框的头文件:#include
//对话框 - 代码:
QAction *p3 = menu->addAction("关于对话框"); connect(p3,&QAction::triggered, [=]() { //是一个静态。如何调用,通过类名::方法名; QMessageBox::about(this,"about","关于QT");//只有一个ok按钮的对话框 } ); //关于问题的一个对话框 QAction *p4 = menu->addAction("问题对话框"); connect(p4,&QAction::triggered, [=]() { //是一个静态。如何调用,通过类名::方法名; int ret = QMessageBox::question(this,"question","Are you ok?", QMessageBox::Ok|QMessageBox::Cancel);//返回的是一个枚举值; //不指定按钮则默认两个按钮:yes no switch (ret) { case QMessageBox::Yes: qDebug()<<"I am ok"; break; case QMessageBox::Cancel: qDebug()<<"I am bad"; break; default: break; } } );
10.2文件对话框
- 头文件:#include
//文件对话框 - 代码:
QAction *p5 = menu->addAction("文件对话框"); connect(p5,&QAction::triggered, [=]() { QString path = QFileDialog::getOpenFileName(this,"open","../", "souce(*.cpp *.h);;Text(*.txt);;all(*.*)");//参数:指定父对象,标题,打开文件路径,该函数返回值是一个字符串; qDebug()<<path; } ); //这样子的文件对话框不能指定文件格式咧。 //getOpenFileName按F1查看帮助文档,就是在该函数的第四个参数里面查看; // tr("Images (*.png *.xpm *.jpg)")--tr是一个转换的字符,以空格分隔;但这个只是图片的格式; //"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"