Qt QML 第三章第四、五节:应用类型、总结

应用类型

本节是一个运行通过不同的应用程序类型,可以用 Qt6编写。它不局限于这里提供的选择,但它会让您更好地了解使用一般Qt 6可以实现的功能

控制台应用

控制台应用不提供图形用户界面,通常作为系统服务的一部分或从命令行调用。Qt6附带了一系列现成的组件,可以帮助您非常有效地创建跨平台控制台应用程序。例如,网络文件 API、字符串处理和高效的命令行解析器。因为 Qt 是 C + + 之上的一个高级 API,所以编程速度和执行速度是成对的。不要认为 Qt 只是一个用户界面工具包-它还有很多东西可以提供!

字符串处理

第一个示例演示如何添加2个常量字符串。无可否认,这不是一个非常有用的应用程序,但它让您了解没有事件循环的本机 C + + 应用程序可能是什么样子。

// module or class includes
#include <QtCore>

// text stream is text-codec aware
QTextStream cout(stdout, QIODevice::WriteOnly);

int main(int argc, char** argv)
{
    // avoid compiler warnings
    Q_UNUSED(argc)
    Q_UNUSED(argv)
    QString s1("Paris");
    QString s2("London");
    // string concatenation
    QString s = s1 + " " + s2 + "!";
    cout << s << Qt::endl;
}

容器类别

此示例向应用程序添加列表和列表迭代。Qt 提供了大量易于使用的容器类,并且具有与其他 Qt 类相同的 API 范例。

QString s1("Hello");
QString s2("Qt");
QList<QString> list;
// stream into containers
list << s1 << s2;
// Java and STL like iterators
QListIterator<QString> iter(list);
while(iter.hasNext()) {
    cout << iter.next();
    if(iter.hasNext()) {
        cout << " ";
    }
}
cout << "!" << Qt::endl;

下面是一个更高级的列表函数,它允许您将一个字符串列表合并到一个字符串中。当您需要处理基于行的文本输入时,这非常方便。使用 QString: : split ()函数也可以进行反转(字符串到字符串列表)。

QString s1("Hello");
QString s2("Qt");
// convenient container classes
QStringList list;
list <<  s1 << s2;
// join strings
QString s = list.join(" ") + "!";
cout << s << Qt::endl;

文件 IO

在下一个代码片段中,我们从本地目录中读取一个 CSV 文件,并循环遍历这些行以从每一行中提取单元格。通过这样做,我们可以从大约20行代码中的 CSV 文件获得表数据。文件读取为我们提供了一个字节流,为了能够将其转换为有效的 Unicode 文本,我们需要使用文本流并将文件作为低级流传递。对于编写 CSV 文件,您只需要以编写模式打开文件,并将行导入文本流。

QList<QStringList> data;
// file operations
QFile file("sample.csv");
if(file.open(QIODevice::ReadOnly)) {
    QTextStream stream(&file);
    // loop forever macro
    forever {
        QString line = stream.readLine();
        // test for null string 'String()'
        if(line.isNull()) {
            break;
        }
        // test for empty string 'QString("")'
        if(line.isEmpty()) {
            continue;
        }
        QStringList row;
        // for each loop to iterate over containers
        foreach(const QString& cell, line.split(",")) {
            row.append(cell.trimmed());
        }
        data.append(row);
    }
}
// No cleanup necessary.

关于使用Qt的基于控制台的应用程序的部分到此结束。

C + + Widget 应用程序

基于控制台的应用程序非常方便,但有时你需要一个图形用户界面(GUI)。此外,基于 GUI 的应用程序可能需要后端来读/写文件、通过网络进行通信或将数据保存在容器中。

在基于小部件的应用程序的第一个代码片段中,我们尽可能少地创建窗口并显示它。在 Qt 中,没有父元素的小部件是一个窗口。我们使用一个作用域指针来确保当指针超出作用域时小部件被删除。Application 对象封装了 Qt 运行时,我们使用 exec ()调用启动事件循环。从那时起,应用程序只对由用户输入(如鼠标或键盘)或其他事件提供程序(如网络或文件 IO)触发的事件作出反应。只有当事件循环退出时,应用程序才会退出。这是通过调用quit() 的应用程序或关闭窗口。

