PlotPyStack / PythonQwt

Qt plotting widgets for Python (pure Python reimplementation of Qwt C++ library)
https://pypi.org/project/PythonQwt/
Other
86 stars 25 forks source link

Visual glitches when trying to scroll a QwtScaleDiv #84

Closed ZeeD closed 3 months ago

ZeeD commented 3 months ago

I'm trying to scroll a plot via an external event, but I'm facing some issues, and I'm trying to understand what is the correct way to do it

running the following code draws a simple chart and a slider. If I use the slider I can see the curve update correctly, but the ticks are stuck. Worse, if I change the application size, I see that only some ticks are drawn.

minimal test case:

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication
from PySide6.QtWidgets import QSlider
from PySide6.QtWidgets import QVBoxLayout
from PySide6.QtWidgets import QWidget

from qwt import QwtPlot
from qwt import QwtPlotCurve
from qwt.scale_div import QwtScaleDiv

app = QApplication([__file__])

root = QWidget()

layout = QVBoxLayout(root)

plot = QwtPlot(root)
QwtPlotCurve.make([0, 9], [0, 9], '', plot)
plot.setAxisScaleDiv(QwtPlot.xBottom, QwtScaleDiv(0, 9, [], [], range(10)))

slider = QSlider(Qt.Orientation.Horizontal, root)
slider.setRange(0, 9)

def change_lower_bound(value: int) -> None:
    plot.axisScaleDiv(QwtPlot.xBottom).setLowerBound(value)
    plot.replot()

slider.valueChanged.connect(change_lower_bound)

root.setLayout(layout)
layout.addWidget(plot)
layout.addWidget(slider)
root.show()

raise SystemExit(app.exec())
PierreRaybaut commented 3 months ago

Hi,

Thanks for the feedback.

Actually, the right way to do this is to set the axis limits using the QwtPlot.setAxisScale method:

from PySide6.QtCore import Qt
from PySide6.QtWidgets import QApplication, QSlider, QVBoxLayout, QWidget

from qwt import QwtPlot, QwtPlotCurve

app = QApplication([])

plot = QwtPlot()
QwtPlotCurve.make([0, 9], [0, 9], "", plot)
plot.setAxisScale(QwtPlot.xBottom, 0, 9)

slider = QSlider(Qt.Orientation.Horizontal)
slider.setRange(0, 9)

def change_lower_bound(value: int) -> None:
    xint = plot.axisScaleDiv(QwtPlot.xBottom).interval()
    plot.setAxisScale(QwtPlot.xBottom, value, xint.maxValue())
    plot.replot()

slider.valueChanged.connect(change_lower_bound)

root = QWidget()
layout = QVBoxLayout(root)
root.setLayout(layout)
layout.addWidget(plot)
layout.addWidget(slider)
root.show()

app.exec()

And here is the recommended way of coding this:

import numpy as np
from PySide6 import QtCore as QC
from PySide6 import QtWidgets as QW

import qwt

class MyWidget(QW.QWidget):
    """A widget that contains a plot and a slider."""

    def __init__(self, parent):
        super().__init__(parent)
        layout = QW.QVBoxLayout(self)
        self.setLayout(layout)

        self.plot = qwt.QwtPlot()
        layout.addWidget(self.plot)
        self.slider = QW.QSlider(QC.Qt.Horizontal)
        self.slider.valueChanged.connect(self.__change_lower_bound)
        layout.addWidget(self.slider)

    def __change_lower_bound(self, value: int) -> None:
        """Change the lower bound of the x-axis."""
        scalediv = self.plot.axisScaleDiv(qwt.QwtPlot.xBottom)
        if scalediv is not None:
            xint = scalediv.interval()
            self.plot.setAxisScale(qwt.QwtPlot.xBottom, value, xint.maxValue())
            self.plot.replot()

    def set_curve(self, x, y, title):
        """Set the curve to be displayed in the plot."""
        qwt.QwtPlotCurve.make(x, y, title, self.plot, linecolor="blue")
        self.slider.setRange(x[0], x[-2])
        self.slider.setValue(x[0])

def test_scroll_scalediv():
    """Test the scrolling of the x-axis scale division."""
    app = QW.QApplication([])
    widget = MyWidget(None)
    widget.show()
    x = np.linspace(-10, 10, 100)
    y = np.sin(x)
    widget.set_curve(x, y, "")
    app.exec()

if __name__ == "__main__":
    test_scroll_scalediv()
ZeeD commented 3 months ago
    xint = plot.axisScaleDiv(QwtPlot.xBottom).interval()
    plot.setAxisScale(QwtPlot.xBottom, value, xint.maxValue())

thanks, that did the trick!