如何在matplotlib中美观地显示通用轴数?

2024-06-03 03:47:05 发布

您现在位置:Python中文网/ 问答频道 /正文

我想做一个简单的图形用户界面,允许用户添加或删除任何数量的跟踪绘图跟踪。看起来是这样的:

enter image description here

我遇到的问题:

  • 我不知道如何使轴不相互重叠,以获得通用的个绘图。你知道吗
  • 当我绘制多个轨迹,然后删除除一个以外的所有轨迹时,由于某种原因会显示两个轴。显示的每条记录道都应该有一个轴。

有办法解决这些问题吗?你可以在下面找到我的代码。我认为唯一应该改变的功能是update_canvas()。要进行尝试,只需使用所需的变量数修改main中的列表name_vars。示例代码的其余部分是自包含的。你知道吗

import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(ApplicationWindow, self).__init__(parent)
        global name_vars
        self.x = np.array([1,2,3,4,5])
        self.y = np.random.random((5, len(name_vars)))
        self.num_vars = np.size(self.y,1)
        self.name_vars = name_vars

        self.tags_on = [0] * self.num_vars
        self.colors = ['#1F77B4','#FF7F0E','#2CA02C','#D62728','#9467BD',
                       '#8C564B','#E377C2','#F7F7F7','#BCBD22','#17BECF']

        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        canvas = FigureCanvas(Figure(figsize=(10, 10)))
        self.canvas_ax = canvas.figure.subplots()
        self.canvas_ax.set_xlabel("Time")
        self.canvas_ax_twin = []

        self.list_tags = QtWidgets.QComboBox(self)
        for name in self.name_vars:
            self.list_tags.addItem(name)
        button_add = QtWidgets.QPushButton('Add', self)
        button_remove = QtWidgets.QPushButton('Remove', self)
        button_add.clicked.connect(self.add_plot)
        button_remove.clicked.connect(self.remove_plot)

        layout = QtWidgets.QGridLayout(self._main)
        layout.addWidget(canvas, 0, 0)
        dropdown_layout = QtWidgets.QHBoxLayout()
        dropdown_layout.addWidget(self.list_tags)
        dropdown_layout.addWidget(button_add)
        dropdown_layout.addWidget(button_remove)
        layout.addLayout(dropdown_layout, 1, 0)
        self.show()

    def add_plot(self):
        selected_tag = self.list_tags.currentIndex()
        self.tags_on[selected_tag] = 1
        self.update_canvas()

    def remove_plot(self):
        selected_tag = self.list_tags.currentIndex()
        self.tags_on[selected_tag] = 0
        self.update_canvas()

    def update_canvas(self):
        # Delete all traces
        self.canvas_ax.clear()
        [i.clear() for i in self.canvas_ax_twin]
        self.canvas_ax_twin = []
        num_plots = 0
        for ii in range(self.num_vars):
            if self.tags_on[ii] == 1:
                # If it's not the first trace, create a twin axis
                if num_plots != 0:
                    self.canvas_ax_twin.append(self.canvas_ax.twinx())
                    self.canvas_ax_twin[-1].plot(self.x, self.y[:,ii], self.colors[num_plots])
                    self.canvas_ax_twin[-1].set_ylabel(self.name_vars[ii])
                    self.canvas_ax_twin[-1].yaxis.label.set_color(self.colors[num_plots])
                    self.canvas_ax_twin[-1].tick_params(axis='y', colors=self.colors[num_plots])
                    num_plots += 1
                # If it's the first trace, use the original axis
                else:
                    self.canvas_ax.plot(self.x, self.y[:,ii], self.colors[num_plots])
                    self.canvas_ax.set_ylabel(self.name_vars[ii])
                    self.canvas_ax.yaxis.label.set_color(self.colors[num_plots])
                    self.canvas_ax.tick_params(axis='y', colors=self.colors[num_plots])
                    num_plots += 1
        # Show the final plot
        self.canvas_ax.figure.canvas.draw()

if __name__ == '__main__':
    # Edit the number of elements in name_vars to try the code
    name_vars = ['V1','V2','V3','V4']
    app = QtWidgets.QApplication([])
    ex = ApplicationWindow()
    ex.show()
    app.exec_()

