Desenvolvimento Prático com Qt para o Componente QComboBox em HarmonyOS

Introdução ao Projeto

Este documento detalha a implementação de um aplicativo completo que utiliza o componente QComboBox em um ambiente HarmonyOS. A aplicação demonstra a seleção em cascata entre províncias e cidades, evidenciando a aplicação prática do Qt Quick Controls 2.15 nesta plataforma. A estrutura inclui entrada de texto, seleções em dois níveis e funcionalidades de exibição e limpeza de dados, com suporte a interações por toque.

Pontos Técnicos Essenciais

1. Ponto de Entrada para HarmonyOS: qtmain()

Ponto Crítico: Em dispositivos HarmonyOS, o ponto de entrada deve ser a função qtmain() em vez de main().

// Abordagem Correta para HarmonyOS
extern "C" int qtmain(int argc, char **argv)
{
    QGuiApplication application(argc, argv);
    QQmlApplicationEngine qmlEngine;
    qmlEngine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    return application.exec();
}

// Abordagem Incorreta (padrão desktop)
int main(int argc, char *argv[])
{
    // Falhará ao iniciar no HarmonyOS
}

Motivo: O HarmonyOS carrega a aplicação Qt como uma biblioteca compartilhada (.so), e seu ciclo de vida é gerenciado pela Ability do sistema. A função qtmain() é o ponto de entrada padrão para o plugin Qt do HarmonyOS.

2. Configuração do Formato de Superfície OpenGL ES

Ponto Crítico: A configuração do QSurfaceFormat deve ocorrer antes da instanciação do QGuiApplication.

// Etapa 1: Configurar o formato de superfície OpenGL ES
QSurfaceFormat surfaceConfig;

surfaceConfig.setAlphaBufferSize(8);
surfaceConfig.setRedBufferSize(8);
surfaceConfig.setGreenBufferSize(8);
surfaceConfig.setBlueBufferSize(8);
surfaceConfig.setDepthBufferSize(24);
surfaceConfig.setStencilBufferSize(8);
surfaceConfig.setRenderableType(QSurfaceFormat::OpenGLES);
surfaceConfig.setVersion(3, 0);

// Definir como formato padrão antes de criar a aplicação
QSurfaceFormat::setDefaultFormat(surfaceConfig);

// Etapa 2: Criar a instância da aplicação Qt
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
QGuiApplication app(argc, argv);

3. Utilização do Componente QML ComboBox

3.1 Definição Básica do ComboBox

ComboBox {
    id: seletorRegiao
    width: parent.width - 180
    height: 100
    model: listaRegioes
    enabled: true
    font.pixelSize: 40
    z: 10
    hoverEnabled: false
    focusPolicy: Qt.ClickFocus

    popup {
        width: seletorRegiao.width
        y: seletorRegiao.height
        z: 1000
    }

    delegate: ItemDelegate {
        width: seletorRegiao.width
        height: 80
        text: modelData
        font.pixelSize: 40
        highlighted: seletorRegiao.highlightedIndex === index
        padding: 10
    }

    onCurrentIndexChanged: {
        console.log("Seleção de região alterada:", currentIndex)
    }
}

3.2 Implementação da Seleção em Cascata

property var listaRegioes: ["Província A", "Província B", "Província C"]

ListModel {
    id: modeloCidades
    ListElement { idRegiao: 0; nomeCidade: "Capital X" }
    ListElement { idRegiao: 0; nomeCidade: "Município Y" }
    ListElement { idRegiao: 1; nomeCidade: "Centro Z" }
}

property int regiaoSelecionada: 0
property var cidadesDisponiveis: []

function atualizarListaCidades() {
    var cidadesFiltradas = []
    for (var i = 0; i < modeloCidades.count; i++) {
        var item = modeloCidades.get(i)
        if (item && item.idRegiao === regiaoSelecionada) {
            cidadesFiltradas.push(item.nomeCidade)
        }
    }
    cidadesDisponiveis = cidadesFiltradas
    seletorCidades.model = cidadesDisponiveis
    if (cidadesDisponiveis.length > 0) {
        seletorCidades.currentIndex = 0
    }
}

