Tick drawing artifact when setting all plot margins to 0
estan opened this issue · 16 comments
I've tried getting the scale draw of the axes and move()ing it to 0,0, but it seems to have no effect.
Sorry, the image above is slightly misrepresentative as I haven't flipped my Y axis yet. It was meant to show the Y axis going from 0 at the top to 1000 at the bottom (how do I do that btw?). My general question is how to position the Y axis at a precise X value, and the X axis at a precise Y value.
It seems plotLayout().setAlignCanvasToScales(True)
will get me halfway there: There's no longer any space between my curve and the end of the canvas, but there's still a gap between the canvas and the axes backbones.
I tried getting rid of this last space with plotLayout().setSpacing(0)
, but looks like it had no effect.
This is apparently a rounding error of some kind, within the code handling the plot layout (here and/or there, or maybe also here).
The render result when setting all margins to 0 is not the same in PythonQwt as in Qwt original C++ library, so there maybe something to dig in the code parts cited above.
This might take some time but it's not hopeless as it works in the original C++ code.
As a note for myself:
- This bug is probably related to a rounding error in QwtPlotLayout
- This bug is not happening with
PyQwt5
(see screenshots below), so maybe it's working with the original Qwt 6 C++ code and something was lost when porting the code in Python. - However, let's remember that
PythonQwt
is based on Qwt 6 whereasPyQwt
is based on Qwt 5: in Qwt v6, all widget coordinates are using floating point values (QPointF
,QRectF
, ...) instead of integer values like version 5 (QPoint
,QRect
, ...).
Here are two screenshots produced by the newly introduced "plot without margins" test:
PyQwt |
PythonQwt |
---|---|
Qwt 5 | Qwt 6 |
![]() |
![]() |
Thanks a lot for looking into this. I made a small Qwt6 C++ test:
#include <cmath>
#include <QApplication>
#include <QFrame>
#include <QWidget>
#include <qwt_plot.h>
#include <qwt_plot_curve.h>
#include <qwt_plot_layout.h>
#include <qwt_point_data.h>
#include <qwt_scale_widget.h>
class FunctionData : public QwtSyntheticPointData
{
public:
FunctionData(double (*y)(double)) : QwtSyntheticPointData(500), d_y(y) { }
virtual double y(double x) const {
return d_y(x);
}
private:
double (*d_y)(double);
};
class TestPlot : public QwtPlot
{
Q_OBJECT
public:
TestPlot(QWidget *parent = 0) : QwtPlot(parent) {
setWindowTitle("Qwt");
enableAxis(QwtPlot::xTop, true);
enableAxis(QwtPlot::yRight, true);
setAxisScale(QwtPlot::xTop, 0, 10);
setAxisScale(QwtPlot::xBottom, 0, 10);
setAxisScale(QwtPlot::yRight, -1, 1);
setAxisScale(QwtPlot::yLeft, -1, 1);
QwtPlotCurve *curve1 = new QwtPlotCurve("Test Curve 1");
curve1->setData(new FunctionData(::sin));
curve1->attach(this);
qobject_cast<QFrame*>(canvas())->setFrameStyle(QFrame::NoFrame);
plotLayout()->setCanvasMargin(0);
axisWidget(QwtPlot::yLeft)->setMargin(0);
axisWidget(QwtPlot::xTop)->setMargin(0);
axisWidget(QwtPlot::yRight)->setMargin(0);
axisWidget(QwtPlot::xBottom)->setMargin(0);
}
};
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
TestPlot plot;
plot.resize(800, 600);
plot.show();
return app.exec();
}
#include "moc_qwttest.cpp"
TEMPLATE = app
CONFIG += qwt
TARGET =
DEPENDPATH += .
INCLUDEPATH += .
# Input
SOURCES += qwttest.cpp
HEADERS += qwttest.cpp
Result:
I can also note: At the moment I'm just doing self.plotLayout().setAlignCanvasToScales(True)
and setting the margin of the scale widgets to 0, not setting the canvas margin to 0 (to avoid the artifact on the tick mark). This gets me almost the look I want. But the curves are still being "cut off" when touching the axis (as seen in both my example and yours), so would love to have this fixed.
@PierreRaybaut: I made a small find:
From qwt_scale_widget.cpp, in QwtScaleWidget::layoutScale
:
const QRectF r = contentsRect();
So it will work with a QRectF
in the subsequent calculations, but in the corresponding PythonQwt
code:
r = self.contentsRect()
So it will work with a QRect
.
Could the problem be somewhere around here?
I gave it a try, changing it to r = QRectF(self.contentsRect())
, but no cigar, not that easy :)
Yesterday, I took the time to compare qwt_plot_layout.cpp and plot_layout.py... no luck: everything seems fine.
Still investigating, from time to time... but it's like finding a needle in a haystack!
Yea, I can imagine :) Wish I had more time to help at the moment. It's no hurry.
I guess it's a matter of going through all related code looking for variables/return values/parameters that should be float or should be int and add assertions of their type.
I suspect the bug is around here, somewhere.
It specifically messes with the yLeft axis and alignCanvasToScales.
I just can't find it yet.
4 years later... nailed it ;-)
👌