一些有用的资源:

  1. https://doc.qt.io/Qt documentation on developer network

2、3为教程:

  1. https://wiki.jikexueyuan.com/project/learn-road-qt/
  2. http://c.biancheng.net/view/1879.html


原文链接:https://wiki.qt.io/Qt_for_Beginners

Introduction to Qt

Qt是一个通常用作图形工具包的跨平台框架,尽管它在创建CLI应用程序时也非常有用。它可以在三种主要的桌面操作系统和移动操作系统上运行,如Symbian, Nokia Belle, Meego Harmattan, Meego或BB10,以及嵌入式设备上运行。Android和iOS的移植也在开发中。

Qt有一个令人印象深刻的模块集合,包括:

  • QtCore**:**提供容器、线程管理、事件管理等功能的基本库。
  • QtGui and QtWidgets**:**桌面GUI工具包,提供了许多图形组件来设计应用程序。
  • QtNetwork**:**这为处理网络通信提供了一组有用的类。
  • QtWebkit**:**webkit引擎,允许在Qt应用程序中使用web页面和web应用程序。
  • QtSQL**:**一个功能齐全的可扩展的SQL RDBM抽象层,支持ODBC, SQLITE, MySQL和PostgreSQL。
  • QtXML**:**支持简单XML解析(SAX)和DOM。
  • QtXmlPatterns**:**支持XSLT、XPath、XQuery和模式验证。


Qt Creator特性

在编写我们的第一个GUI应用程序之前,让我们来看看Qt Creator。Qt Creator是另一种c++ IDE,但它非常适合编写Qt应用程序。它提供了一个文档浏览器和“设计器(designer)”,这使得创建窗口更加容易,所有这些都包装在一个设计良好的用户界面中。它也是可用的最快的IDE之一。

QT中的main.cpp

  1. #include <QApplication>
  2. int main(int argc, char **argv)
  3. {
  4. QApplication app (argc, argv);
  5. return app.exec();
  6. }

QApplication

QApplication是一个非常重要的类。它不仅负责输入参数,还负责许多其他事情,最值得注意的是事件循环。事件循环是在GUI应用程序中等待用户输入的循环。

当调用app.exec()时,启动事件循环。

让我们编译这个应用程序。通过单击左下角的绿色箭头,Qt Creator将编译并执行它。发生了什么?
应用程序似乎启动了,但没有响应。这是很正常的。事件循环正在运行并等待事件,比如鼠标在GUI上单击,但是我们没有提供任何要处理的事件,因此它将无限期地运行

Qt程序是如何编译的

Qt Creator为我们执行调用构建系统的工作,但是了解Qt程序是如何编译的可能会很有趣。

对于小程序,很容易手工编译所有内容,创建对象文件,然后链接它们。但是对于较大的项目,命令行很容易变得难以编写。如果您熟悉Linux,您可能知道所有的程序都是使用一个makefile编译的,这个makefile描述了所有要执行的命令行。但是对于某些项目,甚至编写makefile也可能变得乏味。

qmake是Qt附带的构建系统,它会为您生成那些makefile (还可以使用其他一些makefile,但是我们在这里给出了使用qmake的示例)。通过简单的语法,它生成用于编译Qt程序的makefile。但这并不是它的唯一目标。Qt使用元对象来扩展c++功能,qmake负责准备一个包含此元对象提取阶段的makefile。你将在另一章中看到这一点。

因此,Qt应用程序的编译有3个步骤:

  1. 一个.pro文件是用来描述要编译的项目的
  2. 使用qmake生成makefile;
  3. 使用make(或windows上的nmake或jom)构建程序


QT类层次结构

Qt广泛使用继承,特别是在Widgets模块中。下面的图表显示了其中一些继承的类:
Qt for Beginners - 图1
QObject是Qt中最基本的类。Qt中的大多数类都继承自这个类。QObject提供了一些非常强大的功能,例如:

  • object name :您可以将对象的名称设置为字符串,并根据名称搜索对象。
  • parenting system
  • signals and slots:信号与槽
  • event management:事件管理


