I've created a GUI in pyside2
with an embedded matplotlib
canvas. I'm planning to display a lot of information on this canvas, so I need more vertical space. I figured I could place the canvas inside a QScrollArea
and create a "tall" figure with some subplots below each other.
This answer comes close to what I want, but I don't want horizontal scrolling at all. Rather, I'd like my canvas to expand/shrink according to the GUI, fully occupying the available horizontal space inside the QScrollArea
, while still only allowing for vertical scrolling of the figure.
The following code is a MRE without any scrollbars:
import sys
from PySide2.QtCore import Qt
from PySide2.QtWidgets import (QApplication, QHBoxLayout, QMainWindow,
QPushButton, QVBoxLayout, QWidget)
from matplotlib.backends.backend_qt5agg import (FigureCanvasQTAgg as Canvas,
NavigationToolbar2QT as Navbar)
from matplotlib.pyplot import Figure
class MyApp(QMainWindow):
def __init__(self) -> None:
super().__init__()
# Main Window setup
self.showMaximized()
self.frame = QWidget(self)
self.setCentralWidget(self.frame)
main_layout = QHBoxLayout()
self.frame.setLayout(main_layout)
# Buttons (left column)
buttons_row = QHBoxLayout()
self.plot_above_button = QPushButton(self, text='Plot Data Above')
self.plot_above_button.clicked.connect(self.plot_above)
buttons_row.addWidget(self.plot_above_button)
self.plot_below_button = QPushButton(self, text='Plot Data Below')
self.plot_below_button.clicked.connect(self.plot_below)
buttons_row.addWidget(self.plot_below_button)
main_layout.addLayout(buttons_row)
# Plot (right column)
right_column = QVBoxLayout()
self.fig = Figure(facecolor='white')
self.ax1 = self.fig.add_axes([0.1, 0.3, 0.8, 0.7])
self.ax2 = self.fig.add_axes([0.1, 0.1, 0.8, 0.1])
self.canvas = Canvas(self.fig)
self.canvas.setParent(self)
right_column.addWidget(self.canvas)
right_column.addWidget(Navbar(self.canvas, self.frame))
main_layout.addLayout(right_column)
def plot_above(self):
self.ax1.plot([1, 2], [3, 4])
self.canvas.draw()
def plot_below(self):
self.ax2.plot([1, 2], [3, 4])
self.canvas.draw()
if __name__ == '__main__':
app = QApplication()
gui = MyApp()
gui.show()
sys.exit(app.exec_())
And this is the GUI I see when I execute the code above:
As you can see, the canvas occupies the entire available space, vertically and horizontally. Except for the lacking vertical scrollbar, this is exactly what I want.
Now, if I try to place self.canvas
inside a QScrollArea
, by adding these lines to my code:
from PySide2.QtWidgets import QScrollArea # additional import
# ...
self.canvas.setParent(self) # this is old code
# new code starts here
scroll_area = QScrollArea()
scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
scroll_area.horizontalScrollBar().setEnabled(False)
scroll_area.setWidget(self.canvas)
right_column.addWidget(scroll_area)
right_column.addWidget(Navbar(self.canvas, self.frame)) # this is old code
# ...
I now end up with:
Instead of automatically adjusting its size, the figure just "sits" inside the QScrollArea
, initialized with the default figsize value.
I also tried to manually resize the figure, by writing e.g.:
self.fig = Figure(facecolor='white', figsize=(11, 12))
Which gives me:
This is very close to what I want, but also very hacky: I empirically chose the figsize (11, 12)
based on how the canvas looks like on my screen, but these values could be different in another screen or if I add extra widgets to the left side. The canvas also doesn't resize along with the main window.
Is there a way to achieve what I want?