QwtPlotMarker drehen

Dein Thema passt einfach in kein Forum? Dann probiers mal hier.
Antworten
Gora
Beiträge: 10
Registriert: 24. Mai 2013 16:16

QwtPlotMarker drehen

Beitrag von Gora »

Hi Leute,

ich habe ein kleines Problem mit dem QwtPlotMarker. Ich möchte in einem Plot entlang der gezeichneten Kurven Marker anbringen, und zwar so dass diese entlang der Kurve verlaufen. Das heißt die Marker müssen um einen bestimmten Winkel gedreht werden. Wenn ich aus der Doku das ganze richtig verstanden habe, ist aber nur eine Drehung um 90° erlaubt. Ich habe Google etwas bemüht. Hier wird der Vorschlag gemach über den qpainter die Funktion rotate() aufzurufen.

Ich benutze Qpainter zum ersten mal, habe fürs erste ein beispiel mit paintEvent() mit QPainter aufgebaut die einfachen Sachen funktionier auch soweit auch die Drehung. Ich steige aber nicht durch, wie ich das ganze mit QwtPlotMarker in Verbindung bringen kann. Die Funktion QwtPlotMarker::draw(QPainter*, .....) könnte hier vieleicht hilfreich sein.

ich hoffe einer kann mir hier helfen.
Uwe
Beiträge: 176
Registriert: 9. Oktober 2005 13:37
Wohnort: München

Re: QwtPlotMarker drehen

Beitrag von Uwe »

Woraus bestehen denn Deine Marker: Text, Symbol oder beides ?
Sind die Positionen der Marker identisch zu den Punkten der Kurve ?

Uwe
Gora
Beiträge: 10
Registriert: 24. Mai 2013 16:16

Re: QwtPlotMarker drehen

Beitrag von Gora »

Hallo Uwe

die marker bestehen nur aus Text. Ja die Punkte sind indentisch, das heißt ich nehme tatsächlich bestimmte Koordinaten der Kurven und weise diese den Koordinaten der Marker zu.
Uwe
Beiträge: 176
Registriert: 9. Oktober 2005 13:37
Wohnort: München

Re: QwtPlotMarker drehen

Beitrag von Uwe »

Dann würde ich eher QwtPlotCurve aufbohren, weil Dir hier die Steigung der Linien zur Vefügung stehen um Position und Drehwinkel entsprechend anzupassen.

Also:

Code: Alles auswählen

class YourCurve: public QwtPlotCurve
{
   ...
   virtual void drawCurve( QPainter *painter, int style,
        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
        const QRectF &canvasRect, int from, int to ) const
   {
        QwtPlotCurve::drawCurve( painter, style, xMap, yMap, canvasRect, from, to );
        drawTextLabels( painter, xMap, yMap, canvasRect, from to );
   }
};
Und dann irgendwie so:

Code: Alles auswählen

