终极建议:如果有时间,而且英文能力可以,直接阅读英文文档是最好的:首页或者文档。资料很多,我暂时也没细看,需要时候要仔细研究。
Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应用程序,但这并不是 Qt 的全部;Qt 除了可以绘制漂亮的界面(包括控件、布局、交互),还包含很多其它功能,比如多线程、访问数据库、图像处理、音频视频处理、网络通信、文件操作等,这些 Qt 都已经内置了。Qt 是应用程序开发的一站式解决方案!除了与计算机底层结合特别紧密的应用,大部分应用程序都可以使用 Qt 实现。
Qt Creator的安装参考这篇文章。
Qt类库是Qt的核心,位于Qt/5.9文件夹下,Qt/5.9文件夹下的每个子文件夹对应一种运行平台/编译平台,例如mingw,msvc,arm等,用mingw举例,其目录结构为:
在Qt类库中,以xxxd.a/xxxd.dll格式的文件是用于debug编译的库文件,而xxx.a/xxx.dll则是用于release的,例如libQt5Guid.a/Qt5Guid.dll,libQt5Gui.a/Qt5Gui.dll。
Qt 不是凭空产生的,它是基于现有工具链打造而成的,它所使用的编译器、链接器、调试器等都不是自己的,Qt 官方只是开发了上层工具。
Qt 使用的工具链主要有:GNU(各种软件在linux位于/usr/bin/ 目录,如gcc、g++、ld、ar、make、gbd、ldd[查看可执行文件依赖的共享库])、MinGW(Windows下的GNU工具链,使用Dependency Walker查看可执行文件依赖的共享库)、CMake(跨平台的)、Qt工具集(qmake等,具体如下图)。
Qt SDK 提供了一个命令行环境, 给那些有特殊需求的开发者, 方便他们在不使用 Qt Creator 集成开发环境的情况下编译应用程序。我们可以从如下地方找到命令行程序:
在该命令窗口中,可以运行qmake, qmlsecne等命令。
qmlscene是随着Qt 5发布的 一个工具, 用来加载 QML 文档, 它使得我们可以在应用开发过程中随时查看 QML 代码的效果。这是一个在应用程序完成之前加载和显示QML文档的实用程序。使用qmlscene就可以查看.qml文件的效果了(这里暂时不研究如何使用)。
下面举例说明如何命令行编译:
①创建一个工程文件夹,在其下面编辑.cpp.h.pro.qml.qrc等文件,注意要保存为utf8格式。这里我们只创建一个main.cpp文件:
②打开上图所示的qt命令行终端Qt 5.10.1 for Desktop (MinGW 5.3.0 32 bit)【这是我自己的版本,根据安装版本名称略有区别】,然后cd进我们的工程文件夹。
③逐步编译:
首先输入以下命令创建.pro文件:
qmake -project
然后打开.pro文件,在INCLUDEPATH…行之下添加如下一行(QT5的要求):
greaterThan(QT_MAJOR_VERSION, 4): QT +=widgets
qmake
最后,我们运行以下两句进行编译,编译好的程序就存储在debug和release文件夹下了,我们就可以运行了:
mingw32-make -f Makefile.Debug
mingw32-make -f Makefile.Release
动静态库:Linux/Unix 系统里静态库扩展名一般是 .a,动态库扩展名一般是 .so 。Windows 系统里 VC 编译器用的静态库扩展名一般是 .lib,动态库扩展名一般是 .dll 。MinGW 使用的静态库扩展名为 .a ,而其动态库扩展名则为 .dll。
显式链接与隐式链接:这两种都是动态链接库的使用方式,默认是隐式的,即在链接生成可执行程序时就确立依赖关系,在该程序启动时,操作系统自动会检查它依赖的动态库,并一一加载到该程序的内存空间,在VC环境下,链接时使用动态库对应的 .lib 文件(包含动态库的导出函数声明,但没有实际功能代码),MinGW 是将动态库的导出函数声明放在了 .a 文件里;显式则表现为在程序中直接调用操作系统提供的函数,在必要的位置再加载动态库,Linux 里用 dlopen 函数打开并加载动态库,Windows 里一般用 LoadLibrary 打开并加载动态库。因此,Windows下无论是动态链接还是静态链接,一般都需要.lib/.a文件。
Linux下的图形界面KDE(GNOME是另外一种)就是Qt开发的:KDE 桌面系统已经将 Qt 作为默认的 GUI 库,Gnome 桌面系统则将 GTK+ 作为默认的 GUI 库。
windows下采用MSVC工具集的调试问题:
只安装Visual Studio的话是没有调试工具的,需要安装Windows Software Development Kit (SDK)补充。
Qt支持的项目类型:图形界面程序、控制台程序、Qt提供的一套基于QML的开发框架程序(界面效果酷炫)。
使build文件夹位于项目目录下的办法:将Shadow build勾选去掉即可:
Qt的可以使用VS开发,且也能调用Designer绘制界面,可参考(VS中使用Qt方法详解)。
这里用一个Qt Widgets Application\QMainWindow的项目举例,文件名任意,类名采用默认名称MainWindow。
打开Qt Creator,点击New Project创建项目,注意选择Qt Widgets Application\QMainWindow的配置。
模块支持
Qt 类库以模块的形式组织各种功能,根据项目涉及的功能需求,在项目中添加适当的类库模块支持。使用 “QT += 模块名” 的形式即可加入该模块,例如:
QT += core gui # 添加图形界面模块
QT += sql # 添加数据库模块
Qt版本控制
greaterThan(Qt_MAJOR_VERSION, 4): Qt += widgets # 如果大版本号大于4,则增添widgets模块
可执行文件名设置
TARGET = 可执行文件名
项目模板设置
TEMPLATE = app # 表示项目使用的模板是 app,是一般的应用程序
# 此外还可以设置为lib,vcapp等
文件管理
SOURCES、HEADERS、FORMS分别对应程序中的源文件,头文件,和ui文件,一般是Qt Creator自动修改的。例如:
SOURCES += \
main.cpp \
mainwindow.cpp
HEADERS += \
mainwindow.h
FORMS += \
mainwindow.ui
Qt工具链控制 - 总体控制
CONFIG += console # 允许控制台输出
CONFIG += c++11 # 支持C++11
Qt工具链控制 - 细节控制
DEFINES += QT_DEPRECATED_WARNINGS # 忽略未来废除这类警告,DEFINES用来对Qt工具链的工作进行指示。
添加额外的库
INCLUDEPATH += 额外库头文件目录绝对路径
LIBS += 额外库二进制文件路径
pro更具体的配置介绍,可以参考这篇博文。
或者官方文档关于pro文件的部分。
在Qt Creator中双击ui文件自动进入可视化ui设计器。
属性编辑器
一般位于右下角位置。最大的组别实际表示的是继承关系,例如一个QPushButton,其父类是QAbstractButton,再上一层父类是QWidget,最上层是QObject。
动作与信号/槽编辑器
信号和槽的概念参考这里。
一般在中心最下方。这东西很好用,要灵活使用。
ui的实现机理浅析
绘制好界面后,编译程序,会发现多出了一个ui_xxx.h的文件,其中xxx是类名的全小写。在这个例子中,此时共有四个文件:
mainwindow.h
mainwindow.cpp
mainwindow.ui
ui_mainwindow.h # 自动生成的
我们根据这四个文件中的关键代码分析ui大致的实现原理(其实主要还是依赖Qt的工具链uic、moc等,一般了解即可):
对于控件接收的内置槽函数,程序中不会出现显式的connect过程,这实际上是由于ui_xxx.h文件中,setupUi函数最后的connectSlotsByName函数的作用。connectSlotsByName函数将搜索界面上的所有组件,将与槽函数匹配的信号和槽关联起来,它假设槽函数的名称是:
void on_<发出信号的控件名>_<信号名>(<signal parameters>);
如果接收信号的是窗体而不是窗体上的控件,那么则会在setupUi函数的connectSlotsByName函数之前自动添加connect函数。
而如果是自定义槽函数,则需要手动使用connect函数,规则为:
connect(发出信号的控件指针, SINGAL(信号函数名), 接收信号的widget指针, SLOT(槽函数名));
关于信号与槽机制,需要注意的有:
①connect() 是 QObject 类的一个静态函数,QObject 是几乎所有 Qt 类的基类;
②一个信号可以连接多个槽;
③多个信号可以连接同一个槽;
④一个信号可以连接另外一个信号,例如:
connect(spinNum, SIGNAL(valueChanged(int)), this, SIGNAL (refreshInfo(int));
⑤严格的情况下,信号与槽的参数个数和类型需要一致;
⑥在使用信号与槽的类中,必须在类的定义中加入宏 Q_OBJECT;
⑦当一个信号被发射时,与其关联的槽函数通常被立即执行。
Qt添加资源文件的方法。
①在Qt编辑\项目面板中,右键项目名称,选择添加新文件,然后选择Qt Resource File,这样我们发现多出了一个Resources文件夹和一个qrc文件(我们自己起的名字)。
②右键qrc文件,选择Open In Editor,即可进入编辑页面。资源是按照树状结构管理的,只能有一级目录。页面下侧为编辑区域:
点击<添加>,发现弹出菜单有两个选项,分别是添加前缀和添加文件,我们需要先添加一个前缀,相当于文件夹;然后我们就可以添加资源进来了:
上图中即显示了我添加好的前缀icons。实际上,添加的前缀和文件会在上面的文件区域显示,我们从文件区域点击前缀名称,会发现下面的编辑区域跟着变化了,点击文件的效果也是类似的,这样我们就可以添加文件到已经创建好的几个前缀中,而且可以使用删除按钮删掉资源。
Qt 使用QAction类作为动作,QAction包含了图标、菜单文字、快捷键、状态栏文字、浮动帮助等信息,Qt自己选择使用哪个属性来显示,无需我们关心。同时,Qt 能够保证把QAction对象添加到不同的菜单、工具栏时,显示内容是同步的。也就是说,如果我们在菜单中修改了QAction的图标,那么在工具栏上面这个QAction所对应的按钮的图标也会同步修改。
实际上,action主要就是菜单栏和工具栏的功能。具体操作过程如下:
①添加菜单(对于带有菜单的wight类型,例如QMainWindow):
点击这里的<在这里输入>,即可添加menu以及菜单栏action,添加后发现下发action编辑区出现对应的action。
点击某个action,可以在属性编辑器看到其重要的属性。
在某个action上右键,选择编辑,即可进一步修改该action的配置:
我们可以设置名称、图标、checkable、快捷键等内容。其中Normal Off是关于工具栏显示的优先级的,具体可以参考Qt 关于 QAction 的文档。
②添加工具栏工具:
我们在action栏空白处右键,选择添加,即可创建空的action,然后编辑action的内容,注意要添加一个图标用来显示,然后把它直接拖到工具栏即可:
使用布局,可以让控件随着窗口尺寸变化而变化!且便于我们美化。能使用布局一定要使用布局。
Layouts和Spacers用于布局,布局组件会以红色边框显示。具体说明如下:
使用工具栏布局会更快捷:
工具栏的介绍为(其中Form Layout参考这里):
使用工具栏布局是较为方便的。布局时,选择某个容器类组件,相当于选择了其内部的所有组件。
不选择组件,选择窗体本身,通过工具栏布局,则改变窗体所有组件的总布局,此时窗体尺寸的变化则能使内部的组件一起变化。
只有多尝试,多动手,求知欲强,才能扎实地进步!
上图中的四个按钮用来切换界面编辑模式,模式上面已经有介绍,这里再次强调:
①Edit Widget模式就是正常编辑模式;
②Edit Signals/Slots可以可视化关联信号与槽,并使用内置的一些槽函数功能。进入状态后,拖拽触发组件到目标组件上,即可根据提示设置。
③Edit Buddies模式用来指定伙伴关系,主要用于设置快捷键:
伙伴关系(Buddy)是指界面上一个 Label 和一个组件相关联,进入该模式后,单击一个 Label,按住鼠标左键,然后拖向一个组件,就建立了 Label 和组件之间的伙伴关系。伙伴关系是为了在程序运行时,在窗体上用快捷键快速将输入焦点切换到某个组件上。我们需要把Label组件的命名改为:“原来命令(&某个字母)”,编辑好后,&符号会自动隐藏,程序运行时,只需要按下Alt+该字母,焦点就会来到Label的伙伴组件上。
④Edit Tab Order用来指定Tab键的切换顺序。进入模式后,只需要点击各个组件上显示的序号,序号的内容就会变为点击的顺序。
由于界面设计的底层其实都是由 C++ 语言实现的,底层实现的功能比可视化设计更加强大和灵活,某些界面效果是可视化设计无法完成的。但是这种办法会麻烦很多,包括connect等操作都需要写代码。
①如何创建纯代码界面工程:
创建过程和正常过程是类似的,一般也选择Qt Widgets Application,但是在后续向导中要去掉创建界面(Generate form)的checkBox。这样构建的项目就没有ui文件了。但是直接运行程序,仍然会弹出窗口,只是我们不能可视化设计界面,只能代码设计了。
②类的结构:
首先把需要用到的控件include进来到h文件中,控件定义为私有成员指针,槽函数要规范命名,且使用slots关键字,例如:
class QWDlgManual : public QDialog
{
Q_OBJECT
private:
QCheckBox *chkBoxUnder;
QCheckBox *chkBoxItalic;
QCheckBox *chkBoxBold;
QRadioButton *rBtnBlack;
QRadioButton *rBtnRed;
QRadioButton *rBtnBlue;
QPlainTextEdit *txtEdit;
QPushButton *btnOK;
QPushButton *btnCancel;
QPushButton *btnClose;
void iniUI();//UI 创建与初始化
void iniSignalSlots();//初始化信号与槽的链接
private slots:
void on_chkBoxUnder(bool checked); //Underline 的clicked(bool)信号的槽函数
void on_chkBoxItalic(bool checked);//Italic 的clicked(bool)信号的槽函数
void on_chkBoxBold(bool checked); //Bold 的clicked(bool)信号的槽函数
void setTextFontColor(); //设置字体颜色
public:
QWDlgManual(QWidget *parent = 0);
~QWDlgManual();
};
③布局的使用:
界面中的所有控件一般都定义为private的成员指针,在构造函数中new出来,如果需要布局,则使用QHBoxLayout和QVBoxLayout等类,调用其addWidget函数即可。布局对象不用作为成员变量,只需构造时候new出来即可。整个窗口的布局则直接调用setLayout函数,并以某个布局为参数即可,举例:
void QWDlgManual::iniUI()
{
//创建 Underline, Italic, Bold 3 个CheckBox,并水平布局
chkBoxUnder=new QCheckBox(tr("Underline"));
chkBoxItalic=new QCheckBox(tr("Italic"));
chkBoxBold=new QCheckBox(tr("Bold"));
QHBoxLayout *HLay1=new QHBoxLayout;
HLay1->addWidget(chkBoxUnder);
HLay1->addWidget(chkBoxItalic);
HLay1->addWidget(chkBoxBold);
//创建 Black, Red, Blue 3 个RadioButton,并水平布局
rBtnBlack=new QRadioButton(tr("Black"));
rBtnBlack->setChecked(true);
rBtnRed=new QRadioButton(tr("Red"));
rBtnBlue=new QRadioButton(tr("Blue"));
QHBoxLayout *HLay2=new QHBoxLayout;
HLay2->addWidget(rBtnBlack);
HLay2->addWidget(rBtnRed);
HLay2->addWidget(rBtnBlue);
//创建确定, 取消, 退出3 个 PushButton, 并水平布局
btnOK=new QPushButton(tr("确定"));
btnCancel=new QPushButton(tr("取消"));
btnClose=new QPushButton(tr("退出"));
QHBoxLayout *HLay3=new QHBoxLayout;
HLay3->addStretch();
HLay3->addWidget(btnOK);
HLay3->addWidget(btnCancel);
HLay3->addStretch();
HLay3->addWidget(btnClose);
//创建文本框,并设置初始字体
txtEdit=new QPlainTextEdit;
txtEdit->setPlainText("Hello world\n\nIt is my demo");
QFont font=txtEdit->font(); //获取字体
font.setPointSize(20);//修改字体大小
txtEdit->setFont(font);//设置字体
//创建垂直布局,并设置为主布局
QVBoxLayout *VLay=new QVBoxLayout;
VLay->addLayout(HLay1); //添加字体类型组
VLay->addLayout(HLay2);//添加字体颜色组
VLay->addWidget(txtEdit);//添加PlainTextEdit
VLay->addLayout(HLay3);//添加按键组
setLayout(VLay); //设置为窗体的主布局
}
这是 Qt 中较为高级的用法,这里记录基本功能。
Qt 的元对象系统(Meta-Object System)提供了对象之间通信的信号与槽机制、运行时类型信息和动态属性系统。
①元对象系统由以下三个基础组成:
- QObject 类是所有使用元对象系统的类的基类。
- 在一个类的 private 部分声明Q_OBJECT宏,使得类可以使用元对象的特性,如动态属性、信号与槽。
- MOC(元对象编译器)为每个 QObject 的子类提供必要的代码来实现元对象系统的特性。
②元对象系统的功能:
QObject *obj = new QPushButton;
obj->metaObject()->className (); //返回类的名称字符串"QPushButton"
QTimer *timer = new QTimer; // QTimer 是 QObject 的子类
timer->inherits ("QTimer"); // 返回 true
QObject *obj = new QWidget;
QWidget *widget = qobject_cast<QWidget *>(obj);
类似于flightgear中的属性系统,在 QObject 的子类中,用宏 Q_PROPERTY() 定义属性,位于Q_OBJECT宏后,其使用格式如下:
Q_PROPERTY(属性类型 属性名称 [关键字 参数])
[关键字 参数]可以重复多次,进行多个设置,具体为:
READ 指定一个读取属性值的函数,没有 MEMBER 关键字时必须设置 READ。
WRITE 指定一个设定属性值的函数,只读属性没有 WRITE 设置。
MEMBER 指定一个成员变量与属性关联,成为可读可写的属性,无需再设置 READ 和 WRITE。
RESET 是可选的,用于指定一个设置属性缺省值的函数。
NOTIFY 是可选的,用于设置一个信号,当属性值变化时发射此信号。
DESIGNABLE 表示属性是否在 Qt Designer 里可见,缺省为 true。
CONSTANT 表示属性值是一个常数,对于一个对象实例,READ 指定的函数返回值是常数,但是每个实例的返回值可以不一样。具有 CONSTANT 关键字的属性不能有 WRITE 和 NOTIFY 关键字。
FINAL 表示所定义的属性不能被子类重载。
例如,QWidget具有很多属性:
不管是否用 READ 和 WRITE 定义了接口函数,只要知道属性名称,就可以通过 QObject::property() 读取属性值,并通过 QObject::setProperty() 设置属性值。例如:
QPushButton *button = new QPushButton;
QObject *object = button;
object->setProperty("flat", true);
bool isFlat= object->property ("flat");
QObject::setProperty() 函数可以在运行时为类定义一个新的属性,称之为动态属性。动态属性是针对类的实例定义的。动态属性直接用object->setProperty(“flat”, “true”);这样的函数即可定义。// 这里可能理解不对,暂时存疑。
属性系统还有一个宏 Q_CLASSINFO(),可以为类的元对象定义“名称——值”信息(而Q_PROPERTY是为类本身定义的)。也在Q_OBJECT宏之后使用。用 Q_CLASSINFO() 宏定义附加类信息后,可以通过元对象的一些函数获取类的附加信息,如 classlnfo(int) 获取某个附加信息,函数原型定义如下:
QMetaClassInfo QMetaObject::classInfo(int index) const
返回值是 QMetaClassInfo 类型,有 name() 和 value() 两个函数,可获得类附加信息的名称和值。
位于QtGlobal头文件中,因为其它Qt 类基本都包含了它,所以不用显式地包含它。
①数据类型:
qfloat16 是 Qt 5.9.0 中新增的一个类,用于表示 16 位的浮点数,要使用 qfloat16,需要包含头文件 。
②工具函数:
以 double 或 float 类型数作为参数的,一般有两个参数版本的同名函数,下表只列出double版本:
③工具宏:
QT_VERSION、QT_VERSION_CHECK、QT_VERSION_STR、(Q_BYTE_ORDER、Q_BIG_ENDIAN 和 Q_LITTLE_ENDIAN)、(Q_DECL_IMPORT 和 Q_DECL_EXPORT)、Q_DECL_OVERRIDE、Q_DECL_FINAL、Q_UNUSED(name)、foreach(variable, container)、forever、qDebug(const char * message,…)、(qWarning、qCritical、qFatal、qInfo)
具体查看此处。
Qt 提供了多个基于模板的容器类,这些容器类可以用于存储指定类型的数据项。Qt 的容器类比标准模板库(STL)中的容器类更轻巧、安全和易于使用。这些容器类是隐式共享的,而且它们进行了速度和存储优化,因此可以减少可执行文件的大小。此外,它们还是线程安全的,也就是说它们作为只读容器时可被多个线程访问。
容器类是基于模板的类,但是注意T不能是 QObject 或任何其子类。T 必须是一个可赋值的类型,即T必须提供一个缺省的构造函数,一个复制构造函数和一个赋值运算符。
容器分为顺序容器(QList、QVector等)和关联容器(QMap等)两类。具体参考这里。
用于操作容器的迭代器,Qt提供了两种类型,Java 类型的迭代器和 STL 类型的迭代器。两者比较,Java 类型的迭代器更易于使用,且提供一些高级功能,而 STL 类型的迭代器效率更高,具体参考这里,我们只列出关键点:
①支持迭代器的容器:
QList, QQueue、QLinkedList、QVector, QStack、QSet、QMap
②Java类型迭代器
分为两类,一类是只读的(如果不修改元素用只读效率高),一类是可读可写的,命名规则为:
只读的:Q容器名Iterator
可读可写的:QMutable容器名Iterator或者QMutable容器名Iterator
Java类型迭代器指向元素之间,使用时首先判断 it.hasNext,然后再使用it.next即可。不需要单独判断结尾。需要移除的时候,使用it.remove函数也是移除刚刚越过的项。
QList<QString> list;
list << "A" << "B" << "C" << "D";
QListIterator<QString> i (list);
while (i.hasNext())
qDebug () << i.next ();
迭代器也可以倒过来使用,还有其它一些功能。
③STL类型迭代器
同样的,有两类:
只读的:Q容器名::const_interator
可读可写的:Q容器名::interator
Qt API 包含很多返回值为 QList 或 QStringList 的函数,要遍历这些返回的容器,必须先复制(也就是赋值,比如const QList sizes = splitter->sizes();然后使用sizes)。由于 Qt 使用了隐式共享,这样的复制并无多大开销。
隐式共享
隐式共享是对象的管理方法。一个对象被隐式共享,只是传递该对象的一个指针给使用者,而不实际复制对象数据,只有在使用者修改数据时,才实质复制共享对象给使用者。如在上面的代码中,splitter->sizes() 返回的是一个 QListM 表对象 sizes,但是实际上代码并不将 splitter->sizes() 表示的列表内容完全复制给变量 sizes,只是传递给它一个指针,只有当 sizes 发生数据修改时,才会将共享对象的数据复制给 sizes,这样避免了不必要的复制,减少了资源占用。
④foreach语句
Qt提供了foreach关键字,它只能访问容器的内容,无法修改容器的内容。foreach的语法为:foreach (variable, container)。对于顺序容器,一个典型的用法为:
QLinkedList<QString> list;
...
foreach (const QString &str, list) {
// 加入const比较好,明确表明我们不能修改
if (str.isEmpty()) // foreach可以使用break;
break;
qDebug() << str;
}
对于关联容器,foreach则默认访问键值对中的值,如果需要访问键,可以用容器的keys函数取出容器的键QList,然后再按照顺序容器的方法使用。对于多值映射,则可以如下使用:
QMultiMap<QString, int> map;
...
foreach (const QString &str, map.uniqueKeys()) {
foreach (int i, map.values(str))
qDebug() << str << ':' << i;
}
Qt类库分为五个大类:
我们常用的是基本模块和附加模块,具体参考Qt文档,这里列出基本模块:
其中,Core模块是必须的,是自动包含的,GUI模块也是自动包含的,如果需要移除,则在配置文件添加:QT -= gui。
其它模块需要手动添加,例如:QT += sql。
在附加模块中有很多高级功能,包括对3D功能的支持,需要单独学习才行。列表参考这里。
Qt要是想学好也很漫长啊,更能很丰富,文档很多。当然这也是其强大之处。
Qt提供了很多字符串和数字之间,进制之间的转换函数:
将QString转换为数字的成员函数有:
int toInt(bool * ok = Q_NULLPTR, int base = 10) const
long toLong (bool * ok = Q_NULLPTR, int base = 10) const
short toShort (bool * ok = Q_NULLPTR, int base = 10) const
uint toUInt (bool *ok = Q_NULLPTR, int base = 10) const
ulong toULong (bool *ok = Q_NULLPTR, int base = 10) const
double toDouble(bool *ok = Q_NULLPTR) const
float toFloat (bool * ok = Q_NULLPTR) const
将数字转换为QString的成员函数有(有些是静态的):
QString QString::number(int num, int base = 10);
QString QString::asprintf (const char *cformat, ...);
QStrin str.setNum(int num, int base = 10);
QStrin str.sprintf (const char *cformat, ...);
QString还有很多高级功能:例如prepend函数在前面添加字符串;simplified去掉首尾空格,中间的连续控制只保留一个;left从左边取指定数量字符等等…需要时认真看文档,也可以参考这里和这里。
本教程后面剩余的是一些常用控件和功能的介绍,这里简要列出,需要的话可以再回到原教程学习。
数据滚动QSpinBox,适合限定输入粒度的场景(比如只允许正数)
数值输入组件和显示组件:
时间、日期专用组件:
QComboBox下拉框,从多个里面选择
QPlainTextEdit:
多行文本编辑器(自带的右键快捷菜单),不带格式,而QTextEdit是带有格式的。自带菜单的使用,在 UI 设计器里,选择为 plainTextEdit 的 customContextMenuRequested() 信号生成槽函数,编写如下的代码,就可以创建并显示 QPlainTextEdit 的标准快捷菜单:
void Widget::on_plainTextEdit_customContextMenuRequested(const QPoint &pos)
{
//创建并显示标准弹出式菜单
QMenu* menu=ui->plainTextEdit->createStandardContextMenu();
menu->exec(pos);
}
其它控件包括:列表、树、停靠窗口、Table等,需要时具体研究。
Qt中定时器的使用有两种方法,一种是使用QObject类提供的定时器,还有一种就是使用QTimer类。其精确度一般依赖于操作系统和硬件,但一般支持20ms。具体使用时查找即可。
视图(View)是显示和编辑数据的界面组件,模型(Model)是视图与原始数据之间的接口。将数据和显示分离,具有很多好处:可以将一个数据模型在不同的视图中显示,也可以在不修改数据模型的情况下,设计特殊的视图组件。
其中,数据是实际的数据,如数据库的一个数据表或SQL查询结果,内存中的一个 QStringList,或磁盘文件结构等;视图或视图组件(View)是屏幕上的界面组件,视图从数据模型获得每个数据项的模型索引(model index),通过模型索引获取数据,然后为界面组件提供显示数据;模型或数据模型(Model)与实际数据通信,并为视图组件提供数据接口。它从原始数据提取需要的内容,用于视图组件进行显示和编辑。
在 Model/View 结构中,还提供了代理(Delegate)功能,代理功能可以让用户定制数据的界面显示和编辑方式。在标准的视图组件中,代理功能显示一个数据,当数据被编辑时,代理通过模型索引与数据模型通信,并为编辑数据提供一个编辑器,一般是一个 QLineEdit 组件(举例,我们使用Table时,希望点击某个格子,能够弹出一个小窗口用来编辑格子里面的数据,这个弹出的小窗口就是代理)。模型、视图和代理之间使用信号和槽通信。当源数据发生变化时,数据模型发射信号通知视图组件;当用户在界面上操作数据时,视图组件发射信号表示这些操作信息;当编辑数据时,代理发射信号告知数据模型和视图组件编辑器的状态。
Qt 中与数据模型相关的几个主要的类:
视图组件(View)就是显示数据模型的数据的界面组件,Qt 提供的视图组件如下:
视图组件在显示数据时,只需调用视图类的 setModel() 函数,为视图组件设置一个数据模型就可以实现视图组件与数据模型之间的关联,在视图组件上的修改将自动保存到关联的数据模型里,一个数据模型可以同时在多个视图组件里显示数据。
其中,名为QXXXWidget的几个类叫做便利类(convenience classes),视图组件类的数据采用单独的数据模型,视图组件不存储数据。便利类则为组件的每个节点或单元格创建一个项(item),用项存储数据、格式设置等。所以,便利类没有数据模型,它实际上是用项的方式集成了数据模型的功能,这样就将界面与数据绑定了。所以,便利类缺乏对大型数据源进行灵活处理的能力,适用于小型数据的显示和编辑。
代理:
代理就是在视图组件上为编辑数据提供编辑器,如在表格组件中编辑一个单元格的数据时,缺省是使用一个 QLineEdit 编辑框。代理负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后,又将其保存到数据模型中。
在 Model/View 结构中,数据模型为视图组件和代理提供存取数据的标准接口。在 Qt 中,所有的数据模型类都从 QAbstractltemModel 继承而来,不管底层的数据结构是如何组织数据的,QAbstractltemModel 的子类都以表格的层次结构表示数据,视图组件通过这种规则来存取模型中的数据,但是表现给用户的形式不一样:
为了保证数据的表示与数据存取方式隔离,数据模型中引入了模型索引的概念。通过数据模型存取的每个数据都有一个模型索引,视图组件和代理都通过模型索引来获取数据。QModelIndex 表示模型索引的类。模型索引提供数据存取的一个临时指针,用于通过数据模型提取或修改数据。因为模型内部组织数据的结构随时可能改变,所以模型索引是临时的。如果需要使用持久性的模型索引,则要使用 QPersistentModelIndex 类。
每个数据通过父项的模型索引、行号、列号三元组定位。
在为数据模型的一个项设置数据时,可以赋予其不同项的角色的数据,不同角色分别表示视图组件中显示的字符串、装饰显示、鼠标提示消息、自定义数据等。
QFileSystemModel 提供了一个可用于访问本机文件系统的数据模型,使用 QFileSystemModel 提供的接口函数,可以创建目录、删除目录、重命名目录,可以获得文件名称、目录名称、文件大小等参数,还可以获得文件的详细信息。用于获取磁盘文件目录的数据模型类还有一个 QDirModel,QDirModel 的功能与 QFileSystemModel 类似,但是 QFileSystemModel 采用单独的线程获取目录文件结构,而 QDirModel 不使用单独的线程。
用一个例子程序说明使用方法:
①首先绘制图中的控件;
②在构造函数中,只需要写如下内容,视图和数据就会自动关联:
//...
model=new QFileSystemModel(this); //QFileSystemModel提供单独线程,推荐使用
model->setRootPath(QDir::currentPath()); //设置根目录
ui->treeView->setModel(model); //设置数据模型
ui->listView->setModel(model); //设置数据模型
ui->tableView->setModel(model); //设置数据模型
// 以下代码实现一旦Tree中的某个节点,那么List和Table自动改变
connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
ui->listView,SLOT(setRootIndex(QModelIndex)));
connect(ui->treeView,SIGNAL(clicked(QModelIndex)),
ui->tableView,SLOT(setRootIndex(QModelIndex)));
③下方我们自己设置的一些内容,可以通过树上的点击信号的槽函数实现:
void MainWindow::on_treeView_clicked(const QModelIndex &index)
{
ui->chkIsDir->setChecked(model->isDir(index));
ui->LabPath->setText(model->filePath(index));
ui->LabType->setText(model->type(index));
ui->LabFileName->setText(model->fileName(index));
int sz=model->size(index)/1024;
if (sz<1024)
ui->LabFileSize->setText(QString("%1 KB").arg(sz));
else
ui->LabFileSize->setText(QString::asprintf("%.1f MB",sz/1024.0));
}
所有具体的操作全部封装了,使用起来非常方便。
QStringListModel 用于处理字符串列表的数据模型,它可以作为 QListView 的数据模型,在界面上显示和编辑字符串列表。
QStringListModel 的 setStringList() 函数可以初始化数据模型的字符串列表的内容,stringList() 函数返回数据模型内的字符串列表,在关联的 ListView 组件里编辑修改数据后,数据都会及时更新到数据模型内的字符串列表里。
QStringListModel 提供编辑和修改字符串列表数据的函数,如 insertRows()、removeRows()、setData() 等,这些操作直接影响数据模型内部的字符串列表,并且修改后的数据会自动在关联的 ListView 组件里刷新显示。
QListView拥有setEditTriggers函数,可以设置双击进入list条目编辑等功能。
想法:可以再封装一下,实际上一个.txt文件可以认为每个行是个QString,有很多行,就是个string list,也可以如此操作吧。
QStandardItemModel 是标准的以项数据(item data)为基础的标准数据模型类,通常与 QTableView 组合成 Model/View 结构,实现通用的二维数据的管理功能。
需要用到的类一般有:
具体参考这里。
①五种标准对话框。
也可以自行创建对话框。注意,默认情况下accept(), reject()等槽函数只是隐藏了界面,并未销毁对象。
③Qt文本读写和二进制读写
这个其实很简单,以前竟然没能搞明白。只需要把别人的ui文件,h文件和cpp文件全部导入自己的工程即可。
参考这里即可,很简单,其实就是查找程序依赖的dll文件,然后带着一堆dll文件一起拷走就行了。
如果上传到github,参考这里。
git的使用直接百度吧,还是很值得学习的。项目必须有好的管理才能降低风险,而git是很好的工具。
这一篇粗略复习到此先结束吧。
Qt5编程入门教程 ↩︎