Qt Canvas Painter - Exemple « Compact Health »
Illustre l'utilisation de « QCanvasPainter » dans une application « QWindow ».

Cet exemple montre comment utiliser Qt Canvas Painter dans une application QWindow pure. Le Qt Quick et Qt Widgets ne sont pas utilisés.
L'application affiche tout le contenu via les backends d'QRhi à l'aide de QCanvasPainter. Le rendu et l'intégration de QCanvasPainter avec QRhi sont gérés via QCanvasRhiPaintDriver. Cela signifie que l'accélération matérielle est utilisée lorsqu'elle est disponible pour le backend sélectionné.
Remarque : QRhi fait partie de Qt GUI Private. À ce titre, il n’offre que des garanties de compatibilité limitées. Il n’y a aucune garantie de compatibilité au niveau du code source ou des binaires pour ces classes, ce qui signifie que le fonctionnement de l’API n’est garanti qu’avec la version de Qt sur laquelle l’application a été développée.
Ce que démontre cet exemple
- Rendu d'une interface utilisateur entièrement personnalisée dans une chaîne de swap ( QWindow).
- Création et gestion d’un QRhi, d’une swapchain et de ressources de profondeur-stencil.
- Enregistrement des commandes de dessin via ` QCanvasPainter ` et ` QCanvasRhiPaintDriver`.
- La gestion d’une animation continue avec ` QWindow::requestUpdate()`.
Guide pas à pas
Par défaut, l'application choisit une API de rendu en fonction de la plateforme. Vous pouvez également spécifier un backend particulier à l'aide d'arguments de ligne de commande :
--null--opengl--vulkan--d3d11--d3d12--metal
Si la création du backend échoue, l'exemple tente de se rabattre sur OpenGL ES 2 lorsque celui-ci est disponible.
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;
si (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
#if QT_CONFIG(vulkan)
QVulkanInstance vulkanInstance;
if (graphicsApi== QRhi::Vulkan) {
vulkanInstance.setExtensions(QRhiVulkanInitParams::preferredInstanceExtensions());
if (!vulkanInstance.create()) {
#if QT_CONFIG(opengl)
qWarning("Vulkan is not usable, falling back to OpenGL");
graphicsApi = QRhi::OpenGLES2;
#else
qWarning("Vulkan is not usable, falling back to the null backend");
graphicsApi = QRhi::Null;
#endif
}
}
#endif
MainWindow window(graphicsApi);
#if QT_CONFIG(vulkan)
if (graphicsApi== QRhi::Vulkan)
window.setVulkanInstance(&vulkanInstance);
#endif
window.resize(1920 / 2, 1080 / 2);
window.show();
return app.exec();
}MainWindow est une sous-classe de ` PainterWindow`, qui est une classe de base de ` QWindow`. ` PainterWindow ` implémente une fenêtre qui affiche le contenu rendu via ` QRhi`, l’abstraction graphique de Qt. Elle initialise une instance de ` QRhi ` lorsque la fenêtre est exposée, gère une chaîne d’échange (swapchain) et un tampon de profondeur-stencil, traite les événements de redimensionnement et appelle une fonction virtuelle ` paint() ` implémentée par ` MainWindow`.
Pour mieux comprendre l’implémentation de ` PainterWindow `, il est recommandé de consulter l’exemple « RHI Window », car elle est, pour l’essentiel, très similaire à la classe ` RhiWindow ` de cet exemple.
Une étape spécifique à QCanvasPainter consiste à récupérer une instance de QCanvasPainterFactory une fois que QRhi a été initialisé :
if (!m_factory) {
m_factory = new QCanvasPainterFactory;
m_factory->create(m_rhi.get());
}Le rendu d’une nouvelle image s’effectue dans la fonction ` render() `, qui est appelée lorsque la fenêtre est affichée, redimensionnée, ainsi qu’en réponse aux demandes de mise à jour planifiées via ` requestUpdate()`.
Contrairement à l’exemple de fenêtre RHI, nous ne créerons pas de tampons de sommets et d’uniformes ni de pipelines graphiques via l’API QRhi, et nous n’émettrons pas directement d’appels de dessin. À la place, QCanvasRhiPaintDriver est utilisé pour enregistrer un passage de rendu avec le rendu généré par QCanvasPainter.
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->currentFrameRenderTarget();
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());
}L’implémentation de ` paint() ` de `MainWindow` utilise l’API ` QCanvasPainter ` et n’a pas besoin de gérer directement les détails de l’ QRhi ation de bas niveau.
Tout d’abord, elle charge et enregistre les images si ce n’est pas déjà fait :
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;
}Vient ensuite le dessin proprement dit, qui consiste à remplir et à tracer des chemins, à afficher du texte et à dessiner des images. Par exemple :
// 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();
}QCanvasPainter Les commandes de dessin préparent la géométrie, l’état et l’ QRhi ent le travail de dessin en interne. Le flux de commandes est vidé lorsque la fonction ` endPaint()` est appelée dans ` PainterWindow::render()`. C’est à ce moment-là qu’un passage de rendu est enregistré sur l’ QRhiCommandBuffer associé.
Avant de retourner, ` MainWindow::paint() ` appelle ` requestUpdate()`, une fonction de ` QWindow ` qui demande une nouvelle image synchronisée avec la fréquence de rafraîchissement de l’écran. Cela permet de mettre à jour le contenu en continu pour l’animation.
Consultez le code source complet de l’exemple pour plus de détails sur la mise en œuvre.
© 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.