ComboBox {
    id: seletorRegiao
    onCurrentIndexChanged: {
        if (currentIndex >= 0) {
            regiaoSelecionada = currentIndex
            atualizarListaCidades()
        }
    }
}

3.3 Configuração Crucional para o Dispositivo

No arquivo entry/src/main/module.json5, o campo deviceTypes deve incluir "2in1".

{
  "module": {
    "name": "entry",
    "type": "entry",
    "deviceTypes": [
      "default",
      "tablet",
      "2in1"  // Obrigatório para dispositivos PC/2-em-1
    ]
  }
}

Motivo: Dispositivos como o MateBook são identificados como tipo "2in1". A omissão deste valor impede que a ferramenta de empacotamento loaclize corretamente o arquivo de configuração, resultando em erro de construção.

Soluções para Problemas Comuns

Problema 1: Falta de Resposta a Interações

Sintomas: A interface é renderizada, mas cliques e toques não produzem efeito.

Causas: Janela não ativada, ciclo de eventos não iniciado ou problemas de gerenciamento de foco.

Solução: Garanta a ativação da janela.

// Em main.cpp
QQuickWindow* mainWindow = qobject_cast<QQuickWindow*>(engine.rootObjects().first());
if (mainWindow) {
    mainWindow->show();
    mainWindow->raise();
    mainWindow->requestActivate();
}

// Em main.qml
Component.onCompleted: {
    janelaPrincipal.show()
    janelaPrincipal.requestActivate()
}

Problema 2: ComboBox Não Exibe Menu Suspenso

Causas Comuns: Um MouseArea sobreposto intercepta os eventos, hoverEnabled está true ou o z-index do popup está incorreto.

Solução:

ComboBox {
    hoverEnabled: false
    focusPolicy: Qt.ClickFocus
    popup {
        z: 1000
    }
    // Evite adicionar MouseArea sobre o ComboBox.
}

Problema 3: Tela em Branco ou Preto

Causas: Falha no carregamento do QML, erro na configuração do OpenGL ES ou uso de um componente Window incompatível.

Solução: Verifique o carregamento do QML e utilize ApplicationWindow para melhor suporte.

ApplicationWindow {
    id: raiz
    visible: true
    Component.onCompleted: raiz.requestActivate()
}

Melhores Práticas

  • Gerenciamento de Foco: Use Qt.ClickFocus e garanta a ativação da janela.
  • Otimização para Toque: Defina hoverEnabled: false e garanta alturas mínimas de 44 pixels para itens.
  • Manuseio de Eventos: Permita que os controles processem seus próprios eventos; evite MouseArea desnecessários.
  • Modelos de Dados: Utilize ListModel para dados estruturados e atualize o model do ComboBox programaticamente.

Estrutura e Configuração do Projeto

QComboBox/
├── entry/
│   ├── src/main/
│   │   ├── cpp/ (main.cpp, CMakeLists.txt)
│   │   ├── module.json5 (contém "2in1" em deviceTypes)
│   │   └── resources/ (main.qml, qml.qrc)
│   └── build-profile.json5
└── CMakeLists.txt (configuração raiz)

Exemplo de CMakeLists.txt

cmake_minimum_required(VERSION 3.5.0)
project(QtHarmonySample)

set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick QuickControls2)

add_library(entry SHARED main.cpp qml.qrc)

target_link_libraries(entry PRIVATE
    Qt6::Core
    Qt6::Gui
    Qt6::Qml
    Qt6::Quick
    Qt6::QuickControls2
    QOpenHarmonyPlatformIntegrationPlugin
)

Tags: Qt qml HarmonyOS opengles CMake

Publicado em 6-10 23:42 por Thomas