Qt系统学习第一章(系统程序介绍,重写类,信号和槽,自定义槽和信号,重载信号槽)
第一天:
一个初始Qt程序的简介:
learn1.pro
#qt程序用到的模块 QT += core gui #QT += core gui widgets #如果是qt5.0版本以上,可以不加下面这句话,直接把 widgets 写在上面的模块中即可。这句代码是为了兼容5.0以下的版本 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS #生成的应用程序的名字 TARGET = 123 #指定生成的makefile的类型为app 还可以为lib类型(生成库) TEMPLATE = app #源文件 \ 是换行的意思 SOURCES += \ main.cpp \ mywidget.cpp #头文件 HEADERS += \ mywidget.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
mywidget.h
#ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> //从QWidget派生的 class MyWidget : public QWidget //子类MyWidget继承于父类QWidget { Q_OBJECT //宏:如果使用信号和槽,必须添加这个宏 public: //构造 MyWidget(QWidget *parent = nullptr); //析构 ~MyWidget(); }; #endif // MYWIDGET_H
main.cpp
#include "mywidget.h" #include <QApplication> //应用程序类头文件 //应用程序入口 int main(int argc, char *argv[]) { //应用程序类 每一个qt程序中有且只有一个 QApplication a(argc, argv); //应用向导时创建的窗口类 MyWidget w; //实例化窗口类对象 w (顶层窗口) //显示窗口(窗口类创建出来 默认不显示) w.show(); //应用程序类的退出。走到了后台进行了一个消息循环,监控窗口 return a.exec(); }
mywidget.cpp
#include "mywidget.h" MyWidget::MyWidget(QWidget *parent) : QWidget(parent) { } MyWidget::~MyWidget() { }
添加一个按钮
按F1可以查看类的信息;
其中 Contents是类内包含的内容
Properties:属性;类的属性
Public Functions:公开的成员函数
Public Slots:公开槽函数
Protected Functions:受保护的函数
qmake是所包含的模块、Inherits是继承于谁、Inheritd By是派生了谁
Qt内存回收机制
重写一个类(自定义一个Button继承于QPushbutton):
1、先右键单击左侧的项目树中所在项目名字,选择“添加新文件”;
2、在弹出的新的对话框中选择C++类中的C++ Class;点击choose;
3、Class name:自定义新建类的类名(必须全英文,可以大小写);本例:MyButton
Base class:下拉框中选择需要继承的类;本例:QWidget
Header file:& Source file 不需要管;单击下一步;
4、完成后,在.pro文件中,出现下面所示代码:
SOURCES += \
mybutton.cpp
HEADERS += \
mybutton.h \
5、在mybutton.h文件中,把头文件中的#include <QWidget> 改为 #include <QPushButton> 将
class MyButton : public QWidget
中的QWidget改为QpushButton(因为现在的父类是QPushButton);注意public:中的QWidget *parent中的QWidget不需要改为QPushButton,因为此处传的是parent,因为父窗口有可能是各种类型的窗口,QWidget是所有窗口类的父类,所以不管什么类型的窗口都可以指定父窗口为QWidget
6、按F4直接跳转到mybutton.cpp中,将
QWidget(parent)
改为
QPushButton(parent)
验证内存回收机制:
在mybutton.h中 在class MyButton的public中加MyButton的析构代码,再按F4跳转到mybutton.cpp中添加析构函数
MyButton::~MyButton()
{
qDebug() << "this is mybutton!";
}
在mywidget.cpp中加入自定义类头文件#include "mybutton.h",在构造函数中添加
MyButton* btn = new MyButton(this);
btn->setText("wo shi mybutton");
总代码:
.pro
#qt程序用到的模块 QT += core gui #QT += core gui widgets #如果是qt5.0版本以上,可以不加下面这句话,直接把 widgets 写在上面的模块中即可。这句代码是为了兼容5.0以下的版本 greaterThan(QT_MAJOR_VERSION, 4): QT += widgets CONFIG += c++11 DEFINES += QT_DEPRECATED_WARNINGS #生成的应用程序的名字 TARGET = 123 #指定生成的makefile的类型为app 还可以为lib类型(生成库) TEMPLATE = app #源文件 \ 是换行的意思 SOURCES += \ main.cpp \ mybutton.cpp \ mywidget.cpp #头文件 HEADERS += \ mybutton.h \ mywidget.h # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
mybutton.h
#ifndef MYBUTTON_H #define MYBUTTON_H #include <QPushButton> class MyButton : public QPushButton { Q_OBJECT public: explicit MyButton(QWidget *parent = nullptr); ~MyButton(); signals: }; #endif // MYBUTTON_H
mywidget.h
#ifndef MYWIDGET_H #define MYWIDGET_H #include <QWidget> //从QWidget派生的 #include<QPushButton> //按钮头文件 class MyWidget : public QWidget //子类MyWidget继承于父类QWidget { Q_OBJECT //宏:如果使用信号和槽,必须添加这个宏 public: //构造 MyWidget(QWidget *parent = nullptr); //析构 ~MyWidget(); private: QPushButton b1; QPushButton *b2; //b2是指针,用的时候需要new一块内存(按F4可以直接跳转到对应头文件的.cpp) }; #endif // MYWIDGET_H
main.cpp
#include "mywidget.h" #include <QApplication> //应用程序类头文件 //应用程序入口 int main(int argc, char *argv[]) { //应用程序类 每一个qt程序中有且只有一个 QApplication a(argc, argv); //应用向导时创建的窗口类 MyWidget w; //实例化窗口类对象 w (顶层窗口) //显示窗口(窗口类创建出来 默认不显示) //设置窗口大小和名称类外实现 w.resize(1000,1000); //w.setWindowTitle("1111111111"); w.show(); //应用程序类的退出。走到了后台进行了一个消息循环,监控窗口 return a.exec(); }
mybutton.cpp
#include "mybutton.h" #include<QDebug> MyButton::MyButton(QWidget *parent) : QPushButton(parent) { } MyButton::~MyButton() { qDebug() << "this is mybutton!"; }
mywidget.cpp
#include "mywidget.h" #include "mybutton.h" //自定义类头文件 MyWidget::MyWidget(QWidget *parent) : QWidget(parent) // QWidget类 Inherits: QObject and QPaintDevice { /* * 如果窗口需要依附于另外一个窗口,需要给该窗口指定父类 * 比如 一个button要依附于一个窗口,那这个窗口应该是这个button的父类 * 父窗口显示的时候,子窗口也会随之显示 */ //初始化 b2 = new QPushButton("hello,qt",this); //b2->setText("hello,qt"); //两种都可以 //b2->show(); b1.setParent(this); b1.setText("我是老二"); //改变位置 -- 移动 //窗口的坐标系 原点:窗口左上角 x:向右递增 y:向下递增 b1.move(100,100); //内存自带回收 //1、从QObject派生的类 1、直接 2、间接 //2、指定父类 父类在析构的时候,先析构子类,再析构自己 比如:本例的两个按钮,在关窗口(析构父类)的时候,Qt会先将两个按钮(子类)析构掉,再析构窗口(父类) //创建自定义按钮对象 MyButton* btn = new MyButton(this); btn->setText("wo shi mybutton"); //设置窗口大小和名称类外实现 this->setWindowTitle("mingming..."); //this->resize(1000,1000); this->setFixedSize(1000,1000); this->setWindowIcon(QIcon("C:/Users/muchai/Desktop/1")); //信号和槽 //需求 点一下 "我是老二" b1 按钮,关闭窗口 //connect(b1,发出的信号,this,处理信号的槽函数); //b1是按钮(信号发出者),this是窗口(信号接收者) connect(&b1, &QPushButton::clicked, this, &MyWidget::close); /* * b1 信号的发出者,此参数是一个指针 * &QPushButton::clicked 信号发出者内部的一个信号 //格式: & + 信号发出者类的名字 + :: + 信号的名字 * this 信号的接收者(窗口),此参数是一个指针 * &MyWidget::close 信号的处理函数,属于this //格式: & + this类的名字 + :: + 槽的名字 */ } MyWidget::~MyWidget() { }
信号和槽:
SignalAndSlot.pro 中不需要加写代码
mainwidget.h 中需要添加信号的声明
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #include <QPushButton> class MainWidget : public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = nullptr); ~MainWidget(); private: QPushButton* b1; //信号声明 (成员变量一般都定义为private) QPushButton* b2;
}; #endif // MAINWIDGET_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" MainWidget::MainWidget(QWidget *parent) : QWidget(parent) { /* * 构造函数一般内容为: * 界面初始化 * 添加按钮 * 给按钮添加信号和槽 */ //init b1 = new QPushButton("老大",this); b1->move(100,100); //b2 = new QPushButton("老二",this); b2 = new QPushButton; b2->setParent(this); b2->setText("老二"); b2->move(100,300); //b1 close window //qt信号和槽都是函数 //1、信号只有函数声明,不需要函数定义 //2、槽函数需要声明也需要定义(和普通函数一样),实质上为回调函数(并不会马上调用,而是等时机成熟后,再调用。) //比如 程序运行后,只要不点b1,就永远不会调用. connect(b1, &QPushButton::clicked, this, &QWidget::close); } MainWidget::~MainWidget() { }
实现:
自定义槽函数:
SignalAndSlot.pro 中不需要添加代码
mainwidget.h 中需要添加自定义槽函数的声明
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QMainWindow> #include <QPushButton> class MainWidget : public QMainWindow { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); //自定义槽函数声明 void slotForMainWidget(); private: QPushButton* b1; QPushButton* b2; }; #endif // MAINWIDGET_H
mainwidget.h 中需要添加自定义槽函数的定义(实现)
#include "mainwidget.h" MainWidget::MainWidget(QWidget *parent) : QMainWindow(parent) { b1 = new QPushButton("老大",this); b1->move(100,100); //b2 = new QPushButton("老二",this); b2 = new QPushButton; b2->setParent(this); b2->setText("老二"); b2->resize(90,50); b2->move(100,300); //需求 点击b1,使b2上的文本内容发生变化 //由于Qt中没有改变按钮文本的槽函数,故此处的信号接收者从b2提升为MainWidget,在MainWidget中可以访问到b2对象,再通过settext改变b2内容; //MainWidget中没有专属槽函数针对改变按扭文本的settext,故需要自定义槽函数 /* * 自定义槽函数 * * 1、槽函数在Qt5中可以是任意类成员函数(一般都是),全局函数,静态函数,lambda表达式(无函数名的隐式函数) * 2、槽函数需要与信号的(返回值,参数)相对应;信号返回值 -> 槽函数返回值 | 信号参数 -> 槽函数参数 * 3、信号没有返回值,槽函数返回值:void * 4、举例:信号: void mysig(int, double, QString); * 槽函数: void myslot(int, double, QString); * 5、槽函数的参数是为了接收信号传过来的数据 * 6、槽函数的参数,不能大于信号的参数个数,可以少于信号的参数个数(确保信号参数 -> 槽函数参数的有效性) * 7、槽函数可以重载 */ //connect(b1, &QPushButton::clicked, b2, ) connect(b1, &QPushButton::clicked, this, &MainWidget::slotForMainWidget); } MainWidget::~MainWidget() { } //自定义槽函数定义(实现) void MainWidget::slotForMainWidget() { //b2可以直接使用,因为b2是类(MainWidget)内部的成员变量 b2->setText("我被老大改名了"); }
点击“老大:按钮后,老二文本内容改变 ->
自定义信号:
需求:两个窗口(window1,window2)里各有一个按钮(btn2,btn1)。
点击window1中的btn2,使btn2隐身,window2中的btn1显示出来;
点击window2中的btn1,使btn1隐身,window1中的btn2显示出来。
步骤:
先添加一个窗口类Subwidget
SignalAndSlot.pro
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = SignalAndSlot TEMPLATE = app # The following define makes your compiler emit warnings if you use # any feature of Qt which as 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.0 SOURCES += \ main.cpp \ mainwidget.cpp \ subwidget.cpp HEADERS += \ mainwidget.h \ subwidget.h
mainwidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QMainWindow> #include <QPushButton> #include "subwidget.h" class MainWidget : public QMainWindow { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); //自定义槽函数 void slotHeidMe(); //隐藏窗口槽函数 void slotShowMe(); //显示窗口槽函数 private: QPushButton* b1; QPushButton* b2; //子窗口对象 SubWidget window2; //子窗口 QPushButton* b3; //显示子窗口 }; #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); signals: //自定义信号 必须使用signals 声明 void sigSub(); /* * 1、可以有参数 * 2、可以重载 * 3、返回值为void * 4、发送信号:emit + 信号名 * emit sigsub; */ public slots: void myslot(); private: //一般成员变量定义为private,不然有可能会破坏类的封装 QPushButton* b1; }; #endif // SUBWIDGET_H
main.cpp
#include "mainwidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWidget w; w.setFixedSize(1080,720); w.show(); return a.exec(); }
mainwidget.cpp
#include "mainwidget.h" #include "subwidget.h" MainWidget::MainWidget(QWidget *parent) : QMainWindow(parent) { setWindowTitle("window1"); //b2显示子窗口SubWidget -- window2 b2 = new QPushButton("btn2",this); b2->resize(100,50); b2->move(200,200); //connect(b2, &QPushButton::clicked, window2) //如果信号接收者是window2,在接收信号时,参数应该在window2里面写。但是在window2里面写,只能显示他自己,无法将主窗口隐藏掉,所以还是应该由父类(this)接收信号。 connect(b2, &QPushButton::clicked, this, &MainWidget::slotHeidMe); //两个窗口的显示和隐藏都在主窗口写,通过子窗口发信号告诉主窗口隐藏自己 connect(&window2, &SubWidget::sigSub, this, &MainWidget::slotShowMe); //window2窗口发送sigSub信号,MainWidget接收到信号并进行slotShowMe槽函数的使用 } MainWidget::~MainWidget() { } void MainWidget::slotHeidMe() { //隐藏自己 hide(); //显示window2窗口 window2.show(); } void MainWidget::slotShowMe() { //显示自己 show(); //隐藏window2窗口 window2.hide(); }
subwidget.cpp
#include "subwidget.h" //主窗口的子窗口,需要挂在主窗口下面,故把这个窗口作为主窗口的成员变量 SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { b1 = new QPushButton("btn1",this); b1->resize(100,100); setWindowTitle("window2"); resize(400,600); //connect(b1, &QPushButton::clicked, SubWidget,) //b1点击的目的是为了将信号发出去(sigSub),而SubWidget就可以将sigSub发出去(具体看头文件) connect(b1, &QPushButton::clicked, this, &SubWidget::myslot); } void SubWidget::myslot() { //发送信号 emit sigSub(); //回到主窗口(mainwidget)的信号 }
功能实现:
点击btn2 ->
再点击btn1 ->
重载信号槽
mainwidget.h
#ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QMainWindow> #include <QPushButton> #include "subwidget.h" class MainWidget : public QMainWindow { Q_OBJECT public: MainWidget(QWidget *parent = 0); ~MainWidget(); //自定义槽函数 void slotHeidMe(); //隐藏子窗口槽函数 void slotShowMe(); //显示子窗口槽函数 void slotSubMsg(int num, QString str); //显示子窗口信息 private: QPushButton* b1; QPushButton* b2; //子窗口对象 SubWidget window2; //子窗口 QPushButton* b3; //显示子窗口 }; #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); signals: //自定义信号 必须使用signals 声明 void sigSub(); //重载sigSub() void sigSub(int, QString); public slots: void myslot(); private: //一般成员变量定义为private,不然有可能会破坏类的封装 QPushButton* b1; }; #endif // SUBWIDGET_H
main.cpp
#include "mainwidget.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWidget w; w.setFixedSize(1080,720); w.show(); return a.exec(); }
mainwidget.cpp
#include "mainwidget.h" #include <QDebug> MainWidget::MainWidget(QWidget *parent) : QMainWindow(parent) { setWindowTitle("window1"); b2 = new QPushButton("btn2",this); b2->resize(100,50); b2->move(200,200); //Qt5 信号槽的使用方法 connect(b2, &QPushButton::clicked, this, &MainWidget::slotHeidMe); //connect(&window2, &SubWidget::sigSub, this, &MainWidget::slotShowMe); //connect(&window2, &SubWidget::sigSub, this, &MainWidget::slotSubMsg); //解决 &SubWidget::sigSub 的二义性 通过函数指针来完成 回调函数 void (SubWidget::*MySigSub)() = &SubWidget::sigSub; //Qt定义函数指针时,需要指定一个类 connect(&window2, MySigSub, this, &MainWidget::slotShowMe); void (SubWidget::*MySigPlus)(int, QString) = &SubWidget::sigSub; connect(&window2, MySigPlus, this, &MainWidget::slotSubMsg); //Qt4 信号槽 // SIGNAL() SLOT() 宏 将函数转换为字符串 //预处理(预编译)时展开宏,将所有宏替换掉,将所有头文件替换掉,把所有注释去掉 //connect(&window2, SIGNAL(signSub()), this, SLOT(slotShowMe())); //正确的 //错误的:connect(&window2, SIGNAL(signSub(int , QString)), this, SLOT(abcde(int , QString))); //没有abcdee(int , QString)这个函数,Qt4照样可以编译。 //正确的:connect(&window2, SIGNAL(signSub(int , QString)), this, SLOT(slotSubMsg(int , QString))); } MainWidget::~MainWidget() { } void MainWidget::slotHeidMe() { hide(); window2.show(); } void MainWidget::slotShowMe() { show(); window2.hide(); } void MainWidget::slotSubMsg(int num, QString str) { qDebug() << num << str; }
subwidget.cpp
#include "subwidget.h" SubWidget::SubWidget(QWidget *parent) : QWidget(parent) { b1 = new QPushButton("btn1",this); b1->resize(100,100); setWindowTitle("window2"); resize(400,600); connect(b1, &QPushButton::clicked, this, &SubWidget::myslot); } void SubWidget::myslot() { //发送信号 emit sigSub(); //回到主窗口(mainwidget)的信号 emit sigSub(250, "你是二百五!!!"); }
功能实现:
点击btn2 ->
再点击btn1 ->
//练习: // 举例: 对象1: 我 class Me: public QObject { Q_OBJECT public: Me (QObject *parent = 0); ~Me(); signals: void seeBeauty(); //看到美女 void molestBeauty(); //调戏美女 void writeLoveLetter(QString text); //写情书 void weituoLoveLetter(QString text); //委托情书 public slots: void mouthWatering(); //流口水 }; // 举例: 对象2: 美女 class Beauty: public QObject { Q_OBJECT public: Beauty (); ~Beauty(); signals: void sendLetter(QString text); // public slots: void mouthWatering(); //流口水 void beatSombody(); //打人 void answer(); //回复 - 拒绝 void answer(QString text); }; Me wo; Beauty sweet; //4.小紫 Beauty sweet1; //4.小红 // 场景: //1. 我 看到 美女 之后, 我直流口水; //自己给自己发信号 connect(&wo, &Me::seeBeauty, &wo, &Me::mouthWatering); //2. 我 调戏 美女, 美女抽我 //我发信号 其他人接收 connect(&wo, &Me::molestBeauty, &sweet, &Beauty::beatSombody); //3. 我给两个美女情书,sweet1同意,sweet拒绝 //槽函数的参数个数 小于 信号的参数个数 //1. 信号和槽重载时,需要使用函数指针 //2. 同一个信号可以连接多个槽函数 //connect(&wo, &Me::writeLoveLetter, &sweet, &Beauty::answer); //有重载的时候需要重新定义槽函数 void (Beauty::*slotAnswer)() = &Beauty::answer; connect(&wo, &Me::writeLoveLetter, &sweet, slotAnswer); void (Beauty::*slotAnswer1)(QString) = &Beauty::answer; connect(&wo, &Me::writeLoveLetter, &sweet1, slotAnswer1); //4. 我拜托小红把情书交给她的双胞胎妹妹小紫,小紫说我只喜欢会Qt的程序猿 //信号连接信号 //发出信号 connect(&wo, &Me::weituoLoveLetter, &sweet1, &Beauty::sendLetter); //我委托小红把情书交给 //指定谁来处理小红的信号 connect(&sweet1, &Beauty::sendLetter, &sweet, slotAnswer1); //小红将情书交给小紫,小紫答复
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律