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

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.

Related

Split window Qt

I have Qt C++ related question.
I need window divided in 4 parts. In 3 of them should be QTableWidgets (red, yellow and green areas on the example picture), but they must be synchronized in pairs (red and yellow, red and green - the lines of one continue the lines of the other) so when you scroll red QTableWidget horizontally - it at the same time scrolls horizontally green QTableWidget (and vice versa), and you scroll red QTableWidget vertically - it at the same time scrolls vertically yellow QTableWidget (and vice versa).
Additionally I need to have a possibility to adjust area sizes dragging borders (grey arrows on the picture). So when I drag each border - it changes size of 2 corresponding QTableWidgets.
I would be glad to any idea of how to realize that. Thank in advance!
example
Minimal example to reproduce your image:
#include <QApplication>
#include <QTableWidget>
#include <QSplitter>
#include <QHBoxLayout>
#include <QScrollBar>
#include <QHeaderView>
#include <QTimer>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//////////////////////////////////////////////////////////////////////////
/// Things you can do in Qt Designer
QWidget mainW;
QSplitter *hSplitter = new QSplitter(Qt::Horizontal,&mainW);
QSplitter *vSplitter1 = new QSplitter(Qt::Vertical,&mainW);
QSplitter *vSplitter2 = new QSplitter(Qt::Vertical,&mainW);
QTableWidget *tableRed = new QTableWidget(6,6,&mainW);
tableRed->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableRed->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableRed->verticalHeader()->hide();
tableRed->horizontalHeader()->hide();
QTableWidget *tableGreen = new QTableWidget(1,6,&mainW);
tableGreen->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableGreen->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableGreen->verticalHeader()->hide();
tableGreen->horizontalHeader()->hide();
QTableWidget *tableYellow = new QTableWidget(6,2,&mainW);
tableYellow->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableYellow->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
tableYellow->verticalHeader()->hide();
tableYellow->horizontalHeader()->hide();
QWidget *topLeft = new QWidget(&mainW);
vSplitter1->addWidget(topLeft);
vSplitter1->addWidget(tableYellow);
vSplitter2->addWidget(tableGreen);
vSplitter2->addWidget(tableRed);
hSplitter->addWidget(vSplitter1);
hSplitter->addWidget(vSplitter2);
QHBoxLayout *mainLay = new QHBoxLayout(&mainW);
mainLay->addWidget(hSplitter);
//////////////////////////////////////////////////////////////////////////
QTimer::singleShot(0,vSplitter1,[vSplitter2,vSplitter1](){vSplitter1->setSizes(vSplitter2->sizes());});
QObject::connect(vSplitter1,&QSplitter::splitterMoved,vSplitter2,[vSplitter2,vSplitter1](){vSplitter2->setSizes(vSplitter1->sizes());});
QObject::connect(vSplitter2,&QSplitter::splitterMoved,vSplitter1,[vSplitter2,vSplitter1](){vSplitter1->setSizes(vSplitter2->sizes());});
QObject::connect(tableRed->verticalScrollBar(),&QScrollBar::valueChanged,tableYellow->verticalScrollBar(),&QScrollBar::setValue);
QObject::connect(tableYellow->verticalScrollBar(),&QScrollBar::valueChanged,tableRed->verticalScrollBar(),&QScrollBar::setValue);
QObject::connect(tableRed->horizontalScrollBar(),&QScrollBar::valueChanged,tableGreen->horizontalScrollBar(),&QScrollBar::setValue);
QObject::connect(tableGreen->horizontalScrollBar(),&QScrollBar::valueChanged,tableRed->horizontalScrollBar(),&QScrollBar::setValue);
mainW.show();
return a.exec();
}

Per axis grid properties (color), how it works?

