I'm trying to use QtCharts (specifically ChartView, PieSeries, etc.) in a Qt Quick project using Qt 6.9 with MinGW on Windows.
Everything works perfectly when I run the QML with qmlscene, but as soon as I load a QML file with ChartView in my built app, it crashes at startup or fails to load components like PieSlice.
I’m using qt_add_qml_module()
in CMake, I’ve tried windeployqt --qmldir qml
, and I'm importing QtCharts in QML. Still, the app crashes or reports:
PieSlice was not found. Did you add all imports and dependencies?
I suspect the issue is related to QML import paths or missing plugin deployment, but I'm not sure what step I'm missing.
I am using qt_add_qml_module()
in CMake and have run windeployqt --qmldir qml
to make sure all dependencies are bundled. I’m importing QtCharts in my QML code.
I expected the app to launch and display the chart normally, just like it does when I run the QML file with qmlscene.
I double-checked my main.cpp
and CMake setup, and everything seems correct. To isolate the issue, I even created a new minimal project with just a ChartView on the main window — it still crashes. But when I open the same QML in qmlscene, it works perfectly.
So I decided to create a brand-new project and just put a minimal QtCharts example directly in the main QML window — nothing else. It crashes again. I then ran the same QML file with qmlscene, and it displayed perfectly fine.
I am at a loss as to what to do next. It seems like QtCharts is available in the environment, but the actual application cannot find or load it at runtime. I'm not sure if this is a problem with import paths, plugin deployment, or something else entirely.
CMakeList.txt
:
cmake_minimum_required(VERSION 3.16)
project(rtm_gui_test VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(QT_QML_GENERATE_QMLLS_INI ON)
find_package(Qt6 REQUIRED COMPONENTS Quick Charts)
qt_standard_project_setup(REQUIRES 6.8)
qt_add_executable(apprtm_gui_test
main.cpp
)
qt_add_qml_module(apprtm_gui_test
URI rtm_gui_test
VERSION 1.0
QML_FILES
Main.qml
)
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
set_target_properties(apprtm_gui_test PROPERTIES
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.apprtm_gui_test
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
target_link_libraries(apprtm_gui_test
PRIVATE Qt6::Quick
Qt6::Charts
)
include(GNUInstallDirs)
install(TARGETS apprtm_gui_test
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
Main.qml
:
import QtQuick
import QtQuick.Controls
import QtCharts
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
ChartView {
title: "Line Chart"
anchors.fill: parent
antialiasing: true
LineSeries {
name: "Line"
XYPoint { x: 0; y: 0 }
XYPoint { x: 1.1; y: 2.1 }
XYPoint { x: 1.9; y: 3.3 }
XYPoint { x: 2.1; y: 2.1 }
XYPoint { x: 2.9; y: 4.9 }
XYPoint { x: 3.4; y: 3.0 }
XYPoint { x: 4.1; y: 3.3 }
}
}
}
main.cpp
:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QObject::connect(
&engine,
&QQmlApplicationEngine::objectCreationFailed,
&app,
[]() { QCoreApplication::exit(-1); },
Qt::QueuedConnection);
engine.loadFromModule("rtm_gui_test", "Main");
return app.exec();
}
I'm still working on my project, but I've recently decided to pivot to the QtGraphs module. I'm figuring out the details as I go, but so far, I got a working (ish) solution (So far I am capable of displaying data that is being produced by a C++ module).
Main.qml:
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphs
Window {
width: 640
height: 480
visible: true
title: qsTr("Graph Viewer")
GraphsView {
id: graphsView
anchors.fill: parent
anchors.margins: 16
theme: GraphsTheme {
readonly property color c1: "#DBEB00"
readonly property color c2: "#373F26"
readonly property color c3: Qt.lighter(c2, 1.5)
colorScheme: GraphsTheme.ColorScheme.Dark
seriesColors: ["#2CDE85", "#DBEB00"]
grid.mainColor: c3
grid.subColor: c2
axisX.mainColor: c3
axisY.mainColor: c3
axisX.subColor: c2
axisY.subColor: c2
axisX.labelTextColor: c1
axisY.labelTextColor: c1
}
axisX: ValueAxis {
max: graphController.rangoSuperiorX
min: graphController.rangoInferiorX
tickInterval: 25
subTickCount: 1
labelDecimals: 1
}
axisY: ValueAxis {
max: graphController.rangoSuperiorY
min: graphController.rangoInferiorY
tickInterval: 1
subTickCount: 10
labelDecimals: 1
}
Component {
id: marker
Rectangle {
width: 8
height: 8
color: "white"
radius: width / 2
border.width: 2
border.color: "black"
}
}
LineSeries {
id: lineSeries1
width: 4
pointDelegate: marker
}
Connections {
target: graphController
function onPointAdded(x, y) {
lineSeries1.append(x, y)
}
}
}
}
graphcontroller.cpp:
#include "graphcontroller.h"
#include <QtGraphs/QLineSeries>
#include <QDebug>
// testing
#include <QTimer>
#include <QtGlobal> // for qrand()
#include <QDateTime> // optional: to seed qrand() with time
/*-------------------------------------------------------------------------------------------------*/
/* Constructor del controlador del gráfico */
/*-------------------------------------------------------------------------------------------------*/
GraphController::GraphController(QObject* parent,
int max_rango_x,
int min_rango_x,
int max_rango_y,
int min_rango_y)
: QObject(parent),
n_datos_almacenados(0),
m_rango_superior_x(max_rango_x),
m_rango_inferior_x(min_rango_x),
m_rango_superior_y(max_rango_y),
m_rango_inferior_y(min_rango_y),
valor_maximo_x(min_rango_x),
valor_minimo_x(min_rango_x),
valor_maximo_y(min_rango_y),
valor_minimo_y(min_rango_y)
{
// Inicialización opcional
QTimer* timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, [this]() {
static double x = 0;
double y = rand() % 10;
this->addPoint(x++, y);
});
timer->start(500); // every 500 ms
}
/*-------------------------------------------------------------------------------------------------*/
/* Getter de la serie interna */
/* Nota: No se utiliza directamente en QML con QtGraphs */
/*-------------------------------------------------------------------------------------------------*/
QLineSeries* GraphController::getSeries() {
return &m_internalSeries;
}
/*-------------------------------------------------------------------------------------------------*/
/* Getters y setters para los rangos */
/*-------------------------------------------------------------------------------------------------*/
int GraphController::getRangoSuperiorX() { return m_rango_superior_x; }
int GraphController::getRangoInferiorX() { return m_rango_inferior_x; }
int GraphController::getRangoSuperiorY() { return m_rango_superior_y; }
int GraphController::getRangoInferiorY() { return m_rango_inferior_y; }
void GraphController::setRangoSuperiorX(int x) {
if (m_rango_superior_x != x) {
m_rango_superior_x = x;
emit rangoXChanged();
}
}
void GraphController::setRangoInferiorX(int x) {
if (m_rango_inferior_x != x) {
m_rango_inferior_x = x;
emit rangoXChanged();
}
}
void GraphController::setRangoSuperiorY(int y) {
if (m_rango_superior_y != y) {
m_rango_superior_y = y;
emit rangoYChanged();
}
}
void GraphController::setRangoInferiorY(int y) {
if (m_rango_inferior_y != y) {
m_rango_inferior_y = y;
emit rangoYChanged();
}
}
/*-------------------------------------------------------------------------------------------------*/
/* Agrega un nuevo punto a la serie interna */
/* Emite la señal para que QML lo dibuje en su propio LineSeries */
/*-------------------------------------------------------------------------------------------------*/
void GraphController::addPoint(double x, double y) {
m_internalSeries.append(x, y);
++n_datos_almacenados;
emit pointAdded(x, y);
// Update value bounds
valor_maximo_x = std::max(valor_maximo_x, static_cast<int>(x));
valor_minimo_x = std::min(valor_minimo_x, static_cast<int>(x));
valor_maximo_y = std::max(valor_maximo_y, static_cast<int>(y));
valor_minimo_y = std::min(valor_minimo_y, static_cast<int>(y));
}
void GraphController::updateRanges(double x, double y) {
bool rangoXChangedFlag = false;
bool rangoYChangedFlag = false;
// Update min/max X values
if (x > valor_maximo_x) {
valor_maximo_x = x;
rangoXChangedFlag = true;
}
if (x < valor_minimo_x) {
valor_minimo_x = x;
rangoXChangedFlag = true;
}
// Update min/max Y values
if (y > valor_maximo_y) {
valor_maximo_y = y;
rangoYChangedFlag = true;
}
if (y < valor_minimo_y) {
valor_minimo_y = y;
rangoYChangedFlag = true;
}
// Update control range if x exceeds current range
if (x > m_rango_superior_x) {
m_rango_superior_x = x;
rangoXChangedFlag = true;
}
if (x < m_rango_inferior_x) {
m_rango_inferior_x = x;
rangoXChangedFlag = true;
}
// Emit signals only if needed
if (rangoXChangedFlag)
emit rangoXChanged();
if (rangoYChangedFlag)
emit rangoYChanged();
}
graphcontroller.h:
#ifndef GRAPHCONTROLLER_H
#define GRAPHCONTROLLER_H
#include <QObject>
#include <QtGraphs/QLineSeries>
#include <QQmlEngine>
/*--------------------------------------------------------------------------------------------------------------------*/
/* Controlador que maneja una serie de datos para representar en QML usando QtGraphs */
/*--------------------------------------------------------------------------------------------------------------------*/
class GraphController : public QObject {
Q_OBJECT
Q_PROPERTY(QLineSeries* series READ getSeries CONSTANT) // Acceso opcional desde QML si se desea leer la serie
Q_PROPERTY(int rangoSuperiorX READ getRangoSuperiorX WRITE setRangoSuperiorX NOTIFY rangoXChanged)
Q_PROPERTY(int rangoInferiorX READ getRangoInferiorX WRITE setRangoInferiorX NOTIFY rangoXChanged)
Q_PROPERTY(int rangoSuperiorY READ getRangoSuperiorY WRITE setRangoSuperiorY NOTIFY rangoYChanged)
Q_PROPERTY(int rangoInferiorY READ getRangoInferiorY WRITE setRangoInferiorY NOTIFY rangoYChanged)
QML_ELEMENT // Permite usar GraphController directamente en QML (con import path correcto)
public:
explicit GraphController(QObject* parent = nullptr,
int max_rango_x = 100,
int min_rango_x = 0,
int max_rango_y = 50,
int min_rango_y = 0);
// Métodos públicos
void addPoint(double x, double y); // Añade un nuevo punto y emite signal
void updateRanges(double x, double y); // Autoajuste de rango si se usa
void showCompleteGraph(); // Ajustar los rangos para mostrar toda la grafica
// Getters/setters para Q_PROPERTY
int getRangoSuperiorX();
int getRangoInferiorX();
int getRangoSuperiorY();
int getRangoInferiorY();
void setRangoSuperiorX(int x);
void setRangoInferiorX(int x);
void setRangoSuperiorY(int y);
void setRangoInferiorY(int y);
QLineSeries* getSeries(); // Acceso directo a la serie interna (para pruebas o lógica adicional)
signals:
void pointAdded(double x, double y); // Señal para conectar a QML y añadir visualmente un nuevo punto
void rangoXChanged(); // Notifica a QML que el eje X debe cambiar
void rangoYChanged(); // Notifica a QML que el eje Y debe cambiar
private:
QLineSeries m_internalSeries; // Serie interna mantenida solo en C++ (no compartida con QML)
int n_datos_almacenados;
// Rango de ejes (control y ajustes)
int m_rango_superior_x;
int m_rango_inferior_x;
int m_rango_superior_y;
int m_rango_inferior_y;
// Seguimiento de valores extremos (opcional para autoajuste)
int valor_maximo_x;
int valor_minimo_x;
int valor_maximo_y;
int valor_minimo_y;
};
#endif // GRAPHCONTROLLER_H