QwtPlotMarker drehen
QwtPlotMarker drehen
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.
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.
Re: QwtPlotMarker drehen
Woraus bestehen denn Deine Marker: Text, Symbol oder beides ?
Sind die Positionen der Marker identisch zu den Punkten der Kurve ?
Uwe
Sind die Positionen der Marker identisch zu den Punkten der Kurve ?
Uwe
Re: QwtPlotMarker drehen
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.
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.
Re: QwtPlotMarker drehen
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:
Und dann irgendwie so:
Solange Du nicht richtig viele Labels hast, sollte die Implementierung schnell genug sein.
HTH,
Uwe
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 );
}
};
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();
}
}
}
HTH,
Uwe
Re: QwtPlotMarker drehen
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
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
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();
}
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
Re: QwtPlotMarker drehen
Habe das Problem mit dem Fehler gelöst war anscheinend nur ein Compilerfehler, aber die Drehung ist nicht da.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 ausprobiertWenn ich das so umsetze dann krieg ich den folgenden Fehler: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(); }
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
Re: QwtPlotMarker drehen
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
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
Re: QwtPlotMarker drehen
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:
und zum Schluss das ganze als Beipsiel in simpleplot aus den QWT-Examples getestet
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
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;
}
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();
}
Hast du es in etwa so gemeint? Mich würde deine Meinung dazu interessieren.
Gora