Qwt 仅重新绘制特定区域
Posted
技术标签:
【中文标题】Qwt 仅重新绘制特定区域【英文标题】:Qwt replot only a specific area 【发布时间】:2018-02-26 16:33:22 【问题描述】:我有一个包含许多 QwtPlotCurve
的 QwtPlot
视图,我想突出显示/放大(目前只是尝试更改颜色)离鼠标位置最近的点(因为我将显示一些关于这一点的信息用户按下鼠标按钮时的测量值,我想让他知道当前的“目标”是什么点)。
所以我使用QwtPlotPicker
来获取鼠标位置,然后我设置了一个额外的QwtPlotCurve
曲线,这个单点(“目标”)在其他点之上用不同的颜色绘制。
它可以工作,但我可以完成这项工作的唯一方法是调用QwtPlot::replot()
,每次移动鼠标时都要调用它(因为我可能要绘制数千个点)。
我只想重绘之前高亮点所在的区域(以恢复默认显示),然后只重绘新高亮点所在的区域。但是当我这样做时,这个(调用repaint(QRect)
而不是replot()
),什么也没有发生(没有突出显示点),但是,如果我停用窗口,我看到该点被突出显示,所以看起来repaint
确实一些工作,但不足以让最终用户看到它......
请注意,我禁用了 Qwt 后备存储功能。
这是我的 MCVE:
widget.h:
#include <QDialog>
class QLabel;
class QwtPlotCurve;
class QwtPlot;
class Dialog : public QDialog
Q_OBJECT
public:
Dialog();
public slots:
void onHovered( const QPointF& pt );
private:
std::vector<QwtPlotCurve*> curves;
QwtPlotCurve* highlight;
std::tuple<QwtPlotCurve*,int,QRect> highlighted;
QLabel* closestLabel;
QwtPlot* plot;
;
widget.cpp:
#include "widget.h"
#include <QVBoxLayout>
#include <QLabel>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_picker.h>
#include <qwt_plot_canvas.h>
#include <qwt_picker_machine.h>
#include <sstream>
Dialog::Dialog()
setLayout( new QVBoxLayout() );
plot = new QwtPlot(this);
layout()->addWidget( plot );
layout()->addWidget( closestLabel = new QLabel( this ) );
for ( int i = 0; i != 5; ++i )
QwtPlotCurve* curve = new QwtPlotCurve();
QVector<double> x, y;
for ( int i = 0; i != 10; ++i )
x.push_back( std::rand() );
y.push_back( std::rand() );
curve->setSamples( x, y );
curve->setStyle( QwtPlotCurve::Dots );
curve->setPen( Qt::black, 5 );
curve->attach(plot);
curves.push_back( curve );
highlight = new QwtPlotCurve();
highlight->setSamples( , );
highlight->setStyle( QwtPlotCurve::Dots );
highlight->setPen( Qt::red, 5 );
highlight->attach(plot);
QwtPlotCanvas* canvas = dynamic_cast<QwtPlotCanvas*>( plot->canvas() );
if ( canvas )
canvas->setPaintAttribute( QwtPlotCanvas::BackingStore, false );
plot->replot();
QwtPlotPicker* picker = new QwtPlotPicker( plot->canvas() );
picker->setStateMachine(new QwtPickerTrackerMachine());
connect(picker, SIGNAL(moved(const QPointF&)), this, SLOT(onHovered(const QPointF&)));
// inspired from QwtPlotCurve::closestPoint
int closestPoint( QwtPlotCurve& curve, const QPoint &pos, double *dist )
const size_t numSamples = curve.dataSize();
if ( curve.plot() == NULL || numSamples <= 0 )
return -1;
const QwtSeriesData<QPointF> *series = curve.data();
const QwtScaleMap xMap = curve.plot()->canvasMap( curve.xAxis() );
const QwtScaleMap yMap = curve.plot()->canvasMap( curve.yAxis() );
const double xPos = xMap.transform( pos.x() );
const double yPos = yMap.transform( pos.y() );
int index = -1;
double dmin = DBL_MAX;
for ( uint i = 0; i < numSamples; i++ )
const QPointF sample = series->sample( i );
const double cx = xMap.transform( sample.x() ) - xPos;
const double cy = yMap.transform( sample.y() ) - yPos;
const double dist = sqrt( pow(cx,2) + pow(cy,2) );
if ( dist < dmin )
index = i;
dmin = dist;
if ( dist )
*dist = dmin;
return index;
void Dialog::onHovered( const QPointF& pt )
// mouse moved!
QwtPlotCurve* closest = NULL;
int closestIndex = -1;
double minDist = DBL_MAX;
for ( auto curve : curves )
double dist;
int index = closestPoint( *curve, pt.toPoint(), &dist );
if ( dist < minDist )
minDist = dist;
closestIndex = index;
closest = curve;
if ( !closest )
return;
std::stringstream str;
QPointF closestPoint = closest->sample(closestIndex);
str << "Closest point is " << closestPoint.rx() << "," << closestPoint.ry();
closestLabel->setText( str.str().c_str() );
if ( std::get<0>( highlighted ) == closest &&
std::get<1>( highlighted ) == closestIndex )
// highlighted point is unchanged
return;
else
// highlighted point changed
const QwtScaleMap xMap = plot->canvasMap( QwtPlot::xBottom );
const QwtScaleMap yMap = plot->canvasMap( QwtPlot::yLeft );
const int rectSize = highlight->pen().width() * 2;
const int x = xMap.transform( closestPoint.rx() );
const int y = xMap.transform( closestPoint.ry() );
const QRect cr = plot->canvas()->contentsRect();
highlight->setSamples( closestPoint.rx() , closestPoint.ry() );
QRect smallCR( x - rectSize/2, y - rectSize/2, rectSize, rectSize );
std::tuple<QwtPlotCurve*,int,QRect> newHighlighted closest, closestIndex, smallCR ;
QwtPlotCanvas* canvas = dynamic_cast<QwtPlotCanvas*>( plot->canvas() );
if ( canvas )
if ( std::get<2>( highlighted ) != QRect() )
// repaint previously highlighted area:
canvas->repaint( std::get<2>( highlighted ) );
// repaint newly highlighted area:
canvas->repaint( std::get<2>( newHighlighted ) );
// if you replace lines above by this one, it works!
//canvas->replot();
highlighted = newHighlighted;
main.cpp:
#include <QApplication>
#include "widget.h"
int main( int argc, char* argv[] )
QApplication app( argc, argv );
Dialog dlg;
dlg.show();
return app.exec();
编辑:
如果我将highlight = new QwtPlotCurve();
替换为highlight = new MyCurve();
与MyCurve
定义为:
class MyCurve : public QwtPlotCurve
public:
void drawSeries( QPainter *painter,
const QwtScaleMap &xMap, const QwtScaleMap &yMap,
const QRectF &canvasRect, int from, int to ) const override
static int i = 0;
if ( dataSize() != 0 )
std::cout << "PAINTING " << i++ << std::endl;
QwtPlotCurve::drawSeries( painter, xMap, yMap, canvasRect, from, to );
;
然后我看到控制台在调用每个canvas->repaint
时显示一个新的“PAINTING”,但是红点不可见。现在,如果我将另一个窗口移到我的上方(或按 Alt),则会报告一个新的“PAINTING”,这次最近的点变为红色。正如我所提到的,该方法看起来不错,但不足以让视图按预期重新绘制......
【问题讨论】:
【参考方案1】:您应该使用QwtPlotDirectPainter
,它旨在完全按照您的意愿行事:
QwtPlotDirectPainter 提供了一个 API 来绘制子集(f.e all 添加点)而不擦除/重新绘制绘图画布。
您可以在 Qwt 的“event_filter”示例中使用它:
// Hightlight the selected point
void CanvasPicker::showCursor( bool showIt )
if ( !d_selectedCurve )
return;
QwtSymbol *symbol = const_cast<QwtSymbol *>( d_selectedCurve->symbol() );
const QBrush brush = symbol->brush();
if ( showIt )
symbol->setBrush( symbol->brush().color().dark( 180 ) );
QwtPlotDirectPainter directPainter;
directPainter.drawSeries( d_selectedCurve, d_selectedPoint, d_selectedPoint );
if ( showIt )
symbol->setBrush( brush ); // reset brush
根据showIt
参数,此函数将把点绘制为“选定”或以其原始/未选定样式重新绘制。
你可以在select()
函数中看到它是如何使用的:
void CanvasPicker::select( const QPoint &pos )
[...]
showCursor( false ); // Mark the previously selected point as deselected
d_selectedCurve = NULL;
d_selectedPoint = -1;
if ( curve && dist < 10 ) // 10 pixels tolerance
d_selectedCurve = curve;
d_selectedPoint = index;
showCursor( true ); // Mark the new point as selected.
在您的情况下,我相信您可以直接使用 CanvasPicker
类并进行一些微调,例如在 QEvent::MouseMove
上调用 select()
而不是 QEvent::MouseButtonPress
。
【讨论】:
这看起来很有趣。对于 +50 的声誉,您能否提供一个答案,并更新我的 MCVE 并努力证明这完全有效? 嗨本杰明,我尝试在我的示例中使用QwtPlotDirectPainter
。当鼠标靠近它时使用QwtPlotDirectPainter
而不是调用replot
,我可以轻松地将点颜色从黑色更改为红色。美好的。但是当鼠标靠近另一个点而不重新绘制整个QwtPlot
或至少整个QwtPlotCurve
时,我看不到如何将点颜色从红色变为黑色。我觉得这意味着我的代码将发生深刻的变化,而它显然几乎可以工作:正如我所说的“如果我停用窗口,我会看到这一点被突出显示......”
@jpo38 我在回答中添加了一些关于如何“取消选择”点的详细信息。我不会费心更新你的 MCVE,因为我的答案已经有了它自己的 MCVE(即来自 Qwt 的 event_filter" 示例)。
问题是这只会隐藏/显示给定曲线的给定图。但是,如果绘图区域显示了许多曲线,并且其中一些在显示/隐藏所选项目的“后面”有一些绘图,我怀疑当所选项目将被绘制/未绘制时它们会被重新绘制。这就是为什么我认为正确的方法是要求使给定的 QRect 无效。否则,我需要找到修改后的所有曲线的所有系列,这些曲线需要重新绘制....
我刚刚测试过,在 event_filter 例子中改变了曲线的位置。我现在有与蓝色曲线重叠的洋红色曲线。我确认如果我选择/取消选择一个洋红色点,蓝色曲线重叠项目不会按预期重新绘制......以上是关于Qwt 仅重新绘制特定区域的主要内容,如果未能解决你的问题,请参考以下文章