Ligar a lógica à sua interface de utilizador em QML

Ligar uma infra-estrutura que faça cálculos e fornecer a sua interface de utilizador com os dados a apresentar

Para integrar a lógica na aplicação, iremos precisar das classes de infra-estrutura de C++ que conseguem fazer os cálculos importantes. O desenvolvimento de lógica nos ficheiros QML não é encorajado, pelo que deverá mover o máximo de código para a infra-estrutura, para que o QML seja praticamente usado apenas para a apresentação da interface de utilizador, que é onde ele é melhor.

Para a sua nova classe de infra-estrutura, crie dois ficheiros novos chamados backend.cpp e backend.h. Não se esqueça de adicionar o novo ficheiro .cpp ao executável no src/CMakeLists.txt, junto ao main.cpp.

Adicione o seguinte conteúdo ao novo ficheiro de inclusão (o que tem a extensão .h):

#pragma once

#include <QObject>

class Backend : public QObject
{
    Q_OBJECT

public:
    explicit Backend(QObject *parent = nullptr);
};

O ficheiro .cpp que contém as definições está agora mais ou menos vazio, contendo algo como o seguinte:

#include "backend.h"

Backend::Backend(QObject *parent)
    : QObject(parent)
{

}

De momento, a interface de utilizador não sabe nada sobre a sua classe de infra-estrutura. Para mudar isso, teremos de registar o novo tipo no main.cpp. A infra-estrutura será criada como um singleton, o que significa que só será criado uma vez e existirá durante todo o tempo, desde o início da aplicação até fechá-la.

No main.cpp, a seguir à criação do QQmlApplicationEngine, adicione o registo do tipo da seguinte forma:

    Backend backend;
    qmlRegisterSingletonInstance<Backend>("org.kde.example", 1, 0, "Backend", &backend);

Não se esqueça de incluir o novo ficheiro de inclusão no topo do main.cpp.

A partir de agora, a infra-estrutura ficará conhecida perante o QML como Backend. Está contida dentro de um módulo chamado org.kde.example. Dado que o módulo faz parte da aplicação, não precisa de se preocupar com a gestão de versões do mesmo; deixe-se ficar na 1.0 e use-a de forma consistente em toda a aplicação.

No main.qml, importe o novo módulo:

import org.kde.example 1.0

Agora já ligámos a classe que trata da lógica futura com a aplicação, mas ainda não faz nada. Para mudar isso, vamos adicionar uma propriedade à classe. As propriedades são um pouco mais que uma simples variável. Elas conseguem informar a interface sobre alterações, pelo que poderá actualizar as áreas correctas.

Logo abaixo da macro Q_OBJECT, adicione uma nova Q_PROPERTY.

Q_PROPERTY(QString introductionText READ introductionText WRITE setIntroductionText NOTIFY introductionTextChanged)

Isto parece bastante para uma simples propriedade que iremos usar para mostrar algum texto da infra-estrutura, certo? Mas uma vista mais aproximada revela que a mesma poderá executar lógica em código, quando a propriedade é lida a partir da interface do utilizador e quando é escrita. A mesma irá informar das alterações a nível da interface e da infra-estrutura.

A leitura e modificação são baseadas em funções ‘getter’ (leitura) e ‘setter’ (escrita); como tal, adicione um novo atributo privado à sua classe, como este, e adicione as funções de leitura e escrita mencionadas.

private:
    QString m_introductionText = "Hello World!";

Na secção ‘public’, adicione

    QString introductionText() const;
    void setIntroductionText(const QString &introductionText);
    Q_SIGNAL void introductionTextChanged();

A primeira função é o método de leitura, a segunda é o método de escrita e a terceira é um sinal que é emitido quando a propriedade tiver sido modificada. O sinal não necessita de nenhuma implementação no ficheiro .cpp, dado que não faz muito mais do que ser emitido, mas os métodos de leitura e escrita têm de ser implementados de forma semelhante à seguinte:

QString Backend::introductionText() const
{
    return m_introductionText;
}

void Backend::setIntroductionText(const QString &introductionText)
{
    m_introductionText = introductionText;
    Q_EMIT introductionTextChanged();
}

Como poderá ver, quando for invocado o método de alteração, o sinal será emitido e informará a interface e a infra-estrutura da alteração.

Para mostrar o texto, no main.qml adicione um cabeçalho que o mostra à direita da propriedade ‘text’ do elemento Kirigami.Page que já está contido dentro do modelo.

O código resultante dessa parte do ficheiro deverá ser semelhante ao seguinte:

        ...
        Kirigami.Page {
            title: i18n("develop.kde.org tutorial")

            Kirigami.Heading {
                anchors.centerIn: parent
                text: Backend.introductionText
            }

            actions {
                main: Kirigami.Action {
                    ...

Agora compile e inicie o seu programa de novo.

Parabéns, acabou de aprender:

  • Como registar os tipos de infra-estruturas no QML
  • Adicionar novos elementos ao ficheiros QML
  • Criar novas sub-classes de QObject
  • Como adicionar propriedades e o que fazem
  • O que são os sinais (‘signals’)

Se quiser saber mais sobre a integração entre o QML e o C++, recomendamos a leitura da documentação oficial do Qt.