当您运行代码时,您将看到一个大小为240x120像素的窗口。

include <QtGui>

int main(int argc, char** argv)
{
    QApplication app(argc, argv);
    QScopedPointer<QWidget> widget(new CustomWidget());
    widget->resize(240, 120);
    widget->show();
    return app.exec();
}

自定义小部件

在处理用户界面时,可能需要创建定制的小部件。通常,小部件是一个充满绘制调用的窗口区域。此外,小部件具有如何处理键盘和鼠标输入以及如何对外部触发器作出反应的内部知识。为了在 Qt 中实现这一点,我们需要从 QWidget 派生并重写几个用于绘制和事件处理的函数。

#pragma once

include <QtWidgets>

class CustomWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CustomWidget(QWidget *parent = 0);
    void paintEvent(QPaintEvent *event);
    void mousePressEvent(QMouseEvent *event);
    void mouseMoveEvent(QMouseEvent *event);
private:
    QPoint m_lastPos;
};

在实现中,我们在小部件上绘制一个小边框,在最后一个鼠标位置上绘制一个小矩形。这对于低级自定义小部件来说非常典型。鼠标和键盘事件更改小部件的内部状态并触发绘图更新。我们不会详细介绍这段代码,但是很高兴知道您有这样的可能性。Qt 提供了大量现成的桌面小部件,因此您可能不必这样做。

include "customwidget.h"

CustomWidget::CustomWidget(QWidget *parent) :
    QWidget(parent)
{
}

void CustomWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    QRect r1 = rect().adjusted(10,10,-10,-10);
    painter.setPen(QColor("#33B5E5"));
    painter.drawRect(r1);

    QRect r2(QPoint(0,0),QSize(40,40));
    if(m_lastPos.isNull()) {
        r2.moveCenter(r1.center());
    } else {
        r2.moveCenter(m_lastPos);
    }
    painter.fillRect(r2, QColor("#FFBB33"));
}

void CustomWidget::mousePressEvent(QMouseEvent *event)
{
    m_lastPos = event->pos();
    update();
}

void CustomWidget::mouseMoveEvent(QMouseEvent *event)
{
    m_lastPos = event->pos();
    update();
}

桌面小部件

Qt 开发人员已经为您完成了所有这些工作,并提供了一组桌面小部件,在不同的操作系统上具有原生外观。那么,您的工作就是将这些不同的小部件排列在一个小部件容器中,使之成为更大的面板。Qt 中的小部件也可以是其他小部件的容器。这是通过亲子关系实现的。这意味着我们需要制作现成的小部件,例如按钮、复选框、单选按钮、列表和网格,它们是其他小部件的子部件。实现这一点的一种方法如下所示。

下面是所谓的小部件容器的头文件。

class CustomWidget : public QWidget
{
    Q_OBJECT
public:
    explicit CustomWidget(QWidget *parent = 0);
private slots:
    void itemClicked(QListWidgetItem* item);
    void updateItem();
private:
    QListWidget *m_widget;
    QLineEdit *m_edit;
    QPushButton *m_button;
};

In the implementation, we use layouts to better arrange our widgets. Layout managers re-layout the widgets according to some size policies when the container widget is re-sized. In this example, we have a list, a line edit, and a button, which are arranged vertically and allow the user to edit a list of cities. We use Qt’s signal and slots to connect sender and receiver objects.

在实现中,我们使用布局来更好地安排我们的小部件。当调整容器小部件的大小时,布局管理器根据一些大小策略重新布局小部件。在本例中,我们有一个列表、一个行编辑和一个按钮,它们是垂直排列的,允许用户编辑城市列表。我们使用 Qt 的SIGNAL和SLOT来连接发送方和接收方对象。

CustomWidget::CustomWidget(QWidget *parent) :
    QWidget(parent)
{
    QVBoxLayout *layout = new QVBoxLayout(this);
    m_widget = new QListWidget(this);
    layout->addWidget(m_widget);

    m_edit = new QLineEdit(this);
    layout->addWidget(m_edit);

    m_button = new QPushButton("Quit", this);
    layout->addWidget(m_button);
    setLayout(layout);

    QStringList cities;
    cities << "Paris" << "London" << "Munich";
    foreach(const QString& city, cities) {
        m_widget->addItem(city);
    }

    connect(m_widget, SIGNAL(itemClicked(QListWidgetItem*)), this, SLOT(itemClicked(QListWidgetItem*)));
    connect(m_edit, SIGNAL(editingFinished()), this, SLOT(updateItem()));
    connect(m_button, SIGNAL(clicked()), qApp, SLOT(quit()));
}

void CustomWidget::itemClicked(QListWidgetItem *item)
{
    Q_ASSERT(item);
    m_edit->setText(item->text());
}

void CustomWidget::updateItem()
{
    QListWidgetItem* item = m_widget->currentItem();
    if(item) {
        item->setText(m_edit->text());
    }
}

绘制形状

有些问题可以更直观地看到。如果手头的问题看起来远程像几何对象,Qt 图形视图是一个很好的候选者。图形视图在场景中排列简单的几何形状。用户可以与这些形状进行交互,或者使用算法对它们进行定位。要填充图形视图,您需要一个图形视图和一个图形场景。场景附加到视图,并用图形项填充。

下面是一个简短的例子。首先是带有视图和场景声明的头文件。

class CustomWidgetV2 : public QWidget
{
    Q_OBJECT
public:
    explicit CustomWidgetV2(QWidget *parent = 0);
private:
    QGraphicsView *m_view;
    QGraphicsScene *m_scene;

};

在实现中,场景首先附加到视图。视图是一个小部件,在容器小部件中进行排列。最后,我们在场景中添加一个小矩形,然后在视图中呈现。

include "customwidgetv2.h"

CustomWidget::CustomWidget(QWidget *parent) :
    QWidget(parent)
{
    m_view = new QGraphicsView(this);
    m_scene = new QGraphicsScene(this);
    m_view->setScene(m_scene);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setMargin(0);
    layout->addWidget(m_view);
    setLayout(layout);

    QGraphicsItem* rect1 = m_scene->addRect(0,0, 40, 40, Qt::NoPen, QColor("#FFBB33"));
    rect1->setFlags(QGraphicsItem::ItemIsFocusable|QGraphicsItem::ItemIsMovable);
}

适配数据

到目前为止,我们主要讨论了基本的数据类型以及如何使用小部件和图形视图。在应用程序中,通常需要大量的结构化数据,这些数据可能还需要持久地存储。最后,还需要显示数据。为此,Qt 使用模型。一个简单的模型是字符串列表模型,它用字符串填充,然后附加到列表视图。

m_view = new QListView(this);
m_model = new QStringListModel(this);
view->setModel(m_model);

QList<QString> cities;
cities << "Munich" << "Paris" << "London";
m_model->setStringList(cities);

另一种流行的存储和检索数据的方法是 SQL。Qt 嵌入了 SQLite,还支持其他数据库引擎(例如 MySQL 和 PostgreSQL)。首先,您需要使用模式创建数据库,如下所示:

CREATE TABLE city (name TEXT, country TEXT);
INSERT INTO city VALUES ("Munich", "Germany");
INSERT INTO city VALUES ("Paris", "France");
INSERT INTO city VALUES ("London", "United Kingdom");

要使用 SQL,我们需要将 SQL 模块添加到.pro 文件中

QT += sql

然后我们可以用 C + + 打开我们的数据库。首先,我们需要为指定的数据库引擎检索一个新的数据库对象。使用这个数据库对象,我们打开数据库。对于 SQLite,指定数据库文件的路径就足够了。Qt 提供了一些高级数据库模型,其中之一是表模型。表模型使用一个表标识符和一个可选的 where 子句来选择数据。生成的模型可以像之前的其他模型一样附加到列表视图。

QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("cities.db");
if(!db.open()) {
    qFatal("unable to open database");
}

