When graphing a line series within a small range using QtChart library (for example, between 0 and 1e-15), the line series seemingly does not get rendered. Using larger numbers with the same exact code works perfectly.
I've tried boiling down my code to be as simple as possible, to find any mistakes I was making, but I cannot find any. I suspect this may be a bug in Qt, but I'm posting here in case I missed something before I file a bug report.
I've also failed to find any information online about people experiencing similar bugs, but that may just be an indication that my google-fu is weak.
I originally encountered this bug using PyQt5, but have recreated it in C++ to ensure it was not a PyQt5 specific bug.
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QLegend>
#include <QtCharts/QValueAxis>
QT_CHARTS_USE_NAMESPACE
#define FACTOR 1e-13
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLineSeries *lineseries = new QLineSeries();
lineseries->setName("series");
lineseries->setUseOpenGL(true);
for (double i = 0.0; i < 10.0; i += 1.0)
lineseries->append(QPointF(i*FACTOR, i * FACTOR));
QChart *chart = new QChart();
chart->addSeries(lineseries);
chart->setTitle("Line example");
QValueAxis *axisX = new QValueAxis();
chart->setAxisX(axisX, lineseries);
QValueAxis *axisY = new QValueAxis();
chart->setAxisY(axisY, lineseries);
axisY->setRange(0.0, 9.0*FACTOR);
axisX->setLabelFormat("%.3E");
axisY->setLabelFormat("%.3E");
chart->legend()->setVisible(true);
chart->legend()->setAlignment(Qt::AlignBottom);
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(840, 900);
window.show();
return a.exec();
}
Running the code attached, I would expect to observe a linear line series rendered in the plot, but I see nothing rendered.
Modifying the FACTOR constant to something >= 1e-12 will make the line series display properly.
Related
I am using the qtcharts module of qt.
I am using c++ but it does not matter if the solution comes for another language (I will translate it afterwards).
Problem: I plot a bunch of QLineSeries in a QChart and I want to display the point labels only when hovering them.
I planned to use the signal QXYSeries::hovered() to detect when the mouse moves over a point (the same when the mouse moves away the point).
I know that there exists a member function QXYSeries::setPointLabelsVisible() but it makes visible all the points of the series.
I want to be able to display only one point at a time because the series are relatively large and displaying all the labels would degrade the readability.
Question: Is it possible to display only one point label for a QLineSeries ? If yes, how ?
I could not find such a feature anywhere in the Qt documentation.
Here is a baseline code sample to start with (for convenience):
Declaration:
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow();
};
Definition:
#include <QApplication>
#include <QLineSeries>
#include <QDateTimeAxis>
#include <QValueAxis>
#include <QChartView>
#include <QDateTime>
int main(int argc, char ** argv)
{
QApplication app(argc, argv);
MainWindow w;
w.show();
return app.exec();
}
MainWindow::MainWindow()
{
setWindowTitle("QtCharts baseline");
resize(800, 500);
QtCharts::QChart * chart = new QtCharts::QChart;
chart->setTitle("Baseline sample");
chart->legend()->setAlignment(Qt::AlignRight);
QtCharts::QDateTimeAxis * time_axis = new QtCharts::QDateTimeAxis;
time_axis->setFormat("hh:mm:ss");
time_axis->setTitleText("Time");
time_axis->setTickCount(5);
QtCharts::QValueAxis * value_axis = new QtCharts::QValueAxis;
value_axis->setTitleText("Value (unit)");
value_axis->setTickCount(6);
chart->addAxis(time_axis, Qt::AlignBottom);
chart->addAxis(value_axis, Qt::AlignLeft);
QtCharts::QLineSeries * ls = new QtCharts::QLineSeries;
ls->setName("Test series");
ls->setPointsVisible(true);
//ls->setPointLabelsVisible(true);
QDateTime dt = QDateTime::currentDateTime();
ls->append(dt.toMSecsSinceEpoch(), -10);
ls->append(dt.addSecs(1).toMSecsSinceEpoch(), 8);
ls->append(dt.addSecs(2).toMSecsSinceEpoch(), 27);
ls->append(dt.addSecs(3).toMSecsSinceEpoch(), 12);
ls->append(dt.addSecs(4).toMSecsSinceEpoch(), 42);
chart->addSeries(ls);
ls->attachAxis(time_axis);
ls->attachAxis(value_axis);
QtCharts::QChartView * view = new QtCharts::QChartView;
view->setChart(chart);
this->setCentralWidget(view);
}
One technique you could try is making a "shadow" copy of the line series with just the one or few points you need, sitting on top of the actual data line. Draw with a transparent pen so the line doesn't show up but set the labels to be visible. You can add/remove/change points to the shadow copy of the line series, and only the labels you want drawn can be added to the series. The link shows one
example of the technique.
I need to modify a QPolarChart to create a Satellite SkyPlot.
For this, I need to reverse the radial axis (elevation axis) so that 90 is placed on the origin and 0 on the outer ring.
In the list of members for QValueAxis I found the method setReverse, which seems to do what I need.
However, doing setReverse(true) on the elevation axis does not flip the axis as I was expecting.
How can I reverse the axis then?
This is the minimal code required to run this example:
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QPolarChart>
#include <QtCharts/QValueAxis>
#include <QScatterSeries>
QT_CHARTS_USE_NAMESPACE
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtCharts::QPolarChart *chart = new QtCharts::QPolarChart();
chart->legend()->hide();
QScatterSeries *series = new QScatterSeries();
for (int i = 0; i < 360; i += 10) {
series->append(i, i);
}
chart->addSeries(series);
QValueAxis *azimuthAxis = new QValueAxis();
azimuthAxis->setRange(0, 360);
azimuthAxis->setTickCount(9);
azimuthAxis->setLabelFormat("%d");
azimuthAxis->setLabelsVisible(true);
chart->addAxis(azimuthAxis, QPolarChart::PolarOrientationAngular);
QValueAxis *elevationAxis = new QValueAxis();
elevationAxis->setRange(0, 90);
elevationAxis->setTickCount(7);
elevationAxis->setLabelFormat("%d");
elevationAxis->setLabelsVisible(true);
elevationAxis->setReverse(true); // <-- REVERSE THE AXIS
chart->addAxis(elevationAxis, QPolarChart::PolarOrientationRadial);
QtCharts::QChartView *chartView = new QtCharts::QChartView();
chartView->setChart(chart);
chartView->setRenderHint(QPainter::Antialiasing);
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(800, 600);
window.show();
return app.exec();
}
UPDATE:
After reading Damien's comment:
You may check with isReverse if the command setReverse was taken into account
I made the test by putting qDebug() << elevationAxis->isReverse(); immediately before and immediately after elevationAxis->setReverse(true);, and it prints out false and true, respectively.
qDebug() << elevationAxis->isReverse(); // <-- Prints out "false"
elevationAxis->setReverse(true); // <-- REVERSE THE AXIS
qDebug() << elevationAxis->isReverse(); // <-- Prints out "true"
So the reverse property is indeed changed. However, this change is not reflected visually in the axis.
The documentation for the reverse property reads:
This property holds whether a reverse axis is used.
By default, the value is false.
The reverse axis is supported with a line, spline, and scatter series,
as well as an area series with a cartesian chart. All axes of the same
orientation attached to the same series must be reversed if one is
reversed or the behavior is undefined.
A polar chart is certainly NOT a cartesian chart, so that might be the root of the problem.
I couldn't make the setReversed(true) work whatever the way I tried to do it.
So I found another solution: Do it manually.
I printed the series in reversed way:
QScatterSeries *series = new QScatterSeries();
for (int i = 0; i < 360; i += 10) {
series->append(i, MAX_ELEVATION - i);
}
MAX_ELEVATION equals to 90.
Then I reversed the labels names. For that I had to replace QValueAxis by its derived QCategoryAxis.
The full code
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QPolarChart>
#include <QtCharts/QValueAxis>
#include <QScatterSeries>
#include <QtCharts/QCategoryAxis>
QT_CHARTS_USE_NAMESPACE
#define MAX_ELEVATION 90
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QtCharts::QPolarChart *chart = new QtCharts::QPolarChart();
chart->legend()->hide();
QScatterSeries *series = new QScatterSeries();
for (int i = 0; i < 360; i += 10) {
series->append(i, MAX_ELEVATION - i);
}
chart->addSeries(series);
QValueAxis *azimuthAxis = new QValueAxis();
azimuthAxis->setRange(0, 360);
azimuthAxis->setTickCount(9);
azimuthAxis->setLabelFormat("%d");
azimuthAxis->setLabelsVisible(true);
chart->addAxis(azimuthAxis, QPolarChart::PolarOrientationAngular);
/*
QValueAxis *elevationAxis = new QValueAxis();
elevationAxis->setRange(0, 90);
elevationAxis->setTickCount(7);
elevationAxis->setLabelFormat("%d");
elevationAxis->setLabelsVisible(true);
elevationAxis->setReverse(true); // <-- REVERSE THE AXIS
chart->addAxis(elevationAxis, QPolarChart::PolarOrientationRadial);
*/
QCategoryAxis *elevationAxis = new QCategoryAxis;
elevationAxis->setRange(0, MAX_ELEVATION);
for(unsigned int i = 0; i <= MAX_ELEVATION; i += 15)
elevationAxis->append(QString::number(MAX_ELEVATION-i), i);
elevationAxis->setLabelsPosition(QCategoryAxis::AxisLabelsPositionOnValue);
elevationAxis->setLabelsVisible(true);
chart->addAxis(elevationAxis, QPolarChart::PolarOrientationRadial);
QtCharts::QChartView *chartView = new QtCharts::QChartView();
chartView->setChart(chart);
chartView->setRenderHint(QPainter::Antialiasing);
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(800, 600);
window.show();
return app.exec();
}
It is equivalent to what setReversed(true) should do. It will display your series exactly the way you want without modifying anything of the way the data are computed.
Here you can see what it looks like:
It worked successfully for me.
Hope it can help.
I am currently trying to get my head around Line Charts in Qt. For some reason, my charts seem to show really weird behavior. When I am using the createDefaultAxis it will set the minimum and maximum values according to the minimum and maximum values of the Series behind the chart. While this seems alright at first it's already different from the behavior in this example. The minimum Y-Value there is 1, the minimum value on the axis, however, is 0.
At first, I thought this might just be due to a change to the QChart class, so I created my own Axis and tried again. This time I made the y-Axis range from 0 to 100 and the x-Axis range from 1 to 52. Also, I changed the tick counts to 4 and 52. However, the chart still looked like before and didn't seem to be affected by the changes to the axis.
I included a screenshot of that program here
I hope you can help me fix that. The goal would be that the values of the series match the values on the axes.
Edit: Here is the main.cpp:
#include <QtWidgets/QApplication>
#include <QtWidgets/QMainWindow>
#include <QtCharts/QChartView>
#include <QtCharts/QLineSeries>
#include <QtCharts/QValueAxis>
QT_CHARTS_USE_NAMESPACE
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLineSeries *series = new QLineSeries();
series->append(0, 6);
series->append(2, 4);
series->append(3, 8);
series->append(7, 4);
series->append(10, 5);
*series << QPointF(11, 1) << QPointF(13, 3) << QPointF(17, 6) << QPointF(18, 3) << QPointF(20, 2);
QChart *chart = new QChart();
chart->legend()->hide();
chart->addSeries(series);
QValueAxis *axisX = new QValueAxis();
axisX->setRange(1, 52);
axisX->setMin(1);
axisX->setMax(52);
axisX->setTickCount(52);
QValueAxis *axisY = new QValueAxis();
axisY->setRange(0, 100);
axisY->setMin(0);
axisY->setMax(100);
axisY->setTickCount(4);
chart->setAxisX(axisX);
chart->setAxisY(axisY);
chart->setTitle("Simple line chart example");
QChartView *chartView = new QChartView(chart);
chartView->setRenderHint(QPainter::Antialiasing);
QMainWindow window;
window.setCentralWidget(chartView);
window.resize(400, 300);
window.show();
return a.exec();
}
The series isn't attached to any axis, it will by default scale to utilize the entire plot area of the chart.
You should attach the series to axis created as :
//... After setting up your axis X and Y
chart->setAxisX(axisX);
chart->setAxisY(axisY);
//attach the series to the specific axis.
series->attachAxis(axisX);
series->attachAxis(axisY);
to add axis on chart use following technique.
addAxis(xAxis,postion);
addAxis(yAxis,postion);
and then attached series to it
I have been searching about Threads in QT and ways to process multiple events simultaneously according to their docs, only the main GUI thread can manage GUI related events. So my question is: Is it possible to move multiple labels or objects at the same time during execution? Im trying to create sort of a simulation proyect for school.
What I have right now is: it creates a label everytime I run the function so I need that label to move to a certain spot. Problem is since I need the function executed several times, when it executes again, the previous label stops and moves the new one. After it has completed it goes back to the previous.
Any help is appreciated thanks.
New to asking here and QT in general.
Edit:
Here is what I have of my function:
QLabel *cliente = new QLabel(this);
QPixmap pix("image.jpg");
cliente->setGeometry(10,50,128,128);
cliente->setPixmap(pix);
cliente->show();
int speed = 100;
while (cliente->x()<300){
QTime dieTime = QTime::currentTime().addMSecs(speed);
while (QTime::currentTime() < dieTime){
QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}
cliente->move(cliente->x()+10,cliente->y());
}
To handle the movement of widgets it is most advisable to use the QPropertyAnimation class, but if you want to handle parallel group animations it is opportune to use QParallelAnimationGroup as shown in the following example:
#include <QApplication>
#include <QLabel>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QWidget>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
w.resize(500, 550);
QParallelAnimationGroup group;
for(int i = 0; i < 10; i++){
QLabel *label = new QLabel(QString("label %1").arg(i), &w);
QPropertyAnimation *animation = new QPropertyAnimation(label, "pos");
animation->setDuration(1000);
animation->setStartValue(QPoint(50*i, 0));
animation->setEndValue(QPoint(50*i, 50*(i+1)));
group.addAnimation(animation);
}
group.start();
w.show();
return a.exec();
}
Output:
I would like to plot a semilog chart using Qwt. I have not knowledge about Qwt but I was looking for some examples to guide my code. The problem is that at the moment I don't find someone. Could you help me with a simple code? I want to use a matrix where I can get the x-axis and y-axis values and use them to create the plot. Thank you!
Try this:
QwtPlot *myPlot = new QwtPlot;
QwtPlotCurve *curve1 = new QwtPlotCurve;
QwtPointSeriesData* myData = new QwtPointSeriesData;
QVector<QPointF>* samples = new QVector<QPointF>;
samples->push_back(QPointF(1.0,1.0));
samples->push_back(QPointF(2.0,2.0));
samples->push_back(QPointF(3.0,3.0));
samples->push_back(QPointF(4.0,5.0));
myData->setSamples(*samples);
curve1->setData(myData);
curve1->attach(myPlot);
I used here QVector but qwtplotcurve support double arrays and other things, but I like work with containers. You can choose the best for you. QPoint contains x and y values.
Qwt also offer logarithmic scale engine: http://qwt.sourceforge.net/class_qwt_log_scale_engine.html
I should to say that maybe something wrong with your Qwt, but next code works perfectly on my computer:
#include "mainwindow.h"
#include <QApplication>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QwtPlot *myPlot = new QwtPlot;
QwtPlotCurve *curve1 = new QwtPlotCurve;
QwtPointSeriesData* myData = new QwtPointSeriesData;
QVector<QPointF>* samples = new QVector<QPointF>;
samples->push_back(QPointF(1.0,1.0));
samples->push_back(QPointF(2.0,2.0));
samples->push_back(QPointF(3.0,3.0));
samples->push_back(QPointF(4.0,5.0));
myData->setSamples(*samples);
curve1->setData(myData);
curve1->attach(myPlot);
myPlot->show();
// MainWindow w;
// w.show();
return a.exec();
}