Tkinter 和 Matplotlib:由 FigureCanvasTkAgg 创建的 canvas 的相对位置
Tkinter and Matplotlib: relative position of canvas created by FigureCanvasTkAgg
我正在创建一个 GUI,它应该在主要 window(或 'root',由 root=tk.Tk()
创建)的左侧显示一些小部件(例如按钮和刻度) ) 和右侧的图表。该图将使用 matplotlib 和 FigureCanvasTkAgg()
后端创建(类似于发现的 here)。以下是我到目前为止编写的代码。
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()
结果是这样的:Screenshot of the App's main window。对按钮和缩放小部件使用 .grid()
可以按预期工作(我可以通过指定 row
和 column
将它们放置在我想要的任何位置)。但是,我不能对 canvas 部分有相同的行为。似乎只有 .pack()
可以与 canvas 一起使用(结果显示在上面的 link 中)。
有人知道如何解决这个问题吗?还是我遗漏了什么?
感谢您的任何建议。
我正在使用 python3.3,我已将您的代码更改为:
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()
为了展示我不调用 show
的意思,我还将您的 matplotlib 导入移到了文件的顶部,并更改了其他导入以适合 python 2/3,这似乎是网格对我来说很好,你能澄清一下它没有做你期望它做的事情吗? (如果这仍然不起作用)
使用 canvas() 函数并以 canvas
调用 FigureCanvasTkAgg 时不会出现错误
我正在创建一个 GUI,它应该在主要 window(或 'root',由 root=tk.Tk()
创建)的左侧显示一些小部件(例如按钮和刻度) ) 和右侧的图表。该图将使用 matplotlib 和 FigureCanvasTkAgg()
后端创建(类似于发现的 here)。以下是我到目前为止编写的代码。
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()
结果是这样的:Screenshot of the App's main window。对按钮和缩放小部件使用 .grid()
可以按预期工作(我可以通过指定 row
和 column
将它们放置在我想要的任何位置)。但是,我不能对 canvas 部分有相同的行为。似乎只有 .pack()
可以与 canvas 一起使用(结果显示在上面的 link 中)。
有人知道如何解决这个问题吗?还是我遗漏了什么?
感谢您的任何建议。
我正在使用 python3.3,我已将您的代码更改为:
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()
为了展示我不调用 show
的意思,我还将您的 matplotlib 导入移到了文件的顶部,并更改了其他导入以适合 python 2/3,这似乎是网格对我来说很好,你能澄清一下它没有做你期望它做的事情吗? (如果这仍然不起作用)
使用 canvas() 函数并以 canvas
调用 FigureCanvasTkAgg 时不会出现错误