Auf dieser Seite

Qt Canvas Painter - Kompaktes Gesundheitsbeispiel

Demonstriert die Verwendung von QCanvasPainter in einer QWindow.

Dieses Beispiel demonstriert die Verwendung von Qt Canvas Painter in einer reinen QWindow Anwendung. Qt Quick oder QWidget werden überhaupt nicht verwendet, und es besteht keine Abhängigkeit von anderen Qt-Modulen als Core, Gui und Canvas Painter.

Die Anwendung richtet ein beschleunigtes 3D-Rendering mit QRhi ein und rendert alle Inhalte im Fenster mit QCanvasPainter. Das Rendering und die Integration von QCanvasPainter mit QRhi wird über QCanvasRhiPaintDriver verwaltet.

Exkursion

Neben der Auswahl einer 3D-API, die je nach Plattform verwendet werden soll, bietet die Anwendung auch die Möglichkeit, eine solche über Befehlszeilenargumente anzufordern.

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents);

    QRhi::Implementation graphicsApi;
#if defined(Q_OS_WIN)
    graphicsApi = QRhi::D3D11;
#elif QT_CONFIG(metal)
    graphicsApi = QRhi::Metal;
#elif QT_CONFIG(vulkan)
    graphicsApi = QRhi::Vulkan;
#else
    graphicsApi = QRhi::OpenGLES2;
#endif

    QCommandLineParser cmdLineParser;
    cmdLineParser.addHelpOption();
    QCommandLineOption nullOption({ "n", "null" }, QLatin1String("Null"));
    cmdLineParser.addOption(nullOption);
    QCommandLineOption glOption({ "g", "opengl" }, QLatin1String("OpenGL"));
    cmdLineParser.addOption(glOption);
    QCommandLineOption vkOption({ "v", "vulkan" }, QLatin1String("Vulkan"));
    cmdLineParser.addOption(vkOption);
    QCommandLineOption d3d11Option({ "d", "d3d11" }, QLatin1String("Direct3D 11"));
    cmdLineParser.addOption(d3d11Option);
    QCommandLineOption d3d12Option({ "D", "d3d12" }, QLatin1String("Direct3D 12"));
    cmdLineParser.addOption(d3d12Option);
    QCommandLineOption mtlOption({ "m", "metal" }, QLatin1String("Metal"));
    cmdLineParser.addOption(mtlOption);

    cmdLineParser.process(app);

    if (cmdLineParser.isSet(nullOption))
        graphicsApi = QRhi::Null;
    if (cmdLineParser.isSet(glOption))
        graphicsApi = QRhi::OpenGLES2;
    if (cmdLineParser.isSet(vkOption))
        graphicsApi = QRhi::Vulkan;
    if (cmdLineParser.isSet(d3d11Option))
        graphicsApi = QRhi::D3D11;
    if (cmdLineParser.isSet(d3d12Option))
        graphicsApi = QRhi::D3D12;
    if (cmdLineParser.isSet(mtlOption))
        graphicsApi = QRhi::Metal;

#if QT_CONFIG(opengl)
    QSurfaceFormat fmt;
    fmt.setDepthBufferSize(24);
    fmt.setStencilBufferSize(8);
#ifdef Q_OS_MACOS
    fmt.setVersion(4, 1);
    fmt.setProfile(QSurfaceFormat::CoreProfile);
#endif
    QSurfaceFormat::setDefaultFormat(fmt);
#endif

    MainWindow window(graphicsApi);
    window.resize(1920 / 2, 1080 / 2);
    window.show();

    return app.exec();
}

MainWindow ist eine Unterklasse von PainterWindow, einer QWindow. PainterWindow implementiert ein Fenster, das über QRhi gerenderte Inhalte anzeigt, der 3D-Grafikabstraktion von Qt. Seine Aufgabe ist es, eine QRhi -Instanz zu initialisieren, wenn ein expose-Ereignis empfangen wird, d.h. wenn das Fenster angezeigt wird, eine Swapchain und einen Tiefenschablonenpuffer zu verwalten, während es sich auch darum kümmert, zu handeln, wenn die Größe des Fensters geändert wird, und eine virtuelle paint()-Funktion aufzurufen, die in MainWindow implementiert ist.

Zum besseren Verständnis der PainterWindow-Implementierung empfiehlt es sich, das RHI Window Example zu lesen, da es im Wesentlichen der RhiWindow-Klasse in diesem Beispiel sehr ähnlich ist.

Ein QCanvasPainter-spezifischer Schritt besteht darin, eine QCanvasPainterFactory -Instanz abzurufen, sobald eine QRhi erfolgreich initialisiert wurde:

    if (!m_factory) {
        m_factory = new QCanvasPainterFactory;
        m_factory->create(m_rhi.get());
    }

Das Rendern eines neuen Rahmens erfolgt in der Funktion render(), die aufgerufen wird, wenn das Fenster angezeigt oder in der Größe verändert wird, sowie als Reaktion auf Aktualisierungsanforderungen, die über requestUpdate() geplant werden.

Anders als im RHI Window Example werden wir keine Vertex- und Uniform-Puffer oder Grafik-Pipelines über die QRhi API erstellen und direkt Zeichenaufrufe tätigen. Stattdessen wird QCanvasRhiPaintDriver verwendet, um einen Rendering-Durchgang mit dem QCanvasPainter-generierten Rendering aufzuzeichnen.

void PainterWindow::render() { if (!m_factory || !m_factory->isValid() || !m_rhi || !m_sc) return; if (!m_hasSwapChain || m_notExposed) return; if (m_sc->currentPixelSize() !=  m_sc->surfacePixelSize() || m_newlyExposed) { resizeSwapChain(); if (!m_hasSwapChain) return; m_newlyExposed = false; } QRhi::FrameOpResult r =  m_rhi->beginFrame(m_sc.get()); if (r == QRhi::FrameOpSwapChainOutOfDate) { resizeSwapChain(); if (!m_hasSwapChain) return; r =  m_rhi->beginFrame(m_sc.get()); } if (r ! = QRhi::FrameOpSuccess) {        qDebug("beginFrame failed with %d, retry", r);
        requestUpdate(); return; } QRhiCommandBuffer *cb =  m_sc->currentFrameCommandBuffer();    QRhiRenderTarget *rt =  m_sc->aktuellesFrameRenderTarget();    QCanvasRhiPaintDriver *pd =  m_factory->paintDriver();    QCanvasPainter *painter =  m_factory->painter();  pd->resetForNewFrame();  pd->beginPaint(cb, rt, m_fillColor, size(), float(devicePixelRatio())); paint(painter);  pd->endPaint();  m_rhi->endFrame(m_sc.get()); }

Die paint()-Implementierung von MainWindow arbeitet mit der QCanvasPainter API und muss sich nicht um Low-Level-Details wie die QRhi kümmern.

Zunächst werden Bilder geladen und registriert, falls dies noch nicht geschehen ist:

void MainWindow::paint(QCanvasPainter *p)
{
    if (!m_initialized) {
        auto flags = QCanvasPainter::ImageFlag::GenerateMipmaps;
        m_b1ImageLight = p->addImage(QImage(":/images/icon_random_light.png"), flags);
        m_b1ImageDark = p->addImage(QImage(":/images/icon_random_dark.png"), flags);
        m_b2ImageLight = p->addImage(QImage(":/images/icon_theme_light.png"), flags);
        m_b2ImageDark = p->addImage(QImage(":/images/icon_theme_dark.png"), flags);
        m_b3ImageLight = p->addImage(QImage(":/images/icon_settings_light.png"), flags);
        m_b3ImageDark = p->addImage(QImage(":/images/icon_settings_dark.png"), flags);
        m_sImageLight = p->addImage(QImage(":/images/icon_run_light.png"), flags);
        m_sImageDark = p->addImage(QImage(":/images/icon_run_dark.png"), flags);
        m_initialized = true;
    }

Danach folgt das eigentliche Zeichnen, das das Füllen und Streichen von Pfaden, das Rendern von Text und das Zeichnen von Bildern umfasst. Zum Beispiel:

    // Highlight pressed button
    if (m_selectedButton) {
        p->beginPath();
        p->roundRect(m_views[m_selectedButton].rect, viewRadius);
        p->setLineWidth(2.0f * m_px);
        p->setStrokeStyle(m_theme.highlight());
        p->stroke();
    }

Die QCanvasPainter Füll- und Strichaufrufe bereiten intern Vertex-, Index-, Uniformdaten und QRhi-basierte Zeichenaufrufe vor. Der Strom der Befehle wird geleert, wenn endPaint() in PainterWindow::render() aufgerufen wird. Dort wird ein Rendering-Durchgang auf dem zugehörigen QRhiCommandBuffer aufgezeichnet.

Vor der Rückkehr ruft MainWindow::paint() requestUpdate() auf, eine QWindow Funktion, die einen neuen Frame anfordert, synchronisiert mit der Darstellungsrate des Displays. Dadurch wird sichergestellt, dass der Fensterinhalt ständig aktualisiert wird und somit animiert werden kann.

Der unten verlinkte vollständige Quellcode des Beispiels enthält weitere Einzelheiten.

Beispielprojekt @ code.qt.io

© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.