PDA

View Full Version : How to pick line of QwtPlotMarker using QwtPlotPicker without affecting zooming



npatil15
4th January 2019, 11:13
Design:
Here I have design a graph where I can plot some signals like oscilloscope. Now I need two vertical and two horizontal lines on the graph which can move vertically and horizontally and also show cross points(symbol) when any signals get cross this lines.


PlotterWidget::PlotterWidget(QWidget *parent)
: QGraphicsView( parent ),
layout(new QVBoxLayout(this)), plot(new QwtPlot()), legend(new QwtLegend()),
m_zoomP(new PlotZoomer(QwtPlot::xBottom, QwtPlot::yLeft, plot->canvas()))
{
layout->setAlignment(plot->canvas(), Qt::AlignTop);
layout->setSizeConstraint(QLayout::SetDefaultConstraint);
plot->setSizePolicy(QSizePolicy::Policy::Minimum, QSizePolicy::Policy::Minimum);


layout->addWidget(plot);

plot->plotLayout()->setAlignCanvasToScales(true);//update on crossing the graph

m_zoomM[0] = new QwtPlotMagnifier(plot->canvas()); // ( void ) new QwtPlotMagnifier( canvas );
m_zoomM[0]->setWheelModifiers(Qt::NoModifier); //Scroll --> Both Axis

m_zoomM[1] = new QwtPlotMagnifier(plot->canvas());
m_zoomM[1]->setWheelModifiers(Qt::ControlModifier); //Ctrl + Scroll --> on X-axis
m_zoomM[1]->setAxisEnabled(Qt::XAxis,false);


QWidget *horizontalLineWidget = new QWidget(plot->canvas());
horizontalLineWidget->setFixedHeight(2);
horizontalLineWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
horizontalLineWidget->setStyleSheet(QString("background-color: #c0c0c0;"));

QwtPlotGrid *grid = new QwtPlotGrid();
grid->setPen(QPen(Qt::gray, 0.0, Qt::SolidLine));
grid->enableX(true);
grid->enableXMin(true);
grid->enableY(true);
grid->enableYMin(true);
grid->attach(plot);

legend->setDefaultItemMode(QwtLegendData::Checkable);
QwtSymbol *sym=new QwtSymbol(QwtSymbol::Diamond,QBrush(Qt::red),QPen( Qt::red),QSize(5,5));
d_marker1 = new QwtPlotMarker();
d_marker1->setSymbol(sym);
d_marker1->setLegendIconSize(QSize(20,30));
d_marker1->setLabel(QwtText("VLine1"));
d_marker1->setRenderHint(QwtPlotItem::RenderHint::RenderAntia liased);
d_marker1->setTitle(QwtText("VLine1"));
d_marker1->setLabelOrientation(Qt::Vertical);
d_marker1->setValue(333, 0);
d_marker1->setLineStyle(QwtPlotMarker::VLine);
d_marker1->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
d_marker1->setLinePen(QPen(Qt::blue, 1, Qt::DashDotLine));
d_marker1->setItemAttribute( QwtPlotItem::Legend, true);

d_marker1->attach(plot);

d_marker2 = new QwtPlotMarker();
d_marker2->setSymbol(sym);
d_marker2->setValue(0, 333);
d_marker2->setLabel(QwtText("HLine1"));
d_marker2->setLineStyle(QwtPlotMarker::HLine);
d_marker2->setTitle(QwtText("HLine1"));
d_marker2->setLabelAlignment(Qt::AlignRight | Qt::AlignBottom);
d_marker2->setLinePen(QPen(Qt::blue, 1, Qt::DashDotLine));
d_marker2->attach(plot);

d_picker = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
QwtPlotPicker::CrossRubberBand, QwtPicker::AlwaysOn,
plot->canvas());
d_picker1 = new QwtPlotPicker(QwtPlot::xBottom, QwtPlot::yLeft,
QwtPlotPicker::CrossRubberBand, QwtPicker::AlwaysOn,
plot->canvas());

