为什么FigureCanvasTkAgg在滚动的matplotlib GUI中禁用鼠标滚动事件?

时间:2012-10-23 14:39:23

标签: python matplotlib tkinter

嘿,我制作了这个GUI,它在带有滚动画布的tkinter界面中显示了matplotlib图。但是当绘图添加到画布时,鼠标的滚动事件不再起作用。我完全是tkinter的新手,所以当你检查代码时,如果可以改进,请考虑这一点。所以这就是:

from Tkinter import *

import matplotlib
#matplotlib.use('TkAgg')

from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
import os
import matplotlib.mlab as mlab

class App:

def __init__(self, master, figureList=[]):
    frame = Frame(master, bg='#00BFFF', borderwidth=1, relief=RAISED)
    frame.pack(expand=YES, fill=BOTH)

    master.title("Listed Plots")
    #Fullscreen for windows
    if os.name == 'nt':
        master.wm_state('zoomed')

    #Create a Canvas
    canvas=Canvas(frame,bg='#00BFFF', relief=SUNKEN)       
    canvas.config(highlightthickness=0) 

    #Create a Scrollbar Horisontal
    hbar=Scrollbar(frame,orient=HORIZONTAL)
    hbar.pack(side=BOTTOM,fill=X)
    hbar.config(command=canvas.xview)

    #Create a Scrollbar Vertical
    vbar=Scrollbar(frame,orient=VERTICAL)
    vbar.pack(side=RIGHT,fill=Y)
    vbar.config(command=canvas.yview)

    canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
    canvas.pack(side=TOP,expand=True,fill=BOTH)

    #Create a Frame in the canvas
    canvFrame = Frame(canvas, bg='#00BFFF')
    canvFrame.pack()

    Label(canvFrame, text="Matplots of data", bg='#B4EEB4').pack(side=TOP,fill=X)

    plotNbr = 1
    windowHeight = 10

    for fig in figureList:
        Label(canvFrame, text="#Plotnumber: " + str(plotNbr), bg='#FF7F24').pack(side=TOP,fill=X)

        frm = Frame(canvFrame, bg='#9FB6CD')
        frm.pack(side=TOP, fill=X)

        self.canvasMPL2, self.canvasMPLToolBar2 = getCanvas(frm, fig)
        self.canvasMPL2.pack(side=TOP)
        self.canvasMPLToolBar2.pack(side=TOP)

        #Create space for the plot and tool bar.
        windowHeight = windowHeight + 680
        plotNbr = plotNbr + 1


    canvas.config(width=1200,height= windowHeight)
    canvas.config(scrollregion=(0,0,1200, windowHeight))

    canvas.create_window(0,0, anchor = NW, window = canvFrame, width = 1200, height = windowHeight)

    canvas.focus_set() #Doesnt work with FigureCanvasTkAgg, this steals the focus 
    #canvas.focus_force()
    #canvas.grab_set_global()

    #scrollwheel settings
    canvas.configure(yscrollincrement='25')

    def rollWheel(event):
        #print "Mousescroll"
        direction = 0
        if event.num == 5 or event.delta == -120:
            direction = 1
        if event.num == 4 or event.delta == 120:
            direction = -1
        event.widget.yview_scroll(direction, UNITS)

    canvas.bind('<MouseWheel>', lambda event: rollWheel(event))
    canvas.bind('<Button-4>', lambda event: rollWheel(event))
    canvas.bind('<Button-5>', lambda event: rollWheel(event))


plottedFigures = [] #To store matplotlib figures

def addPlottedFig(figure):
    '''
    Matplotlib figures to be shown in the GUI
    '''
    plottedFigures.append(figure)


def getCanvas(masterWidget, figure=None):
    '''
    Returns canvas of plot and canvas of tool bar
    '''
    if(figure==None):
        f = getHistoGramPlot() #For testing
        canvas = FigureCanvasTkAgg(f, master=masterWidget)
    else:
        canvas = FigureCanvasTkAgg(figure, master=masterWidget)

    canvas.show()
    toolbar = NavigationToolbar2TkAgg( canvas, masterWidget )
    toolbar.update()

    #return the plot and the toolbar
    return canvas.get_tk_widget(), canvas._tkcanvas

