Hi,

Yeah, this is actually harder than you'd think since when everything is in 
a full UI layout with windows resizing etc the extra space has to come or 
go from somewhere... You need to either restrict the zoom range of each 
axis so that the other doesn't exceed its limits (but that will stop a full 
zoom-out of that axis), or dynamically grow or shrink the whole plot area 
and pad out with empty space.

If you want to go the first path (restricting zoom based on size of other 
axis), you'll need to do a bunch of manual handling of the ViewBox signals 
such as sigRangeChanged, sigResized etc:
https://pyqtgraph.readthedocs.io/en/latest/_modules/pyqtgraph/graphicsItems/ViewBox/ViewBox.html#ViewBox
That should work but would a bit tedious.

Your comment about matplotlib reminded me I had to work around something 
similar previously when embedding matplotlib multi-panel plot into a Qt Ul, 
where I wanted the whole plot area to maintain a fixed aspect ratio. It 
turns out set_aspect doesn't work with twinned and shared axes. It was 
adapted from something I found elsewhere on the internet so it's a bit 
hacky and not well documented, sorry. Basically it's a QWidget that you 
insert another widget inside (eg. PlotWidget or similar) and it handles the 
extra padding required to keep the widget at a fixed ratio.

#import stuff here as needed...
class AspectRatioWidget(QWidget):
    """A widget that will maintain a specified aspect ratio.
    Good for plots where we want to fill the maximum space without 
stretching the aspect ratio."""

    def __init__(self, widget, parent, aspect_ratio):
        super().__init__(parent)
        self.aspect_ratio = aspect_ratio
        self.setLayout(QBoxLayout(QBoxLayout.LeftToRight, self))
        self.layout().addItem(QSpacerItem(0, 0))
        self.layout().addWidget(widget)
        self.layout().addItem(QSpacerItem(0, 0))

    def setAspectRatio(self, aspect_ratio):
        self.aspect_ratio = aspect_ratio
        self._adjust_ratio(self.geometry().width(), 
self.geometry().height());

    def resizeEvent(self, e):
        self._adjust_ratio(e.size().width(), e.size().height())

    def _adjust_ratio(self, w, h):
        if w / h > self.aspect_ratio:  # too wide
            self.layout().setDirection(QBoxLayout.LeftToRight)
            widget_stretch = h * self.aspect_ratio
            outer_stretch = (w - widget_stretch) / 2 + 0.5
        else:  # too tall
            self.layout().setDirection(QBoxLayout.TopToBottom)
            widget_stretch = w / self.aspect_ratio
            outer_stretch = (h - widget_stretch) / 2 + 0.5

        self.layout().setStretch(0, outer_stretch)
        self.layout().setStretch(1, widget_stretch)
        self.layout().setStretch(2, outer_stretch)


It was used something like this. You'll need to adapt the matplotlib 
FigureCanvas part to be a pyqtgraph PlotWidget or GraphicsLayoutWidget or 
similar.

        self.resultplot_figureCanvas = 
FigureCanvas(mpl.figure.Figure(constrained_layout=True))
        # A widget hacky thing to keep fixed aspect ratio of plot, since 
matplotlib.axes.Axes.set_aspect doesn't work
        # when there is both twinned and shared axes...
        self.resultplot_aspectwidget = 
AspectRatioWidget(self.resultplot_figureCanvas, parent=self, 
aspect_ratio=1.5)
        
self.resultplots_groupBox.layout().addWidget(self.resultplot_aspectwidget)


Patrick
On Wednesday, 25 August 2021 at 12:54:30 pm UTC+9:30 James wrote:

