C++ Qwt仅回复特定区域

C++ Qwt仅回复特定区域,c++,qt,qwt,C++,Qt,Qwt,我有一个QwtPlot视图,其中包含许多QwtPlotCurve,我想突出显示/显示距离鼠标位置最近的点(目前只是尝试更改颜色)(因为当用户按下鼠标按钮时,我会显示一些关于这个测量点的信息,我想让他知道什么点是当前的“目标”) 因此,我使用一个QwtPlotPicker获取鼠标位置,然后设置一条额外的QwtPlotCurve曲线,该曲线带有一个点(“目标”),以不同的颜色绘制在其他点之上 它可以工作,但我唯一能做到这一点的方法是调用QwtPlot::replot(),每次移动鼠标时都会调用它(因

我有一个
QwtPlot
视图,其中包含许多
QwtPlotCurve
,我想突出显示/显示距离鼠标位置最近的点(目前只是尝试更改颜色)(因为当用户按下鼠标按钮时,我会显示一些关于这个测量点的信息,我想让他知道什么点是当前的“目标”)

因此,我使用一个
QwtPlotPicker
获取鼠标位置,然后设置一条额外的
QwtPlotCurve
曲线,该曲线带有一个点(“目标”),以不同的颜色绘制在其他点之上

它可以工作,但我唯一能做到这一点的方法是调用
QwtPlot::replot()
,每次移动鼠标时都会调用它(因为我可能会绘制数千个点)

我只想重新绘制先前高亮显示点所在的区域(恢复默认显示),然后只重新绘制新高亮显示点所在的区域。但当我这样做时(调用
重新绘制(QRect)
而不是
replot()
),什么也不会发生(没有点高亮显示)但是,如果我停用该窗口,我会看到该点突出显示,因此它看起来像是
repaint
做了一些工作,但不足以让最终用户看到它

请注意,我禁用了Qwt备份存储功能

这是我的MCVE:

widget.h:

然后,当调用每个
canvas->repaint
时,我看到控制台显示一个新的“绘制”,但红点不可见。现在,如果我在我的窗口上移动另一个窗口(或按Alt键),一个新的“绘制”已报告,这一次最近的点变为红色。因此,正如我所提到的,该方法看起来不错,但不足以按预期重新绘制视图…

您应该使用它,它的设计完全符合您的要求:

QwtPlotDirectPainter提供了绘制子集(f.e all)的API 添加点),而无需擦除/重新绘制绘图画布

您可以在Qwt的“”示例中使用它:

// 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
}

在您的情况下,我相信您可以直接使用
CanvasPicker
类,只需进行一些微调,比如调用
select()
关于
QEvent::MouseMove
而不是
QEvent::MouseButtonPress

这看起来很有趣。为了获得+50的声誉,请您提供一个答案,更新我的MCVE并努力证明它完全有效吗?嗨,本杰明,我尝试在我的示例中使用
QwtPlotDirectPainter
。我可以很容易地更改一个点或者,当鼠标靠近它时,使用
QwtPlotDirectPainter
而不调用
replot
,从黑色变为红色。很好。但我不知道当鼠标靠近另一个点时,如果不重新绘制整个
QwtPlot
,或者至少整个
QwtPlotCurve。我觉得这意味着我的代码会发生深刻的变化,而它显然几乎可以正常工作:正如我所说的“如果我停用窗口,我会看到该点突出显示…”@jpo38我在我的回答中添加了一些关于如何“取消选择”一点的细节。我不会费心更新你的MCVE,因为我的回答已经有了自己的MCVE(即事件过滤器)“Qwt中的示例)。问题是,这只会隐藏/显示给定曲线的给定绘图。但是如果绘图区域中有许多曲线正在显示,并且其中一些曲线的某些绘图处于“后面”被显示/隐藏的所选项目,我怀疑它们会在被绘制/未绘制时被重新绘制。这就是为什么我认为正确的方法是要求使给定的QRect无效。否则,我需要找到修改后的曲线周围需要重新绘制的所有曲线序列的所有部分……我刚刚测试过,chang在事件过滤器示例中编辑了曲线的位置。我现在有了与蓝色曲线重叠的品红曲线。我确认,如果我选择/取消选择品红点,蓝色曲线重叠项不会按预期重新绘制。。。
#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;
    }
}
#include <QApplication>
#include "widget.h"
int main( int argc, char* argv[] )
{
    QApplication app( argc, argv );
    Dialog dlg;

    dlg.show();

    return app.exec();
}
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 );
    }
};
// 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
}
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.
    }
}