m_model = QSqlTableModel(this);
m_model->setTable("city");
m_model->setHeaderData(0, Qt::Horizontal, "City");
m_model->setHeaderData(1, Qt::Horizontal, "Country");

view->setModel(m_model);
m_model->select();

对于更高级别的模型操作,Qt 提供了一个排序文件代理模型,允许对模型进行排序、筛选和转换。

QSortFilterProxyModel* proxy = new QSortFilterProxyModel(this);
proxy->setSourceModel(m_model);
view->setModel(proxy);
view->setSortingEnabled(true);

Filtering is done based on the column that is to be filters, and a string as filter argument.

筛选是基于要被筛选的列和作为筛选器参数的字符串进行的。

proxy->setFilterKeyColumn(0);
proxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
proxy->setFilterFixedString(QString)

过滤器代理模型比这里演示的要强大得多。

笔记

本文概述了可以用Qt 5开发的各种经典应用程序。桌面正在移动,很快,移动设备将成为我们明天的桌面。移动设备有不同的用户界面设计。它们比桌面应用程序简单得多。他们只做一件事,而且做得简单专注。动画是移动体验的重要组成部分。用户界面需要生动流畅。传统的Qt技术不太适合这个市场。

Qt 快速应用程序

现代软件开发中存在一个内在的冲突。用户界面的速度比我们的后端服务快得多。在传统技术中,您以与后端相同的速度开发所谓的前端。当客户希望在项目期间更改用户界面或在项目期间开发用户界面的想法时,这会导致冲突。敏捷项目,需要敏捷方法。

Qt Quick 提供了一个声明性环境,其中用户界面(前端)像 HTML 一样声明,而后端使用原生 C + + 代码。这使您可以同时获得两个世界的最佳结果。

下面是一个简单的 Qt Quick UI

import QtQuick

Rectangle {
    width: 240; height: 240
    Rectangle {
        width: 40; height: 40
        anchors.centerIn: parent
        color: '#FFBB33'
    }
}

声明语言称为 QML,它需要一个运行时来执行它。Qt 提供了一个名为 qml 的标准运行时。还可以编写自定义运行库。为此,我们需要一个快速视图,并将主 QML 文档设置为来自 C + + 的源代码。然后您可以显示用户界面。

#include <QtGui>
#include <QtQml>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine("main.qml");
    return app.exec();
}

让我们回到之前的例子。在一个例子中,我们使用了一个 C + + 城市模型。如果我们能够在我们的声明性 QML 代码中使用这个模型,那就太好了。

为了实现这一点,我们首先对前端进行编码,以查看我们希望如何使用城市模型。在这种情况下,前端需要一个名为 cityModel 的对象,我们可以在列表视图中使用它。

import QtQuick

Rectangle {
    width: 240; height: 120
    ListView {
        width: 180; height: 120
        anchors.centerIn: parent
        model: cityModel
        delegate: Text { text: model.city }
    }
}

为了启用 cityModel,我们可以重用以前的模型,并向根上下文添加上下文属性。根上下文是主文档中的另一个根元素。

m_model = QSqlTableModel(this);
... // some magic code
QHash<int, QByteArray> roles;
roles[Qt::UserRole+1] = "city";
roles[Qt::UserRole+2] = "country";
m_model->setRoleNames(roles);
engine.rootContext()->setContextProperty("cityModel", m_model);

总结

我们已经看到了如何安装 Qt SDK 以及如何创建我们的第一个应用程序。然后,我们向您介绍了不同的应用程序类型,从而给出了 Qt 的概述,并展示了 Qt 为应用程序开发提供的一些特性。我希望您对 Qt 有一个良好的印象,它是一个非常丰富的用户界面工具包,提供了应用程序开发人员所希望的一切,甚至更多。不过,Qt 不会将您锁定到特定的库中,因为您总是可以使用其他库,甚至可以自己扩展 Qt。当涉及到支持不同的应用程序模型时,它也非常丰富: 控制台、经典的桌面用户界面和触摸用户界面。

Next
Previous
Contents

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值