简体   繁体   中英

Tkinter and Matplotlib: relative position of canvas created by FigureCanvasTkAgg

I'm creating a GUI that should display a few widgets (eg buttons and scales) on the left side of the main window (or 'root', created by root=tk.Tk() ) and a graph on the right side. The graph would be created using matplotlib and the FigureCanvasTkAgg() backend (similar to what is found here ). Below is the code I've written so far.

import tkMessageBox, tkFileDialog
import sys
# Tkinter is for python 2; tkinter is for python 3
if sys.version_info[0] < 3:
    import Tkinter as tk
else:
    import tkinter as tk


class MainApp(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.parent.title('App')
        # call the widgets
        self.okButton()
        self.quitButton()
        self.readDataButton()
        self.clearDataButton()
        self.velScale()
        self.canvas()

    # print messages on the screen
    def printMessage(self):
        if (self.data):
            print("Data is loaded and accessible from here (printMessage()).")
        else:
            print('No data loaded...')

    ### OK button
    def okButton(self):
        self.okButton = tk.Button(self, text='Test', command=self.printMessage)
        self.okButton.grid()

    ### Quit button
    def quitButton(self):
        self.quitButton = tk.Button(self, text='Quit', command=self.confirmQuit)
        self.quitButton.grid()
    # confirm quitting
    def confirmQuit(self):
        answer = tkMessageBox.askyesno(title="App", message="Do you really want to quit?")
        if (answer):
            self.quit()

    # Read data button
    def readDataButton(self):
        self.data = None
        self.readDataButton = tk.Button(self, text='Import data', command=self.readData)
        self.readDataButton.grid()
    # reading data
    def readData(self):
        import os
        fullPath = dataList = tkFileDialog.askopenfilename(initialdir='path/to/initialdir')
        dataDir = os.path.split(fullPath)[0]+'/'
        self.data = readData(fullPath)

    # Clear data from current session
    def clearDataButton(self):
        self.clearData = tk.Button(self, text='Clear data', command=self.confirmClearData)
        self.clearData.grid()
    # confirm clearing data
    def confirmClearData(self):
        answer = tkMessageBox.askyesno(title="App", message="Are you sure you want to clear the loaded data?")
        if (answer):
            self.data = None
            tkMessageBox.showwarning(title="App", message="Data has been deleted.")

    # Velocity scale
    def velScale(self):
        self.velVar = tk.StringVar()
        velLabel = tk.Label(self, text="Scale value:", textvariable=self.velVar)
        velLabel.grid(row=4, column=0, columnspan=2, sticky=tk.W+tk.E)
        velScale = tk.Scale(self, from_=-500, to=+500, orient=tk.HORIZONTAL, resolution=20,
                        sliderlength=20, showvalue=0,
                        length=200, width=20,
                        command=self.onVelScale)
        velScale.grid(row=5, column=0)
    # update velLabel
    def onVelScale(self, val):
        self.velVar.set("Scale value: {:+0.0f}".format(float(val)))

    # Canvas
    def canvas(self):
        import matplotlib
        import matplotlib.pyplot as plt
        matplotlib.use("TkAgg")
        from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
        from matplotlib.figure import Figure

        self.f = Figure(figsize=(4,2))
        self.a = self.f.add_subplot(111)
        self.a.plot([1,2,3,4,5,6,7,8],[5,6,1,3,8,9,3,5])

        self.canvas = FigureCanvasTkAgg(self.f, master=self.parent)
        self.canvas.show()
        self.canvas.get_tk_widget().pack()

        self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.parent)
        self.toolbar.update()
        self.canvas._tkcanvas.pack()



if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("800x600+10+10")
    root.resizable(0, 0)
    MainApp(root).pack(side=tk.TOP)
    root.mainloop()

The result is this: Screenshot of the App's main window . Using .grid() for the button and scale widgets works as expected (I can place them wherever I want by specifying row and column ). However, I cannot have the same behavior for the canvas part. It seems that only .pack() will work with the canvas (and the result is shown in the link above).

