Qt是一个跨平台的C++图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面所需的所有功能。它是完全面向对象的,很容易扩展,并且允许真正的组件编程。
.pro就是工程文件(project),它是qmake自动生成的用于生产makefile的配置文件
QT += core gui //包含的模块
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets //大于Qt4版本 才包含widget模块
CONFIG += c++11 //使用c++11的特性
DEFINES += QT_DEPRECATED_WARNINGS
# 工程中包含的文件
SOURCES += \ //源文件
main.cpp \
widget.cpp
HEADERS += \ //头文件
widget.h
FORMS += \ //设计文件
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget> //包含头文件 QWidget 窗口类
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT //Q_OBJECT宏,允许类中使用信号和槽的机制
public:
Widget(QWidget *parent = nullptr); //构造函数
~Widget(); //析构函数
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
#include "widget.h"
#include <QApplication>
//main程序入口 argc命令行变量的数量 argv命令行变量的数组
int main(int argc, char *argv[])
{
QApplication a(argc, argv); //a应用程序对象,在Qt中,应用程序对象 有且仅有一个
Widget w; //窗口对象 Widget父类 -> QWidget
w.show(); //窗口对象 默认不会显示,必须要调用show方法显示窗口
return a.exec(); //让应用程序对象进入消息循环
}
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)//构造函数
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget() //析构函数
{
delete ui;
}
信号和槽是 Qt 的核心,它让两个互不相干的对象连接起来,当一个对象的状态改变时,可以通知另一个对象。在Qt中我们需要使用connect函数进二者的关联。
connect(* sender, &signal, * receiver, &slot);
参数解释:
例子:点击按钮,关闭窗口
//创建一个按钮
QPushButton * btn = new QPushButton("关闭窗口",this);
//连接信号与槽
connect(btn, &QPushButton::clicked, this, &QWidget::close);
/*
btn -信号发送者(这里是按钮)
&QPushButton::clicked -发送的信号(这里是点击按钮信号)
this -信号接受者(这里是本窗口Widget)
&QWidget::close -信号接收者收到信号干的事(这里是调用的是关闭窗口的函数)
*/
案例–下课后,老师触发饿了信号,学生响应信号,请客吃饭
首先定义一个学生类和老师类:
老师类中声明信号 饿了 hungry
signals:
void hungury();//只需要声明,不需要实现
学生类中声明槽 请客 treat
public slots://早期Qt版本 必须要写到public slots,高级版本可以写到 public或者全局下
void treat();//需要声明,需要实现
在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客
void MyWidget::ClassIsOver()
{
//下课函数,调用后,触发老师饿了的信号
emit teacher->hungury();
}
学生响应了槽函数,并且打印信息
//自定义槽函数 实现
void Student::treat()
{
qDebug() << "请老师吃饭!";
}
定义对象
teacher = new Teacher(this);
student = new Student(this);
在窗口中连接信号槽 connect(teacher,&Teacher::hungury,student,&Student::treat);
并且调用下课函数,测试打印出 “请老师吃饭!”
ClassIsOver();
自定义信号和发生重载的解决办法
自定义的信号 hungry带参数,需要提供重载的自定义信号和 自定义槽
void hungury(QString name); 自定义信号
void treat(QString name ); 自定义槽
但是由于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后在做连接
void (Teacher:: * teacherSingal)(QString) = &Teacher::hungury;
void (Student:: * studentSlot)(QString) = &Student::treat;
connect(teacher,teacherSingal,student,studentSlot);
C++11 中的 Lambda 表达式用于定义并创建匿名的函数对象,以简化编程工作。用 Lambda 表达式,我们就不需要在类中对槽函数做任何的声明了。Lambda 表达式是 C++ 11 的内容,在比较低的 Qt版本中,要注意在 Pro 项目文件中加入 CONFIG += C++ 11。
Lambda表达式的基本构成:
[capture](parameters) mutable ->return-type
{
statement
}
//[外部变量访问方式说明符](操作符重载函数参数)mutable ->函数返回值{函数体}
① 外部变量访问方式说明符
[]
,标识一个 Lambda 的开始,这部分必须存在,不能省略。外部变量访问方式说明符只能使用定义 Lambda 为止时 Lambda 所在作用范围内可见的局部变量(包括 Lambda 所在类的 this)。外部变量访问方式说明符有以下形式:
空
。没有使用任何函数对象参数。
=
。函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
&
。函数体内可以使用 Lambda 所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
this
。函数体内可以使用 Lambda 所在类中的成员变量。
a
。将 a 按值进行传递。按值进行传递时,函数体内不能修改传递进来的 a 的拷贝,因为默认情况下函数是const 的。要修改传递进来的 a 的拷贝,可以添加 mutable 修饰符。
&a
。将 a 按引用进行传递。
a, &b
。将 a 按值进行传递,b 按引用进行传递。
=,&a, &b
。除 a 和 b 按引用进行传递外,其他参数都按值进行传递。
&, a, b
。除 a 和 b 按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数
标识重载的 () 操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标示符
mutable
,这部分可以省略。按值传递函数对象参数时,加上 mutable 修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
QPushButton * myBtn = new QPushButton (this);
QPushButton * myBtn2 = new QPushButton (this);
myBtn2->move(100,100);
int m = 10;
connect(myBtn,&QPushButton::clicked,this,[m] ()mutable { m = 100 + 10; qDebug() << m; });//m=110
connect(myBtn2,&QPushButton::clicked,this,[=] () { qDebug() << m; });//m=10
qDebug() << m;//m=10
④ 函数返回值
->返回值类型
,标识函数返回值的类型,当返回值为void,或者函数体中只有一处 return 的地方(此时编译器可以自动推断出返回值类型)时,这部分可以省略。
int ret = []()->int{return 1000}();
qDebug() << "ret=" << ret; //ret=1000
⑤ 函数体
{},标识函数的实现,这部分不能省略,但函数体可以为空。
扩展知识: 按值传递与按引用传递的区别?
按值传递,不会改变当前调用函数里实参,按引用传值,会改变调用里面实参。 按值传递,可以保护实参不被修改,效率比较低。 按引用传值,本质是传递一个指针指向地址值,实参会发生修改,效率更高。
QMainWindow是一个为用户提供主窗口程序的类,包含一个菜单栏(menu bar)、多个工具栏(tool bars)、多个锚接部件(dock widgets)、一个状态栏(status bar)及一个中心部件(central widget),是许多应用程序的基础,如文本编辑器,图片编辑器等。
一个主窗口最多只有一个菜单栏。位于主窗口顶部、主窗口标题栏下面。
//创建菜单 只能一个
QMenuBar * bar = menuBar();
//添加到窗口中
setMenuBar(bar);
//菜单栏
QMenu * fileMenu = bar->addMenu("文件");
QMenu * editMenu = bar->addMenu("编辑");
//菜单项
QAction * newAction = fileMenu->addAction("新建");
//添加分割线
fileMenu->addSeparator();
QAction * openAction = fileMenu->addAction("打开");
主窗口的工具栏上可以有多个工具条,通常采用一个菜单对应一个工具条的的方式,也可根据需要进行工具条的划分。
setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea)
setMoveable(false)
//工具条不可移动, 只能停靠在初始化的位置上
代码承接上面菜单栏部分代码
//创建工具栏 可以多个
QToolBar * toolBar = new QToolBar(this);
//添加到窗口,设置默认停靠区域
addToolBar(Qt::LeftToolBarArea,toolBar);
//设置允许的停靠区域 左右停靠
toolBar->setAllowedAreas(Qt::LeftToolBarArea | Qt::RightToolBarArea);
//设置浮动 不可浮动
toolBar->setFloatable(false);
//设置移动 不可移动
toolBar->setMovable(false);
//工具栏
toolBar->addAction(newAction);
toolBar->addSeparator(); //添加分割线
toolBar->addAction(openAction);
//工具栏添加控件
QPushButton * btn = new QPushButton("按钮",this);
toolBar->addWidget(btn);
QStatusBar 派生自 QWidget 类,使用方法与 QWidget 类似,状态栏只能有一个。
代码承接上面
//创建状态栏 只能一个
QStatusBar *stBar = statusBar();
//设置到窗口中
setStatusBar(stBar);
//状态栏添加标签控件
QLabel * label = new QLabel("提示信息",this);
stBar->addWidget(label);
QLabel * label2 = new QLabel("右侧提示信息",this);
stBar->addPermanentWidget(label2);
铆接部件 QDockWidget,也称浮动窗口,可以有多个。
//浮动窗口 可以多个
QDockWidget * dockWidget = new QDockWidget("浮动窗口",this);
//设置到窗口中
addDockWidget(Qt::BottomDockWidgetArea,dockWidget);
//设置允许的停靠区域 上下停靠
dockWidget->setAllowedAreas(Qt::TopDockWidgetArea | Qt::BottomDockWidgetArea);
除了以上几个部件,中心显示的部件都可以作为核心部件,例如一个记事本文件,可以利用QTextEdit做核心部件,中心部件只能有一个。
//设置中心部件 只能一个
QTextEdit * edit =new QTextEdit(this);
setCentralWidget(edit);
1.将图片文件拷贝到项目位置下
2.右键项目->添加新文件 –> Qt - > Qt recourse File - >给资源文件起名xxx,创建后生成 xxx.qrc
3.右键 xxx.qrc ,选择 open in editor 编辑资源,添加前缀、添加文件
4.使用Qt资源 “ : + 前缀名 + 文件名 ” eg:ui->actionnew->setIcon(QIcon(":/icon/image/jj.ico"));
对话框分为模态对话框和非模态对话框。模态对话框,会阻塞同一应用程序中其它窗口的输入。非模态对话框,不会阻塞同一应用程序中其它窗口的输入。
模态对话框很常见,比如“打开文件”功能。你可以尝试一下记事本的打开文件,当打开文件对话框出现时,我们是不能对除此对话框之外的窗口部分进行操作的。非模态对话框,例如查找对话框,我们可以在显示着查找对话框的同时,继续对记事本的内容进行编辑。
//模态对话框
connect(ui->actionnew,&QAction::triggered,[=](){
QDialog dlg(this);
dlg.resize(300,100);//设置对话框大小
dlg.setWindowTitle(tr("Hello, Model dialog!"));//设置对话框标题
dlg.setWindowIcon(QIcon(":/icon/image/jj.ico")); //设置对话框图标
dlg.exec();
qDebug() << "模态对话框弹出了";
});
//非模态对话框
connect(ui->actionnew,&QAction::triggered,[=](){
QDialog * dlg = new QDialog (this); //防止一闪而过,创建到堆区
dlg->resize(300,100); //设置对话框大小
dlg->setWindowTitle(tr("Hello, Modeless dialog!")); //设置对话框标题
dlg->setWindowIcon(QIcon(":/icon/image/jj.ico")); //设置对话框图标
dlg->show();
dlg->setAttribute(Qt::WA_DeleteOnClose); // 55号属性 对话框关闭时,自动销毁对话框。
qDebug() << "非模态对话框弹出了";
});
标准对话框,是 Qt 内置的一系列对话框,用于简化开发。事实上,有很多对话框都是通用的,比如打开文件、设置颜色、打印设置等。这些对话框在所有程序中几乎相同,因此没有必要在每一个程序中都自己实现这么一个对话框。
Qt 的内置对话框大致分为以下几类:
//消息对话框
connect(ui->actionopen,&QAction::triggered,[=](){
//错误对话框
QMessageBox::critical(this,"错误对话框","错误信息");
//信息对话框
QMessageBox::information(this,"提示对话框","提示信息");
//提问对话框
//参数1:父亲 参数2:标题 参数3:提示内容 参数4:按键类型 参数5:默认关联回车按键
QMessageBox::question(this,"提问对话框","提问信息",QMessageBox::Save|QMessageBox::Cancel);
//警告对话框
QMessageBox::warning(this,"警告对话框","警告信息");
//关于对话框
QMessageBox::about(this,"关于","By 简简");
QMessageBox::aboutQt(this);
});
QColor color = QColorDialog::getColor(QColor(255,0,0));
qDebug() << "r = " << color.red() << " g = " << color.green() << " b = " << color.blue() ;
//参数1:父亲 参数2:标题 参数3:默认打开路径 参数4:过滤文件格式
QString str = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\admin\\Desktop","(*.txt)");
qDebug() << str; // 返回值是选取的路径
bool flag;
QFont font = QFontDialog::getFont(&flag,QFont("华文彩云",36));
qDebug() << "字体:"<< font.family().toUtf8().data() << "字号:" << font.pointSize() << "是否加粗:" << font.bold() << "是否倾斜:" << font.italic();
//设置单选按钮 男默认选中
ui->rBtnMan->setChecked(true);
//选中女后 打印信息
connect(ui->rBtnWoman,&QRadioButton::clicked,[=](){
qDebug() << "选中了女了!";
});
//多选按钮 2是选中 0是未选中 1是半选
connect(ui->cBox,&QCheckBox::stateChanged,[=](int state){
qDebug() << state;
});
//利用listWidget写诗
QListWidgetItem * item = new QListWidgetItem("锄禾日当午");
//将一行诗放入到listWidget控件中
ui->listWidget->addItem(item);
item->setTextAlignment(Qt::AlignHCenter);
//treeWidget树控件使用
//设置水平头
ui->treeWidget->setHeaderLabels(QStringList()<< "英雄"<< "英雄介绍");
QTreeWidgetItem * liItem = new QTreeWidgetItem(QStringList()<< "力量");
QTreeWidgetItem * minItem = new QTreeWidgetItem(QStringList()<< "敏捷");
QTreeWidgetItem * zhiItem = new QTreeWidgetItem(QStringList()<< "智力");
//加载顶层的节点
ui->treeWidget->addTopLevelItem(liItem);
ui->treeWidget->addTopLevelItem(minItem);
ui->treeWidget->addTopLevelItem(zhiItem);
//追加子节点
QStringList heroL1;
heroL1 << "刚被猪" << "前排坦克,能在吸收伤害的同时造成可观的范围输出";
QTreeWidgetItem * l1 = new QTreeWidgetItem(heroL1);
liItem->addChild(l1);
//TableWidget控件
//设置列数
ui->tableWidget->setColumnCount(3);
//设置水平表头
ui->tableWidget->setHorizontalHeaderLabels(QStringList()<<"姓名"<< "性别"<< "年龄");
//设置行数
ui->tableWidget->setRowCount(5);
//设置正文
//ui->tableWidget->setItem(0,0, new QTableWidgetItem("亚瑟"));
QStringList nameList;
nameList<< "亚瑟"<< "赵云"<< "张飞"<< "关羽" << "花木兰";
QList<QString> sexList;
sexList << "男"<< "男"<< "男"<< "男"<< "女";
for(int i = 0 ; i < 5 ;i ++)
{
int col = 0;
ui->tableWidget->setItem(i,col++, new QTableWidgetItem(nameList[i]));
ui->tableWidget->setItem(i,col++, new QTableWidgetItem(sexList.at(i)));
//int 转 QString
ui->tableWidget->setItem(i,col++, new QTableWidgetItem( QString::number(i+18)));
}
栈控件(Stacked Widget )
ui->stackedWidget->setCurrentIndex(1);
下拉框(Combo Box)
ui->comboBox->addItem("奔驰");
标签(Label ) QLabel
//利用QLabel显示图片
ui->lbl_Image->setPixmap(QPixmap(":/Image/butterfly.png"));
//利用QLabel显示 gif动态图片
QMovie * movie = new QMovie(":/Image/mario.gif");
ui->lbl_movie->setMovie(movie);
//播放动图
movie->start();
Q t当中使用 QFile 类对文件进行读写操作,对文本文件也可以与 QTextStream 一起使用,这样读写操作会更加简便。QFileInfo 可以用来获取文件的信息。QDir 可以用于对文件夹进行操作。
//获取文件路径
//参数1:父亲 参数2:标题 参数3:默认打开路径
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\admin\\Desktop");
QFile file(path); //path参数就是读取文件的路径
//设置打开方式
file.open(QIODevice::ReadOnly);
//一次读取所有
QByteArray array = file.readAll();
QString str = QString(array);//类型转换
//按行读
while(!file.atEnd())//atEnd()判断是否读到文件尾
{
QByteArray array += file.readLine();
QString str = QString(array);//类型转换
}
//对文件对象进行关闭
file.close();
QFile::open()
函数打开文件时需要传递 QIODevice::OpenModeFlag
枚举类型的参数,决定文件以什么方式打开,QIODevice::OpenModeFlag
类型的主要取值如下:
这些取值可以组合,例如 QIODevice::ReadOnly | QIODevice::Text
表示以只读和文本方式打开文件。
Qt默认使用utf-8的编码方式,在Linux中一般没有问题,因为Linux默认也是用utf-8。不过Windows里一般用的是gb18030,所以除非我们明确将文件保存成utf-8编码,否则在Qt代码里需要对编码做一些转换操作:
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\admin\\Desktop");
QFile file(path);
file.open(QIODevice::ReadOnly);
QByteArray array = file.readAll();
QTextCodec * codec = QTextCodec::codecForName("gb18030");
QString str = codec->toUnicode(array);//类型转换
file.close();
QString path = QFileDialog::getOpenFileName(this,"打开文件","C:\\Users\\admin\\Desktop");
QFile file(path);
file.open(QIODevice::ReadOnly);
QTextStream in(&file)
while(!in.atEnd())
{
QString str += in.readLine();
}
file.close();
file.open(QIODevice::Append); //用追加方式进行写
file.write("测试");
file.close();
file.open(QIODevice::Append); //用追加方式进行写
QTextStream out(&file)
QString str = "测试";
out << str;
file.close();
使用 QFileInfo 获取文件的有关信息。QFileInfo 有很多类型的函数。比如:
//QFileInfo 文件信息类
QFileInfo info(path);
qDebug() << "大小:" << info.size() << " 后缀名:" << info.suffix() << " 文件名称:"<<info.fileName() << " 文件路径:"<< info.filePath();
qDebug() << "创建日期:" << info.created().toString("yyyy/MM/dd hh:mm:ss");
qDebug() << "最后修改日期:"<<info.lastModified().toString("yyyy-MM-dd hh:mm:ss");
});
QString -> QByteArray QString.toUtf8();
QByteArray->QString QString(QByteArray)
QByteArray -> std::string QByteArray.toStdString();
std::string -> char * string.data();