赞
踩
【qt】版本:4.5x
【vs】版本:vs2015
【语言】:c++
单击 Qt Creator 的菜单项文件->新建文件或项目,出现如图 1 所示的对话框。在这个对话框里选择需要创建的项目或文件的模板
对话框中选择项目类型为 Qt Widgets Application 后,单击“Choose…”按钮后更改文件名字及存储位置,设置好项目名称和保存路径后,单击“Next”按钮
选择自己常用的开发平台,我这里选用的是Vs2015,next
下一步
窗口左侧有上下两个子窗口,上方的目录树显示了项目内文件的组织结构,显示当 前项目为 Demo。项目的名称构成目录树的一个根节点,Qt Creator 可以打开多个项目,但是只有一个活动项目,活动项目的项目名称节点用粗体字体表示。
在项目名称节点下面,分组管理着项目内的各种源文件,几个文件及分组分别为以下几项:
Demo.pro 是项目管理文件,包括一些对项目的设置项。
Headers 分组,该节点下是项目内的所有头文件(.h),图 中所示项目有一个头文件 mainwindow.h,是主窗口类的头文件。
Sources 分组:该节点下是项目内的所有 C++源文件(.cpp),图 5中所示项目有两个 C++ 源文件,mainwindow.cpp 是主窗口类的实现文件,与 mainwindow.h 文件对应。main.cpp 是主函数文件,也是应用程序的入口。
Forms 分组:该节点下是项目内的所有界面文件(.ui)。图中所示项目有一个界面文件mainwindow.ui,是主窗口的界面文件。界面文件是文本文件,使用 XML 语言描述界面的组成。
左侧上下两个子窗口的显示内容可以通过其上方的一个下拉列表框进行选择,可以选择的显示内容包括项目、打开文档、书签、文件系统、类视图、大纲等。上方的子窗口显示了项目的文件目录树,下方显示打开的文件列表。可以在下方选择显示类视图,这样下方则显示项目内所有的类的结构,便于程序浏览和快速切换到需要的代码位置。
双击文件目录树中的文件mainwindow.ui,出现下图
将一个Label组件拖放到设计的窗体上面。双击刚刚放置的 Label 组件,可以编辑其文字内容,将文字内容更改为“Hello, World!”。右下角属性自行测试
单击主窗口左侧工具栏上的“项目”按钮
每个编译器又有 Build 和 Run 两个设置界面。在 Build 设置界面上,有一个“Shadow build” 复选框。如果勾选此项,编译后将在项目的同级目录下建立一个编译后的文件目录,目录名称包含编译器信息,这种方式一般用于使用不同编译器创建不同版本的可执行文件。如果不勾选此项,编译后将在项目的目录下建立“Debug”和“Release”子目录用于存放编译后的文件。
首先对项目进行编译,没有错误后,再运行程序。
day01中通过在 xxx.ui 文件中拖拽 Label 组件,设计出了一个显示 “Hello,World!” 的窗口
今天我们完全舍弃 xxx.ui 文件,亲手编写代码实现day01效果
首先新建一个项目,前面和正常生成一致直到这里不创建界面
生成完成后对比Demo和Demo02不难发现缺少了ui文件
main.cpp 文件,可以看到该文件包含的所有代码:
除了第 6、8 行代码外,其它代码的含义分别是:
1~2 行:由于 main() 函数中分别定义了 QApplication 和 MainWindow 类的对象,因此需要引入 mainwindows.h 和 QApplication 头文件。mainwindow.h 文件是我们自己创建的,引入时用" "双引号括起来;QApplication 是 Qt 提供给我们的,引入时用<>括起来。
第 7 行:MainWindow 是自定义的类,继承自 QMainWindow 主窗口类,因此 MainWindow 也是一个主窗口类。w 是 MainWindow 类实例化出的对象,表示一个主窗口。
第 8 行:默认情况下,Qt 提供的所有组件(控件、部件)都是隐藏的,不会自动显示。通过调用 MainWindow 类提供的 show() 方法,w 窗口就可以在程序运行后显示出来。
创建项目时,对话框中定义了一个继承自 QMainWindow 的主窗口类,并起名为 MianWindow,该类的定义部分位于 mainwindow.h 头文件中,实现部分位于 mainwindow.cpp 源文件中。
查看 mainwindow.h 和 mainwindow.cpp 文件中的代码并分析
初始状态下,MainWindow 类由 Q_OBJECT、构造函数和析构函数组成,这里重点介绍一下 Q_OBJECT 和构造函数:
Q_OBJECT:本质是一个已定义好的宏,所有需要“信号和槽”功能的组件都必须将 Q_OBJECT 作为 private 属性成员引入到类中。本节设计的界面程序不会用到“信号和槽”,因此可以删除 Q_OBJECT。有关信号和槽,我们会在《Qt信号和槽机制详解》一节详细介绍。
带参的构造函数:QWidget 是所有组件的基类,借助 parent 指针,可以为当前窗口指定父窗口。例如图 1 中,QLabel 文本框位于主窗口中,主窗口就是它的父窗口。当父窗口被删除时,所有子窗口也会随之一起删除。当然也可以不指定父窗口,那么当前窗口就会作为一个独立的窗口,不会受到其它窗口的影响。
直接运行程序,会输出下图所示的界面:
看到的就是 main() 函数中创建的 w 主窗口。由于没有往 w 窗口中放置任何组件,所以 w 是一个空白窗口。
接下来想要实现类似Day01的文本框需要对文件进行修改
mainwindow.h
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QLabel> // 引入 QLable 文件框组件的头文件 class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = 0); ~MainWindow(); private: QLabel *lab; // 定义一个私有的 QLabel 指针对象 }; #endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
// 创建一个 QLable 对象
this->lab = new QLabel("Hello,World!",this);
}
MainWindow::~MainWindow()
{
}
运行结果如下
至此我们用代码实现了控件的添加
和先前空的 MainWindow 类相比,做了如下修改: 添加了一个 QLabel 类的指针对象,相应地要引入头文件;
在构造函数中定义了一个 QLabel 类的文本框对象,通过调用它的带参构造函数,设置它的父对象为当前类的对象,同时设置
“Hello,World!” 为要显示的文本信息。
其中我们要注意
“Hello,World!” 文本框的父窗口是主窗口,所以文本框位于主窗口中(位置默认在窗口的左上角),主窗口关闭时文本框也会随之关闭。
Qt 是一个著名的 GUI 框架,用来开发和用户交互的图形界面。作为 GUI 框架,丰富的控件和灵活的事件机制是不可或缺的,Qt 在这一方面做得非常优秀。
t 提供了很多现成的控件。打开某个带 ui 文件的 Qt Widgets Application 项目,ui 文件的 Widget Box 一栏展示了 Qt 提供的几乎所有控件:
Qt 中的每个控件都由特定的类表示,每个控件类都包含一些常用的属性和方法,所有的控件类都直接或者间接继承自 QWidget 类。实际开发中,我们可以使用 Qt 提供的这些控件,也可以通过继承某个控件类的方式自定义一个新的控件。
Qt 中所有可视化的元素都称为控件,我们习惯将带有标题栏、关闭按钮的控件称为窗口。例如,下图展示了两种常用的窗口,实现它们的类分别是 QMainWindow 和 QDialog。
QMainWindow 类生成的窗口自带菜单栏、工具栏和状态栏,中央区域还可以添加多个控件,常用来作为应用程序的主窗口; QDialog
类生成的窗口非常简单,没有菜单栏、工具栏和状态栏,但可以添加多个控件,常用来制作对话框。
除了 QMainWindow 和 QDialog 之外,还可以使用 QWidget 类,它的用法非常灵活,既可以用来制作窗口,也可以作为某个窗口上的控件。
实际开发中,制作应用程序的主窗口可以用 QMainWindow 或者 QWdiget;制作一个提示信息的对话框就用 QDialog 或 QWidget;如果暂时无法决定,后续可能作为窗口,也可能作为控件,就选择 QWidget。
什么是事件
事件指的是应用程序和用户之间的交互过程,例如用户按下某个按钮,点击某个输入框等等。实际上除了用户会与应用程序进行交互外,操作系统也会与应用程序进行交互,例如当某个定时任务触发时,操作系统会关闭应用程序,这也是一个事件。
Day02 分析第一个Qt程序中提到,Qt 界面程序的 main() 主函数中首先要创建一个 QApplication 类的对象,函数执行结束前还要调用 QApplication 对象的 exec() 函数。一个 Qt 界面程序要想接收事件,main() 函数中就必须调用 exec() 函数,它的功能就是使程序能够持续不断地接收各种事件。
Qt 程序可以接收的事件种类有很多,例如鼠标点击事件、鼠标滚轮事件、键盘输入事件、定时事件等。每接收一个事件,Qt 会分派给相应的事件处理函数来处理。所谓事件处理函数,本质就是一个普通的类成员函数,以用户按下某个 QPushButton 按钮为例,Qt会分派给 QPushButton 类中的mousePressEvent() 函数处理。
事件处理函数通常会完成两项任务,分别是:
修改控件的某些属性,比如当用户按下按钮时,按钮的背景颜色会发生改变,从而提示用户已经成功地按下了按钮; 运用信号和槽机制处理事件。
这次我们创建一个不带 ui 文件的 Qt Widgets Application 项目,项目中只保留一个 main.cpp 源文件,删除其它文件(mainwindows.h 和 mainwindow.cpp)。将下述代码直接拷贝到 main.cpp 文件:
#include <QApplication> #include <QWidget> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); //添加窗口 QWidget widget; //定义一个按钮,它位于 widget 窗口中 QPushButton But("按钮控件",&widget); //设置按钮的位置和尺寸 But.setGeometry(10,10,100,50); //信号与槽,实现当用户点击按钮时,widget 窗口关闭 QObject::connect(&But,&QPushButton::clicked,&widget,&QWidget::close); //让 widget 窗口显示 widget.show(); return a.exec(); }
整个程序的运行过程如下: 先创建了一个 QWidget 窗口,在窗口上添加一个 QPushButton 按钮; 当用户点击按钮时,Qt
会将此事件分派给 QPushButton 类的 mousePressEvent() 事件处理函数;
mousePressEvent()函数内部会改变按钮的属性,提示用户成功按下了按钮,还会调用 clicked()
函数发出“用户点击按钮”的信号。对于发出的信号,程序中调用 connect() 函数指定 QWidget 类的 close() 函数接收
clicked() 信号,close() 函数会关闭 widget 窗口。
信号为输入行为包括并不局限于(点击,拖动等)
槽为信号所对应的事件
在 Qt 中,用户和控件的每次交互过程称为一个事件,比如“用户点击按钮”是一个事件,“用户关闭窗口”也是一个事件。每个事件都会发出一个信号,例如用户点击按钮会发出“按钮被点击”的信号,用户关闭窗口会发出“窗口被关闭”的信号。
Qt 中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每个信号,控件都会做出相应的响应动作。
信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。例如,“按钮被按下”这个信号可以用 clicked() 函数表示,“窗口关闭”这个槽可以用 close() 函数表示,信号和槽机制实现“点击按钮会关闭窗口”的功能,其实就是 clicked() 函数调用 close() 函数的效果。
信号函数和槽函数通常位于某个类中,和普通的成员函数相比,它们的特别之处在于:
信号函数用 signals 关键字修饰,槽函数用 public slots、protected slots 或者 private slots 修饰。signals 和 slots 是 Qt 在 C++ 的基础上扩展的关键字,专门用来指明信号函数和槽函数;
信号函数只需要声明,不需要定义(实现),而槽函数需要定义(实现)。
注意,并非所有的控件之间都能通过信号和槽关联起来,信号和槽机制只适用于满足以下条件的控件:
控件类必须直接或者间接继承自 QObject 类。Qt 提供的控件类都满足这一条件,这里提供一张 Qt常用类的继承关系的高清图片,感兴趣的读者可以简单了解一下。
控件类中必须包含 private 属性的 Q_OBJECT 宏。将某个信号函数和某个槽函数关联起来,需要借助 QObject 类提供的 connect() 函数。
connect() 是 QObject 类中的一个静态成员函数,专门用来关联指定的信号函数和槽函数。
关联某个信号函数和槽函数,需要搞清楚以下 4 个问题:
实现“按下按钮后窗口关闭”为例,先创建一个窗口和一个按钮,如下所示:
//添加窗口
QWidget widget;
//定义一个按钮,它位于 widget 窗口中
QPushButton But("按钮控件",&widget);
信号发送者是 But 按钮对象,要发送的信号是“按钮被点击”,可以用 QPushButton 类提供的 clicked() 信号函数表示;信号的接收者是 widget 主窗口对象,“窗口关闭”作为信号对应的槽,可以用 QWidget 类提供的 close() 函数表示。
在 Qt5 版本之前,connect() 函数最常用的语法格式是:
QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
各个参数的含义分别是:
用 connect() 函数将 But 按钮的 clicked() 信号函数和 widget 窗口的 close() 槽函数关联起来,实现代码如下:
connect(&But, SIGNAL(clicked()), &widget, SLOT(close()));
如此就实现了“按下按钮会关闭窗口”的功能。
qt5补充
创建一个不含 ui 文件的 Qt Widgets Application 项目,只保留 main.cpp 源文件,删除 mainwindow.h 和 mainwindow.cpp 文件。在 main.cpp 文件中编写如下代码:
#include <QApplication> #include <QWidget> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); //添加窗口 QWidget widget; //定义一个按钮,它位于 widget 窗口中 QPushButton But("按钮控件",&widget); //设置按钮的位置和尺寸 But.setGeometry(10,10,100,50); //信号与槽,实现当用户点击按钮时,widget 窗口关闭 QObject::connect(&But,&QPushButton::clicked,&widget,&QWidget::close); //让 widget 窗口显示 widget.show(); return a.exec(); }
此外两种语法均可实现信号及槽的链接
如上图所示,由于使用了 conect() 函数将 But 的 clicked() 信号函数和 widget 的 close() 槽函数关联起来,所以生成了“点击按钮后主窗口关闭”的效果。
QLabel基础功能是显示一串文本
QLabel 是 Qt 帮我们写好的一个控件类,间接继承自 QWidget 类,它的继承关系如下:
QLabel -> QFrame -> QWidget
除了显示一串文本外,QLabel 控件上还可以放置图片、超链接、动画等内容
本质上,每个文本框都是 QLabel 类的一个实例对象。QLabel 类提供了两个构造函数,分别是:
QLabel(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())
QLabel(const QString &text, QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags())
各个参数的含义分别是:
需要注意的是,第一个构造函数中的 parent 和 f 参数都有默认值,因此 QLabel 类还隐含了一个默认构造函数。也就是说,创建 QLable 对象时可以不传递任何参数,或者只给 txt 参数传递一个字符串,就可以成功创建一个文本框。通常情况下,我们会给 text 和 parent 参数传递相应的值,即在创建文本框的同时指定文本内容和父窗口。
QLabel 控件只用来显示文本、图像等内容,很好与用户交互。但是,当 QLabel 控件内包含超链接内容时,可以使用 QLabel 类提供的两个信号函数:
除此外这些信号和槽函数外,QLabel 类还从父类处继承了一些信号和槽函数,这里不再一一罗列。
仍然是无ui,仅保留main.cpp
根据试验发现移除显示1秒的模式更符合操作逻辑
示例如下:
//main.cpp #include <QApplication> #include <QLabel> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建一个文本框 QLabel lab; //设置文本框内容居中显示 lab.setAlignment(Qt::AlignCenter); //设置文本框的坐标和尺寸 lab.setGeometry(100,100,400,400); //设置文本框的外观,包括字体的大小和颜色、按钮的背景色 lab.setStyleSheet("QLabel{font:30px;color:red;background-color:rgb(f9,f9,f9);}"); //设置文本框要显示超链接内容 lab.setText("<a href=\"https://blog.csdn.net/qq_22855077?spm=1000.2115.3001.5343\">qtday05"); //当用户鼠标位于文本框上时,显示提示内容 lab.setToolTip("点击超链接显示URL"); //提示内容显示 1 秒 //lab.setToolTipDuration(1000); //为文本框设置信号和槽,当用户点击超链接时,将文本框内容改为超链接的 URL QObject::connect(&lab,&QLabel::linkActivated,&lab,&QLabel::setText); //程序运行后,文本框显示 lab.show(); return a.exec(); }
结果如下
QPushButton 类间接继承自 QWidget 类,它的继承关系如下:
QPushButton -> QAbstractButton -> QWidget
QAbstractButton 类是所有按钮控件类的基类,包含很多通用的按钮功能
QPushButton 类专门用来创建可按压的按钮,也就是我们平时最常见的按钮形式
QPushButton 类提供了 3 个构造函数,分别是:
QPushButton(QWidget *parent = Q_NULLPTR)
QPushButton(const QString &text, QWidget *parent = Q_NULLPTR)
QPushButton(const QIcon &icon, const QString &text, QWidget *parent = Q_NULLPTR)
参数解释如下:
注意,第一个构造函数的 parent 参数附有默认值,所以 QPushButton 类还隐含着一个默认构造函数。也就是说,实例化 QPushButton 类对象时可以不传递任何参数。
GUI 程序中,按钮的主要任务是完成和用户之间的交互,下表罗列了 QPushButton 类常用的信号函数和槽函数:
仍然是无ui,仅main.cpp
#include <QApplication> #include <QWidget> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget widget; //设置 widget 窗口的标题 widget.setWindowTitle("QWidget窗口"); //创建一个按钮,并内嵌到 widget 窗口中 QPushButton but("QPushButton按钮",&widget); //按钮的位置位于距 widget 窗口左上角 (100,100) 的位置 but.move(100,100); //设置按钮上文字的大小。 but.setStyleSheet("QPushButton{font:20px;}"); //调整按钮的尺寸 but.resize(200,200); //建立信息和槽,当用户点击并释放按钮后,该按钮隐藏。 QObject::connect(&but,&QPushButton::clicked,&but,&QPushButton::hide); widget.show(); return a.exec(); }
实际开发中,我们经常用到 QLineEdit 输入框,比如接收用户输入的个人信息、账户信息、角色名称等,就可以用 QLineEdit 实现
QLineEdit 是 Qt 提供的一个控件类,它直接继承自 QWdiget 类,专门用来创建单行输入框
每个单行输入框都是 QLineEdit 类的一个实例对象,QLineEdit 类提供有两个构造函数,分别是:
QLineEdit(QWidget *parent = Q_NULLPTR)
QLineEdit(const QString &contents, QWidget *parent = Q_NULLPTR)
参数解释如下:
QLineEdit 输入框还可以对用户输入的内容加以限定:
仍然无ui,仅main.cpp
#include <QApplication> #include <QWidget> #include <QLineEdit> using namespace std; int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建一个窗口,作为输入框的父窗口 QWidget widget; //设置窗口的标题 widget.setWindowTitle("QWidget窗口"); //接下来,分别创建两个输入框,用于让用户分别输入账号和密码 //创建账号输入框 QLineEdit lineEdit(&widget); //指定输入框位于父窗口中的位置 lineEdit.move(100,100); //设置提示信息 lineEdit.setPlaceholderText("请输入账号"); //让输入框显示“一键清除”按钮 lineEdit.setClearButtonEnabled(true); //创建密码输入框 QLineEdit lineEditPass(&widget); lineEditPass.setPlaceholderText("请输入密码"); lineEditPass.move(100,150); //指定文本显示方式,保护用户账号安全 lineEditPass.setEchoMode(QLineEdit::Password); //指定窗口的尺寸和显示文字的大小 widget.resize(500,300); widget.setFont(QFont("宋体",16)); widget.show(); return a.exec(); }
结果如图:
QListWidget 是 Qt 提供的控件类,专门用来创建列表。QListWidget 类的继承关系如下:
QListWidget -> QListView -> QAbstractItemView -> QAbstractScrollArea -> QFrame -> QWidget
QListView功能强大,门槛较高
QListWidget简单列表,门槛低
通过实例化 QListWidget 类,可以很轻松地创建一个列表。QListWidget 类只提供了 1 个构造函数:
QListWidget(QWidget *parent = Q_NULLPTR)
parent 参数用来指定新建列表的父窗口,该参数的默认值是 Q_NULLPTR,表示新建控件没有父窗口。
语法层面上分析,可以不为 QListWidget 列表指定父窗口,那么它将作为一个独立的窗口。但实际开发中,通常会为 QListWidget 列表指定一个父窗口(例如 QWidget 窗口),它将作为父窗口中的一个子控件,和窗口中的其它控件一起搭配使用。
QListWidget 列表控件可以显示多份数据,每份数据习惯称为列表项(简称项),每个列表项都是 QListWidgetItem 类的实例对象。也就是说,QListWidget 中有多少个列表项,就有多少个 QListWidgetItem 类对象。
默认情况下,QListWidget 中每个列表项独自占用一行,每个列表项中可以包含文字、图标等内容。实际开发中,我们还可以将指定的窗口或者控件放置到列表项中显示,例如 QWidget 窗口、QLabel 文本框、QPushButton 按钮、QLineEdit 输入框等。
借助 QListWidgetItem 类,可以轻松管理 QListWidget 中的每个列表项,包括:
当然,QListWidgetItem 类还提供有很多其它的成员方法,这里不再一一罗列。
对于刚刚创建好的 QListWidget 类对象,不包含任何 QListWidgetItem 类对象,就是一个空列表。借助 QListWidget 类以及父类提供的属性和方法,我们可以对新建列表执行多种操作。
属性名或方法名 | 功 能(含 义) |
---|---|
count 属性 | 保存当前列表中含有的列表项的总数。该属性的值可以通过 count() 方法获取。 |
currentRow 属性 | -保存当前选择的列表项所在的行数。该属性的值可以通过 currentRow() 方法获取,也可以通过 setCurrentRow(int row) 方法修改当前选择的列表项。 |
sortingEnabled 属性 | 决定当前 QlistWidget 列表是否开发排序功能,默认值为 false,即不开启排序功能。该属性的是可以通过 isSortingEnabled() 方法获取,可以通过 setSortingEnabled(bool enable) 方法进行修改。 |
SelectionMode 属性 | 指明当前列表中是否可以同时选择多个列表项,或者是否只能连续选择多个列表项。该属性是枚举类型,可选值有 5 个: - QAbstractItemView::SingleSelection:用户最多只能选择一个列表项 QAbstractItemView::ContiguousSelection:用户按住 Shift 键,可以连续选择多个列表项。 QAbstractItemView::ExtendedSelection:用户按住 Ctrl 键,可以选中多个列表项。当用户按住Shift 键,也可以连续选择多个列表项。 QAbstractItemView::MultiSelection:用户可以选择多个列表项。 QAbstractItemView::NoSelection:用户无法选择任何列表项。该属性的值可以通过 selectionMode() 方法获取,也可以通过 setSelectionMode(QAbstractItemView::SelectionMode mode) 方法进行修改。 |
ViewMode 属性 | 指定 QListWidget 是按行显示数据是分列显示数据该属性是枚举类型,可选值有 2 个:QListView::ListMode:一行一行地显示列表项,默认情况下,各个列表项不能拖动。QListView::IconMode:分列显示列表项,默认情况下,各个列表项可以拖动。----------------------------------该属性的值可以通过 viewMode() 方法获取,也可以通过 setViewMode(ViewMode mode) 方法进行修改。 |
void addItem ( const QString & label )void addItem ( QListWidgetItem * item )void addItems ( const QStringList & labels ) | 向 QListWidget 列表的尾部添加指定项,可以是一个文本(label)、一个列表项(item),还可以一次性添加多个文本(labels)。 |
void QListWidget::setItemWidget(QListWidgetItem *item, QWidget *widget) | 将指定的 widget 窗口添加到 item 列表项中。 |
currentItem() | 返回当前选中的列表项。 |
removeItemWidget(QListWidgetItem *item) | 删除指定的 item 列表项。 |
sortItems(Qt::SortOrder order = Qt::AscendingOrder) | 默认将所有列表项按照升序排序,通过指定参数为 Qt::DescendingOrder,可以进行降序排序。 |
takeItem(int row) | 返回位于 row 行的列表项。 |
selectedItems() | 返回当前被选择的所有列表项。 |
仍然是无ui,仅main.cpp
//main.cpp #include <QApplication> #include <QWidget> #include <QListWidget> #include <QLabel> #include <QListWidgetItem> using namespace std; class QMyLabel:public QLabel{ Q_OBJECT public slots: void rsetText(QListWidgetItem *item); }; void QMyLabel::rsetText(QListWidgetItem *item){ this->setText(item->text()); } int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建一个窗口,作为输入框的父窗口 QWidget widget; //设置窗口的标题 widget.setWindowTitle("QWidget窗口"); widget.resize(500,500); QListWidget listWidget(&widget); listWidget.resize(500,400); listWidget.setFont(QFont("宋体",14)); listWidget.addItem("C语言中文网"); listWidget.addItem("http://c.biancheng.net"); listWidget.addItem(new QListWidgetItem("Qt教程")); QMyLabel print; print.setText("选中内容"); print.setParent(&widget); print.resize(500,100); print.move(0,400); print.setAlignment(Qt::AlignCenter); QObject::connect(&listWidget,&QListWidget::itemClicked,&print,&QMyLabel::rsetText); widget.show(); return a.exec(); } //QMyLabel类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc"
出现无法打开moc问题先清理–qmake–构建–运行
还不行重启再如上操作,我是这样过编译的
运行结果如下:
QTableWidget 是 Qt 提供的一种表格控件,,类似于我们经常使用的 Excel 表格,可以将数据以表格的方式展示给用户。
整个 QTableWidget 表格可以分为 3 个区域:
默认情况下,表格会显示表头,表头的内容为行号或列号。根据实际需要,我们可以将表头隐藏起来。
QTableWidget 继承自 QTableView 类,QTableView 类也可以用来显示表格控件。QTableWidget 可以看做是 QTableView 的“简易版”或者“升级版”,它们的区别在于:
总之,QTableWidget 只适合显示少量的数据(几百或几千个),如果想要显示更多的数据,应该用 QTableView。此外,QTableView 还有一些更高级的用法,我们会在讲解 QTableView 时做重点介绍。
使用 QTableWidget 控件,必须先引入QTableWidget头文件。
QTableWidget 类提供了 2 个构造函数,分别是:
QTableWidget(QWidget *parent = Q_NULLPTR)
QTableWidget(int rows, int columns, QWidget *parent = Q_NULLPTR)
第一个构造函数可以在指定的 parent 父窗口中创建一个空的表格,表格中不显示任何单元格。第二个构造函数可以在指定的 parent 父窗口中创建一个表格,表格中整齐地排列着 rows 行 columus 列的单元格,每个单元格都是空的
实用 QTableWidget 表格之前,必须指定表格的行和列。我们可以直接调用第 2 个构造函数,这样既创建了表格又指定了行和列。当然,也可以调用第 1 个构造函数先创建表格,然后借助 QTableWidget 类提供的成员方法指定行和列,两种方式都可以。
与数组下标类似,QTableWidget 表格单元格的行标和列标都是从 0 开始。
QTableWidget 表格中,每个单元格都是 QTableWidgetItem 类的实例对象。
定义 QTableWidgetItem 类的实例对象之前,程序中要引入QTableWidgetItem头文件。
QTableWidgetItem 类提供了 4 个构造函数:
QTableWidgetItem(int type = Type)
QTableWidgetItem(const QString &text, int type = Type)
QTableWidgetItem(const QIcon &icon, const QString &text, int type = Type)
QTableWidgetItem(const QTableWidgetItem &other) //复制(拷贝)构造函数
参数解释如下:
QTableWidgetItem 类还对<小于运算符进行了重载,根据各个单元格存储的文本内容(字符串),多个单元格之间可以直接比较大小。借助这一特性,我们可以很轻易地实现“单元格排序”功能
QTableWidget 类提供的信号函数,可以监听用户对表格中的哪个单元格进行了何种操作,常见的操作包括点击、双击、按下、编辑等。
仍然是无ui,仅main.cpp
#include <QApplication> #include <QWidget> #include <QLabel> #include <QTableWidget> #include <QTableWidgetItem> #include <QStringList> #include <QDebug> #include <QPushButton> using namespace std; class QMyLabel:public QLabel{ Q_OBJECT public slots: void rsetText(QTableWidgetItem * item); }; void QMyLabel::rsetText(QTableWidgetItem * item){ this->setText(item->text()); } int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建一个窗口,作为输入框和列表框的父窗口 QWidget widget; //设置窗口的标题 widget.setWindowTitle("QTableWidget控件"); //自定义窗口的大小 widget.resize(900,500); //在 widget 窗口中添加一个 4 行 3 列的表格 QTableWidget TableWidget(4,3,&widget); //自定义表格的尺寸和字体大小 TableWidget.resize(900,350); TableWidget.setFont(QFont("宋体",20)); //设置表格中每一行的表头 TableWidget.setHorizontalHeaderLabels(QStringList() << "教程" << "网址" << "状态"); //设置表格数据区内的所有单元格都不允许编辑 TableWidget.setEditTriggers(QAbstractItemView::NoEditTriggers); //设置表格中每一行的内容 TableWidget.setItem(0,0,new QTableWidgetItem("C语言教程")); TableWidget.setItem(0,1,new QTableWidgetItem("http://c.biancheng.net/c/")); TableWidget.setItem(0,2,new QTableWidgetItem("已更新完毕")); TableWidget.setItem(1,0,new QTableWidgetItem("Qt教程")); TableWidget.setItem(1,1,new QTableWidgetItem("http://c.biancheng.net/qt/")); TableWidget.setItem(1,2,new QTableWidgetItem("正在更新")); TableWidget.setItem(2,0,new QTableWidgetItem("C++教程")); TableWidget.setItem(2,1,new QTableWidgetItem("http://c.biancheng.net/cplus/")); TableWidget.setItem(2,2,new QTableWidgetItem("已更新完毕")); //向 widget 窗口中添加一个文本框 QMyLabel lab; lab.setText("选中单元格"); lab.setParent(&widget); //自定义文本框的尺寸和位置 lab.resize(900,150); lab.move(0,350); lab.setAlignment(Qt::AlignCenter); lab.setFont(QFont("宋体",16)); widget.show(); //为表格和文本框之间建立关联,当用户点击表格中某个单元格时,文本框显示单元格内的文本内容。 QObject::connect(&TableWidget,&QTableWidget::itemClicked,&lab,&QMyLabel::rsetText); return a.exec(); } //QMyLabel类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc"
运行结果如下:
QTreeWidget 是 Qt 框架提供的一种树形控件,它能以树形结构展示数据(或者文件)之间的包含关系
QTreeWidget 类专门用来创建树形控件,使用此类前需在项目中引入QTreeWidget头文件。
QTreeWidget 类只提供了 1 个构造函数:
QTreeWidget(QWidget *parent = Q_NULLPTR)
parent 参数用于为新建树形控件指定父窗口。当为新建 QTreeWidget 对象指定父窗口后,它将作为该窗口中的一个控件;parent 参数用于为新建树形控件指定父窗口。当为新建 QTreeWidget 对象指定父窗口后,它将作为该窗口中的一个控件
QTreeWidget\QTreeView的关系和区别:
QTreeWidget 可以看作“简易版”或“升级版”的 QTreeView,前者的使用方式更加简单,入门门槛低
通常情况下,我们习惯将树形结构中的每份数据称为一个结点。QTreeWidget 控件中,每个结点都是 QTreeWidgetItem 类的实例对象。也就是说,QTreeWidget 类对象代表整个树形控件,而 QTreeWidgetItem 类对象代表树形控件中的结点。
使用 QTreeWidgetItem 类创建结点之前,项目中需引入QTreeWidgetItem头文件。
QTreeWidgetItem 类提供的构造函数有很多,常用的有如下几个:
//创建一个新结点,设置结点中包含的数据(strings),将该结点添加到指定的 parent 树形结构中
QTreeWidgetItem(QTreeWidget *parent, const QStringList &strings, int type = Type)
//创建一个新结点,将其插入到 parent 树形结构中 preceding 结点之后的位置
QTreeWidgetItem(QTreeWidget *parent, QTreeWidgetItem *preceding, int type = Type)
//创建一个新结点,将其添加到指定 parent 结点中,作为 parent 结点的子结点
QTreeWidgetItem(QTreeWidgetItem *parent, int type = Type)
//创建一个新结点,指定结点中包含的文本内容(strings),将其添加到指定 parent 结点中,作为 parent 的子结点
QTreeWidgetItem(QTreeWidgetItem *parent, const QStringList &strings, int type = Type)
//创建要给新结点,将其插入到 parent 结点中 preceding 结点之后的位置。
QTreeWidgetItem(QTreeWidgetItem *parent, QTreeWidgetItem *preceding, int type = Type)
type 参数通常不需要手动指定,保持默认即可。
QTreeWidgetItem 还提供了很多实用的成员方法,下表罗列了一些常用的方法。借助它们,我们可以轻松地管理 QTreeWidget 控件中的各个结点。
向 QTreeWidget 控件中添加结点,具体可分为两种情况,一种是添加最顶层的结点,另一种是为某个结点添加子结点。
//1、调用相应的构造函数,直接将结点作为树形控件的顶层结点
QTreeWidgetItem topItem(&treeWidget);
//2、调用 QTreeWidget 类的 addTopLevelItem() 方法
QTreeWidgetItem topItem2;
treeWidget.addTopLevelItem(&topItem2);
第一种方法,我们向 treeWidget 树形控件中成功添加了 topItem 顶层结点;第二种方法,我们先创建了 topItem2 结点,然后借助 addTopLevelItem() 方法将其添加到 treeWidget 树形控件中,作为该控件的第二个顶层结点。
同样,为某个结点添加子结点的方法也有两种,分别是:
//1、调用相应的构造函数,直接指定新结点的父结点
QTreeWidgetItem childItem(&item);
//2、先创建一个新结点,调用 QTreeWidgetItem 类提供的 addChild() 方法,可以为某个结点添加子结点。
QTreeWidgetItem childItem2;
item2.addChild(&childItem2);
除此之外,还有其它添加结点的方法,例如使用QList<QTreeWidgetItem *> items一次性向树形控件中添加多个顶层结点或者子结点:
QList<QTreeWidgetItem *> items;
items.append(&item);
items.append(&item2);
treeWidget.addTopLevelItems(items);
Qt 对 C++ STL 库中的容器进行了更好的封装,QList 容器就是其中之一。通过先将 item 和 item2 添加到 items 容器中,然后再将 items 传递给 treeWidget 对象的 addTopLevelItems() 方法,就可以将 items 容器中的所有结点添加到 treeWidget 控件中,作为该控件的顶层结点。
对于每个 QTreeWidgetItem 类的结点,除了可以放置文本信息外,还可以添加 icon 图标。
#include
#include
#include
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
widget.setWindowTitle(“MyTreeWidget控件”);
QTreeWidget treeWidget(&widget);
QTreeWidgetItem item;
item.setText(0,“Day09”);
item.setIcon(0,QIcon(“D:\xxxx.ico”));
treeWidget.addTopLevelItem(&item);
widget.show();
return a.exec();
}
setCheckState(0,Qt::Unchecked);
默认情况下,QTreeWidgetItem 结点不显示复选框,通过调用 setCheckState() 方法,可以使结点自带的复选框显现出来。复选框的状态有 3 种:
所谓部分选中状态,是指当该结点下包含多个带复选框的子节点时,如果用户只选中了其中一部分子结点,那么通常将该结点设置为部分选中状态。
根据需要,QTreeWidget 控件中每一行可以存放多列结点。
> #include <QApplication> #include <QWidget> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QStringList> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget widget; widget.setWindowTitle("MyTreeWidget控件"); widget.resize(600,300); QTreeWidget treeWidget(&widget); //设置列数 treeWidget.setColumnCount(3); treeWidget.resize(600,300); //添加顶层结点 QTreeWidgetItem topItem; topItem.setText(0,"教程"); treeWidget.addTopLevelItem(&topItem); //添加子结点 QStringList c; c << "C语言教程" << "http://c.biancheng.net/c/" <<"已完成"; QTreeWidgetItem childItem1(&topItem,c); QStringList qt; qt << "Qt教程" << "http://c.biancheng.net/qt/" <<"未完成"; QTreeWidgetItem childItem2(&topItem,qt); QStringList python; python << "Python教程" << "http://c.biancheng.net/python/" <<"已完成"; QTreeWidgetItem childItem3(&topItem,python); widget.show(); return a.exec(); }
默认情况下,每一列的表头是当前列的列号。必要的时候,我们还可以指定每一列的表头。
QStringList top;
top << "str1" << "str2" <<"str3";
treeWidget.setHeaderLabels(top);
此外,QTreeWidget 控件默认会显示每一列的表头,通过调用从 QTreeView 父类继承过来的 setHeaderHidden() 方法,可以将表头隐藏起来。例如:
> treeWidget.setHeaderHidden(true);
当参数值为 true 时,表示将表头隐藏起来,反之则表示不隐藏表头。
QTreeWidgetItem 结点除了可以显示文本信息、icon图标、复选框等基本内容外,还可以容纳其它的一些的控件,例如输入框、按钮等。
QTreeWidget 类提供有 setItemWidget() 方法,可以将其它控件添加到指定列的结点中:
void QTreeWidget::setItemWidget(QTreeWidgetItem *item, int column, QWidget *widget)
参数解释:
下为将表格作为节点的例子:
#include <QApplication> #include <QWidget> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QStringList> #include <QTableWidget> int main(int argc, char *argv[]) { QApplication a(argc, argv); QWidget widget; widget.setWindowTitle("MyTreeWidget控件"); widget.resize(600,300); QTreeWidget treeWidget(&widget); //设置列数 treeWidget.resize(600,300); //添加顶层结点 QTreeWidgetItem topItem; treeWidget.addTopLevelItem(&topItem); //创建表格控件 QTableWidget TableWidget(4,3); //设置表格中每一行的内容 TableWidget.setItem(0,0,new QTableWidgetItem("C语言教程")); TableWidget.setItem(0,1,new QTableWidgetItem("http://c.biancheng.net/c/")); TableWidget.setItem(0,2,new QTableWidgetItem("已更新完毕")); TableWidget.setItem(1,0,new QTableWidgetItem("Qt教程")); TableWidget.setItem(1,1,new QTableWidgetItem("http://c.biancheng.net/qt/")); TableWidget.setItem(1,2,new QTableWidgetItem("正在更新")); TableWidget.setItem(2,0,new QTableWidgetItem("C++教程")); TableWidget.setItem(2,1,new QTableWidgetItem("http://c.biancheng.net/cplus/")); TableWidget.setItem(2,2,new QTableWidgetItem("已更新完毕")); //将表格控件作为结点内容 treeWidget.setItemWidget(&topItem,0,&TableWidget); widget.show(); return a.exec(); }
QTreeWidget 控件提供了很多信号函数,可以捕捉用户的诸多动作,例如点击或双击了某个结点、某个结点被修改等等,如下罗列了几个常用的信号函数:
//用户单击某个结点时,会触发此信号函数
void QTreeWidget::itemClicked(QTreeWidgetItem *item, int column)
//用户双击某个结点时,会触发此信号函数
void QTreeWidget::itemDoubleClicked(QTreeWidgetItem *item, int column)
//当某个结点的内容被修改时,会触发此信号函数
void QTreeWidget::itemChanged(QTreeWidgetItem *item, int column)
//当用户选择其它结点时,会触发此信号函数
void QTreeWidget::currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *previous)
//控件树形控件中的所有结点
void QTreeWidget::clear()
//折叠指定结点所有的子结点
void QTreeWidget::collapseItem(const QTreeWidgetItem *item)
//展开指定结点所有的子结点
void QTreeWidget::expandItem(const QTreeWidgetItem *item)
仍然是无ui,仅main.cpp
//main.cpp #include <QApplication> #include <QWidget> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QStringList> #include <QTableWidget> //自定义槽函数 class MyWidget:public QWidget{ Q_OBJECT public slots: void treeWidgetClicked(QTreeWidgetItem * item); }; //接收 itemClicked() 信号函数传递过来的 item 参数 void MyWidget::treeWidgetClicked(QTreeWidgetItem *item){ //遍历 item 结点所有的子结点 for(int i=0;i<item->childCount();i++){ //找到每个子结点 QTreeWidgetItem *childItem = item->child(i); //将子结点的选中状态调整为和父结点相同 childItem->setCheckState(0,item->checkState(0)); } } int main(int argc, char *argv[]) { QApplication a(argc, argv); MyWidget widget; widget.setWindowTitle("MyTreeWidget控件"); widget.resize(600,300); QTreeWidget treeWidget(&widget); //设置列数 treeWidget.setColumnCount(3); treeWidget.resize(600,300); //添加顶层结点 QTreeWidgetItem topItem; topItem.setText(0,"教程"); topItem.setCheckState(0,Qt::Unchecked); treeWidget.addTopLevelItem(&topItem); //添加子结点 QStringList c; c << "C语言教程" << "http://c.biancheng.net/c/" <<"已完成"; QTreeWidgetItem childItem1(&topItem,c); childItem1.setCheckState(0,Qt::Unchecked); QStringList qt; qt << "Qt教程" << "http://c.biancheng.net/qt/" <<"未完成"; QTreeWidgetItem childItem2(&topItem,qt); childItem2.setCheckState(0,Qt::Unchecked); QStringList python; python << "Python教程" << "http://c.biancheng.net/python/" <<"已完成"; QTreeWidgetItem childItem3(&topItem,python); childItem3.setCheckState(0,Qt::Unchecked); //添加信号和槽机制,当某个结点被点击时,调整所有子结点的选中状态,同时将所有子结点展开。 QObject::connect(&treeWidget,&QTreeWidget::itemClicked,&widget,&MyWidget::treeWidgetClicked); QObject::connect(&treeWidget,&QTreeWidget::itemClicked,&treeWidget,&QTreeWidget::expandItem); widget.show(); return a.exec(); } //MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc"
QMessageBox 是 Qt 框架中常用的一个类,可以生成各式各样、各种用途的消息对话框
很多 GUI 程序都会用到消息对话框,且很多场景中使用的消息对话框是类似的,唯一的区别只是提示信息不同。为了提高程序员的开发效率,避免重复地“造轮子”,Qt 开发者设计好了几种通用的 QMessageBox 消息对话框,需要时可以直接使用。
information 对话框常用于给用户提示一些关键的信息,它的外观如下图所示:
在项目中使用 information 消息对话框,直接调用 QMessageBox 类中的 information() 静态成员方法即可,该方法的语法格式如下:
StandardButton QMessageBox::information(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
各个参数的含义是:
information() 函数会返回用户按下的按钮。StandardButton 是 QMessageBox 类中定义的枚举类型,每个枚举值代表一种按钮。StandardButton 类型中的值有很多,下表给大家罗列了几个常用的:
示例:
QMessageBox::StandardButton result = QMessageBox::information(&widget, "Title","text");
其中,widget 是我们创建好的 QWidget 窗口,创建好的 information 对话框会显示在 widget 窗口的前面。通过用 result 接收 information() 函数的返回值,我们可以得知用户选择的是哪个按钮。
critical 消息对话框常用于给用户提示“操作错误”或“运行失败”的信息,它的外观如下图所示:
项目中使用 critical 消息对话框,直接调用 QMessageBox 类提供的 critical() 静态成员方法即可,该方法的语法格式为:
StandardButton QMessageBox::critical(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
各个参数的含义以及返回值的含义,都与 information() 函数相同,这里不再重复赘述。
示例:
QMessageBox::StandardButton result=QMessageBox::critical(&widget, "Title","text");
其中,widget 是我们创建好的 QWidget 窗口,创建好的 critical 对话框会显示在 widget 窗口的前面
question 对话框常用于向用户提出问题并接收用户的答案,它的外观如下图所示:
项目中使用 question 对话框,可以直接调用 QMessageBox 类的 question() 静态成员方法,该方法的语法格式为:
StandardButton QMessageBox::question(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = StandardButtons( Yes | No ),
StandardButton defaultButton = NoButton)
示例:
QMessageBox::StandardButton result=QMessageBox::question(&widget, "Title","text");
warining 对话框常用于向用户显示一些警告信息,它的外观如下图所示:
项目中使用 warning 对话框,可以直接调用 QMessageBox 类的 warning() 静态成员方法,该方法的语法格式为:
StandardButton QMessageBox::warning(QWidget *parent,
const QString &title,
const QString &text,
StandardButtons buttons = Ok,
StandardButton defaultButton = NoButton)
示例:
QMessageBox::StandardButton result=QMessageBox::warning(&widget, "Title","text");
about 对话框常常作为介绍某个产品或某项功能的临时窗口,它的外观如下图所示:
注意,about 对话框没有固定的图标,它显示的图标可能来自父窗口、包含父窗口的顶层窗口等,也可能使用和 information 对话框相同的图标。
语法:
void QMessageBox::about(QWidget *parent, const QString &title, const QString &text)
我们只能设置 aboutQt 对话框的 parent 父窗口和 title 标题,不能自定义它的内容。所以在实际场景中,aboutQt() 对话框很少使用。
以上 6 种通用的 QMessageBox 对话框,界面上的图片无法修改,按钮上的文字也无法修改(例如无法将 OK、No 改成中文)。如果想修改它们,就需要自定义一个 QMessageBox 对话框。
QMessageBox对话框的创建
程序中创建 QMessageBox 对象,必须先引入QMessageBox头文件。QMessageBox 类提供了两个构造函数,分别是:
QMessageBox::QMessageBox(QWidget *parent = Q_NULLPTR)
QMessageBox::QMessageBox(Icon icon,
const QString &title,
const QString &text,
StandardButtons buttons = NoButton,
QWidget *parent = Q_NULLPTR,
Qt::WindowFlags f = Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint)
第一个构造函数可以创建一个“空白”对话框,即对话框中不包含任何文本和按钮。当然,通过调用 QMessageBox 类提供的成员方法,可以向“空白”对话框中添加各种元素(图标、文本、按钮等)。
第二个构造函数中,各个参数的含义是:
#include <QApplication>
#include <QMessageBox>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//创建 QMessageBox 类对象
QMessageBox MyBox(QMessageBox::Question,"Title","text",QMessageBox::Yes|QMessageBox::No);
//使 MyBox 对话框显示
MyBox.exec();
return a.exec();
}
程序中创建了一个 MyBox 对话框,通过调用 QMessageBox 提供的 exec() 方法,可以使 MyBox 对话框弹出。运行程序可以发现,MyBox 对话框的外观和 question 对话框完全一样。
QMessageBox对话框的使用
QMessageBox 类提供了很多功能实用的成员方法,方便我们快速地制作出实际场景需要的对话框。
下表给大家罗列了常用的一些 QMessageBox 类成员方法:
示例:
#include <QApplication> #include <QMessageBox> #include <QPushButton> #include <QDebug> int main(int argc, char *argv[]) { QApplication a(argc, argv); QMessageBox MBox; MBox.setWindowTitle("QMessageBox自定义对话框"); MBox.setText("这是一个自定义的对话框"); MBox.setIconPixmap(QPixmap("C:\\Users\\xiexuewu\\Desktop\\icon_c.png")); QPushButton *agreeBut = MBox.addButton("同意", QMessageBox::AcceptRole); MBox.exec(); if (MBox.clickedButton() == (QAbstractButton*)agreeBut) { //在 Qt Creator 的输出窗口中输出指定字符串 qDebug() << "用户点击了同意按钮"; } return a.exec(); }
操作 QMessageBox 对话框,最常用的信号函数是 buttonClicked() 函数,最常用的槽函数是 exec() 函数,它们的语法格式和功能如下表所示。
示例:
//main.cpp #include <QApplication> #include <QWidget> #include <QMessageBox> #include <QPushButton> #include <QAbstractButton> QPushButton* agreeBut; QPushButton* disagreeBut; class MyWidget:public QWidget{ Q_OBJECT public slots: void buttonClicked(QAbstractButton * butClicked); }; void MyWidget::buttonClicked(QAbstractButton * butClicked){ if(butClicked == (QAbstractButton*)disagreeBut){ this->close(); } } int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 MyWidget myWidget; myWidget.setWindowTitle("主窗口"); myWidget.resize(400,300); //创建消息框 QMessageBox MyBox(QMessageBox::Question,"",""); MyBox.setParent(&myWidget); //设置消息框的属性为对话框,它会是一个独立的窗口 MyBox.setWindowFlags(Qt::Dialog); MyBox.setWindowTitle("协议"); MyBox.setText("使用本产品,请您严格遵守xxx规定!"); //自定义两个按钮 agreeBut = MyBox.addButton("同意", QMessageBox::AcceptRole); disagreeBut = MyBox.addButton("拒绝", QMessageBox::RejectRole); myWidget.show(); //添加信号和槽,监听用户点击的按钮,如果用户拒绝,则主窗口随之关闭。 QObject::connect(&MyBox,&QMessageBox::buttonClicked,&myWidget,&MyWidget::buttonClicked); MyBox.exec(); return a.exec(); } //MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc"
作为一款成熟的 GUI 框架,Qt 提供了很多摆放控件的辅助工具(又称布局管理器或者布局控件),它们可以完成两件事:
Qt 共提供了 5 种布局管理器,每种布局管理器对应一个类,分别是 QVBoxLayout(垂直布局)、QHBoxLayout(水平布局)、QGridLayout(网格布局)、QFormLayout(表单布局)和 QStackedLayout(分组布局),它们的继承关系如下图所示:
QVBoxLayout垂直布局
直布局指的是将所有控件从上到下(或者从下到上)依次摆放,例如:
程序中使用 QVBoxLayout 布局控件,需提前引入QVBoxLayout头文件。每个 QVBoxLayout 控件本质都是 QVBoxLayout 类的实例对象,该类提供了两个构造函数,分别是:
QVBoxLayout()
QVBoxLayout(QWidget *parent)
创建 QVBoxLayout 控件的同时可以指定父窗口,那么它将作为父窗口中管理其它控件的工具;也可以暂时不指定父窗口,待全部设置完毕后再将其添加到某个窗口中。
示例:
#include <QApplication> #include <QWidget> #include <QLabel> #include <QVBoxLayout> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 QWidget widget; widget.setWindowTitle("QVBoxLayout垂直布局"); //创建垂直布局管理器 QVBoxLayout *layout=new QVBoxLayout; //设置布局管理器中所有控件从下往上依次排列 layout->setDirection(QBoxLayout::BottomToTop); //连续创建 3 个文本框,并设置它们的背景和字体大小 QLabel lab1("Label1"); lab1.setStyleSheet("QLabel{background:#dddddd;font:20px;}"); lab1.setAlignment(Qt::AlignCenter); QLabel lab2("Label2"); lab2.setStyleSheet("QLabel{background:#cccccc;font:20px;}"); lab2.setAlignment(Qt::AlignCenter); QLabel lab3("Label3"); lab3.setStyleSheet("QLabel{background:#ffffff;font:20px;}"); lab3.setAlignment(Qt::AlignCenter); //将 3 个文本框和 2 个空白行添加到管理器中,它们的伸缩系数比是 2:1:2:3:3 layout->addStretch(2); layout->addWidget(&lab1,1); layout->addWidget(&lab2,2); layout->addWidget(&lab3,3); layout->addStretch(3); //将布局管理器添加到 widget 窗口中 widget.setLayout(layout); widget.show(); return a.exec(); }
程序中做了以下几个操作:
通过调用 setDirection() 方法,将添加到 QVBoxLayout 管理器中的所有控件(包括空白行)按照从下到上的顺序依次摆放。举个例子,由于 lab1 文本框是第二个添加到管理器中的,因此在最终显示的界面中,lab1 位于倒数第二的位置。
通过调用 addStrech() 方法,向管理器中先后添加了两个空白行,它们的伸缩系数分别为 2 和 3,因此 widget 窗口中的空白区域会平均分为 5 份,一个空白行占 3 份,另一个占 2 份。
通过调用 addWidget() 方法,向管理器中先后添加了 3 个文本框,它们的拉伸系数比为 1:2:3,所以当我们拉伸 widget 窗口时,三个文本框的大小(宽度)呈现 1:2:3 的关系。
通过调用 setLayout() 方法,成功地将 layout 布局管理器添加到了 widget 窗口中。当然,也可以在创建 layout 对象时指定 widget 作为它的父窗口,两种方式是完全等价的。
水平布局指的是将所有控件从左到右(或者从右到左)依次摆放,例如:
使用 QHBoxLayout 水平布局控件,程序中要提前引入QHBoxLayout头文件。QHBoxLayout 和 QVBoxLayout 继承自同一个类,它们的用法非常相似,比如 QHBoxLayout 类也提供了两个构造函数:
QHBoxLayout()
QHBoxLayout(QWidget *parent)
QHBoxLayout 类也没有新添任何成员方法,它只能使用从父类继承的成员方法。因此,QVBoxLayout常用方法 中罗列的所有成员方法也同样适用于 QHBoxLayout 对象。
示例:
#include <QApplication> #include <QWidget> #include <QLabel> #include <QHBoxLayout> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 QWidget widget; widget.setWindowTitle("QHBoxLayout水平布局"); //创建水平布局管理器 QHBoxLayout *layout=new QHBoxLayout; //设置布局管理器中所有控件的布局方向为从右往左依次排列 layout->setDirection(QBoxLayout::RightToLeft); //连续创建 3 个文本框,并设置它们的背景和字体大小 QLabel lab1("Label1"); lab1.setStyleSheet("QLabel{background:#dddddd;font:20px;}"); lab1.setAlignment(Qt::AlignCenter); QLabel lab2("Label2"); lab2.setStyleSheet("QLabel{background:#cccccc;font:20px;}"); lab2.setAlignment(Qt::AlignCenter); QLabel lab3("Label3"); lab3.setStyleSheet("QLabel{background:#ffffff;font:20px;}"); lab3.setAlignment(Qt::AlignCenter); //将 3 个文本框和 2 个空白列添加到管理器中,它们的拉伸系数比是 2:1:2:3:3 layout->addStretch(2); layout->addWidget(&lab1,1); layout->addWidget(&lab2,2); layout->addWidget(&lab3,3); layout->addStretch(3); //将布局管理器添加到 widget 窗口中 widget.setLayout(layout); widget.show(); return a.exec(); }
QGridLayout网格布局
QGridLayout 的行标和列标都从 0 开始,例如图 6 中 one 按钮的位置为 (0, 0),Four 按钮的位置为 (2, 0)。我们可以随意指定 QGridLayout 的行数和列数,各个控件可以随意摆放,必要时某些位置可以空着不用。
使用 QGridLayout 网格控件,程序中需引入QGridLayout头文件。每个 QGridLayout 控件都是 QGridLayout 类的一个实例对象,该类提供了两个构造函数,分别是:
QGridLayout(QWidget *parent)
QGridLayout()
示例:
#include <QApplication> #include <QWidget> #include <QLabel> #include <QGridLayout> #include <QPushButton> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 QWidget widget; widget.setWindowTitle("QGridLayout网格布局"); //创建 4 个按钮和 1 个文本框 QPushButton *but1 = new QPushButton("but1"); QPushButton *but2 = new QPushButton("but2"); QLabel *lab3 = new QLabel("lab"); lab3->setStyleSheet("QLabel{background:#dddddd;font:20px;}"); lab3->setAlignment(Qt::AlignCenter); QPushButton *but3 = new QPushButton("but3"); QPushButton *but4 = new QPushButton("but4"); //创建网格布局控件 QGridLayout *layout = new QGridLayout; //向 layout 中添加控件,并指定各个控件的位置 layout->addWidget(but1, 0, 0); layout->addWidget(but2, 0, 2); layout->addWidget(lab3, 1, 0, 3, 3); layout->addWidget(but3, 4, 0); layout->addWidget(but4, 4, 2); //将 layout 添加到 widget 窗口中 widget.setLayout(layout); widget.show(); return a.exec(); }
Qt 提供了很多种输入框控件,包括 QLineEdit 单行输入框、QTextEdit 多行输入框等。通常情况下,每个输入框的旁边都会附带一些文字(又称标签),用来提示用户需要输入的信息。例如,图 8 中第一个输入框的标签为 “Name”,提示用户填写自己的姓名。
使用 QFormLayout 布局控件之前,程序中应引入QFormLayout头文件。每一个表单布局控件都是 QFormLayout 类的一个实例对象,该类仅提供了一个构造函数:
QFormLayout(QWidget *parent = Q_NULLPTR)
示例:
#include <QApplication> #include <QWidget> #include <QLineEdit> #include <QFormLayout> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 QWidget widget; widget.setWindowTitle("QFormLayout表单布局"); //创建 4 个按钮和 1 个文本框 QFormLayout* layout = new QFormLayout(); //设置表单中的标签都位于控件的上方 layout->setRowWrapPolicy(QFormLayout::WrapAllRows); //添加 3 行输入框和标签 layout->addRow("Name:",new QLineEdit()); layout->addRow("Email:",new QLineEdit()); layout->addRow("Adress:",new QLineEdit()); //设置行间距和列间距为 10 layout->setSpacing(10); //将 layout 表单添加到 widget 窗口中 widget.setLayout(layout); widget.show(); return a.exec(); }
QStackedLayout 布局管理器可以容纳多个控件或者窗口,但每次只显示其中的一个。类似于vscode中的设置界面
使用 QStackedLayout 布局控件,程序中必须先引入QStackedLayout头文件。 每个 QStackedLayout 控件都是 QStackedLayout 类的一个实例对象,该类提供有 3 个构造函数,分别是:
QStackedLayout()
QStackedLayout(QWidget *parent)
QStackedLayout(QLayout *parentLayout)
借助第二个构造函数,我们可以将 QStackedLayout 添加到指定的 parent 窗口中;借助第三个构造函数,我们可以将 QStackedLayout 嵌入到指定的 parentLayout 布局控件中
#include <QApplication> #include <QWidget> #include <QPushButton> #include <QLabel> #include <QLineEdit> #include <QStackedLayout> #include <QListWidget> #include <QHBoxLayout> int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 QWidget widget; widget.setWindowTitle("QStackedLayout分组布局"); widget.resize(600,400); //向主窗口中添加一个水平布局控件 QHBoxLayout *layout=new QHBoxLayout; //创建一个列表 QListWidget listWidget(&widget); listWidget.setMinimumWidth(150); listWidget.setFont(QFont("宋体",14)); listWidget.addItem("QPushButton"); listWidget.addItem("QLabel"); listWidget.addItem("QLineEdit"); //新建 3 个窗口,分别放置文本框、按钮和单行输入框 QWidget widget1; widget1.setMinimumSize(400,400); QPushButton but1("这是一个按钮",&widget1); QWidget widget2; widget2.setMinimumSize(400,400); QLabel lab1("这是一个文本框",&widget2); QWidget widget3; widget3.setMinimumSize(400,400); QLineEdit edit("这是一个单行输入框",&widget3); //创建一个分组布局,将 3 个窗口添加到分组控件中 QStackedLayout *stackedLayout = new QStackedLayout; stackedLayout->addWidget(&widget1); stackedLayout->addWidget(&widget2); stackedLayout->addWidget(&widget3); //layout 第一列添加 QListWidget 控件,第二列添加分组布局控件,设置它们的伸缩系数比为 1:4 layout->addWidget(&listWidget,1); layout->addLayout(stackedLayout,4); //将 layout 水平布局控件添加到 widget 窗口中 widget.setLayout(layout); widget.show(); //连接信号和槽,实现当点击列表中的某一项,切换分组布局管理器显示的控件 QObject::connect(&listWidget,&QListWidget::currentRowChanged,stackedLayout,&QStackedLayout::setCurrentIndex); return a.exec(); }
默认情况下,每个 Qt 项目都包含一个后缀名为.pro、名称和项目名相同的文件,我们通常称它为项目管理文件或者工程管理文件(简称 pro 文件)。
任何一个 Qt 项目都至少包含一个 pro 文件,此文件负责存储与当前项目有关的配置信息,比如:
所谓模块,可以简单地理解为文件夹或者压缩包,内部包含多个功能相近的类。作为一款成熟的 GUI 框架,Qt 提供了大量的类,根据这些类的功能,Qt 将它们分成了几个组,每个组称为一个模块。打开 Qt Creator 的帮助界面并搜索“All modules”,可以看到 Qt 的所有模块
一个项目中可能包含上百个源文件,Qt 编译这些源文件的方法是:先由 qmake 工具根据 pro 文件记录的配置信息生成相应的 makefile 文件,然后执行 make 命令完成对整个项目的编译。也就是说,pro 文件存储的配置信息是用来告知编译器如何编译当前项目的,所以一个 Qt 项目要想完美运行,既要保证各个源文件中程序的正确性,还要保证 pro 文件中配置信息的合理性。
对于一个刚刚创建好的 Qt 项目,pro 文件并不是空的,而是包含一些基本的配置信息。实际开发中,Qt 会自动修改 pro 文件的内容,但有时也需要我们手动修改,例如程序中用到某个第三方库时,就需要我们手动修改 pro 文件。
pro文件中的配置信息
以上是 Demo.pro 配置文件中默认包含的内容。其中,#号是注释符号,除了以#号开头的注释内容外,其它内容都是当前项目的配置信息,比如QT += core gui、TARGET = Demo等。
根据上表中对各个配置项的讲解,您可以很轻松地搞清楚 Demo.pro 文件中各个配置项的含义,这里不再过多赘述。
上表中,大部分配置项不需要我们手动修改,比如 SOURCES、HEADERS、FORMS 等,当我们添加或者删除项目中的源文件时,Qt 会自动修改这些配置项。有些配置项需要手动修改,比如 QT 配置项,接下来重点给大家讲解 QT 的用法。
前面提到,Qt 根据各个类的功能将它们分到不同的模块,因此程序中要想使用某个类,必须完成两项准备工作:
QT 用来指明当前项目中用到的所有模块,它的默认值是 core 和 gui,分别表示引入 Core 模块和 GUI 模块:
每个新创建的 Qt GUI 项目中,都默认包含 Core 模块和 GUI 模块,如果项目中用不到它们,可以使用QT -=删除。例如,删除项目中包含的 GUI 模块,只需在 pro 文件中添加一条配置信息:
QT -= gui
除了 Core 和 GUI 模块外,Qt 还有 SQL(包含操作数据库相关的类)、Widgets(包含构建界面的所有控件类)、Multimedia(包含提供音频、视频等功能的类)等模块,Qt 项目不会自动包含这些模块。例如,项目中用到 SQL 模块中的一些类时,需要在 pro 文件中添加如下配置信息:
QT += sql
那么,当程序中用到某个类时,如何知道它属于哪个模块呢?很简单,先将该类所在的头文件中引入到程序中,然后鼠标选中头文件并按Fn+F1组合键,打开该头文件的使用手册后就可以看到它所属的模块。以程序中使用 QWidget 窗口类为例,先在程序中添加如下语句:
#include <QWidget>
紧接着,鼠标选中“QWidget”并按Fn+F1组合键,打开下图所示的 QWdiget 类使用手册,可以看到该类所属的模块为 widgets。
实际开发中,如果仅使用 Qt 提供的信号函数和槽函数,会经常遇到信号函数的参数类型和个数无法满足实际需求、信号函数和槽函数的参数类型不匹配等问题。解决此类问题,最简单有效的方式就是:自定义场景需要的信号函数和槽函数。
自定义信号函数
信号函数指的是符合以下条件的函数:
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//修饰信号函数的关键字
signals:
//自定义的信号函数
void MySignal(QString message);
};
我们自定义了一个继承自 QWidget 的 MyWidget 类,QWidget 是 QObject 的子类,所以 MyWidget 间接继承自 QObject 类。MyWidget 类中自定义了名为 MySignal 的信号函数(可以简称 MySignal 信号),它用 signals 关键字修饰,没有返回值,也没有定义(实现),仅有 1 个参数。
对于 MySignal() 信号函数,程序中不会直接调用它,而是借助 connect() 连接某个槽函数,实现的语法格式是:
MyWidget myWidget;
QObject::connect(&myWidget,&MyWidget::MySignal,信号接收者,槽函数);
一旦确定了信号接收者和槽函数,当 MySignal 信号发出后,与之相连的槽函数就会执行。那么,程序中如何发出 MySignal 信号呢?
对于 Qt 提供给我们的信号函数,其底层已经设置好了信号发出的时机,例如按下鼠标时、点击 Enter 回车键时等等。对于自定义的信号,我们需要自己指定信号发出的时机,这就需要用到 emit 关键字。emit 中文意思为“发出、射出”,是 Qt 在 C++ 基础上扩展的一个关键字,专门用来发射信号。
以定义好的 MySignal 信号为例,修改 MyWidget 类为:
class MyWidget:public QWidget{
//Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制
Q_OBJECT
//自定义信号函数
signals:
void MySignal(QString mess);
public:
void emitSignal(){
emit MySignal(message);
}
private:
QString message;
};
我们为 MyWidget 类新增了一个 emitSignal() 方法和一个 message 属性,emitSignal() 方法中的emit MySignal(message);语句就表示发射 MySignal 信号。当程序中执行 emitSingal() 函数时,就会发出 MySignal 信号,message 属性的值也会随信号一同发出,对应的槽函数可以接收到 message 的值。
对于每一个自定义的信号函数,程序中都应该提供发射该信号的方法(函数),而且这样的方法(函数)可以有多个。
自定义槽函数
Qt5 中,槽函数既可以是普通的全局函数、也可以是类的成员函数、静态成员函数、友元函数、虚函数,还可以用 lambda 表达式表示。
和信号函数不同,槽函数必须手动定义(实现)。槽函数可以在程序中直接调用,但主要用来响应某个信号。自定义一个槽函数时,需要注意以下几点:
举个例子,自定义响应 MySignal 信号的槽函数:
class MyWidget:public QWidget{ //Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制 Q_OBJECT signals: void MySignal(QString mess1,QString mess2); public: void emitSignal(){ emit MySignal(message1,message2); } //类的成员函数 void recSlot1(QString mess){ qDebug() << "执行 recSlot1() 成员函数,输出" << mess; } //指明定义的是槽函数 public slots: void recSlot2(QString mess1,QString mess2){ qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2; } public: QString message1; QString message2; }; //全局函数 void recSlot3(){ qDebug() << "执行 recSlot3() 全局函数"; }
程序中,重点关注 recSlot1()、recSlot2()、recSlot3() 这 3 个函数:
slots 关键字可以和 public、protected、private 搭配使用,它们的区别是:
通常情况下,槽函数使用 public slots 修饰。
很多读者会问,既然 public 修饰的成员函数可以当做槽函数,为什么还要提供 slots 关键字呢?笔者认为,“兼容旧的 Qt 版本”是其中的一个原因。Qt4 中的槽函数只能是 slots 修饰的类成员函数,Qt5 中取消了这一限制,但考虑到要兼容旧的 Qt 版本,Qt5 保留了旧版本中 connect() 函数的语法格式,也保留了 slots 关键字。
调用 connect() 函数,将 MySignal() 信号函数分别连接 recSlot1()、recSlot2()、recSlot3() 三个槽函数,实现代码为:
//类的成员函数作为槽函数
QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1);
//信号函数和槽函数相连接
QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2);
//全局函数作为槽函数
QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3);
//main.cpp #include <QApplication> #include <QWidget> #include <QDebug> class MyWidget:public QWidget{ //Q_OBJECT 是一个宏,添加它才能正常使用 Qt 的信号和槽机制 Q_OBJECT //信号函数 signals: void MySignal(QString mess1,QString mess2); public: //发射信号的函数 void emitSignal(){ emit MySignal(message1,message2); } //普通类成员函数 void recSlot1(QString mess){ qDebug() << "执行 recSlot1() 成员函数,输出" << mess; } //槽函数 public slots: void recSlot2(QString mess1,QString mess2){ qDebug() << "执行 recSlot2() 槽函数,输出"<< mess1 << " " << mess2; } public: QString message1; QString message2; }; //全局函数 void recSlot3(){ qDebug() << "执行 recSlot3() 全局函数"; } int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建主窗口 MyWidget mywidget; mywidget.message1 = "day13"; mywidget.message2 = "qt"; //类的成员函数作为槽函数 QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot1); //信号函数和槽函数相连接 QObject::connect(&mywidget,&MyWidget::MySignal,&mywidget,&MyWidget::recSlot2); //全局函数作为槽函数 QObject::connect(&mywidget,&MyWidget::MySignal,&recSlot3); mywidget.show(); //发射 Signal 信号 mywidget.emitSignal(); return a.exec(); } //MyWidget类的定义应该放到 .h 文件中,本例中将其写到 main.cpp 中,程序最后需要添加 #include "当前源文件名.moc" 语句,否则无法通过编译。 #include "main.moc"
执行程序,会弹出一个 myWidget 空白窗口,同时输出以下信息:
执行 recSlot1() 成员函数,输出 “day13”
执行 recSlot2() 槽函数,输出 “day13” “qt”
执行 recSlot3() 全局函数
QFile 类支持对文件进行读取、写入、删除、重命名、拷贝等操作,它既可以操作文件文件,也可以操作二进制文件。
使用 QFile 类操作文件之前,程序中需引入头文件。创建 QFile 类的对象,常用的构造函数有:
QFile::QFile()
QFile::QFile(const QString &name)
参数 name 用来指定要操作的目标文件,包含文件的存储路径和文件名,存储路径可以使用绝对路径(比如 “D:/Demo/test.txt”)或者相对路径(比如"./Demo/test.txt"),路径中的分隔符要用 “/” 表示。
通常情况下,我们会调用第二个构造函数,直接指明要操作的文件。对于第一个构造函数创建的 QFile 对象,需要再调用 setFileName() 方法指明要操作的文件。
与 C++ 读写文件的规则一样,使用 QFile 读写文件之前必须先打开文件,调用 open() 成员方法即可,常用的语法格式为:
bool QFile::open(OpenMode mode)
mode 参数用来指定文件的打开方式,下表罗列了此参数的可选值以及各自的含义:
根据需要,可以为 mode 参数一次性指定多个值,值和值之间用|分割。比如:
注意,传递给 mode 参数的多个值之间不能相互冲突,比如 Append 和 Truncate 不能同时使用。
如果文件成功打开,open() 函数返回 true,否则返回 false。
QFile 类提供了很多功能实用的方法,可以快速完成对文件的操作,下表列举了常用的一些:
#include <QFile> #include <QDebug> int main(int argc, char *argv[]) { //创建 QFile 对象,同时指定要操作的文件 QFile file("D:/demo.txt"); //对文件进行写操作 if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } //向文件中写入两行字符串 file.write("qt学习\n"); file.write("day14"); //关闭文件 file.close(); //重新打开文件,对文件进行读操作 if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } //每次都去文件中的一行,然后输出读取到的字符串 char * str = new char[100]; qint64 readNum = file.readLine(str,100); //当读取出现错误(返回 -1)或者读取到的字符数为 0 时,结束读取 while((readNum !=0) && (readNum != -1)){ qDebug() << str; readNum = file.readLine(str,100); } file.close(); return 0; }
执行程序,“qt学习” 和 “day14” 先写入 D 盘的 demo.txt 文件,然后再从文件中将它们读取出来。
#include <QFile> #include <QDebug> int main(int argc, char *argv[]) { //指定要写入文件的数据 qint32 nums[5]={1,2,3,4,5}; //写入文件之前,要将数据以二进制方式存储到字节数组中 QByteArray byteArr; byteArr.resize(sizeof(nums)); for(int i=0;i<5;i++){ //借助指针,将每个整数拷贝到字节数组中 memcpy(byteArr.data()+i*sizeof(qint32),&(nums[i]),sizeof(qint32)); } //将 byteArr 字节数组存储到文件中 QFile file("D:/demo.dat"); file.open(QIODevice::WriteOnly); file.write(byteArr); file.close(); //再次打开文件,读取文件中存储的二进制数据 file.open(QIODevice::ReadOnly); QByteArray resArr = file.readAll(); //输出读取到的二进制数据 qDebug()<<"resArr: "<<resArr; //将二进制数据转化为整数 char* data = resArr.data(); while(*data){ qDebug() << *(qint32*)data; data += sizeof(qint32); } return 0; }
执行程序,demo.dat 文件中会存储 {1,2,3,4,5} 这 5 个整数的二进制形式,同时输出以下内容:
resArr: “\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x05\x00\x00\x00”
1
2
3
4
5
单独使用 QFile 类读写文件的过程既繁琐又复杂,Qt 提供了两个辅助类 QTextStream 和 QDataStream,前者用来读写文件文件,后者用来读写二进制文件,QFile 可以和它们搭配使用,从整体上提高读写文件的开发效率。
和单独使用 QFile 类读写文本文件相比,QTextStream 类提供了很多读写文件相关的方法,还可以设定写入到文件中的数据格式,比如对齐方式、写入数字是否带前缀等等。
使用 QTextStream 类之前,程序中要先引入头文件。QTextStream 类提供了很多种构造函数,常用的是:
QTextStream(QIODevice *device)
QIODevice 是 QFile 的父类,因此在构造 QTextStream 类的对象时,需要传递一个 QFile 类的对象。
下表罗列了 QTextStream 类常用的一些方法:
成员方法
QTextStream 类重载了>>输入运算符和>>输出运算符,使读写文本文件变得更简单。例如,用 QTextStream 实现【实例一】的程序如下:
#include <QFile> #include <QDebug> #include <QString> #include <QTextStream> int main(int argc, char *argv[]) { //创建 QFile 对象,同时指定要操作的文件 QFile file("1.txt"); //对文件进行写操作 if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } QTextStream out(&file); //向文件中写入两行字符串 out << (QString)"qt学习\n" << (QString)"day14"; //关闭文件 file.close(); //重新打开文件,对文件进行读操作 if(!file.open(QIODevice::ReadOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } QTextStream in(&file); //一直读,直至读取失败 while(!in.atEnd()){ QString str; //从文件中读取一个字符串 in >> str; qDebug() << str; } file.close(); return 0; }
和iostream类似,QTextStream 类提供了两种格式化输出的方法,一种是调用该类的成员方法,例如表 3 中的 setFieldAlignment()、setFieldWidth 等,另一种是调用 QTextStream 类提供的格式描述符,下表罗列了常用的一些:
#include <QFile> #include <QDebug> #include <QString> #include <QTextStream> int main(int argc, char *argv[]) { QFile file("1.txt"); if(!file.open(QIODevice::WriteOnly|QIODevice::Text)){ qDebug()<<"文件打开失败"; } QTextStream out(&file); //将 10 的十六进制数写入文件 out << hex << 10; //设置每份数据占用 10 个字符的位置 out.setFieldWidth(10); //以右对齐的方式写入 3.14 out << left << 3.14; //后续数据以左对齐的方式写入文件 out.setFieldAlignment(QTextStream::AlignRight); out << 2.7; //关闭文件 file.close(); return 0; }
程序运行后,1.txt 存储的文本内容为:
a3.14 2.7
QDataStream 类的用法和 QTextStream 非常类似,最主要的区别在于,QDataStream 用于读写二进制文件。
使用 QDataStream 类之前,程序中要引入头文件。创建 QDataStream 对象常用的构造函数为:
QDataStream::QDataStream(QIODevice *d)
下表罗列了 QDataStream 类常用的成员方法:
QDataStream 类也对<<和>>进行了重载,举个简单的例子,用 QDataStream 重新实现实例二:
#include <QFile> #include <QDebug> #include <QDataStream> int main(int argc, char *argv[]) { //指定要写入文件的数据 qint32 nums[5]={1,2,3,4,5}; QFile file("1.dat"); file.open(QIODevice::WriteOnly); //创建 QDataStream 对象 QDataStream out(&file); //将 nums 数组中的整数逐个写入到二进制文件中 for(int i=0;i<5;i++){ out << nums[i]; } file.close(); //再次打开文件,读取文件中存储的二进制数据 file.open(QIODevice::ReadOnly); QDataStream in(&file); //读取二进制文件中的数据 while(!in.atEnd()){ //每次读取一个整数 qint32 num; in >> num; qDebug() << num; } return 0; }
输出结果为:
1
2
3
4
5
该学生信息管理系统将学生信息保存到文件中,用户借助界面上的表格、列表、按钮、输入框等控件,可以对学生信息进行查看、添加、删除、查找、更改、保存等操作。
整个学生信息管理系统,需要设计两个界面,一个是上图所示的主界面,另一个是添加学生信息的界面,如下图所示:
主界面的设计实现思路是:将 QHBoxLayout 作为主界面的布局工具,内部添加两个 QGroupBox 分组框,从而将整个界面一分为二:
所示的添加学生信息界面非常简单,只需要自定义一个继承自 QDialog 的窗口类,用 QVBoxLayout 作为该窗口的布局工具,并依次将 QFormLayout 和 QHBoxLayout 添加到 QVBoxLayout 中:
学生信息管理系统的功能实现
整个学生信息管理系统,由以下几个文件构成:
各个文件的作用分别是:
例如在本项目中,MainWidget.cpp 文件 flushTable() 函数的功能是更新 QTableWidget 表格控件中显示的学生信息。更新学生信息之前,需要调用 disconnect() 函数切断 cellChanged() 信号函数与其它所有槽函数的关联,然后才能正常更新数据,更新完成后再恢复 cellChanged() 与其它槽函数的关联。
之所以更新数据前必须切断 cellChanged() 与其它槽函数的关联,是因为更新表格数据会不断地触发 cellChanged() 信号,最终会导致程序崩溃。
2、从文件中删除和更改某个学生信息
实现对学生信息的“增删查改”操作中,删除和修改学生信息的实现过程更复杂一些,本项目中采取的实现方法是:不断地从 student.txt 文件中读取学生信息,判断读取到的学生信息是否需要删除或修改,如果不需要,则直接写入 student_temp.txt 文件;反之如果需要删除,则直接将读取到的信息丢弃,如果需要修改,则将修改后的学生信息写入到 student_temp.txt 文件中。
最终,student_temp.txt 文件中存储的就是最新的学生信息,我们可以将 student.txt 文件中的内容删除,然后将 student_temp.txt 文件中的内容拷贝到 student.txt 文件中,最后删除 student_temp.txt 文件;也可以直接删除 student.txt 文件,然后将 student_temp.txt 文件的名称改为 student.txt,本项目中采用的是第二种方法。
3、为项目添加图标
image.qrc 文件是用来为项目添加图标的,本项目选用的是 C 语言中文网的 icon 图标,就存储在当前项目的文件夹内,如下图所示:
首先,我们要在项目中新建一个后缀名为 qrc 的文件,鼠标移动到项目名上右击选择“添加新文件”,Qt Creator 会弹出如下对话框:
选择 “Qt -> Qt Rescource File”,可以创建一个 qrc 文件。在此基础上,在 image.qrc 上右击选择“添加现有文件”,选中项目中的 logo.ico 图标,就被成功地将图标添加到项目中。
接下来,哪个界面需要添加图标,直接调用 setWindowIcon() 方法即可,例如:
setWindowIcon(QIcon(":/logo.ico"));
icon 图标的存储路径可以通过右击项目中的 logo.ico 图标,选择 "Copy Path “:logo.ico” 即可获得。
进度拖拉一下, 详情参考c语言中文网
分享 Qt 程序(项目)的方式无非两种,要么直接分享程序的源代码,要么分享程序生成的可执行文件。
和直接分享源码相比,大多数人会选择后者。但遗憾地是,Qt Creator 默认以动态链接的方式生成可执行文件,该文件无法独立运行,必须为其提供所需的动态链接库。也就是说,只分享 Qt Creator 生成的可执行文件是不行的,必须将运行所需的动态链接库一起分享,可执行文件才能在他人的电脑上正常运行。
为可执行文件添加图标
为可执行文件添加 icon 图标的方法很简单,将事先准备好的 icon 图标拷贝到程序对应的文件夹中,然后在 pro 工程文件内添加如下指令:
RC_ICONS += logo.ico
其中,logo.ico 是图标文件的名称。重新对程序进行编译、链接,最终生成的就是带 icon 图标的可执行文件。
Qt程序打包
打包 Qt 程序,通常选用以 release 模式生成的可执行文件。和 debug 模式相比,release 模式生成的可执行文件体积更小,运行效率更快。
Qt Creator 默认以 debug 模式生成可执行文件,如下图所示,可以手动修改 Qt Creator 以 release 模式生成可执行文件:
选择“Release”之后,再次运行程序,生成的可执行文件可以在下图所示的路径中找到:
找到可执行文件之后,将其拷贝到一个空的文件夹,比如笔者将其拷贝到了新建的 D:\StuInfoFile 文件夹中,如下图所示:
此时的 StuInfoFile.exe 是无法运行的,双击它系统会提示类似“找不到 xxx.dll”的错误信息。
在“开始”中找到 Qt 命令行程序并打开,如下图所示:
在命令行中,先执行 “cd D:\StuInfoFile” 命令进入 StuInfoFile 文件夹,然后再执行 “windeployqt StuInfoFile.exe” 命令。windeployqt 是 Qt 提供的 Windows 平台打包工具,它能找到 StuInfoFile.exe 可执行文件需要的所有动态链接库,并将它们拷贝到当前文件夹中。
成功执行命令之后,StuInfoFile 文件夹内会增加很多文件夹和文件(如下图所示),这些都是 StuinfoFile.exe 执行所需要的。
再次双击 StuInfoFile.exe,如果它可以成功执行,表明打包操作是成功的。我们可以直接将 StuInfoFile 文件夹分享给他人,只要是 Windows 平台,都可以直接点击运行 StuinfoFile.exe 文件。
其中一个管用
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB2312"));
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
资料来源:c语言中文网
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。