Widgets (小部件)能够响应事件,并使用
parenting system、信号和插槽机制。所有小部件都继承QObject。最基本的小部件是[QWidget**](https://doc.qt.io/qt-5/qwidget.html#)。QWidget包含了大多数用来描述窗口或widget的属性,比如位置和大小、鼠标指针、工具提示等等。

Parenting system

Parenting system是Qt中处理对象,特别是widgets(小部件)的一种方便的方式。从QObject继承的任何对象都可以有父对象和子对象。这个层次树使很多事情变得方便:

  • 当一个对象被销毁时,它的所有子对象也被销毁。因此,在某些情况下调用delete是可选的。
  • 所有qobject都有findChildfindChildren方法,可用于搜索给定对象的子对象。
  • QWidget中的子部件会自动出现在父部件中。

下面的代码片段在一个QPushButton中创建一个QPushButton:

  1. #include <QApplication>
  2. #include <QPushButton>
  3. int main(int argc, char **argv)
  4. {
  5. QApplication app (argc, argv);
  6. QPushButton button1 ("test");
  7. QPushButton button2 ("other", &button1);
  8. button1.show();
  9. return app.exec();
  10. }

Qt for Beginners - 图2
您还可以注意到,当关闭应用程序时,在堆栈上分配的button1将被释放。因为button2有button1作为父元素,所以它也被删除。您甚至可以在Qt Creator的analyze部分中测试它,通过搜索内存泄漏—不会有任何内存泄漏。

Subclassing QWidget(子类化的QWidget)

到目前为止,我们已经将所有代码都放在了main函数中。对于我们的简单示例来说,这不是问题,但对于越来越复杂的应用程序,我们可能希望将代码分割成不同的类。

  • 通常要做的是创建一个用于显示窗口的类,并将此窗口中包含的所有小部件实现为该类的属性。

在Qt Creator中,您可以自动创建一个带有文件>新文件或项目> c++ > c++类的新类:

Qt for Beginners - 图3
使类继承QWidget,您将获得类似于下面的代码:

  1. // Header
  2. #ifndef WINDOW_H
  3. #define WINDOW_H
  4. #include <QWidget>
  5. class Window : public QWidget
  6. {
  7. Q_OBJECT
  8. public:
  9. explicit Window(QWidget *parent = 0);
  10. signals:
  11. public slots:
  12. };
  13. #endif // WINDOW_H
  1. Source
  2. #include "window.h"
  3. Window::Window(QWidget *parent) :
  4. QWidget(parent) {}

可以看到Qt Creator自动生成一个类模板。注意在标题中有一些新元素:

  • The Q_OBJECT macro.
  • A new category of methods : signals
  • A new category of methods : public slots

观察者模式

几乎所有的UI工具包都有一种机制来检测用户操作,并对该操作做出响应。其中一些使用回调,另一些使用侦听器,但基本上,所有这些都是受观察者模式** observer pattern)**的启发。

观察者模式用于观察对象想要通知其他观察者对象状态变化的情况。以下是一些具体的例子:

  • 用户单击了一个按钮,应该会显示一个菜单。
  • 一个web页面刚刚加载完毕,进程应该从这个加载的页面中提取一些信息。
  • 一个用户正在滚动一个项目列表(例如在一个app store),并到达了终点,所以其他项目应该被加载。

