QT是一个跨平台的C++图形用户界面(GUI)应用程序框架。
创建项目
- QMainWindow 经典Windows窗口,带有菜单栏
- QWidget 控件
- QDialog 对话框
信号和槽
与一般的GUI框架类似,Qt中也有各种各样的组件,组件之间有父子关系,都有一个大的事件循环(qApplication.exec())。但是不同的地方在于一般的GUI(例如Swing和Tkinter)都是直接通过给组件监听事件来进行交互,而Qt是通过信号(signals
)和槽(slots
)机制实现对事件的监听,个人觉得这种方式耦合性更低,功能更加强大。
信号和槽都是函数,通过connect
函数完成通信:
static QMetaObject::Connection QObject::connect
(const QObject *sender, const char *signal,
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection)
sender
是发送者,signal
是信号,receiver
是接收者,method
是槽,当信号产生时会通知接收者调用槽。
信号的特点:
-
信号必须有
signals
关键字来声明 -
信号没有返回值,但是可以有参数
-
槽函数的参数和返回值一般与信号保持一致(这意味着槽也没有返回值),槽函数的参数可以比信号少,反之不行
-
信号可以只有函数声明,没有定义
-
使用
emit mysignal()
发送信号 -
一个信号可以连接多个槽函数,槽函数的执行顺序是随机的,不能控制。一个槽函数可以被多个信号连接。信号槽连接成功后还可以使用
disconnect
取消连接。- -
信号也可以重载,不过不建议,在Qt5中挺麻烦,因为没有办法简单的通过函数名确认是哪个信号,还得通过函数指针赋值。在Qt4中可以使用
SIGNAL
和SLOT
宏,但是因为宏不会检查函数名是否错误,并且要求槽函数必须要有slots
关键字,因此不建议使用connect(A, SIGNAL(func()), B, SLOT(deal_func())); connect(A, SIGNAL(func(int, int)), B, SLOT(deal_func(int, int)));
槽的特点:
- 槽和信号的参数和返回值都必须相同,这意味槽的返回值必须是
void
。信号会将自己的参数传递给槽 - 在Qt4版本中要求槽必须有
slots
关键字,Qt5中没有要求 - 可以使用lambda表达式替代
receiver
和槽函数。需要在.pro
文件中添加CONFIG += C++11
- 如果使用lambda表达式作为槽函数,尽可能使用值传递,因为如果使用引用传递的话有可能当槽函数被触发时,所引用的局部变量已经被释放掉,从而导致访问非法空间的问题。
- 槽函数可能会再次发送信号,但是这与让槽运行的信号的发送方已经没有关系了
使用信号和槽必须在类中添加Q_OBJECT
宏
Qt中只要对象继承了QObject
,通过对象树,都会被Qt自动回收。因此不用担心new
后没有delete
。
坐标系统
- 对于主窗口,坐标系统相对于屏幕左上角
- 对于子窗口,坐标系统相对于父窗口空白区域左上角(不包括边框)
内存回收
对于QObject
,通过指定父对象将其放在对象树上,对动态申请的内存进行释放
模态对话框
模态对话框无法操作对话框后的内容,非模态对话框可以进行操作。
在使用非模态对话框时候,需要注意程序不会阻塞在对话框这里,因此需要注意对话框的生存周期应该比较长,但是不能简单简单地使用动态分配内存,因为这样对话框需要等到程序关闭时才能释放内存,会导致内存泄漏。合适的做法是在动态分配内存后设置对话框的属性,另其在关闭时自动释放内存。
//There may be memory leaks
//QDialog *dlg = new QDialog(this);
//dlg->show();
QDialog *dlg = new QDialog;
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->show();
其他
- 通过
qDebug() <<
的方式进行IO,并且自动会换行,需要头文件<QDebug>
- 为了将
QString
转换为char *
,应该首先使用.toUtf8()
方法转换成QByteArray
类型,然后再使用.data()
方法获得char *
类型 - Ctrl+R编译运行
- F4在.cpp和.h文件之间切换
- *.pro项目文件,使用头文件需要添加对应的模块
- Q_OBJECT 为信号与槽定义的宏