Добавьте вертикальную полосу прокрутки к встроенному холсту matplotlib, сохраняя его горизонтальный размер, занимающий всю область QScrollArea. - PullRequest
0 голосов
/ 17 апреля 2020

Я создал GUI в pyside2 со встроенным matplotlib холстом. Я планирую отобразить много информации на этом холсте, поэтому мне нужно больше вертикального пространства. Я подумал, что мог бы поместить холст внутри QScrollArea и создать «высокую» фигуру с несколькими подзаговорами друг над другом.

Этот ответ приближается к тому, что я хочу, но я не вообще не хочу горизонтальной прокрутки. Скорее, Я бы хотел, чтобы мой холст расширялся / сжимался в соответствии с GUI, полностью занимая доступное горизонтальное пространство внутри QScrollArea, но при этом позволяя только вертикальную прокрутку рисунка.

Следующий код является MRE без полос прокрутки:

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_())

И это GUI, которое я вижу, когда выполняю код выше:

enter image description here

Как видите, холст занимает все доступное пространство, по вертикали и по горизонтали. За исключением отсутствия вертикальной полосы прокрутки, это именно то, что я хочу.

Теперь, если я попытаюсь поместить self.canvas в QScrollArea, добавив эти строки в мой код:

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
        # ...

Теперь я получаю:

enter image description here

Вместо автоматической настройки своего размера фигура просто «сидит» внутри QScrollArea, инициализирована со значением figsize по умолчанию.

Я также попытался вручную изменить размер фигуры, написав, например:

        self.fig = Figure(facecolor='white', figsize=(11, 12))

, что дает мне:

enter image description here

Это очень близко к тому, что я хочу, но также и очень забавно: я эмпирически выбрал figsize (11, 12) на основе того, как холст выглядит на моем экране, но эти значения могут отличаться в другом экран или если я добавлю дополнительные виджеты на левой стороне. Размер холста также не изменяется вместе с основным окном.

Есть ли способ добиться того, чего я хочу?

1 Ответ

0 голосов
/ 17 апреля 2020

После попытки решить это в течение другого дня, я наконец понял это. Я пишу ответ здесь на случай, если кто-нибудь столкнется с этой проблемой в будущем.

Мы можем переопределить resizeEvent из GUI, чтобы изменить ширину холста до QScrollArea текущей ширины:

    def resizeEvent(self, e):
        self.canvas.resize(self.scroll_area.width(), self.canvas.height())
        super().resizeEvent(e)

Если я вызову self.showMaximized() в конце метода init, изменение размера сработает автоматически. Это означает, что размер холста правильно изменяется при первом показе окна, и он адаптируется при изменении размера окна.

Это результат:

enter image description here

И полный код, использованный для генерации GUI:

import sys

from PySide2.QtCore import Qt
from PySide2.QtWidgets import (QApplication, QHBoxLayout, QMainWindow, 
                            QPushButton, QScrollArea, 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.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', figsize=(12, 12))
        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)

        self.scroll_area = QScrollArea()
        self.scroll_area.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.scroll_area.horizontalScrollBar().setEnabled(False)
        self.scroll_area.setWidget(self.canvas)

        right_column.addWidget(self.scroll_area)

        right_column.addWidget(Navbar(self.canvas, self.frame))
        main_layout.addLayout(right_column)

        self.showMaximized()

    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()

    def resizeEvent(self, e):
        self.canvas.resize(self.scroll_area.width(), self.canvas.height())
        super().resizeEvent(e)


if __name__ == '__main__':
    app = QApplication()
    gui = MyApp()
    gui.show()
    sys.exit(app.exec_())
...