d_picker->setStateMachine(new QwtPickerDragPointMachine);
d_picker1->setStateMachine(new QwtPickerClickPointMachine);

d_picker->setRubberBandPen(QColor(Qt::green));
d_picker->setTrackerPen(QColor(Qt::blue));
d_picker->setEnabled(true);

connect(d_picker1, SIGNAL(selected(const QPointF &)),
SLOT(selected(const QPointF &)));


plot->setFocusProxy(plot->canvas());
plot->setFocusPolicy(Qt::StrongFocus);
plot->setFocus();
plot->installEventFilter(this);


plot->setAutoReplot(true);
setScaleType("Auto");

plot->insertLegend(legend, QwtPlot::RightLegend );
plot->replot();

connect( legend, SIGNAL( checked( const QVariant &, bool, int ) ), SLOT( &Input::legendChecked( const QVariant &, bool ) ) );
}

And to pick the Line I have write below code


void PlotterWidget::selected(const QPointF &pos)
{
qDebug()<<"selected";
const QwtPlotItemList& items = plot->itemList();

QMetaObject MetaObject = this->staticMetaObject;
QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator ("MarkLine"));

for ( QwtPlotItemIterator i = items.begin(); i != items.end(); ++i )
{
if ( (*i)->rtti() == QwtPlotItem::Rtti_PlotMarker )
{
m_mark = static_cast<QwtPlotMarker*>(*i);
if(!m_mark->isVisible())
{
continue;
}
if (sqrt( pow( (m_mark->xValue() - pos.x()), 2 )) <= 5 || sqrt( pow( (m_mark->yValue() - pos.y()), 2 )) <=5)
{
setLineMarker(static_cast<MarkLine>(MetaEnum.keysToValue(m_mark->label().text().toLatin1())));
switch(getLineMarker())
{
case VLine1:
d_marker1->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
break;
case HLine1:
d_marker2->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
break;
};
connect(d_picker, SIGNAL(moved(const QPoint &)),
SLOT(moved(const QPoint &)));
}
}
}
}


Problem:
I can able to pick line on grid when its of size x(0-1000) and y(0-1000), but the problem is when I'm trying to zoom in/out too much then I cant able to pass this below IF statement properly, because the distance is too less to pick Line.

if (sqrt( pow( (m_mark->xValue() - pos.x()), 2 )) <= 5 || sqrt( pow( (m_mark->yValue() - pos.y()), 2 )) <=5)

Need solution how to pick this line properly without affecting zooming the graph ?
It is possible during Zooming, make QwtPlotMarker not get move along with the grid or not zoomed?
And also while zooming, QwtPlotMarker Line get hidden somewhere and difficult to find, so how to deal with this ?

I really get stuck in this, please show me the way.

Thanks a lot for reading this post.

npatil15
8th January 2019, 11:26
I have added event filter which identify the wheel up/down and based on that I have manage to increase the 5 using setting variable m_pickPoint in the event and it work for normal condition.

if (sqrt( pow( (m_mark->xValue() - pos.x()), 2 )) <= m_pickPoint || sqrt( pow( (m_mark->yValue() - pos.y()), 2 )) <=m_pickPoint )

So here I'm unable to understand at what factor I should increase or decrease the value m_pickPoint, so that the above logic work properly and pick the exact line.

case QEvent::Wheel:
QWheelEvent* we = static_cast<QWheelEvent*>(e);
if(we->angleDelta().y()>0)
{
m_pickPoint = m_pickPoint + m_pickPoint*0.05;
qDebug()<<"Position"<<m_pickPoint;
}
else
{
m_pickPoint = m_pickPoint - m_pickPoint*0.05;
qDebug()<<"Negative"<<m_pickPoint;
}

Please provide your solution, where as this above solution looks kind of dirty, so if their is some elegant solution then please suggest.

Uwe
8th January 2019, 11:46
You have to translate the coordinates of your marker and the mouse click into widget coordinates. See https://qwt.sourceforge.io/class_qwt_scale_map.html#a504f1627e0e02ddac03d2ce4 6507b865