void YourCurve::drawTextLabels(  QPainter *painter,
        const QwtScaleMap &xMap, const QwtScaleMap &yMap,
        const QRectF &canvasRect, int from, int to ) const
{
    for ( int i = from; i <= to; i++ )
    {
         const QPointF sample = d_series->sample( i );
     
         // widget coordinates    
         double x = xMap.transform( sample.x() );
         double y = yMap.transform( sample.y() );

         if ( canvasRect.contains( QPointF( x, y ) )
         {
             // Abstand und Rotation in Abhängigkeit vom Kurvenverlauf
             x+ = ...;
             y += ...;
             double rotation = ...

             painter.save();
             painter.translate( x, y );
             painter.rotate( rotation );
             painter.drawText( ... );
             painter.restore();
        }
    }
}
Solange Du nicht richtig viele Labels hast, sollte die Implementierung schnell genug sein.

HTH,
Uwe
Gora
Beiträge: 10
Registriert: 24. Mai 2013 16:16

Re: QwtPlotMarker drehen

Beitrag von Gora »

Hallo Uwe

danke für den Vorschlag. Ich habe diesen übers Wochenende ausprobiert und er funktioniert soweit. Aber trotzdem nochmal die Frage könnte man es ähnlicher weise mit dem QWTPlotMarker anstellen. Ich habe folgendes ausprobiert

Code: Alles auswählen

class MyQwtMarker:public QwtPlotMarker
{

public:
    virtual void drawLabel( QPainter *painter, const QRectF & canvasRect, const QPointF &pos ) const
    {
        QwtPlotMarker::drawLabel(painter, canvasRect, pos);
        drawTextLabels(painter, canvasRect, pos);
    }
    void drawTextLabels(QPainter *painter, const QRectF & canvasRect, const QPointF &pos) const;
};

void MyQwtMarker::drawTextLabels2(QPainter *painter, const QRectF & canvasRect, const QPointF &pos) const
{
    double rotation = 45;
    painter->save();
    painter->rotate( rotation );
    painter->restore();
}
Wenn ich das so umsetze dann krieg ich den folgenden Fehler:
Fehler:LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall MyQwtMarker::drawTextLabels(class QPainter *,class QRectF const &,class QPointF const &)const " (?drawTextLabels2@MyQwtMarker@@QBEXPAVQPainter@@ABVQRectF@@ABVQPointF@@@Z)" in Funktion ""public: virtual void __thiscall MyQwtMarker::drawLabel(class QPainter *,class QRectF const &,class QPointF const &)const " (?drawLabel@MyQwtMarker@@UBEXPAVQPainter@@ABVQRectF@@ABVQPointF@@@Z)".

obwohl es nach deinem Bespiel funktioniert.

Gora
Gora
Beiträge: 10
Registriert: 24. Mai 2013 16:16

Re: QwtPlotMarker drehen

Beitrag von Gora »

Gora hat geschrieben:Hallo Uwe

danke für den Vorschlag. Ich habe diesen übers Wochenende ausprobiert und er funktioniert soweit. Aber trotzdem nochmal die Frage könnte man es ähnlicher weise mit dem QWTPlotMarker anstellen. Ich habe folgendes ausprobiert

Code: Alles auswählen

class MyQwtMarker:public QwtPlotMarker
{

public:
    virtual void drawLabel( QPainter *painter, const QRectF & canvasRect, const QPointF &pos ) const
    {
        QwtPlotMarker::drawLabel(painter, canvasRect, pos);
        drawTextLabels(painter, canvasRect, pos);
    }
    void drawTextLabels(QPainter *painter, const QRectF & canvasRect, const QPointF &pos) const;
};

void MyQwtMarker::drawTextLabels2(QPainter *painter, const QRectF & canvasRect, const QPointF &pos) const
{
    double rotation = 45;
    painter->save();
    painter->rotate( rotation );
    painter->restore();
}
Wenn ich das so umsetze dann krieg ich den folgenden Fehler:
Fehler:LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""public: void __thiscall MyQwtMarker::drawTextLabels(class QPainter *,class QRectF const &,class QPointF const &)const " (?drawTextLabels2@MyQwtMarker@@QBEXPAVQPainter@@ABVQRectF@@ABVQPointF@@@Z)" in Funktion ""public: virtual void __thiscall MyQwtMarker::drawLabel(class QPainter *,class QRectF const &,class QPointF const &)const " (?drawLabel@MyQwtMarker@@UBEXPAVQPainter@@ABVQRectF@@ABVQPointF@@@Z)".

obwohl es nach deinem Bespiel funktioniert.

Gora
Habe das Problem mit dem Fehler gelöst war anscheinend nur ein Compilerfehler, aber die Drehung ist nicht da.
Uwe
Beiträge: 176
Registriert: 9. Oktober 2005 13:37
Wohnort: München

Re: QwtPlotMarker drehen

Beitrag von Uwe »

Dein Code zeichnet ja auch gar nichts - kann also gar nicht funktionieren.

Stattdessen musst Du QwtPlotMarker::drawLabel überladen. Nachdem die Implementierung für Labels, die an vertikale Linien angepasst sind, ohnehin Code für eine Drehung um 90° enthält, solltest Du das leicht ableiten können.

Zum Verständnis: die Rotation geht immer um den Punkt ( 0, 0 ). Daher musst Du immer erst eine Translation auf den Punkt um den Du drehen willst aufsetzen und nachdem Du den Drehwinkel festgelegt hast auf Position ( 0, 0 ) zeichnen.

Uwe
Gora
Beiträge: 10
Registriert: 24. Mai 2013 16:16

Re: QwtPlotMarker drehen

Beitrag von Gora »

Hallo Uwe

danke für den Tipp. Daran hatte ich auch schon gedacht, habe an der stelle allerdings nicht genau gewusst wie ich es mit dem privaten Member d_data umgehen soll. Habe jetzt folgendes Ausgearbeitet:

Code: Alles auswählen

#ifndef MYMARKER_H
#define MYMARKER_H
#include "qwt_plot_marker.h"

class MyMarker:public QwtPlotMarker
{
public:
    MyMarker();
    virtual void drawLabel( QPainter *painter, const QRectF & canvasRect, const QPointF &pos ) const;
    void setAngle(double );
private:
    class MyPrivateData;
    MyPrivateData *d_data;
    double angle;
};
#endif // MYMARKER_H

Code: Alles auswählen

#include "mymarker.h"
#include "qwt_symbol.h"
MyMarker::MyMarker()
{
    angle=0.0;
}

class MyMarker::MyPrivateData
{
public:
    MyPrivateData():
        labelAlignment( Qt::AlignCenter ),
        labelOrientation( Qt::Horizontal ),
        spacing( 2 ),
        symbol( NULL ),
        style( QwtPlotMarker::NoLine ),
        xValue( 0.0 ),
        yValue( 0.0 )
    {
    }

    ~MyPrivateData()
    {
        delete symbol;
    }

    QwtText label;
    Qt::Alignment labelAlignment;
    Qt::Orientation labelOrientation;
    int spacing;

    QPen pen;
    const QwtSymbol *symbol;
    LineStyle style;

    double xValue;
    double yValue;
};

void MyMarker::drawLabel( QPainter *painter, const QRectF & canvasRect, const QPointF &pos ) const
{
    MyPrivateData * myData=new MyPrivateData;
    myData->label=label();

    myData->labelAlignment=labelAlignment();
    myData->labelOrientation=labelOrientation();
    myData->spacing=spacing();

    myData->pen=linePen();
    myData->symbol=symbol();
    myData->style=lineStyle();

    myData->xValue=xValue();
    myData->yValue=yValue();

    if ( myData->label.isEmpty() )
        return;

    Qt::Alignment align = myData->labelAlignment;
    QPointF alignPos = pos;

    QSizeF symbolOff( 0, 0 );

    switch ( myData->style )
    {
    case QwtPlotMarker::VLine:
    {
        // In VLine-style the y-position is pointless and
        // the alignment flags are relative to the canvas

        if ( myData->labelAlignment & Qt::AlignTop )
        {
            alignPos.setY( canvasRect.top() );
            align &= ~Qt::AlignTop;
            align |= Qt::AlignBottom;
        }
        else if ( myData->labelAlignment & Qt::AlignBottom )
        {
            // In HLine-style the x-position is pointless and
            // the alignment flags are relative to the canvas

            alignPos.setY( canvasRect.bottom() - 1 );
            align &= ~Qt::AlignBottom;
            align |= Qt::AlignTop;
        }
        else
        {
            alignPos.setY( canvasRect.center().y() );
        }
        break;
    }
    case QwtPlotMarker::HLine:
    {
        if ( myData->labelAlignment & Qt::AlignLeft )
        {
            alignPos.setX( canvasRect.left() );
            align &= ~Qt::AlignLeft;
            align |= Qt::AlignRight;
        }
        else if ( myData->labelAlignment & Qt::AlignRight )
        {
            alignPos.setX( canvasRect.right() - 1 );
            align &= ~Qt::AlignRight;
            align |= Qt::AlignLeft;
        }
        else
        {
            alignPos.setX( canvasRect.center().x() );
        }
        break;
    }
    default:
    {
        if ( myData->symbol &&
             ( myData->symbol->style() != QwtSymbol::NoSymbol ) )
        {
            symbolOff = myData->symbol->size() + QSizeF( 1, 1 );
            symbolOff /= 2;
        }
    }
    }

    qreal pw2 = myData->pen.widthF() / 2.0;
    if ( pw2 == 0.0 )
        pw2 = 0.5;

    const int spacing = myData->spacing;

    const qreal xOff = qMax( pw2, symbolOff.width() );
    const qreal yOff = qMax( pw2, symbolOff.height() );

    const QSizeF textSize = myData->label.textSize( painter->font() );

    if ( align & Qt::AlignLeft )
    {
        alignPos.rx() -= xOff + spacing;
        if ( myData->labelOrientation == Qt::Vertical )
            alignPos.rx() -= textSize.height();
        else
            alignPos.rx() -= textSize.width();
    }
    else if ( align & Qt::AlignRight )
    {
        alignPos.rx() += xOff + spacing;
    }
    else
    {
        if ( myData->labelOrientation == Qt::Vertical )
            alignPos.rx() -= textSize.height() / 2;
        else
            alignPos.rx() -= textSize.width() / 2;
    }

    if ( align & Qt::AlignTop )
    {
        alignPos.ry() -= yOff + spacing;
        if ( myData->labelOrientation != Qt::Vertical )
            alignPos.ry() -= textSize.height();
    }
    else if ( align & Qt::AlignBottom )
    {
        alignPos.ry() += yOff + spacing;
        if ( myData->labelOrientation == Qt::Vertical )
            alignPos.ry() += textSize.width();
    }
    else
    {
        if ( myData->labelOrientation == Qt::Vertical )
            alignPos.ry() += textSize.width() / 2;
        else
            alignPos.ry() -= textSize.height() / 2;
    }

    painter->translate( alignPos.x(), alignPos.y() );
    if ( myData->labelOrientation == Qt::Vertical )
        painter->rotate( -90.0 );
    painter->rotate( angle );
    const QRectF textRect( 0, 0, textSize.width(), textSize.height() );
    myData->label.draw( painter, textRect );
}

void MyMarker::setAngle(double newAngle){
    angle=newAngle;
}
und zum Schluss das ganze als Beipsiel in simpleplot aus den QWT-Examples getestet

Code: Alles auswählen

#include <qapplication.h>
#include <qlayout.h>
#include <qwt_plot.h>
#include <qwt_plot_marker.h>
#include <qwt_plot_curve.h>
#include <qwt_legend.h>
#include <qwt_point_data.h>
#include <qwt_plot_canvas.h>
#include <qwt_plot_panner.h>
#include <qwt_plot_magnifier.h>
#include <qwt_text.h>
#include <qwt_symbol.h>
#include <qwt_math.h>
#include <math.h>
#include "mymarker.h"

//-----------------------------------------------------------------
//              simple.cpp
//
//      A simple example which shows how to use QwtPlot connected
//      to a data class without any storage, calculating each values
//      on the fly.
//-----------------------------------------------------------------

class FunctionData: public QwtSyntheticPointData
{
public:
    FunctionData( double( *y )( double ) ):
        QwtSyntheticPointData( 100 ),
        d_y( y )
    {
    }

    virtual double y( double x ) const
    {
        return d_y( x );
    }

private:
    double( *d_y )( double );
};

class ArrowSymbol: public QwtSymbol
{
public:
    ArrowSymbol()
    {
        QPen pen( Qt::black, 0 );
        pen.setJoinStyle( Qt::MiterJoin );

        setPen( pen );
        setBrush( Qt::red );

        QPainterPath path;
        path.moveTo( 0, 8 );
        path.lineTo( 0, 5 );
        path.lineTo( -3, 5 );
        path.lineTo( 0, 0 );
        path.lineTo( 3, 5 );
        path.lineTo( 0, 5 );

        QTransform transform;
        transform.rotate( -30.0 );
        path = transform.map( path );

        setPath( path );
        setPinPoint( QPointF( 0, 0 ) );

        setSize( 10, 14 );
    }
};

class Plot : public QwtPlot
{
public:
    Plot( QWidget *parent = NULL );

protected:
    virtual void resizeEvent( QResizeEvent * );

private:
    void populate();
    void updateGradient();
};


Plot::Plot( QWidget *parent ):
    QwtPlot( parent )
{
    setAutoFillBackground( true );
    setPalette( QPalette( QColor( 165, 193, 228 ) ) );
    updateGradient();

    setTitle( "A Simple QwtPlot Demonstration" );
    insertLegend( new QwtLegend(), QwtPlot::RightLegend );

    // axes
    setAxisTitle( xBottom, "x -->" );
    setAxisScale( xBottom, 0.0, 10.0 );

    setAxisTitle( yLeft, "y -->" );
    setAxisScale( yLeft, -1.0, 1.0 );

    // canvas
    QwtPlotCanvas *canvas = new QwtPlotCanvas();
    canvas->setLineWidth( 1 );
    canvas->setFrameStyle( QFrame::Box | QFrame::Plain );
    canvas->setBorderRadius( 0 );

    QPalette canvasPalette( Qt::white );
    canvasPalette.setColor( QPalette::Foreground, QColor( 133, 190, 232 ) );
    canvas->setPalette( canvasPalette );

    setCanvas( canvas );

    // panning with the left mouse button
    ( void ) new QwtPlotPanner( canvas );

    // zoom in/out with the wheel
    ( void ) new QwtPlotMagnifier( canvas );

    populate();
}

void Plot::populate()
{
    // Insert new curves
    QwtPlotCurve *cSin = new QwtPlotCurve( "y = sin(x)" );
    cSin->setRenderHint( QwtPlotItem::RenderAntialiased );
    cSin->setLegendAttribute( QwtPlotCurve::LegendShowLine, true );
    cSin->setPen( Qt::red );
    cSin->attach( this );

    QwtPlotCurve *cCos = new QwtPlotCurve( "y = cos(x)" );
    cCos->setRenderHint( QwtPlotItem::RenderAntialiased );
    cCos->setLegendAttribute( QwtPlotCurve::LegendShowLine, true );
    cCos->setPen( Qt::blue );
    cCos->attach( this );

    // Create sin and cos data
    cSin->setData( new FunctionData( ::sin ) );
    cCos->setData( new FunctionData( ::cos ) );

    // Insert markers

    //  ...a horizontal line at y = 0...
    QwtPlotMarker *mY = new QwtPlotMarker();
    mY->setLabel( QString::fromLatin1( "y = 0" ) );
    mY->setLabelAlignment( Qt::AlignRight | Qt::AlignTop );
    mY->setLineStyle( QwtPlotMarker::HLine );
    mY->setYValue( 0.0 );
    mY->attach( this );

    //  ...a vertical line at x = 2 * pi
    QwtPlotMarker *mX = new QwtPlotMarker();
    mX->setLabel( QString::fromLatin1( "x = 2 pi" ) );
    mX->setLabelAlignment( Qt::AlignLeft | Qt::AlignBottom );
    mX->setLabelOrientation( Qt::Vertical );
    mX->setLineStyle( QwtPlotMarker::VLine );
    mX->setLinePen( Qt::black, 0, Qt::DashDotLine );
    mX->setXValue( 2.0 * M_PI );
    mX->attach( this );

    const double x = 7.7;

    // an arrow at a specific position
    MyMarker *mPos = new MyMarker;
    mPos->setRenderHint( QwtPlotItem::RenderAntialiased, true );
    mPos->setItemAttribute( QwtPlotItem::Legend, true );
    mPos->setSymbol( new ArrowSymbol() );
    mPos->setAngle(90);
    mPos->setValue( QPointF( x, ::sin( x ) ) );
    mPos->setLabel( QString( "x = %1" ).arg( x ) );
    mPos->setLabelAlignment( Qt::AlignRight | Qt::AlignBottom );
    mPos->attach( this );
}

void Plot::updateGradient()
{
    QPalette pal = palette();

    const QColor buttonColor = pal.color( QPalette::Button );

    QLinearGradient gradient( rect().topLeft(), rect().bottomLeft() );
    gradient.setColorAt( 0.0, Qt::white );
    gradient.setColorAt( 0.7, buttonColor );
    gradient.setColorAt( 1.0, buttonColor );

    pal.setBrush( QPalette::Window, gradient );
    setPalette( pal );
}

void Plot::resizeEvent( QResizeEvent *event )
{
    QwtPlot::resizeEvent( event );

    // Qt 4.7.1: QGradient::StretchToDeviceMode is buggy on X11
    updateGradient();
}

int main( int argc, char **argv )
{
    QApplication a( argc, argv );

    Plot *plot = new Plot();

    // We put a dummy widget around to have
    // so that Qt paints a widget background
    // when resizing

    QWidget window;
    QHBoxLayout *layout = new QHBoxLayout( &window );
    layout->setContentsMargins( 0, 0, 0, 0 );
    layout->addWidget( plot );

    window.resize( 600, 400 );
    window.show();

    return a.exec();
}

in der Funktion populate() ist ein neues Objekt der Klasse myMarker und die Funktion setAngle() zu finden.
Hast du es in etwa so gemeint? Mich würde deine Meinung dazu interessieren.

Gora
Antworten