网站建站平台排行榜,263企业邮箱入口登录官网,深圳外贸公司上班工资高吗,淘宝网站的论坛做的怎么样目录
1.Qt简介和案例
2.第一个Qt程序
3.学会查看帮助文档
4.创建一个按钮
5.对象树简介
6.Qt的坐标系
7. 信号和槽
7.1自定义信号和槽
7.2信号连接信号
7.3拓展
7.4Qt4版本以前的connect
1.Qt简介和案例
Qt是一个跨平台的C图形用户界面应用程序框架(就是一个库吧…目录
1.Qt简介和案例
2.第一个Qt程序
3.学会查看帮助文档
4.创建一个按钮
5.对象树简介
6.Qt的坐标系
7. 信号和槽
7.1自定义信号和槽
7.2信号连接信号
7.3拓展
7.4Qt4版本以前的connect
1.Qt简介和案例
Qt是一个跨平台的C图形用户界面应用程序框架(就是一个库吧)它是完全面向对象的允许真正意义上的组件式编程。
Qt是Linux桌面环境KDE的基础它有商业版和开源版。
Qt的成功案例有很多例如Linux桌面环境KDE、WPS等等。
它的优点如下
跨平台几乎支持所有平台接口简单一定程度上简化了内存回收机制(对象树)可以进行嵌入式开发
2.第一个Qt程序
在创建项目时会提示选择三个基类当中的某一个 这三个基类之间的关系如下 QMainWindow和QDialog继承自QWidget
QWidget是一个空白窗口QMainWindow是一个带有菜单栏、工具栏、状态栏等等的一个窗口QDialog是一个提示对话框
项目创建完成后会有一个.pro文件这个文件时当前项目的工程文件里面有一些很重要的内容不要不要随意修改.pro文件的内容甚至不要加注释。 可以看到默认情况下Qt包含了core模块和gui模块如果是4版本以上的话还会添加一个widgets模块。原因在于4版本以前widgets模块属于gui模块4版本以后将widgets独立出来了。
当前项目自动构建出了一些源文件和头文件可以很轻松的找到主函数 其中应用程序对象有且仅有一个 Widget类继承自QWidget调用该类的show方法可以单独显示一个窗口 注意Widget类定义当中的Q_OBJECT这是一个宏它的作用是让该类支持信号和槽机制。还要注意其构造函数有一个名为parent指向QWidget类的指针这与对象树有关稍后做解释。
3.学会查看帮助文档
学习任何技术都需要有帮助文档Qt提供了这样的帮助文档。选中关键字并且按下F1可以弹出帮助文档 还有一种方法是直接打开帮助文档的独立引用程序它的位置在Qt安装路径/版本号/mingw_64/bin下 4.创建一个按钮
Qt当中的按钮类为QPushButton通过帮助文档可以查看的到 接下来在代码当中创建一个按钮并让其显示
#include widget.h#include QApplication
#include QPushButton // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn new QPushButton;btn-show();// 显示w.show();return a.exec();
} 可以看到如果使用QPushButton类的show方法是单独打开一个窗口并显示。很显然这并不是想要的效果。如果想要依附于已存在的窗口显示只需要为按钮指定一个父亲即可
#include widget.h#include QApplication
#include QPushButton // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn new QPushButton;btn-setParent(w);// 依附于已经存在的窗口//btn-show();// 显示w.show();return a.exec();
}此时按钮便依附于已存在的窗口了。如果想要设置一下按钮的大小、显示文字、改变位置可以使用QPushButton类的某些成员函数
#include widget.h#include QApplication
#include QPushButton // 包含头文件
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn new QPushButton;btn-setParent(w);// 依附于已经存在的窗口btn-setText(First Button);// 显示文字btn-resize(200,200);// 设置一下按钮的大小btn-move(200,100);// 移动到指定为止//btn-show();// 显示w.resize(600,400);// 固定一下窗口的大小w.show();return a.exec();
}5.对象树简介
在没有接触Qt之前上面的代码对于C程序员来说是难受的因为new了一个QPushButton对象却并没有做delete操作。
事实上在Qt当中有些情况下并不需要做delete操作这就是对象树的作用。
对象树是Qt当中的一种自动回收内存的机制它可以简化程序员的工作。就如上面的代码程序员可以不用担心内存泄漏即不用delete。
想要不delete堆上的内存空间必须符合两个条件
new出来的对象必须是QObject的亲缘类对象该对象必须指定一个QObject的亲缘类对象
QObject是最顶层的基类基本上Qt当中的内置类都继承自于QObect。因此比如QWidget、QMainWindow、QDialog、QPushButton等等它们的最顶层的基类就是QObject。
当new出来的对象指定了父亲之后就相当于加入了对象树。这颗对象树的任意节点(对象)析构时会自动释放其所有子孙节点(对象)。
此时可以做一个实验即自定义一个类取名为MyTest并继承自QPushButton类指定它的父亲为Widget类
#ifndef MYTEST_H
#define MYTEST_H#include QWidget
class MyTest : public QWidget
{Q_OBJECT
public:explicit MyTest(QWidget *parent nullptr);~MyTest();
signals:};#endif // MYTEST_H
#include mytest.h
#include QDebug
MyTest::MyTest(QWidget *parent): QWidget{parent}
{}MyTest::~MyTest()
{qDebug() ~MyPushButton();
}修改一下Wdiget类的析构函数
#include widget.h
#include QDebug
Widget::Widget(QWidget *parent): QWidget(parent)
{
}Widget::~Widget()
{qDebug() ~Widget();
}
然后在主函数当中new出来MyPushButton类指定其父亲为Wdiget运行后关闭窗口观察打印结果
#include widget.h#include QApplication
#include QPushButton // 包含头文件
#include mytest.h
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;MyTest *mbtn new MyTest(w);w.resize(600,400);w.show();return a.exec();
} 关闭窗口后的输出结果可以看到mbtn所指向的堆空间并没有手动delete但是由于对象树的存在不会造成内存泄漏。
这里可以用一幅图来解释 此时可能会有个问题即刚才说的是对象树当中的某个对象释放时会先析构其所有子孙节点。那么为什么先打印父对象的析构后打印子孙对象的析构呢这个原因在于析构函数分成两部分执行一是执行析构函数函数体内的代码二是执行函数体之后的清理工作。函数体内的代码是释放当前对象的大部分资源例如对象内部new出来的空间函数体之后是清理对象本身。所以对象树可以理解成清理对象本身之前先析构其所有子孙对象。
6.Qt的坐标系
Qt的窗口是有坐标系的上面在控制按钮的位置时涉及到坐标系。Qt窗口的坐标系如下图所示 7. 信号和槽
其实信号和槽可以理解为信号和信号处理。熟悉Linux系统编程的话理解信号和槽不会太难。
信号和槽涉及到两个角色即信号的发送方和信号的接收方涉及到两个动作即信号的发送和信号的处理。
总体而言可以分成两个部分 connect是一个函数作用类似于Linux当中的signal()。它的作用我个人理解成注册一个信号产生时需要做的动作。
通过帮助文档可以查看该函数 原型可以是这样的 connect(信号的发送发要发送的信号信号的接收方接收到信号后要做的动作) 做一个小任务即创建一个按钮点击该按钮后关闭窗口
#include widget.h#include QApplication
#include QPushButton // 包含头文件#include mytest.h
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;QPushButton *btn new QPushButton(w);btn-setText(PushButton);btn-move(200,100);btn-resize(100,100);QObject::connect(btn,QPushButton::clicked,w,Widget::close);// 注册信号w.show();return a.exec();
} 该按钮点击后窗口就关闭了。
综上信号和槽的优点非常明显
使得编程多样化可以实现更多丰富的功能非常自由低耦合信号的发送方和接收方是低耦合的这样设计有利于代码维护出错的概率降低
7.1自定义信号和槽
如果自定义了一些类并且想让这些类也能够使用信号和槽机制Qt是支持的。
创建一个Student类和Teacher类signals关键字下写自定义信号函数该函数只声明不实现public slots关键字(较高版本的Qt可以写到public下和全局下)下写自定义槽函数该函数要声明和定义。
当前的任务是Teacher发出一个下课信号学生做出走出教室动作。
#ifndef TEACHER_H
#define TEACHER_H#include QObjectclass Teacher : public QObject
{Q_OBJECT
public:explicit Teacher(QObject *parent nullptr);signals:void ClassOver();// 下课只声明不实现
};#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H#include QObjectclass Student : public QObject
{Q_OBJECT
public:explicit Student(QObject *parent nullptr);void GoOutOfClass();// 要声明和实现
signals:};#endif // STUDENT_H
#include student.h
#include QDebug
Student::Student(QObject *parent): QObject{parent}
{}
void Student::GoOutOfClass()
{qDebug() 学生走出教室!;
}
此时在主函数当中完成任务
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu new Student;Teacher *tec new Teacher;QObject::connect(tec,Teacher::ClassOver,stu,Student::GoOutOfClass);emit tec-ClassOver();// emit关键字发送信号delete stu;delete tec;return a.exec();
} 此时要注意一个关键字即emit它的作用在于触发信号。
其他方面无论是信号函数还是槽函数都是可以带参数的。
这里再做一个实验即让信号函数之间发生重载槽函数之间发生重载
#ifndef TEACHER_H
#define TEACHER_H#include QObject
#include QString
class Teacher : public QObject
{Q_OBJECT
public:explicit Teacher(QObject *parent nullptr);signals:void ClassOver();// 下课只声明不实现void ClassOver(QString str);
};#endif // TEACHER_H
#ifndef STUDENT_H
#define STUDENT_H#include QObject
#include QString
class Student : public QObject
{Q_OBJECT
public:explicit Student(QObject *parent nullptr);void GoOutOfClass();// 要声明和实现void GoOutOfClass(QString str);
signals:};#endif // STUDENT_H
#include student.h
#include QDebug
Student::Student(QObject *parent): QObject{parent}
{}
void Student::GoOutOfClass()
{qDebug() 学生走出教室!;
}void Student::GoOutOfClass(QString str)
{qDebug() 老师说: str;
}那么在主函数当中修改一下代码
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu new Student;Teacher *tec new Teacher;QObject::connect(tec,Teacher::ClassOver,stu,Student::GoOutOfClass);emit tec-ClassOver(下课啦!);// emit关键字发送信号delete stu;delete tec;return a.exec();
} 此时出现了编译报错原因在于connect函数当中的参数信号函数和槽函数根本没有明确唯一指向导致编译器报错。解决方法是利用函数指针来明确唯一指向
int main(int argc, char *argv[])
{QApplication a(argc, argv);Student *stu new Student;Teacher *tec new Teacher;void (Teacher::*tecSignal)(QString) Teacher::ClassOver;void (Student::*stuSlot)(QString) Student::GoOutOfClass;QObject::connect(tec,tecSignal,stu,stuSlot);emit tec-ClassOver(下课啦!);// emit关键字发送信号delete stu;delete tec;return a.exec();
}
需要注意在声明和定义槽函数时它们的返回值都为void。
如果需要取消注册的话可以使用disconnect函数这里就不做示例了。
7.2信号连接信号
信号不一定非要与槽函数connect在一起信号和信号之间也可以连接。
现在实现一个需求即创建一个按钮按钮点击后老师下课学生走出教室。此时代码可以修改为
int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;Student *stu new Student(w);Teacher *tec new Teacher(w);void (Teacher::*tecSignal)() Teacher::ClassOver;void (Student::*stuSlot)() Student::GoOutOfClass;QObject::connect(tec,tecSignal,stu,stuSlot);QPushButton *btn new QPushButton(w);btn-setText(PushButton);btn-resize(200,100);QObject::connect(btn,QPushButton::clicked,tec,tecSignal);w.resize(600,400);w.show();return a.exec();
} 此时点击按钮会让老师产生下课信号随后学生走出教室。
但是注意这里有个非常局限的东西那就是信号连接信号时作为槽函数的信号函数无法传递参数这个时候就要配合全局函数或者lambda表达式即在它们内部触发信号
7.3拓展
下面的规则是总结和拓展
一个信号可以连接多个槽函数多个信号可以连接同一个槽函数信号和槽函数的参数类型必须一一对应信号函数的参数个数可以多于槽函数的参数个数
对于规则3和4的解释为槽函数和可以选择接收和不接受信号传递过来的参数如果选择接收了信号的参数那么类型必须一致。以一幅图来理解 7.4Qt4版本以前的connect
上面介绍的都是Qt4版本之后的connect函数用法在Qt4版本之前写法是下面这样的
QObject::connect(tec,SIGNAL(Teacher::ClassOver(QString)),stu,SLOT(Student::GoOutOfClass(QString)));
显然这样的方式是非常直观的因为可以免除函数指针的繁琐写法。但是这种写法必然存在恶劣的缺点否则较新的Qt版本也不会出现较新的写法。
根本原因在于SIGNAL和SLOT关键字会将括号内容字符串化并且这些字符串之间还不会做任何的比较所以导致编译器无法报错导致后续维护项目困难。