Qwt仅重新绘制特定区域

时间:2018-02-26 16:33:22

标签: c++ qt qwt

我有一个包含许多QwtPlot的{​​{1}}视图,我想要突出显示最近点到鼠标位置的/ mignify(目前只是尝试更改颜色)(因为我会显示一些当用户按下鼠标按钮时,有关此测量点的信息,我希望他知道当前“目标”是什么点。)

所以我使用QwtPlotCurve获取鼠标位置,然后我设置一条额外的QwtPlotPicker曲线,用这个单点(“目标”)绘制其他颜色不同的颜色。

它有效,但我能做到这一点的唯一方法是调用QwtPlotCurve,每次移动鼠标时都会调用它(因为我可能会绘制数千个点)。

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

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

这是我的MCVE:

widget.h:

repaint

widget.cpp:

#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;
};

main.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;
    }
}

编辑:

如果我将#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 ); } }; 时显示一个新的“绘画”,但红点不会变得可见。现在,如果我移动另一个窗口(或按Alt),则会报告新的“PAINTING”,此时最近的点变为红色。正如我所提到的,该方法看起来不错,但还不足以让视图按预期重新绘制......

1 个答案:

答案 0 :(得分: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类,并进行一些微调,例如在select()上调用QEvent::MouseMove而不是QEvent::MouseButtonPress