Does anybody have any idea on how to workaround this? Or am I missing something?

Thank you for any suggestion.

I'm working with python3.3, I've changed your code to this:

import sys
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
# Tkinter is for python 2; tkinter is for python 3
if sys.version_info[0] < 3:
    import Tkinter as tk
    import tkMessageBox, tkFileDialog

else:
    import tkinter as tk
    from tkinter import messagebox as tkMessageBox
    from tkinter import filedialog as tkFileDialog

class MainApp(tk.Frame):

    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.parent = parent
        self.parent.title('App')
        # call the widgets
        self.okButton()
        self.quitButton()
        self.readDataButton()
        self.clearDataButton()
        self.velScale()
        self.canvas()

    # print messages on the screen
    def printMessage(self):
        if (self.data):
            print("Data is loaded and accessible from here (printMessage()).")
        else:
            print('No data loaded...')

    ### OK button
    def okButton(self):
        self.okButton = tk.Button(self, text='Test', command=self.printMessage)
        self.okButton.grid(column=1, row=1, sticky="nesw")

    ### Quit button
    def quitButton(self):
        self.quitButton = tk.Button(self, text='Quit', command=self.confirmQuit)
        self.quitButton.grid(column=1, row=2, sticky="nesw")
    # confirm quitting
    def confirmQuit(self):
        answer = tkMessageBox.askyesno(title="App", message="Do you really want to quit?")
        if (answer):
            self.quit()

    # Read data button
    def readDataButton(self):
        self.data = None
        self.readDataButton = tk.Button(self, text='Import data', command=self.readData)
        self.readDataButton.grid(column=1, row=3, sticky="nesw")
    # reading data
    def readData(self):
        import os
        fullPath = dataList = tkFileDialog.askopenfilename(initialdir='path/to/initialdir')
        dataDir = os.path.split(fullPath)[0]+'/'
        self.data = readData(fullPath)

    # Clear data from current session
    def clearDataButton(self):
        self.clearData = tk.Button(self, text='Clear data', command=self.confirmClearData)
        self.clearData.grid(column=1, row=4, sticky="nesw")
    # confirm clearing data
    def confirmClearData(self):
        answer = tkMessageBox.askyesno(title="App", message="Are you sure you want to clear the loaded data?")
        if (answer):
            self.data = None
            tkMessageBox.showwarning(title="App", message="Data has been deleted.")

    # Velocity scale
    def velScale(self):
        self.velVar = tk.StringVar()
        velLabel = tk.Label(self, text="Scale value:", textvariable=self.velVar)
        velLabel.grid(row=4, column=0, columnspan=2, sticky=tk.W+tk.E)
        velScale = tk.Scale(self, from_=-500, to=+500, orient=tk.HORIZONTAL, resolution=20,
                        sliderlength=20, showvalue=0,
                        length=200, width=20,
                        command=self.onVelScale)
        velScale.grid(column=1, row=5, sticky="nesw")
    # update velLabel
    def onVelScale(self, val):
        self.velVar.set("Scale value: {:+0.0f}".format(float(val)))

    # Canvas
    def canvas(self):
        self.f = Figure(figsize=(4,2))
        self.a = self.f.add_subplot(111)
        self.a.plot([1,2,3,4,5,6,7,8],[5,6,1,3,8,9,3,5])

        self.canvas = FigureCanvasTkAgg(self.f, master=self)
        self.canvas.get_tk_widget().grid(column=2, row=1, rowspan=5, sticky="nesw")

        self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.parent)

if __name__ == "__main__":
    root = tk.Tk()
    root.geometry("800x600+10+10")
    root.resizable(0, 0)
    MainApp(root).pack(side=tk.TOP)
    root.mainloop()

to show what i meant about not calling show i also moved your matplotlib imports to the top of the file and changed the other imports to suit python 2/3, this seems to grid fine for me, can you clarify what it wasn't doing that you expected it to do? (if this still doesn't work)

使用canvas()函数并将FigureCanvasTkAgg作为canvas调用不会出错

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM