Qt Canvas Painter - Ejemplo de salud compacta
Demuestra el uso de QCanvasPainter en un QWindow.

Este ejemplo demuestra el uso de Qt Canvas Painter en una aplicación QWindow pura. Qt Quick o QWidget no se utilizan en absoluto, y no hay ninguna dependencia de módulos Qt aparte de Core, Gui, y Canvas Painter.
La aplicación configura el renderizado 3D acelerado utilizando QRhi, y renderiza todo el contenido de la ventana con QCanvasPainter. El renderizado y la integración de QCanvasPainter con QRhi se gestionan a través de QCanvasRhiPaintDriver.
Recorrido
Además de elegir una API 3D en función de la plataforma, la aplicación también ofrece la posibilidad de solicitar una utilizando argumentos de línea de comandos.
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 es una subclase de PainterWindow, que es una QWindow. PainterWindow implementa una ventana que muestra contenido renderizado a través de QRhi, la abstracción de gráficos 3D de Qt. Su tarea es inicializar una instancia de QRhi al recibir un evento de exposición, es decir, cuando se muestra la ventana, gestionar una cadena de intercambio y un búfer de profundidad-esténcil, a la vez que se encarga de actuar cuando se redimensiona la ventana, e invocar una función virtual paint() que se implementa en MainWindow.
Para una mejor comprensión de la implementación de PainterWindow, se recomienda consultar el Ejemplo de Ventana RHI, ya que en esencia es muy similar a la clase RhiWindow de ese ejemplo.
Un paso específico de QCanvasPainter es recuperar una instancia de QCanvasPainterFactory una vez que QRhi se ha inicializado correctamente:
if (!m_factory) { m_factory = new QCanvasPainterFactory; m_factory->create(m_rhi.get()); }
El renderizado de un nuevo marco ocurre en la función render(), que es invocada cuando la ventana es mostrada, redimensionada, y en respuesta a peticiones de actualización programadas a través de requestUpdate().
Al contrario que en el ejemplo de la ventana RHI, no crearemos buffers de vértices y uniformes ni pipelines gráficos a través de la API QRhi, ni realizaremos llamadas a dibujar directamente. En su lugar, QCanvasRhiPaintDriver se utiliza para grabar un pase de renderizado con el renderizado generado por 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()); }
La implementación de paint() de MainWindow funciona con la API QCanvasPainter, y no necesita preocuparse de detalles de bajo nivel como el QRhi.
En primer lugar, carga y registra las imágenes si aún no se ha hecho:
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; }
A esto le sigue el dibujo propiamente dicho, que implica el relleno y trazado de rutas, la representación de texto y el dibujo de imágenes. Por ejemplo:
// 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(); }
Las llamadas de relleno y trazo de QCanvasPainter preparan internamente vértices, índices, datos uniformes y llamadas de dibujo basadas en QRhi. El flujo de comandos se vacía cuando se llama a endPaint() en PainterWindow::render(). Ahí es donde se registra un pase de renderizado en el QRhiCommandBuffer asociado.
Antes de volver, MainWindow::paint() llama a requestUpdate(), una función de QWindow que solicita un nuevo fotograma, sincronizado con la velocidad de presentación de la pantalla. Esto es lo que asegura que el contenido de la ventana se actualice continuamente y así pueda animarse.
Consulta el código fuente completo del ejemplo enlazado más abajo para más detalles.
© 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.