创建项目时需要选择基类
Qt窗口基类:QMainWindow、QWidget、QDialog。
QMainWindow是用于创建带菜单栏的PC端窗口的基类
QWidget是所有图形界面中控件的基类
QDialog是用于创建对话框的基类
.pro是项目文件。因为Qt选择的项目管理工具为qmake,qmake根据.pro文件存储的项目信息生成对应的Makefile,然后在使用make命令来编译项目文件
.pro是使用qmake语言编写的。每行#
后的内容为注释;QT += core gui
表示引入Qt库中的Core和Gui模块;greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
表示Qt版本大于4时引入Qt库中的Widgets模块**(在Qt5项目中添加了Widgets模块则无需再添加GUI模块)**;TARGET = test
表示应用程序即生成的.exe文件名为test;TEMPLATE = app
表示指定Makefile的类型可执行程序
指定源文件
SOURCES += \
main.cpp \
widget.cpp
指定头文件
HEADERS += \
widget.h
Qt5的基本模块:Qt Widgets、Qt Webkit、Qt Test、Qt SQL、Qt Quick、Qt QML、Qt Mulitmedia Widgets、Qt Core、Qt GUI、Qt Network、Qt Mulitmedia、Qt Quick Controls、Qt Quick Dialogs
widget.h、widget.cpp、main.cpp都是项目自动生成的。其中widget.h和widget.cpp是实现控件的代码,继承于QWidget,创建时默认提供默认构造函数和默认析构函数。main.cpp则同所有C++项目一样是程序的执行入口
widget.h文件创建时可能包括信号与槽的内容,等后面学习时再来理解
main.cpp
//引入头文件
#include "widget.h" //窗口控件类
#include //QApplication是Qt的应用程序类
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //QApplication应用程序类有且只能有一个实例化对象
Widget w; //初始化窗口控件(默认隐藏)
w.show(); //手动显示窗口控件
return a.exec(); //a.exec()作用为使程序进入循环,等待事件发生。若为return 0,则程序只运行一次就直接结束
}
项目文件夹中可能还会存在.user文件,保存的是项目所有者用户的项目信息,包括项目编译的路径等。在使用他人的Qt项目或者更换编译环境时无法直接编译,需要先删除.user文件再进行编译
新建一个空项目HelloQt,项目中只包含一个空的项目文件HelloQt.pro,添加内容QT += widgets
在项目中新建一个C++源文件main.cpp
#include
#include //窗口控件基类
#include //按钮类
int main(int argc, char **argv)
{
QApplication a(argc, argv);
QWidget w;
w.setWindowTitle("Hello QT Wolrd"); //设置窗口标题
QPushButton b;
b.setText("Hello~"); //设置按钮文本
b.setParent(&w); //设置按钮的父对象
b.move(100,100) //移动按钮到指定坐标,窗口左上角为零点
QPushButton b1(&w); //通过构造函数传参指定父对象
b1.setText("Wolrd~");
w.show(); //显示窗口及子控件
a.exec();
return 0;
}
控件之间如果不指定父对象,则各个控件为独立的窗口,没有联系。指定父对象,子对象会显示在父对象的窗口内,当父对象执行show()
函数时,子对象也会自动显示,不需要手动执行show()
函数。
指定父对象的方法:
setParent(QWidget parent = nullptr)
来指定控件的父对象
是Qt对象之间通信的接口
#include
#include
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QPushButton button("Quit");
QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);//连接函数
button.show();
return app.exec();
}
使用connect()函数来实现信号与槽的连接。在main函数中使用时,需要使用作用域限定符说明connect所属的类为QObject。即QObject::connect();
原型:connect(sender, signal, receiver, slot);
作用:连接信号与槽,使指定的信号发生时,回调已连接的槽函数来处理信号
参数:
sender
为QObject类型,是发送信号的控件指针。一般对发送者取引用来使用signal
为发送信号的指针,通过查询当前控件类的文档来得知有哪些信号类型,使用时需要作用域限定符指定所属的控件类。一般对信号取引用来使用receiver
为QObject类型,是接受信号的控件指针。一般对接受者取引用来使用slot
为槽函数指针,也是需要查询文档选择槽函数,以及使用作用域限定符指定所属控件类。一般对槽函数取引用来使用
信号需要在类中用signals关键字来声明。signals是Qt的关键字,不是C++关键字
信号没有返回值,但可以有参数
信号可以重载
信号就是函数的声明,只需声明,无需定义
使用方法为emit mySignal();
相关代码实例
SubWidget.h
#include
#include
class SubWidget : public QWidget
{
public:
void mySlot();
signals:
void mySignal();
private:
QPushButton b;
};
SubWidget.cpp
#include "SubWidget.h"
SubWidget::SubWidget(QWidget *parent) : QWidget(parent)
{
b.setParent(this);
connect(&b, &QPushButton::clicked, this, &SubWidget::mySlot);
}
SubWidget::mySlot()
{
emit mySignal();//发送自定义信号
}
信号重载后,在使用connect函数时需要解决信号二义性的问题
conncet(&subWin, &SubWidget::mySignal, this, &MainWidget::mySlot1);
conncet(&subWin, &SubWidget::mySignal, this, &MainWidget::mySlot2);
解决方法:
使用函数指针代替信号作为实参
void (SubWidget::*f1)() = &SubWidget::mySignal;
connect(&subWin, f1, this, &MainWidget::mySlot1);
void (SubWidget::*f2)(int, QString) = &SubWidget::mySignal;
connect(&subWin, f2, this, &MainWidget::mySlot2);
因为这里使用的是SubWidget类中定义的信号mySignal,所以函数指针需要添加SubWidget的作用域予以限定
Qt4的信号连接
class MainWidget : public QWidget
{
public slots:
void mySlot1();
void mySlot2(int, QString);
};
conncet(&subWin, SIGNAL(mySignal()), this, SLOT(mySLot1()));
conncet(&subWin, SIGNAL(mySignal(int, QString)), this, SLOT(mySLot1(int, QString)));
Qt4的槽函数必须用slots关键字修饰
SIGNAL和SLOT两个宏会将信号和槽的函数名转换成字符串,不进行错误检查。当函数名出现错误时编译不会报错
自定义槽可以使用任意的成员函数、普通的全局函数、静态函数作为槽函数
槽函数的参数和返回值需要和信号一致。因为信号没有返回值,所以槽函数也应该没有返回值。
一个信号可以绑定多个槽函数,多个信号也可以绑定一个槽函数
相关代码实例
MainWidget.h
#include
#include
class MainWidget : public QWidget
{
public:
void mySlot();
private:
QPushButton b;
}
MainWidget.cpp
#include "MainWidget.h"
MainWidget::MainWidget(QWidget *parent) : QWidget(parent)
{
QPushButton b;
b.setParent(this);
connect(&b, &QPushButton::released, this, &MainWidget::mySlot);
}
void MainWodget::mySlot()
{
b.setText("123");
}
因为Lamda表达式是C++11的新特性,所以需要才.pro项目文件中添加CONFIG += C++11
QPushButton *b = new QPushButton(this);
conncet(b4, &QPushButton::clicked, this,
[](bool isChecked)
{
qDebug << isChecked;
}
);
信号QPushButton::clicked
是有一个bool类型的参数checked的,且默认值为false,所以槽函数也需要有一个同样类型的参数isChecked传入Lamda表达式中
每个窗口或者控件的坐标是相对于父对象或者主屏幕而言的。对于没有父对象的窗口或者控件,其坐标位置是相对于主屏幕的;对于有父对象的窗口或者控件,其坐标位置是相对于父对象的
原点:主屏幕或父对象的左上角
x轴:以向右为正方向
y轴:以向下为正方向
Qt维护着一个对象树,在程序结束时从下往上自动回收对象内存
在创建 QObject 对象时,可以提供一个其父对象,我们创建的这个QObject对象会自动添加到其父对象的children列表。当父对象析构的时候,这个列表中的所有对象也会被析构
在释放QObject对象时,如果该对象有父对象,则将该对象从其父对象中的children列表中删除;如果该对象有children列表,则自动释放列表中的所有对象
QWidget是能够在屏幕上显示的一切组件的父类。QWidget继承于QObject,因此也继承了这种对象树关系
使用QMainWindow作为项目框架的基类进行开发
QMainWindow是Qt预设好的主窗口,QMainWindow继承于QWidget
一个QMainWindow窗口包括菜单栏、工具栏、停靠窗口、状态栏、中央窗口
#include "MyMainWindow.h"
#include
#include
#incldue <QAction>
#include
#include
#include
#include
#include
#include
#include
MyMainWindow::MyMainWindow(QWidget *parent) : QMainWindow(parent)
{
//菜单栏
QMenuBar *mBar = menuBar();//获取菜单栏。QMainWindow自带菜单栏,不需要new新建
QMenu *pFile = mBar->addMenu("File");//添加菜单
QAction *pNew = pFile->addAction("new");//添加菜单项,
connect(pNew, &QAction::triggered, this,
[=]()
{
qDebug() << "new done!";
}
);//通过信号将菜单项与具体实现连接起来
pFile->addSeperator();//添加分割线
//工具栏。是菜单项的快捷方式
QToolBar *toolBar = addToolBar("toolBar");
toolBar->addAction(pNew);//直接使用菜单项指针添加快捷键
QPushButton *b = new QPushButton(this);//通过创建控件添加快捷键
b->setText("^_^");//此行代码可以改为设置图片
toolBar->addWidget(b);//将新建控件加入到工具栏中
connect(b, &QPushButton::clicked, this
[=]()
{
b->setText("123");
}//通过信号将快捷键与具体实现连接起来
);
//状态栏
QStatusBar *sBar = statusBar();//获取状态栏
QLabel *label = new QLabel(this);//新建标签并指定父对象
label->setText("Normal text file");//设置标签名字
sBar->addWidget(label);//将标签添加到左下角状态栏,添加顺序为从左往后
sBar->addPermanentWidget(label);//将标签添加到右下角状态栏,添加顺序为从左往后
//中央窗口
QTextEdit *textEdit = new QTextEdit(this);//新建文本窗口
setCentralWidget(textEdit);//将文本窗口设置为中央窗口
//停靠窗口(浮动窗口)
QDockWidget *dock = new QDockWidget(this);//新建浮动窗口
addDockWidget(Qt::RightDockWidgetArea, dock);//将浮动窗口添加到主窗口中。第一个参数是枚举类型,需要查看帮助文档,用于设置浮动窗口停靠的方向
QTextEdit *tEdit = new QTextEdit(this);
dock->setWidget(tEdit);
}
对话框分为模态对话框和非模态对话框。模态对话框就是指除非对话框被关闭,否则不能和同一个应用程序的其他窗口及进行交互;非模态对话框就是指当对话框被打开时,用户既可以和对话框交互,也可以和同一应用程序中的其他窗口进行交互
显示一个对话框的方法:
使用exec()
:它总是以模态来显示对话框。可以通过函数返回值判断用户退出对话框时点击的按钮
QDialog dlg;
dlg.exec();
使用show()
:既能以模态显示,也能以非模态显示对话框。具体由对话框的modal属性决定
QDialog dlg1, dlg2;
//模态
dlg1.setModal(true);
dlg1.show();
//非模态
dlg2.setModal();
dlg2.show();
对话框的modal属性:默认值为false,意思为非模态,此时show()
函数显示的对话框是非模态的;若将值设置为true,意思为模态的,此时show()
函数显示的对话框是模态的。modal属性的值可以通过对话框控件的成员函数setModal(bool)
来改变,参数默认为false。
exec()
是阻塞的,show()
是非阻塞的。当使用exec()
显示对话框时,程序会阻塞在该函数直到返回;当使用show()
显示对话框时,程序会按顺序继续执行下去。
由于show()
是非阻塞的,如果将QDialog对象设置为局部变量,则会导致对话框生命周期太短,对话框只会闪烁一下即结束。所以需要使用new动态分配内存空间QDialog *dlg = new QDialog();
。并且通过dlg->setAttribute(Qt::WA_DeleteOnClose);
将对话框设置为关闭即释放内存,防止内存泄漏
需要头文件#include
标准对话框是模态对话框
可以通过QMessageBox的静态成员函数快速创建一个标准对话框:
QMessage::about(parent,title,text,);
是关于对话框。参数parent指父对象,title指对话框的标题,text指对话框的文本内容,QMessage::question(parent,title,text,buttons);
是问题对话框。前三个参数同上,buttons想要使用的按钮集合,默认对话框上只有YES和NO两个按钮。返回值为StandButton类型,可以通过返回值判断用户退出对话框所按下的按钮也可以手动配置对话框的各个属性:
QMessageBox msgBox;
msgBox.setWindowTitle("This is the Title");
msgBox.setText("This is the Text");
msgBox.setStandardButton(QMessageBox::OK | QMessageBox::Close);
msgBox.setDefaultButton(QMessageBox::OK);
msgBox.setAttribute(Qt::WA_DeleteOnClose);//避免内存泄漏
msg.exec();
需要头文件#include
文件对话框是用于操作文件的对话框
使用QFileDialog类的全局静态函数QFileDialog::getOpenFileName(parent,caption,dir,filter,selectedFilter,options);
创建文件对话框。参数parent是指定父对象,caption是对话框标题,dir是打开对话框时的默认目录,filter是过滤器,selectedFilter是指定默认的过滤器,options是其他的一些枚举类型的参数。函数返回被选择文件的路径
其他静态函数QFileDialog::getSaveFileName();
也可以创建文件对话框,返回值也是所选文件路径