From this source code:
#include "mainwindow.h"
#include <Q3DScatter>
using namespace QtDataVisualization;
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
Q3DScatter *graph = new Q3DScatter;
QWidget *widget = QWidget::createWindowContainer(graph);
setCentralWidget(widget);
}
MainWindow::~MainWindow() {}
I get:
See that wall directly in front, its grayed (can I describe it this way?) if compared to the other walls. How can I tweak grid lines that way? I mean just one wall.
Basically, you can change the color of the grid-line with the following code.
#include <QApplication>
#include <QtDataVisualization/Q3DScatter>
#include <QtDataVisualization/Q3DLight>
#include <QtDataVisualization/Q3DTheme>
#include <QDebug>
#include <QTimer>
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
auto scatter = new QtDataVisualization::Q3DScatter;
auto widget=QWidget::createWindowContainer(scatter);
widget->show();
scatter->activeTheme()->setType(QtDataVisualization::Q3DTheme::ThemeQt);
scatter->activeTheme()->setGridLineColor(QColor("red"));
auto light = scatter->scene()->activeLight();
auto camera = scatter->scene()->activeCamera();
int counter = 0;
QTimer timer;
timer.start(1 / 60);
QObject::connect(&timer, &QTimer::timeout, [&]() {
camera->setXRotation(counter++/60);
});
return a.exec();
}
Still, there seems to be no effect on how the grid-lines are rendered. I think this is do to the lighting of the 3D scene and of course of the camera position.
My small example program rotates the camera of the scene and therefore also the lighting of the grid-lines changes.
I think, there is little you can do here, as the interfaces doesn't allow you to change the shader code of the grid-lines.
Solution might be to add a custom 3d item, set a mesh and texture color. Resize the item to look like a grid axis and set the position. Repeat the idea for as many axis lines it is required.
Wondering if there is a (better) alternative approach.

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 C++ : Moving several labels at the same time

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:

Qt widget (with layout) space. What is it? How to remove?

I have written a program that uses widgets as container (for other widgets). Because the contents of the container varies along the program life, the container widget has a layout associated to it so it resizes properly.
The problem is that the container seems to consume some space.
In the following program, I have reproduced the problem: I have a group with a few labels, where one of them is included in a container (the widget w -and its layout t- includes the label "what is that extra space?").
My goal is to get the spacing between all labels the same, regardless whether they are in containers or not. (the container should not consume space)
I have also tried to color the different parts of the widgets. Where is my padding? What is the extra space between the widgets (between the blue). And how do I remove it?
#include <QApplication>
#include <QtCore>
#include <QMainWindow>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QLabel>
#include <QMdiArea>
#include <QMdiSubWindow>
#include <stdlib.h>
QMdiArea* g1;
QGroupBox* g1a;
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QMainWindow* main_window = new(QMainWindow);
main_window->resize(200, 200);
main_window->setWindowTitle("Hello");
g1a = new QGroupBox("G1A", g1);
QVBoxLayout *g1a_l = new QVBoxLayout(g1a);
g1a_l->setSpacing(0);
main_window->setCentralWidget(g1a);
g1a_l->addWidget((QLabel*)new QLabel(" Nice Label1"));
g1a_l->addWidget((QLabel*)new QLabel(" Nice Label2"));
QWidget* w=new QWidget(0);
w->setStyleSheet( "border: 2 solid blue; padding: 2 solid yellow;" );
QVBoxLayout* t=new QVBoxLayout(w);
t->setSpacing(0);
t->addWidget(new QLabel("What is that extra space??",w));
g1a_l->addWidget(w);
g1a_l->addWidget((QLabel*)new QLabel(" Nice Label3"));
g1a_l->addWidget((QLabel*)new QLabel(" Nice Label4"));
//sub_window->adjustSize();
main_window->show(); //How to I get that to recaclulate the size of its contents?
return app.exec();
}
This is contentsMargin
To remove it:
t->setContentsMargins(0,0,0,0);
The space you referring to is a content margin. The docu here describes how the widgets are drawn. To get rid of that extra space you have to call:
widget->setContentsMargins(0, 0, 0, 0);
I believe the end result should look like this:
The method I recall to control spacing is to add dedicated QSpacerItem items between the widgets.
But I'm first trying to figure out what w is doing. Why not call g1a_l->addLayout(t); directly?