【问题标题】:Unexpected behavior of matplotlib.patches upon window resizing调整窗口大小时 matplotlib.patches 的意外行为
【发布时间】:2016-01-26 05:05:48
【问题描述】:

我正在开发一个 Python 3.4 应用程序,用于显示 EEG 信号的功率谱密度图。我想在图表上叠加色块以指示 delta、theta、alpha、beta 和 gamma 的标准频带。

下面的骨架程序显示了按下“显示”按钮时图形在弹出窗口中的显示方式。调整第一个弹出窗口的大小时一切都很好。调整第二个弹出窗口的大小时会出现此问题(通过再次单击“显示”按钮)。然后,色块出现在不正确的位置,并且与指示的频带不对应。

import tkinter as tk
from tkinter import BOTH, TOP, Y
from tkinter import ttk
from tkinter import Frame
import matplotlib.pyplot as plt
import matplotlib.patches as patches
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

# The following is used for psd semi-log graphs only
XMIN = 0 # minimum frequency
XMAX = 55 # maximum frequency
YMIN = 0.1 # minimum PSD value
YMAX = 100 # maximum PSD value
lblY = YMAX - 30

band_patches = [
    patches.Rectangle((XMIN, YMIN), 4, YMAX, facecolor="blue", alpha=0.2),
    patches.Rectangle((4, YMIN), 4, YMAX, facecolor="cyan", alpha=0.2),
    patches.Rectangle((8, YMIN), 4, YMAX, facecolor="green", alpha=0.2),
    patches.Rectangle((12, YMIN), 18, YMAX, facecolor="orange", alpha=0.2),
    patches.Rectangle((30, YMIN), XMAX-30, YMAX, facecolor="magenta", alpha=0.2),
]


class PV(ttk.Frame):

    def __init__(self, name='demo'):
        ttk.Frame.__init__(self, name=name)
        self.pack(expand=Y, fill=BOTH)
        self.master.title('Demo')
        self._create_viewer_panel()

    def _create_viewer_panel(self):
        viewerPanel = Frame(self, name='pv')
        viewerPanel.pack(side=TOP, fill=BOTH, expand=Y)
        # create the notebook
        nb = ttk.Notebook(viewerPanel, name='notebook')
        nb.enable_traversal()
        nb.pack(fill=BOTH, expand=Y, padx=2, pady=3)
        self._create_UI_tab(nb)  #NEW

    def _create_UI_tab(self, nb):
        # frame to hold contentx
        self.frame = ttk.Frame(nb, height='10i', width='8i', name='main')
        btn0 = ttk.Button(self.frame, text='Display', width='25', command=self.draw_psd)
        btn0.grid(row=0, column=1)
        # add to notebook (underline = index for short-cut character)
        nb.add(self.frame, text='Main', underline=0, padding=2)

    def draw_psd(self):
        popup = tk.Tk()
        popup.geometry('720x480') # Set dimensions of popup window to 800x500 pixels
        popup.wm_title("Power Spectral Density")
        p = plt.figure()
        self.ax = plt.subplot(111)

        box = self.ax.get_position()
        self.ax.set_position([box.x0, box.y0, box.width, box.height*0.95])

        self.ax.set_yscale('log')
        self.ax.set_xscale('linear')
        plt.xlabel('Frequency (Hz)')
        plt.xlim(XMIN, XMAX)
        plt.ylim(YMIN, YMAX)
        for ptch in band_patches:
            self.ax.add_patch(ptch)
        self.ax.annotate('delta', xy=(2,lblY), fontsize=10, color=None, horizontalalignment='center')
        self.ax.annotate('theta', xy=(6,lblY), fontsize=10, color=None, horizontalalignment='center')
        self.ax.annotate('alpha', xy=(10,lblY), fontsize=10, color=None, horizontalalignment='center')
        self.ax.annotate('beta', xy=(21,lblY), fontsize=10, color=None, horizontalalignment='center')
        self.ax.annotate('gamma', xy=(42,lblY), fontsize=10, color=None, horizontalalignment='center')

        plt.show()

        canvas = FigureCanvasTkAgg(p, master=popup)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
        toolbar = NavigationToolbar2TkAgg(canvas, popup)
        toolbar.update()
        canvas._tkcanvas.pack(side=tk.TOP, expand=1)

        popup.mainloop()
        return None

if __name__ == '__main__':
    PV().mainloop()

我怀疑这是初始化坐标的问题,但我不知道如何解决它。有什么建议吗?

