Skip to content

Axis ticks misaligned with canvas grid after replacing the scale draw #113

Closed

Description

@PierreRaybaut

Description

After replacing the scale draw of an axis with QwtPlot.setAxisScaleDraw (or QwtScaleWidget.setScaleDraw), the axis ticks may no longer be aligned with the canvas: grid lines and plot items are drawn at positions that do not match the tick marks and labels of the axis. The misalignment persists through replot() and disappears only when the scale division changes (e.g. after panning or zooming).

The issue was found in DataLab, which replaces the bottom scale draw on each refresh of its signal view (see DataLab-Platform/DataLab#457).

Steps to reproduce

import numpy as np
from qtpy import QtWidgets as QW

from qwt import QwtPlot, QwtPlotCurve, QwtPlotGrid, QwtScaleDraw

app = QW.QApplication([])
plot = QwtPlot()
x = np.linspace(3.06e-7, 4.1e-7, 1000)
QwtPlotCurve.make(x, np.tanh((x - 3.1e-7) * 1e8), plot=plot)
QwtPlotGrid().attach(plot)
# Scale bounds that are not major ticks: labels do not overflow the scale ends
plot.setAxisScale(QwtPlot.xBottom, 3.06e-7, 4.1e-7)
plot.resize(600, 400)
plot.show()
plot.replot()
app.processEvents()


def ends(plot):
    """Scale ends in canvas coordinates: (axis widget, canvas map)"""
    widget = plot.axisWidget(QwtPlot.xBottom)
    offset = widget.x() - plot.canvas().x()
    smap = widget.scaleDraw().scaleMap()
    cmap = plot.canvasMap(QwtPlot.xBottom)
    return (smap.p1() + offset, smap.p2() + offset), (cmap.p1(), cmap.p2())


print(ends(plot))  # e.g. ((-1.0, 516.0), (-1, 516)): aligned
plot.setAxisScaleDraw(QwtPlot.xBottom, QwtScaleDraw())
plot.replot()
print(ends(plot))  # e.g. ((42.0, 483.0), (-1, 516)): ticks no longer match the canvas

Root cause

QwtScaleWidget.setScaleDraw passes the alignment, the scale division and the transformation of the previous scale draw to the new one, but not its position and length. The new scale draw has a zero length when layoutScale() is called: all ticks are mapped to the same position, so getBorderDistHint() returns half the width of the end labels instead of 0. The axis is laid out with these border distances, while the stored border distances (borderDist), which QwtPlot.canvasMap() uses to place grid lines and plot items, are left unchanged.

On the next replot(), QwtPlot.updateAxes() calls setBorderDist() with the correct values, which are equal to the stored ones: nothing is laid out again, and the axis keeps its wrong geometry until the next scale division change.

Proposed fix

In QwtScaleWidget.setScaleDraw, also pass the position and the length of the previous scale draw to the new one before layoutScale(), so that the border distance hint is computed from the actual scale geometry.

Activity

  1. PierreRaybaut commented on Oct 29, 2026

    @PierreRaybaut
    CollaboratorAuthor

    Fixed by #114 (merged into master as 36fffb6, not released yet). DataLab also stops replacing the scale draw on regular refreshes: DataLab-Platform/DataLab#458 (DataLab-Platform/DataLab#457).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions