Thank you for the information and recommendations. I was afraid that it might be fairly subtle/complicated, but your code example will be a big help.
James > On Aug 25, 2021, at 1:04 AM, Patrick <[email protected]> wrote: > > 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 a topic in the Google > Groups "pyqtgraph" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/pyqtgraph/noNWNG3RBg0/unsubscribe > <https://groups.google.com/d/topic/pyqtgraph/noNWNG3RBg0/unsubscribe>. > To unsubscribe from this group and all its topics, send an email to > [email protected] > <mailto:[email protected]>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/pyqtgraph/21011612-577b-4069-8c31-4efa52c4bb33n%40googlegroups.com > > <https://groups.google.com/d/msgid/pyqtgraph/21011612-577b-4069-8c31-4efa52c4bb33n%40googlegroups.com?utm_medium=email&utm_source=footer>. -- 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/A922ED27-7921-4216-A6F0-A2C9374C3F18%40gmail.com.
