Reverse axis of QPolarChart - c++

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.

Related

How to make only one point label visible for QLineSeries/QXYSeries

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.

How to exclude empty QEntity from bounding volume computation

My company uses Qt3D to display its CAD models. Wee tried to use the function QCamera::viewEntity(Qt3DCore::QEntity *entity) in order to compute the bounding sphere of a given entity and also to fit the entity to the screen.
Now, we stumbled across an unsolvable problem in case of empty QEntity nodes. I'll call a node empty, if it does not contain any vertex/point after all. In this case I expected, that it should be neglected in the computation of bounding volume. Instead it seems, that it will be treated, as it has a bounding sphere with center (0.,0.,0.) and radius 0.
The following code illustrates the issue:
main.cpp
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QFrame>
#include <Qt3DRender/QRenderSettings>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QCamera>
#include <Qt3DExtras/QSphereMesh>
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DExtras/QForwardRenderer>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QCameraLens>
#include <QPushButton>
Qt3DCore::QEntity* createSphereMesh()
{
auto sphereMat = new Qt3DExtras::QDiffuseSpecularMaterial;
sphereMat->setDiffuse(QColor(Qt::blue));
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(0.5);
auto meshEntity = new Qt3DCore::QEntity;
meshEntity->addComponent(mesh);
meshEntity->addComponent(sphereMat);
return meshEntity;
}
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto view = new Qt3DExtras::Qt3DWindow();
view->defaultFrameGraph()->setClearColor(QColor(127, 127, 127));
auto settings = view->renderSettings();
settings->setActiveFrameGraph(view->activeFrameGraph());
auto rootEntity = new Qt3DCore::QEntity();
view->setRootEntity(rootEntity);
auto sphere1 = createSphereMesh();
sphere1->setParent(rootEntity);
auto trafo1 = new Qt3DCore::QTransform;
trafo1->setTranslation(QVector3D(20, 10, 0));
sphere1->addComponent(trafo1);
auto sphere2 = createSphereMesh();
sphere2->setParent(rootEntity);
auto trafo2 = new Qt3DCore::QTransform;
trafo2->setTranslation(QVector3D(20, -10, 0));
sphere2->addComponent(trafo2);
QObject::connect(view->camera()->lens(), &Qt3DRender::QCameraLens::viewSphere, [&](const QVector3D& center, float radius) {
qDebug() << "Bounding Sphere:" << center << radius;
auto boundingSphereEntity = new Qt3DCore::QEntity;
auto sphereMat = new Qt3DExtras::QDiffuseSpecularMaterial;
sphereMat->setAlphaBlendingEnabled(true);
sphereMat->setDiffuse(QColor(255,255,255,80));
auto mesh = new Qt3DExtras::QSphereMesh();
mesh->setRadius(radius);
boundingSphereEntity->addComponent(sphereMat);
boundingSphereEntity->addComponent(mesh);
auto trafoAll = new Qt3DCore::QTransform;
trafoAll->setTranslation(center);
boundingSphereEntity->addComponent(trafoAll);
boundingSphereEntity->addComponent(mesh);
boundingSphereEntity->addComponent(sphereMat);
boundingSphereEntity->setParent(rootEntity);
});
auto container = QWidget::createWindowContainer(view);
auto viewAllBtn = new QPushButton("View All");
QObject::connect(viewAllBtn, &QPushButton::clicked, [&]() {
view->camera()->viewAll();
});
QFrame frame;
frame.setFixedSize(500, 500);
frame.setLayout(new QVBoxLayout);
frame.layout()->addWidget(container);
frame.layout()->addWidget(viewAllBtn);
frame.show();
return a.exec();
}
I have two blue spheres with radius 0.5 being at distance 20 from each other. I'm expecting a bounding sphere with center (20,0,0) and radius (10.5).
Instead the program prints:
Bounding Sphere: QVector3D(12.1432, 2.14455, -1.32699e-08) 14.9644
It seems that the value 2.14455 comes really from nowhere and that my add bounding volume sphere is somewhat unpredictable.
If I will replace the translation QVector3D(20, 10, 0) by QVector3D(0,10,0) the result will be like expected.
How, can I exclude the root entity, from my bounding volume computation?

QLineSeries does not show up when working with numbers < 1e-14

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.

Qt Adjusting axes does not adjust the chart itself

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

How to make bullet points bigger in password mode for QLineEdit?

I have a QLineEdit to which I set the echoMode to QLineEdit::Password like this:
myLineEdit->setEchoMode(QLineEdit::Password);
The bullets are shown, but they are too small for the purpose of my application:
I need to make them bigger like this:
I have tried to increase the font size using a stylesheet like that:
myLineEdit->setStyleSheet("QLineEdit { font-size: 20px; }");
This indeed makes the bullets bigger, but the text gets bigger as well.
How to increase the size of bullet points preserving the size of the text?
You can set a unicode character that shows a larger circle through lineedit-password-character:
#include <QApplication>
#include <QFormLayout>
#include <QLineEdit>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
auto lay = new QFormLayout(&w);
QLineEdit *normal_le = new QLineEdit;
normal_le->setEchoMode(QLineEdit::Password);
normal_le->setText("Pass");
lay->addRow("Normal: ", normal_le);
for(int i: {9210, 9679, 9899, 11044}){
QLineEdit *le = new QLineEdit;
le->setEchoMode(QLineEdit::Password);
le->setText("Pass");
le->setStyleSheet(QString("QLineEdit[echoMode=\"2\"]{lineedit-password-character: %1}").arg(i));
lay->addRow(QString::number(i), le);
}
w.show();
return a.exec();
}