> Thanks for the suggestion Patrick. This does work for me in the minimal 
> working example above, as long as the glw.resize() sets the aspect ratio of 
> the GraphicsLayoutWidget to match that of the desired plot (and the GLW 
> fits on my screen). But when I try this in a GUI, I have a problem. See 
> e.g. the following MWE in which I have a PlotWidget and a QPushButton in a 
> QHBoxLayout().
>
> Without resizing the PlotWidget, the axis limits are not respected (should 
> be x=[0,2000], y=[0,6000]).  And calling resize on the PlotWidget seems to 
> have no effect on the displayed size of the PW (see code below and attached 
> image).
>
> So it looks like the plot axes are forced to fill the entire PlotWidget 
> area (or at least they do by default), which makes it hard to enforce an 
> aspect ratio for the axes that doesn't match the aspect ratio of the 
> PlotWidget itself.
> There must be a way to do this (e.g. in matplotlib I can configure a set 
> of axes to have whatever aspect ratio I'd like, independent of the 
> dimensions of the figure that holds those axes -- see code below and 
> attached image).
>
> # pyqtgraph version
>
> import sys
> import PyQt5.QtWidgets as qtw
>
> import pyqtgraph as pg
>
> app = qtw.QApplication(sys.argv)
> window = qtw.QWidget()
> window.setWindowTitle('Fixed aspect ratio')
> layout = qtw.QHBoxLayout()
> layout.addWidget(qtw.QPushButton('Button'))
>
> _width  = 2000
> _height = 6000
>
> pw = pg.PlotWidget()
> pw.setLimits(xMin=0, xMax=_width, yMin=0, yMax=_height)
> pw.setRange(xRange=(0, _width), yRange=(0, _height), padding=0, 
> update=True, disableAutoRange=True)
> pw.resize(_width, _height)
> pw.setAspectLocked(lock=True, ratio=1)
> pw.setMouseEnabled(x=False, y=False)
> layout.addWidget(pw)
>
> window.setLayout(layout)
> window.show()
>
> # Plot a square                                                            
>                                                      
> pw.plot([_width/4, _width/2, _width/2, _width/4, _width/4], [_width/4, 
> _width/4, _width/2, _width/2, _width/4])
>
> sys.exit(app.exec_())
>
>
> # Matplotlib version
> import matplotlib.pyplot as plt
> fig = plt.figure(figsize=(15,5))  # aspect ratio 3:1                      
>       
> ax1 = fig.add_subplot(1,1,1, adjustable='box', aspect=1)
> ax1.plot(range(10))  # axes are 1:1 within the 3:1 figure                  
>      
> plt.show()
>
>
> On Monday, August 23, 2021 at 11:52:26 PM UTC-4 Patrick wrote:
>
>> Hi,
>>
>> Does this behave better? Using setLimits(), since setXRange() etc is just 
>> a once-off change to the view bounds.
>>
>> from pyqtgraph.Qt import QtGui, QtCore
>> import pyqtgraph as pg
>>
>> app = pg.mkQApp("Range Example")
>>
>> glw = pg.GraphicsLayoutWidget(show=True, title="GLW title")
>> #glw.setBackground('w')
>> glw.setWindowTitle('pyqtgraph example: Plotting')
>>
>> plt = glw.addPlot(title='plot title')
>>
>> _width = 2000
>> _height = 6000
>> glw.resize(_width/3, _height/3)
>> plt.setLimits(xMin=0, xMax=_width, yMin=0, yMax=_height)
>> plt.setXRange(0, _width, padding=0)
>> plt.setYRange(0, _height, padding=0)
>> plt.setAspectLocked(lock=True, ratio=1)
>>
>> plt.plot([_width/4, _width/2, _width/2, _width/4, _width/4], [_width/4, 
>> _width/4, _width/2, _width/2, _width/4])
>>
>> if __name__ == '__main__':
>> pg.mkQApp().exec_()
>>
>> Patrick
>> On Tuesday, 24 August 2021 at 6:25:45 am UTC+9:30 James wrote:
>>
>>> Hi,
>>>
>>> I'd like to force the x-range and y-range to specific values meaning 
>>> that the plot axes should start and stop at those limits (no padding). I 
>>> also want to specify a fixed aspect ratio of 1:1 (so that dx=100 takes the 
>>> same screen space as dy=100, i.e. a square will display with equal number 
>>> of pixels in length and width).
>>>
>>> When I try the following, the displayed x-axis extends beyond the 
>>> specified limits. Do I need to force the ViewBox to have a specific size 
>>> that is consistent with the fixed aspect ratio and x/y ranges?
>>>
>>> from pyqtgraph.Qt import QtGui, QtCore
>>> import pyqtgraph as pg
>>>
>>> app = pg.mkQApp("Range Example")
>>>
>>> glw = pg.GraphicsLayoutWidget(show=True, title="GLW title")
>>> glw.setBackground('w')
>>> glw.setWindowTitle('pyqtgraph example: Plotting')
>>>
>>> plt = glw.addPlot(title='plot title')
>>>
>>> _width  = 2000
>>> _height = 6000
>>> glw.resize(_width/3, _height/3)
>>> plt.setXRange(0, _width, padding=0)
>>> plt.setYRange(0, _height, padding=0)
>>> plt.setAspectLocked(lock=True, ratio=1)
>>>
>>> if __name__ == '__main__':
>>>     pg.mkQApp().exec_()
>>>
>>>
>>>
>>>
>>>
>>>

-- 
You received this message because you are subscribed to the Google Groups 
"pyqtgraph" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/pyqtgraph/21011612-577b-4069-8c31-4efa52c4bb33n%40googlegroups.com.

Reply via email to