2016-01-26 2 views
1

Я работаю над приложением Python 3.4 для отображения графиков плотности мощности для сигналов ЭЭГ. Я хочу наложить цветовые пятна на графики, чтобы указать стандартные полосы частот для дельта, тета, альфа, бета и гамма.Неожиданное поведение matplotlib.patches при изменении размера окна

Программа скелета ниже показывает, как график появляется во всплывающем окне при нажатии кнопки дисплея. Все отлично при изменении размера первого всплывающего окна. Проблема возникает при изменении размера второго всплывающего окна (при повторном нажатии кнопки дисплея). Затем цветные пятна появляются в неправильных местах и ​​не соответствуют указанным полосам частот.

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

Я подозреваю, что это проблема с инициализацией координат, но я не понимаю, как это исправить. Какие-либо предложения?

ответ

0

Похоже, вы должны переопределить исправления для каждого нового участка - в порядке для их «преобразования» для правильной работы.

У вас должен быть только один экземпляр Tk для каждого приложения. Для вторичных окон использовать Toplevel.

Кроме того, когда вы встраивание 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() 
+0

Oblivion, спасибо за проницательный и замечательный ответ. Я применил ваше предложение использовать Toplevel для вторичных окон и решает проблему изменения размера этих окон. Избавиться от «интерфейса pyplot» будет значительно сложнее - в моем коде есть десятки вхождений plt и их замена - дело не простое. На данный момент всплывающие окна для временных рядов-графиков, спектрограмм, радиолокационных карт, похоже, работают нормально. Я полагаю, что могут быть скрытые проблемы, но я не понимаю, почему это проблема. –

Смежные вопросы