Uwe

npatil15
9th January 2019, 04:54
Hi,

Thanks for the reply,

I have no trouble while catching/translating exact coordinates on the plot/widget. The problem is when I zoom in/out, the IF logic doen't work properly because I cant handle the m_pickPoint, which help me get the closet area to pick the QwtPlotMarker line. So looking for a solution how do I pick a line with such solution which doesn't affect on zoom in/out.

Or is their a easiest way, that I can create a line widget and simply do drag and drop event. I have tried the same but dont know how to handle it, because I just want to drag to vertically or horizontally only not other way.

Uwe
9th January 2019, 06:51
The zoomer installs an event filter for the plot canvas to handle the mouse events. In general it is possible to install as many events filters as you like - they are processed in reverse order of how they have been installed until one of them returns true.
So you can simply install your own event filter - or create a QwtPicker/QwtPlotPicker - after attaching the zoomer.

Uwe

npatil15
9th January 2019, 10:43
I have implement the eventfilter attached to zoomer which detect mouseevent and using this I can drag and drop the line on the graph.
Also based on the zoom level I have manipulate the QwtPlotPicker to pick the line properly. But the way I implement is not picking the line properly when the zoom level is too deep, so normally it works perfectly.

Uwe
9th January 2019, 15:59
I have implement the eventfilter attached to zoomer ...
I would create an extra QwtPlotPicker for this usecase, where you return false from it eventFilter, when you don't want to forward the event to the zoomer. But this is not necessary to solve your problem.


But the way I implement is not picking the line properly when the zoom level is too deep, so normally it works perfectly.
Here we are back to my previous hint. Translate point and marker position into widget coordinates - then you can use a fixed distance in pixels, regardless of the zoom level.

npatil15
10th January 2019, 04:47
I got your point, and sure this will be the solution if I translate point and marker into widget coordinates.
But as I'm new for qwt and not sure how to do that. Can you please give me some code snippets or any link which explains about it ?

Uwe
10th January 2019, 07:54
See https://qwt.sourceforge.io/class_qwt_plot_picker.html#a8da18fb8cdcc1c336e99d4 b69c338dfc

Uwe

npatil15
10th January 2019, 09:12
Somehow I have tried with the mention solution, which transform point and marker into widget coordinates. And its working fine.

if(sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_mark->xValue()) - m_plot->transform(QwtPlot::xBottom, pos.x())), 2 )) <= 5 || sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_mark->yValue()) - m_plot->transform(QwtPlot::xBottom, pos.y())), 2 )) <=5 )

Here I have two marker (in future it will be four). Before when I used m_mark->xValue() its always fixed until I drag to other position(regardless of zooming). And when I used m_plot->transform(QwtPlot::xBottom, m_mark->xValue()) its value get changes by simply zoom in/out which should not happen. How I can prevent it from changing its value on zooming ?

Added after 54 minutes:

Please have a look on below logic,


void OscilloscopeWidget::selected(const QPointF &pos)
{
const QwtPlotItemList& items = m_plot->itemList();

QMetaObject MetaObject = this->staticMetaObject;
QMetaEnum MetaEnum = MetaObject.enumerator(MetaObject.indexOfEnumerator ("MarkLine"));

//m_zoomP->setEnabled(false);
for ( QwtPlotItemIterator i = items.begin(); i != items.end(); ++i )
{
if ( (*i)->rtti() == QwtPlotItem::Rtti_PlotMarker )
{
QwtPlotMarker *m_mark = static_cast<QwtPlotMarker*>(*i);

qDebug()<<"Marker :"<<m_mark->label().text().toLatin1();
qDebug()<<"X Values :"<<m_plot->transform(QwtPlot::xBottom, m_mark->xValue())<<m_plot->transform(QwtPlot::xBottom, pos.x());
qDebug()<<"Y Values :"<<m_plot->transform(QwtPlot::xBottom, m_mark->yValue())<<m_plot->transform(QwtPlot::xBottom, pos.y());

if(!m_mark->isVisible())
{
continue;
}
if(sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_mark->xValue()) - m_plot->transform(QwtPlot::xBottom, pos.x())), 2 )) <= 5 || sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_mark->yValue()) - m_plot->transform(QwtPlot::xBottom, pos.y())), 2 )) <=5 )
{

setLineMarker(static_cast<MarkLine>(MetaEnum.keysToValue(m_mark->label().text().toLatin1())));
switch(getLineMarker())
{
case VLine1:
m_line[0]->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
break;
case HLine1:
m_line[1]->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
break;
}
connect(m_picker[0], SIGNAL(moved(const QPoint &)), SLOT(moved(const QPoint &)));
}
}
}
}