def initiate(FigureList=None):
    '''
    Start the GUI Application
    '''
    root = Tk()
    app = App(root, FigureList)
    root.mainloop()


def main():
    '''
    Shows 2 histograms plots in a tkinter GUI
    '''
    fig1 = getHistoGramPlot()
    fig2 = getHistoGramPlot()

    figList = [fig1, fig2]

    initiate(figList)

def getHistoGramPlot():
    '''
    Return a figure of a histogram
    '''
    mu, sigma = 100, 15
    x = mu + sigma * np.random.randn(10000)
    fig = plt.figure(figsize=(12, 6), dpi=100)
    ax = fig.add_subplot(111)
    n, bins, patches = ax.hist(x, 50, normed=1, facecolor='green', alpha=0.6)
    bincenters = 0.5*(bins[1:]+bins[:-1])
    mu = np.median(x)
    sigma = np.std(x)
    y = mlab.normpdf( bincenters, mu, sigma)
    l = ax.plot(bincenters, y, 'r--', linewidth=1)
    ax.set_xlabel('Values')
    ax.set_ylabel('Probability')
    xlimTop = max(x)
    xlimBottom = min(x)
    ax.set_xlim(xlimBottom, xlimTop)
    ax.grid(True)

    return fig

if __name__ == '__main__':
    main()

2 个答案:

答案 0 :(得分:3)

快速&amp;肮脏的解决方案:

  • 使主画布成为App类的属性
  • 使rollWheel成为App类的方法
  • 在窗口而不是画布上创建鼠标滚轮绑定。这将 捕获窗口内所有小部件的鼠标滚轮事件。
  • 在鼠标滚轮事件上滚动画布

from Tkinter import *

import matplotlib
#matplotlib.use('TkAgg')

from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import numpy as np
import os
import matplotlib.mlab as mlab

class App:

    def __init__(self, master, figureList=[]):
        frame = Frame(master, bg='#00BFFF', borderwidth=1, relief=RAISED)
        frame.pack(expand=YES, fill=BOTH)

        master.title("Listed Plots")
        #Fullscreen for windows
        if os.name == 'nt':
            master.wm_state('zoomed')

        #Create a Canvas
        self.canvas = canvas = Canvas(frame,bg='#00BFFF', relief=SUNKEN)       
        canvas.config(highlightthickness=0) 

        #Create a Scrollbar Horisontal
        hbar=Scrollbar(frame,orient=HORIZONTAL)
        hbar.pack(side=BOTTOM,fill=X)
        hbar.config(command=canvas.xview)

        #Create a Scrollbar Vertical
        vbar=Scrollbar(frame,orient=VERTICAL)
        vbar.pack(side=RIGHT,fill=Y)
        vbar.config(command=canvas.yview)

        canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
        canvas.pack(side=TOP,expand=True,fill=BOTH)

        #Create a Frame in the canvas
        canvFrame = Frame(canvas, bg='#00BFFF')
        canvFrame.pack()

        Label(canvFrame, text="Matplots of data", bg='#B4EEB4').pack(side=TOP,fill=X)

        plotNbr = 1
        windowHeight = 10

        for fig in figureList:
            Label(canvFrame, text="#Plotnumber: " + str(plotNbr), bg='#FF7F24').pack(side=TOP,fill=X)

            frm = Frame(canvFrame, bg='#9FB6CD')
            frm.pack(side=TOP, fill=X)

            self.canvasMPL2, self.canvasMPLToolBar2 = getCanvas(frm, fig)
            self.canvasMPL2.pack(side=TOP)
            self.canvasMPLToolBar2.pack(side=TOP)

            #Create space for the plot and tool bar.
            windowHeight = windowHeight + 680
            plotNbr = plotNbr + 1


        canvas.config(width=1200,height= windowHeight)
        canvas.config(scrollregion=(0,0,1200, windowHeight))

        canvas.create_window(0,0, anchor = NW, window = canvFrame, width = 1200, height = windowHeight)

        canvas.focus_set() #Doesnt work with FigureCanvasTkAgg, this steals the focus 
        #canvas.focus_force()
        #canvas.grab_set_global()

        #scrollwheel settings
        canvas.configure(yscrollincrement='25')

