一、Qt的下载和安装
1.1 Qt下载:
https://download.qt.io/archive/qt/
从上面这个网址找到对应版本的安装包下载就可以,注:Qt在5.1.5版本之后不再提供开源版本,所以我下载的是5.13.2版本的,具体网址:
https://download.qt.io/archive/qt/5.13/5.13.2/
下载到用户主目录,例如:/home/user
1.2 安装
首先查看是否有执行权限,如果没有执行权限,使用
chmod +x qt-opensource-linux-x64-5.13.2.run
命令添加执行权限,然后执行这个文件就可以完成安装
1.3 配置环境变量
使用qmake -v
命令,查看当前qmake版本,如果失败,说明环境变量没有配置
通过echo $PATH
查看当前环境变量
使用root用户或者root权限,修改/etc/enviroment文件,添加路径/home/user/Qt5.13.2/5.13.2/gcc_64/bin:/home/user/Qt5.13.2/Tools/QtCreator/bin到环境变量。
注:重启生效
/home/user/Qt5.13.2/5.13.2/gcc_64/bin —— 添加之后,qmake命令可用,assistant 命令可用
/home/user/Qt5.13.2/Tools/QtCreator/bin —— 添加之后,qtcreator命令可用
二、创建Qt程序,构建Qt应用
2.1 创建Qt工程
- 使用
mkdir projectname
创建工程目录 - 使用
cd projectname
命令切换到工程目录 - 使用
vim main.cpp
创建并编写主程序文件 - 构建工程:执行
qmake -project
,生成工程文件(pro后缀) - 使用
vim projectname.pro
打开生成的工程文件,在文件里面添加构建项QT += widgets
- 创建Makefile:使用
qmake
可以根据工程文件自动生成Makefile - 编译链接:执行
make
命令即可完成编译和链接 - 测试,执行
./projectname
,启动程序
2.2 练习程序
编写程序实现一个窗口里面有一个标签(label)和一个按钮(button),label默认显示文本为“中文”,当点击按钮后,label切换为"English"。来回切换。
// main.h
#include <QLabel>
class QLabelEx : public QLabel
{
// Q_OBJECT宏,当需要自定义信号和槽时,在类的开头加上这个宏(因为声明信号和槽的语法不是标准C++语法,加上这个之后,qmake才能正常编译)
Q_OBJECT
public:
QLabelEx(const QString &text, QWidget *parent):QLabel(text, parent){};
// 添加槽的语法,槽就是一个成员函数,加上slots关键字说明就可以
public slots:
void switchLanguage()
{
if (text() == "English")
{
setText("中文");
}
else
{
setText("English");
}
}
};
// main.cpp
#include <QApplication>
#include <QLabel>
#include <QPushButton>
#include <QDialog>
#include "main.h"
int main(int argc,char *argv[])
{
// 创建一个app对象,代表程序
QApplication app(argc, argv);
// 主窗口,也就是label和button的父窗口
QDialog parent;
parent.resize(540, 360);
QLabelEx label("中文", &parent);
label.move(20, 300);
QPushButton button("切换语言", &parent);
button.move(300, 300);
// 通过QObject::connect函数把button的clicked信号和label的switchLanguage槽绑定,这样当button对象发出clicked信号时,会自动调用label的槽函数switchLanguage。
QObject::connect(&button, SIGNAL(clicked(void)), &label, SLOT(switchLanguage(void)));
parent.show();
// 程序进入控制循环,等待外部事件
return app.exec();
}
注意:需要自定义信号和槽的类,要写在头文件里面,否则make会报错。
具体参考这篇文章的说明:https://www.cnblogs.com/superxuezhazha/p/6188993.html
三、Qt中文编码
3.1 Qt内部编码
Qt应用程序编程接口以及其内部实现所用的编码,都是unicode编码(utf-16)
当使用其他编码时,有可能会导致出现乱码
3.2 Qt编码转换
可以使用QTextCodec进行编码转换
- QTextCodec *coder = QTextCodec::codecForName(“gbk”);
- QString string = coder->toUnicode(“gbk编码的中文字符串”);
四、信号和槽
4.1 信号和槽是Qt的一种通信机制,实现对象间的数据交互
当用户或系统触发了某一动作,导致某个控件状态改变,该空间就会发射一个信号
槽函数和普通的成员函数没有太大的区别,也可以像普通成员函数一样调用。
主要区别在于用途,槽函数主要是用于相应某一个信号。也就是说当触发对应的信号时,对应的槽函数会被调用。
4.2 信号和槽的定义
// 需要直接或者间接继承自QObject类
class XX : public QObject
{
// 类开头需要添加Q_OBJECT宏
Q_OBJECT
// 信号
signals:
void signal_func(...) // 信号函数,只写声明,不能写定义
// 槽
public slots:
void slot_func(...){}
};
4.3 信号和槽的连接
使用QObject类的静态成员函数:
QObject::connect(QObject *sender, const char *signal, QObject *receiver, const char method);
其中:
sender —— 发送信号的对象的指针
signal —— 要发送的信号,使用SIGNAL(…)宏把对应的信号转换成参数需要的const char类型
receiver —— 接收信号的对象的指针
method —— 接收信号后要调用的槽函数,使用SLOT(…)宏把对应的槽函数转换成需要的类型
- 信号和槽的连接要求:
信号的参数个数要大于等于槽的参数个数
或者槽的参数个数大于信号,但是槽多出来的参数都具有默认值,此时也可以 - 信号和槽的对应关系
一对一(一个信号对应一个槽函数)
一对多 (一个信号对应多个槽函数,此时槽函数的执行顺序不确定)
多对一 (多个信号对应一个槽函数, 如果触发了多个信号,那么槽函数被调用多次)
信号也可以直接和信号相连(信号级联)
五、面向对象的Qt编程
实例:编写一个实现加法运算的计算器
// 头文件
#ifndef __CALCULATOR_H_
#define __CALCULATOR_H_
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QHBoxLayout>
#include <QDoubleValidator>
class CalcDialog : public QDialog
{
Q_OBJECT
public:
CalcDialog();
public slots:
// 槽函数
void enableButton(); // 当两个编辑框输入的都是数字时,使能等号按钮
void calculate(); // 当点击等号按钮时,计算并输出结果
private:
QLineEdit *m_editX;
QLineEdit *m_editY;
QLineEdit *m_editZ;
QLabel *m_op;
QPushButton *m_calc;
};
#endif //__CALCULATOR_H_
// cpp文件
#include "calculator.h"
CalcDialog::CalcDialog()
{
// 初始化控件
m_editX = new QLineEdit(this);
m_editX->setAlignment(Qt::AlignRight);
m_editX->setValidator(new QDoubleValidator());
// 右操作数
m_editY = new QLineEdit(this);
m_editY->setAlignment(Qt::AlignRight);
m_editY->setValidator(new QDoubleValidator());
// 显示结果
m_editZ = new QLineEdit(this);
m_editZ->setAlignment(Qt::AlignRight);
m_editZ->setReadOnly(true);
// 操作符显示
m_op = new QLabel("+", this);
// 等号按钮
m_calc = new QPushButton("=", this);
m_calc->setEnabled(false);
// 水平布局器
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_editX);
layout->addWidget(m_op);
layout->addWidget(m_editY);
layout->addWidget(m_calc);
layout->addWidget(m_editZ);
// 设置当前窗口的布局器为layout,实现布局
setLayout(layout);
// 绑定信号和槽
QObject::connect(m_editX, SIGNAL(textChanged(const QString&)), this, SLOT(enableButton()));
QObject::connect(m_editY, SIGNAL(textChanged(const QString&)), this, SLOT(enableButton()));
QObject::connect(m_calc, SIGNAL(clicked()), this, SLOT(calculate()));
}
void CalcDialog::enableButton()
{
bool b_Xok, b_Yok;
m_editX->text().toDouble(&b_Xok);
m_editY->text().toDouble(&b_Yok);
m_calc->setEnabled(b_Xok && b_Yok);
}
void CalcDialog::calculate()
{
double res = m_editX->text().toDouble() + m_editY->text().toDouble();
QString str = QString::number(res);
m_editZ->setText(str);
}
六、Qt设计师
用于界面的设计布局,可以把界面的布局通过可视化窗口配置出来,然后通过生成的ui文件自动生成头文件,减少代码编写工作量
界面设计注意事项:
- 主窗口的objectName,需要设置为类名,自动生成的代码会把这个作为类名使用
- 主窗口的WindowTitle,设置为窗口名,会在窗口标题栏显示
- 程序中要使用的控件的objectName,需要设置成对应的变量名,代码里面要通过变量名去访问控件
- 设计好的界面保存到工程目录下面
3.1 使用设计好的界面文件
通过Qt设计师窗口设计的界面,会保存为**.ui**后缀的ui文件,代码里面用到的话需要把ui文件编译成.h后缀的头文件
使用命令uic filename.ui -o ui_filename.h
完成对ui文件的编译,得到可以使用的头文件
对自动生成的类的使用,可以通过以下两种方式;
- 通过继承来使用
- 通过组合来使用
实例:实现登录窗口
- 通过Qt设计师设计界面,生成LoginDialog.ui文件
- 通过继承完成对设计师界面生成的类的使用
通过继承方式来使用Qt设计师生成的类
// 通过继承来使用Qt设计师生成的类
// 头文件
#ifndef __LOGINDIALOG_H_
#define __LOGINDIALOG_H_
#include <QDialog>
#include "ui_LoginDialog.h"
//继承Ui::LoginDialog类从而使用界面
class LoginDialogInhert : public QDialog, public Ui::LoginDialog
{
// 说明需要自定义信号和槽的宏
Q_OBJECT
public:
LoginDialogInhert(); // 构造函数
public slots:
void OnAccept(); // 槽函数,当点击ok时调用
void OnReject(); // 槽函数,当点击cancel时调用
};
#endif // __LOGINDIALOG_H_
// cpp文件
#include "loginDialogInhert .h"
#include <QMessageBox>
#include <QDebug>
LoginDialogInhert::LoginDialogInhert()
{
// 初始化界面
setupUi(this);
// 连接信号和槽
QObject::connect(m_butBox, SIGNAL(accepted()), this, SLOT(OnAccept()));
QObject::connect(m_butBox, SIGNAL(rejected()), this, SLOT(OnReject()));
}
void LoginDialogInhert::OnAccept()
{
// 当点击ok时,判读用户名和密码是否匹配
if (m_usernameEdit->text() == "user" && m_passwordEdit->text() == "123456")
{
qDebug("登录成功");
close(); // 关闭主窗口
}
else
{
// 如果不匹配,弹出提示框,这个提示框只有一个ok按钮,起提示作用
QMessageBox msgBox(QMessageBox::Critical, "错误", "用户名或密码错误", QMessageBox::Ok,this);
//
msgBox.exec();
}
}
void LoginDialogInhert::OnReject()
{
// 如果用户点击了取消,就弹出这个确认提示框,让用户确认是否真的要退出,所以有两个按钮,一个是yes,一个是no
QMessageBox msgBox(QMessageBox::Question, "退出?", "确认退出登录吗?", QMessageBox::Yes | QMessageBox::No, this);
if (msgBox.exec() == QMessageBox::Yes)
{
//如果点击了yes,那么退出登录程序
close();
}
}
通过组合的方式来使用Qt设计师生成的类
// 头文件
#ifndef __LOGINDIALOG2_H_
#define __LOGINDIALOG2_H_
#include "ui_LoginDialog.h"
#include <QDialog>
class LoginDialogCom : public QDialog
{
Q_OBJECT
public:
LoginDialogCom();
~LoginDialogCom();
public slots:
void OnAccept();
void OnReject();
private:
// 通过一个私有成员变量来使用界面
// 这种情况下,ui是构造函数里面通过new构造的指针对象,需要记得写析构函数delete掉
Ui::LoginDialog *ui;
};
#endif // __LOGINDIALOG2_H_
// cpp文件
#include "loginDialogcom.h"
#include <QDebug>
#include <QMessageBox>
LoginDialogCom::LoginDialogCom():ui(new Ui::LoginDialog())
{
// 使用组合的方式时,对界面里面的成员的操作需要通过ui->来调用对应的成员
ui->setupUi(this);
QObject::connect(ui->m_butBox, SIGNAL(accepted()), this, SLOT(OnAccept()));
QObject::connect(ui->m_butBox, SIGNAL(rejected()), this, SLOT(OnReject()));
}
LoginDialogCom::~LoginDialogCom()
{
delete ui;
}
void LoginDialogCom::OnAccept()
{
if (ui->m_usernameEdit->text() == "user" && ui->m_passwordEdit->text() == "password")
{
qDebug() << "登录成功了,哈哈哈" << endl;
close();
}
else
{
QMessageBox msgBox(QMessageBox::Critical, "错误", "用户名或密码错误", QMessageBox::Ok, this);
msgBox.exec();
}
}
void LoginDialogCom::OnReject()
{
QMessageBox msgBox(QMessageBox::Question, "确定?", "确定要退出登录吗", QMessageBox::Yes | QMessageBox::No, this);
if (msgBox.exec() == QMessageBox::Yes)
{
close();
}
}