【问题讨论】:

    标签: python python-3.x matplotlib tkinter resize


    【解决方案1】:

    看起来您必须为每个新地块重新定义补丁 - 按顺序 以便他们的“转换”正常工作。

    每个应用程序只能有一个 Tk 实例。对于辅助窗口 使用顶层。

    此外,当您将 matplotlib 嵌入到 tkinter 应用程序中时,它是 最好避免使用所谓的“pyplot 接口” - plt.show() 等。相反, 明确使用 matplotlib API:plt.xlabel(...) -> ax.set_xlabel(...)

    import tkinter as tk
    from tkinter import BOTH, TOP, Y
    from tkinter import ttk
    from tkinter import Frame, Toplevel
    ##import matplotlib.pyplot as plt
    import matplotlib.patches as patches
    from matplotlib.figure import Figure
    from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
    
    
    XMIN = 0 # minimum frequency
    XMAX = 55 # maximum frequency
    YMIN = 0.1 # minimum PSD value
    YMAX = 100 # maximum PSD value
    lblY = YMAX - 30
    
    ##band_patches = [
    ##    patches.Rectangle((XMIN, YMIN), 4, YMAX, facecolor="blue", alpha=0.2),
    ##    patches.Rectangle((4, YMIN), 4, YMAX, facecolor="cyan", alpha=0.2),
    ##    patches.Rectangle((8, YMIN), 4, YMAX, facecolor="green", alpha=0.2),
    ##    patches.Rectangle((12, YMIN), 18, YMAX, facecolor="orange", alpha=0.2),
    ##    patches.Rectangle((30, YMIN), XMAX-30, YMAX, facecolor="magenta", alpha=0.2),
    ##]
    
    class WinPsd(Toplevel):
        def __init__(self, master, cnf={}, **kw):
            Toplevel.__init__(self, master, **kw)
            self._create_plot()
    
        def _create_plot(self):
            self.fig = Figure()
            self.ax = self.fig.add_subplot(111)
    
            ax = self.ax
            box = ax.get_position()
            ax.set_position([box.x0, box.y0, box.width, box.height*0.95])
    
            ax.set_yscale('log')
            ax.set_xscale('linear')
    
            ax.set_xlabel('Frequency (Hz)')
            ax.set_xlim(XMIN, XMAX)
            ax.set_ylim(YMIN, YMAX)
    
            band_patches = [
                            patches.Rectangle((XMIN, YMIN), 4, YMAX,
                                              facecolor="blue", alpha=0.2
                                              ),
                            patches.Rectangle((4, YMIN), 4, YMAX,
                                              facecolor="cyan", alpha=0.2
                                              ),
                            patches.Rectangle((8, YMIN), 4, YMAX,
                                              facecolor="green", alpha=0.2
                                              ),
                            patches.Rectangle((12, YMIN), 18, YMAX,
                                              facecolor="orange", alpha=0.2
                                              ),
                            patches.Rectangle((30, YMIN), XMAX-30, YMAX,
                                              facecolor="magenta", alpha=0.2
                                              ),
                           ]
    
            for ptch in band_patches:
                ax.add_patch(ptch)
            ax.annotate('delta', xy=(2,lblY), fontsize=10, color=None, horizontalalignment='center')
            ax.annotate('theta', xy=(6,lblY), fontsize=10, color=None, horizontalalignment='center')
            ax.annotate('alpha', xy=(10,lblY), fontsize=10, color=None, horizontalalignment='center')
            ax.annotate('beta', xy=(21,lblY), fontsize=10, color=None, horizontalalignment='center')
            ax.annotate('gamma', xy=(42,lblY), fontsize=10, color=None, horizontalalignment='center')
    
            xdata,ydata = [],[]
            self.line, = ax.plot(xdata, ydata)
    
            canvas = FigureCanvasTkAgg(self.fig, master=self)
    
            toolbar = NavigationToolbar2TkAgg(canvas, self)
            toolbar.update()
            canvas._tkcanvas.pack(fill='both', expand=1)
            canvas.show()
    
        def draw(self, xdata, ydata):
            self.line.set_xdata(xdata)
            self.line.set_ydata(ydata)
            self.fig.canvas.draw()
    
    
    class WinMain(tk.Tk):
        def __init__(self, name='demo', **kw):
            tk.Tk.__init__(self, **kw)
            self.title(name)
            self._create_viewer_panel()
    
        def _create_viewer_panel(self):
            viewerPanel = ttk.Frame(self, name='pv')
            viewerPanel.pack(side=TOP, fill=BOTH, expand=Y)
            nb = ttk.Notebook(viewerPanel, name='notebook')
            nb.enable_traversal()
            nb.pack(fill=BOTH, expand=Y, padx=2, pady=3)
            self._create_UI_tab(nb) 
    
        def _create_UI_tab(self, nb):
            self.frame = ttk.Frame(nb, height='10i', width='8i', name='main')
            btn0 = ttk.Button(self.frame, text='Display', width='25', command=self.show_psd)
            btn0.grid(row=0, column=1)
            nb.add(self.frame, text='Main', underline=0, padding=2)
    
        def show_psd(self):
            xdata,ydata = [range(50)],[range(50)] #get some data from somewhere
            popup = WinPsd(self)
            popup.wm_title("Power Spectral Density")
            popup.draw(xdata, ydata)
    
    
    if __name__ == '__main__':
        WinMain().mainloop()
    

    【讨论】:

    • Oblivion,感谢您提供富有洞察力且非常有用的答案。我已经实施了您将 Toplevel 用于辅助窗口的建议,它解决了调整这些窗口大小的问题。摆脱“pyplot 接口”将变得相当困难——我的代码中有几十个 plt 出现,替换它们并不是一件简单的事情。目前,时间序列图、频谱图、雷达图的弹出窗口似乎工作正常。我想可能有隐藏的问题,但我真的不明白为什么这是一个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 1970-01-01
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多