##        def rollWheel(event):
##            #print "Mousescroll"
##            direction = 0
##            if event.num == 5 or event.delta == -120:
##                direction = 1
##            if event.num == 4 or event.delta == 120:
##                direction = -1
##            event.widget.yview_scroll(direction, UNITS)

        master.bind('<MouseWheel>', lambda event,s=self: self.rollWheel(event))
        canvas.bind('<Button-4>', lambda event: rollWheel(event))
        canvas.bind('<Button-5>', lambda event: rollWheel(event))

    def rollWheel(self, event):
        #print "Mousescroll"
        direction = 0
        if event.num == 5 or event.delta == -120:
            direction = 1
        if event.num == 4 or event.delta == 120:
            direction = -1
##        event.widget.yview_scroll(direction, UNITS)
        self.canvas.yview_scroll(direction, UNITS)    

plottedFigures = [] #To store matplotlib figures

def addPlottedFig(figure):
    '''
    Matplotlib figures to be shown in the GUI
    '''
    plottedFigures.append(figure)


def getCanvas(masterWidget, figure=None):
    '''
    Returns canvas of plot and canvas of tool bar
    '''
    if(figure==None):
        f = getHistoGramPlot() #For testing
        canvas = FigureCanvasTkAgg(f, master=masterWidget)
    else:
        canvas = FigureCanvasTkAgg(figure, master=masterWidget)

    canvas.show()
    toolbar = NavigationToolbar2TkAgg( canvas, masterWidget )
    toolbar.update()

    #return the plot and the toolbar
    return canvas.get_tk_widget(), canvas._tkcanvas

def initiate(FigureList=None):
    '''
    Start the GUI Application
    '''
    root = Tk()
    app = App(root, FigureList)
    root.mainloop()


def main():
    '''
    Shows 2 histograms plots in a tkinter GUI
    '''
    fig1 = getHistoGramPlot()
    fig2 = getHistoGramPlot()

    figList = [fig1, fig2]

    initiate(figList)

def getHistoGramPlot():
    '''
    Return a figure of a histogram
    '''
    mu, sigma = 100, 15
    x = mu + sigma * np.random.randn(10000)
    fig = plt.figure(figsize=(12, 6), dpi=100)
    ax = fig.add_subplot(111)
    n, bins, patches = ax.hist(x, 50, normed=1, facecolor='green', alpha=0.6)
    bincenters = 0.5*(bins[1:]+bins[:-1])
    mu = np.median(x)
    sigma = np.std(x)
    y = mlab.normpdf( bincenters, mu, sigma)
    l = ax.plot(bincenters, y, 'r--', linewidth=1)
    ax.set_xlabel('Values')
    ax.set_ylabel('Probability')
    xlimTop = max(x)
    xlimBottom = min(x)
    ax.set_xlim(xlimBottom, xlimTop)
    ax.grid(True)

    return fig

if __name__ == '__main__':
    main()

答案 1 :(得分:1)

class App:代码的缩进缩减不正确。我假设rollWheel__init__中定义的函数。

进行此更改:

    def rollWheel(event):
        #print "Mousescroll"
        direction = 0
        if event.num == 5 or event.delta == -120:
            direction = 1
        if event.num == 4 or event.delta == 120:
            direction = -1
        # Tell the canvas to scroll directly
        canvas.yview_scroll(direction, UNITS)  

    canvas.bind_all('<MouseWheel>', lambda event: rollWheel(event))
    canvas.bind_all('<Button-4>', lambda event: rollWheel(event))
    canvas.bind_all('<Button-5>', lambda event: rollWheel(event))

有关.bind_all和tkinter绑定的详细信息,请查看effbot's tutorial