QSlider Iteration Misses Sample Data on QCustomPlot
Hi,
I use QSlider and QCustomPlot widgets together in the project. It works ok generally but when I iterate QSlider once ( single iteration ) after zooming on QCustomPlot a few times( range size decreases), some data missed and can not be examined in detail. Lets say previous x axis range (10:20:000 – 10:20:300), the next range after iteration becomes (10:20:460 - 10:20:760 ). So, 160 miliseconds could not be seen. In this context, my implementation is as follows;
Code:
connect(ui->horizontalScrollBar, SIGNAL(valueChanged(int)), this, SLOT(horzScrollBarChanged(int)));
connect(ui->plot->xAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(xAxisChanged(QCPRange)));
void MainWindow::horzScrollBarChanged(int value)
{
if (qAbs(ui->plot->xAxis->range().center()-value/100.0) > 0.01) // if user is dragging plot, we don't want to replot twice
{
ui->plot->xAxis->setRange(value/100.0, ui->plot->xAxis->range().size(), Qt::AlignCenter);
ui->plot->replot();
}
}
void MainWindow::xAxisChanged(QCPRange range)
{
ui->horizontalScrollBar->setValue(qRound(range.center()*100.0)); // adjust position of scroll bar slider
ui->horizontalScrollBar->setPageStep(qRound(range.size()*100.0)); // adjust size of scroll bar slider
}
Could you please help about this problem ? Thanks in advance for your replies..
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Quote:
Could you please help about this problem ? Thanks in advance for your replies..
You don't say what the range of your slider is, but you are probably running into round off error when you divide the integer slider "value" by 100. There is also a bug - if the axis range.center() - value / 100 is less than 0.01 at the end of the -last- slider move, then the range doesn't get updated to the final range. So your optimization to avoid repaints is tripping you up.
Try this instead. A trick to detect when the user is dragging the slider (and to avoid excessive repaints) that works no matter what the axis range is:
Create a QTimer as a member of your UI class that holds the slider. When the slider changes (your horzScrollBarChanged() slot), record the current position in a member variable, start or restart the timer with a 250 ms timeout, and then exit the slot. Connect the timeout() signal to another slot where you actually set the new axis range.
As long as the user keeps moving the slider, the timer won't time out (because it is continuously restarted with each move) and the plot won't be repainted. As soon as the slider stops moving for 250 ms, the timeout will trigger the repaint. You can do variations on this - repaint the window every 'n' moves or when the change has become 'large enough' so the plot stays alive. Depends on how fast the plot can repaint. In any case, you never have to check for the size of the change, and the last slider position is always processed.
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Hi,
Firstly I would like to thank your for your time. What do you mean by saying " exit the slot when the timer expired "? By the way, it is flickered for some time (in miliseconds ) until setting the new value but can be noticed with eye. Is there any way about removal of flickering ? Another question, axis range missing problem continues with this way of handling if I didn't applied in wrong way. May this problem ( missed range ) be related to sensitivity of numbers. I mean, if I write a new widget which overrides QSlider, with setValue( double ) and valueChanged(double).. Could you please also share your ideas about it ?
Code:
#ifndef CUSTOMSLIDER_H
#define CUSTOMSLIDER_H
#include <QApplication>
#include <QtGui>
#include <QVBoxLayout>
#include <QSlider>
#include <QLabel>
class CustomSlider
: public QSlider { Q_OBJECT
public:
}
signals:
void doubleValueChanged(double value);
public slots:
void setValue(double value)
{
qDebug()<<"Double Slider - setValue - value :" << value;
?? I suppose the position of slider should be updated according to the value. But I don't know how to do it. I could not find QSlider::setValue function implementation
emit doubleValueChanged(value);
}
void notifyValueChanged(double value) {
qDebug()<<"notifyValueChanged - value :" << value;
}
};
#endif // CUSTOMSLIDER_H
Thanks in advance for your reply,
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Rewriting QSlider is not going to fix your problem.
1. If your slider has a range of 0 - 100, then the best you can do is 1% precision of your full axis range. If you want to do better than that, then you have to use a larger slider range. A 0 - 1000 range with a step size of 1 will give you 0.1% precision (and you divide by 1000, not 100).
2. There is a logic error in your slider slot: If the difference is < .01 on the last slider move, the if statement will fail, and the new position will not be updated.
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Hi,
As you stated, I removed the check of "diff < 0,01" to handle the last slider correctly. Additionally, 0 - 1000 range setting just worked. But after 5 times zoom, when the range size decreases, data miss problem still exists with one iteration of QSlider.
I mean, when "the range size" is smaller than "the range which corresponds to one slider move", data miss problem occurs. And this statement emerges when 5-6 times zoom made. Do you have any suggestion to manage this situation ?
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Quote:
Do you have any suggestion to manage this situation ?
I think you need to look at the calculations you are doing. It sounds to me like you are dividing the unzoomed axis range by 100 or 1000 or whatever. If you do that, you will eventually reach the point you get to - the precision of the slider is less than the current zoom range, so there are areas that get skipped.
I don't really know how you want this zoom feature to behave so it is hard for me to suggest a solution.
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Hi again,
Lets say our record is 40 seconds.. QSlider range is 0-1000, and I set the range of slider at initialization, not dynamically. So in this case one move of slider is equal to 0,04 seconds. When a lot of zooms are made, the difference btw lower and upper range is smaller than one move size (0.04 seconds). That's why areas are skipped .. Isn't there any way of changing "area size of one qslider move" ? If the area corresponding to the one qslider move decreases, my problem will be resolved. Do you still think DoubleSlider widget is not a solution ?
Thanks in advance,
Kind Regards
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Quote:
So in this case one move of slider is equal to 0,04 seconds.
That's what I suspected - you are using the unzoomed (full) range of your data to set the slider increment.
Each time you zoom in (or out), you need to reset the mapping between slider value and range so that it matches the current zoomed-in range. So if the initial range is 0 - 40 seconds, and you zoom in by 2x around the center, then the new range is 10 - 30 seconds. The next step halves that again, to 15 - 25 seconds, and so on. You can probably change the slider range to something like 0 - 10 or 0 - 20 steps, so at most you can zoom in by 2^20 if each step zooms by 2x. If a 2x zoom for each step is too much, then use a smaller factor - 1.5 - but not less than 1.0 (which is no zoom at all).
Re: QSlider Iteration Misses Sample Data on QCustomPlot
As you realized, I should update the value of one move of QSlider dynamically when zoomed. As fas as I examined, “One QSlider Move = QCustomPlot Range Size/ QSlider Maximum Width"
For every zoom, I already update the range of QCustomPlot and value of QSlider but somehow "One QSlider Move" not updated. Is this a bug of QSlider ? I traced logs but after zoom operation, one move iteration width is the same with not zoomed one ( 0.04 seconds ). Do you know the way of updating one move iteration value dynamically ?
There is a setter of QSlider, setSingleStep(int). I thought I can set it dynamically when zoomed, but it takes only integer values..
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Quote:
Is this a bug of QSlider ?
No, it is a bug in your logic. You need to compute the zoom factor after each step, and use that new factor to scale the plot for the next step. You do not need to change the slider range, ever. What you need to change is how your code translates the current slider value and current plot range into a new width in seconds.
You probably should post your complete code for how you are doing your zoom operation, and explain in detail what should happen to the plot each time the user moves the slider one step.
Re: QSlider Iteration Misses Sample Data on QCustomPlot
For every zoom, xAxisChanged slot is triggered. So I set new range and values to increase the precision of QSlider, but it didn't work. When range size of QCustomPlot is smaller than one iteration width of QSlider, some areas get skipped.
Code:
AbstractMainWindow(parent),
ui(new Ui::View)
{
precision = 1000.0;
/* ui->horizontalScrollBar = QSlider*/
ui->horizontalScrollBar->setTracking(true);
ui->horizontalScrollBar->setVisible(false);
ui->horizontalScrollBar->setMinimum(0);
ui->horizontalScrollBar->setMaximum(precision);
}
void View::horzScrollBarChanged(int value)
{
double ratio = (double)value/(double)(ui->horizontalScrollBar->maximum());
double val = ratio*graphmaxValue;
qDebug()<<"horzScrollBarChanged value : " <<val << "range size:"<< plot->xAxis->range().size();
qDebug()<<"horzScrollBarChanged range : " << plot->xAxis->range();
plot->xAxis->setRange(val, plot->xAxis->range().size(), Qt::AlignCenter);
plot->replot();
}
void View::xAxisChanged(QCPRange range)
{
qDebug()<<"xAxisChanged : range " <<range << "range size:" <<range.size() << "range center:" << range.center();
int value = qRound((range.center()*precision)/graphmaxValue);
double currentRangeSize = range.size();
/* initialRangeSize is set when the plot is loaded firstly */
double ratio = currentRangeSize*1000/initialRangeSize;
int newSliderMaximum = (ratio)*1000;
ui->horizontalScrollBar->setMaximum(newSliderMaximum);
ui->horizontalScrollBar->adjustSize();
qDebug()<<"xAxisChanged horzScrollBar max: " << ui->horizontalScrollBar->maximum();
int newRangeValue = (value/1000.0)*(ui->horizontalScrollBar->maximum());
ui->horizontalScrollBar->setValue(newRangeValue);
}
Re: QSlider Iteration Misses Sample Data on QCustomPlot
Quote:
increase the precision of QSlider
You keep referring to QSlider, but your code indicates a horizontal scrollbar. Which is it?
Re: QSlider Iteration Misses Sample Data on QCustomPlot
It is QSlider, "ui->horizontalScrollBar" is an instance of QSlider..