Tags: nameselfplottagsbuttonvarsaxtwin
1条回答
网友
1楼 · 发布于 2024-06-03 03:47:05

我建议把逻辑和实际策划分开。这样就更容易坚持到底。这解决了关于不删除所有轴的第二个问题。你知道吗

不让轴重叠的问题可以通过将附加双轴的位置设置为离轴一定距离来解决,具体取决于您有多少轴。你知道吗

ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))

其中n是从0开始的轴数。主轴(n=0)应排除在外,第一个轴将保持在位置1。进一步的轴以0.1的步长定位。你知道吗

然后,也可以调整主轴的右边距,为多余的脊椎留出足够的空间。你知道吗

import numpy as np
from matplotlib.backends.qt_compat import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvas
from matplotlib.figure import Figure

class ApplicationWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None, name_vars=[]):
        super(ApplicationWindow, self).__init__(parent)

        self.x = np.array([1,2,3,4,5])
        self.y = np.random.random((5, len(name_vars)))
        self.num_vars = np.size(self.y,1)
        self.name_vars = name_vars
        self.tags_on = [0] * self.num_vars

        self._main = QtWidgets.QWidget()
        self.setCentralWidget(self._main)

        self.figure = Figure(figsize=(10, 10))
        canvas = FigureCanvas(self.figure)
        self.left = self.figure.subplotpars.left
        self.right = self.figure.subplotpars.right
        self.canvas_ax = canvas.figure.subplots()
        self.canvas_ax.set_xlabel("Time")
        self.axes = [self.canvas_ax]

        self.list_tags = QtWidgets.QComboBox(self)
        for name in self.name_vars:
            self.list_tags.addItem(name)
        button_add = QtWidgets.QPushButton('Add', self)
        button_remove = QtWidgets.QPushButton('Remove', self)
        button_add.clicked.connect(self.add_plot)
        button_remove.clicked.connect(self.remove_plot)

        layout = QtWidgets.QGridLayout(self._main)
        layout.addWidget(canvas, 0, 0)
        dropdown_layout = QtWidgets.QHBoxLayout()
        dropdown_layout.addWidget(self.list_tags)
        dropdown_layout.addWidget(button_add)
        dropdown_layout.addWidget(button_remove)
        layout.addLayout(dropdown_layout, 1, 0)
        self.show()

    def add_plot(self):
        selected_tag = self.list_tags.currentIndex()
        self.tags_on[selected_tag] = 1
        self.update_canvas()

    def remove_plot(self):
        selected_tag = self.list_tags.currentIndex()
        self.tags_on[selected_tag] = 0
        self.update_canvas()

    def create_nth_axes(self, n, dataset):
        if n == 0:
            ax = self.canvas_ax
        else:
            ax = self.canvas_ax.twinx()
            ax.spines["right"].set_position(("axes", 1+(n-1)*0.1))
            for direction in ["left", "bottom", "top"]:
                ax.spines[direction].set_visible(False)
            # adjust subplotparams to make space for new axes spine
            new_right = (self.right-self.left)/(1+(n-1)*0.1)+self.left
            self.figure.subplots_adjust(right=new_right)
        color = next(self.canvas_ax._get_lines.prop_cycler)['color']
        ax.set_ylabel(self.name_vars[dataset], color=color)
        ax.plot(self.x, self.y[:,dataset], color=color)
        return ax

    def clear_canvas(self):
        # Clear main axes
        self.canvas_ax.clear()
        # clear and remove other axes
        for ax in self.axes[1:]:
            ax.clear()
            ax.remove()
        self.axes = [self.canvas_ax] 
        self.figure.subplots_adjust(right=0.9)

    def update_canvas(self):
        self.clear_canvas()
        k = 0
        for i, tag in enumerate(self.tags_on):
            if tag:
                ax = self.create_nth_axes(k, i)
                if k > 0:
                    self.axes.append(ax)
                k += 1  
        self.canvas_ax.figure.canvas.draw()

if __name__ == '__main__':
    # Edit the number of elements in name_vars to try the code
    name_vars = ['V1','V2','V3','V4']
    app = QtWidgets.QApplication([])
    ex = ApplicationWindow(name_vars=name_vars)
    ex.show()
    app.exec_()

相关问题 更多 >