Check the output of qDebug() as below,
When I click on the plot/widget, it checks for all the QwtPlotMarker available in the widget. Currently I have two Marker, so the for loop checks for two times when I click ones and in the given output the 3rd and 5th line qDebug output is wrong and I dont know from where this values comes. Also this values are not fixed as on the zoom in/out it changes and cause conflicts on picking the line Marker. How I can prevent this value ?

unknown(0)[19800]: Marker : "VLine1"
unknown(0)[19800]: X Values : 138 155
unknown(0)[19800]: Y Values : 2 327.813
unknown(0)[19800]: Marker : "HLine1"
unknown(0)[19800]: X Values : 2 155
unknown(0)[19800]: Y Values : 15.26 327.813

Uwe
10th January 2019, 10:33
Before when I used m_mark->xValue() its always fixed until I drag to other position(regardless of zooming).
Sure, plot coordinates are not affected by zooming. And when moving a marker to a different plot coordinate, the plot coordinate changes - kind of obvious.


And when I used m_plot->transform(QwtPlot::xBottom, m_mark->xValue()) its value get changes by simply zoom in/out which should not happen. How I can prevent it from changing its value on zooming ?
Widget coordinates are calculated by mapping plot coordinates according to the scales. When changing the scales the mapping changes and you end up with different widget coordinates. That's how it is.

But what exactly is your question ?

Uwe

npatil15
10th January 2019, 11:00
Yes you right, after several debugging I found that what you said.
I have used different logic which solved my problem, I'll mention here for information.

if(sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_line[0]->xValue()) - m_plot->transform(QwtPlot::xBottom, pos.x())), 2 )) <= 5 )
{
qDebug()<<"Marker 1st IF :"<<m_mark->label().text().toLatin1();
setLineMarker(static_cast<MarkLine>(MetaEnum.keysToValue(m_line[0]->label().text().toLatin1())));
m_line[0]->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
connect(m_picker[0], SIGNAL(moved(const QPoint &)), SLOT(moved(const QPoint &)));
break;
}
else if(sqrt( pow( (m_plot->transform(QwtPlot::xBottom, m_line[1]->yValue()) - m_plot->transform(QwtPlot::xBottom, pos.y())), 2 )) <=5)
{
qDebug()<<"Marker 2nd IF :"<<m_mark->label().text().toLatin1();
setLineMarker(static_cast<MarkLine>(MetaEnum.keysToValue(m_line[1]->label().text().toLatin1())));
m_line[1]->setLinePen(QPen(Qt::blue, 3, Qt::SolidLine));
connect(m_picker[0], SIGNAL(moved(const QPoint &)), SLOT(moved(const QPoint &)));
break;
}
Here instead of using m_mark->xValue() I have used directly my defined marker position m_line[0]->xValue(). I may looking for elegant/dynamic solution but no problem.
The problem is with my logic I guess. So now its working fine with unlimited zooming action.

Now I have an one more query, as QwtPlotMarker's are stick to the scale(say line is at X-Position 50) and if I zoom out, the scale is now showing 10 - 40. Now my Line Marker is disappear as its position is 50. So how I can prevent the Line Marker from going outside of the widget ? i.e., how I can set boundary for Line Marker ?