观察者模式在GUI应用程序中随处可见,并且常常导致一些样板代码(Boilerplate code

在计算机编程中,样板代码或只是样板代码是在许多地方必须包含的代码片段,这些代码很少或根本没有改变。当使用被认为冗长的语言时,程序员必须编写大量的代码来完成较小的功能。这样的代码称为样板代码

Qt创建时的想法是删除这些样板代码,并提供漂亮而干净的语法,而信号和插槽机制就是答案。

信号与槽(Signals and slots)

Qt提供了两个高级概念:信号和槽,而不是拥有可观察对象观察者,并对它们进行注册。

  • 信号是一个对象可以发送的消息,大部分时间用来通知状态的改变。
  • 是一个用来接受和响应信号的函数。

为了响应一个信号,一个插槽必须连接到一个信号。Qt提供了方法**QObject::connect**。它是这样使用的,带有两个宏信号和槽。

  1. FooObjectA *fooA = new FooObjectA();
  2. FooObjectB *fooB = new FooObjectB();
  3. QObject::connect(fooA, SIGNAL (bared()), fooB, SLOT (baz())); //⭐

假设FooObjectA有一个暴露的信号,而FooObjectB有一个baz
你必须在两个宏:信号和槽 内写信号和槽的签名(函数的签名)。如果你想了解这些宏的功能,请阅读本章的最后一节。

注意:
基本上,信号和槽是方法(函数),它们可能有参数,也可能没有,但不会返回任何东西。虽然将信号作为方法的概念并不常见,但槽实际上是一个真正的方法,可以在其他方法中像往常一样调用,或者在响应信号时调用。

传输信息(Transmitting information)

信号和插槽机制在响应按钮点击时很有用,但它能做的远不止这些。
例如,它还可以用来交流信息。

  • 假设在播放歌曲时,需要一个进度条来显示歌曲结束前还有多少时间。媒体播放器可能有一个用于检查媒体进程的类。该类的实例可能会周期性地发送带有进度值的tick信号。这个信号可以连接到一个QProgressBar,用来显示进度。

信号和槽的特征

简单来说:信号和槽 具有 “多对多”的关系。

  • 一个信号可以连接到几个插槽上
  • 许多信号可以连接到一个插槽上
  • 一个信号可以连接到一个信号: 它是信号中继。如果发送第一个信号,则发送第二个信号。

元对象

Qt提供了一个元对象系统。元对象(字面意思是“在对象之上”)是一种实现一些编程范例的方法,这通常是用纯c++无法实现的,比如:

  • Introspection(自省) :在运行时检查类型的能力
  • 异步函数调用

要在应用程序中使用这种元对象功能,我们可以子类化QObject并标记它,以便元对象编译器(moc)可以解释和翻译它。

重要的宏

  • Q_OBJECT

    • 信号槽连接及其语法不能由普通c++编译器解释。moc是用来将QT语法如“连接”、“信号”、“插槽”等转换成常规c++语法的。这是通过在包含使用这种语法的 类定义的头中指定Q_OBJECT宏来实现的。
      1. class MyWidget : public QWidget
      2. {
      3. Q_OBJECT
      4. public:
      5. MyWidget(QWidget *parent = 0);
      6. }
  • signals

  • public / protected / private slots

信号和插槽也是两个非常重要和有用的宏。当信号发出时,元对象系统用于比较信号的签名,来检查连接,并使用它的签名来查找插槽。这些宏实际上用于将提供的方法签名转换为与存储在元对象中的方法签名匹配的字符串。

创建自定义信号和槽

创建自定义信号和插槽非常简单。以下是创建顺序:

  • add Q_OBJECT macro
  • add signals section, and write signals prototypes
  • add public slots or protected slots or private slots sections, and write slots prototypes
  • implement slots as normal methods
  • establish connections


创建自定义槽

为了实现插槽,我们:

  • 首先需要使类能够发送信号并拥有插槽(见前一章)。这是通过在类声明(通常在标题中)中设置Q_OBJECT宏来实现的。
  • 在此之后,应该在相应的部分声明一个槽,并作为普通方法实现。
  • 最后,插槽连接到信号上。


创建信号

  • 对于槽,我们首先需要添加Q_OBJECT宏。信号也应该在信号部分声明,不需要实现它们。

它们使用emit关键字发出:

  1. //注意,为了发送有参数的信号,你必须在信号发射中传递它们:
  2. emit mySignal(firstParameter, secondParameter …);