Python 如何将多个单独的图形放入一个Tkinter窗口?
我一直在运行以下脚本:Python 如何将多个单独的图形放入一个Tkinter窗口?,python,matplotlib,tkinter,Python,Matplotlib,Tkinter,我一直在运行以下脚本: from threading import Thread import serial import time import collections import matplotlib.pyplot as plt import matplotlib.animation as animation import struct import copy from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg,
from threading import Thread
import serial
import time
import collections
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import struct
import copy
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import tkinter as Tk
from tkinter.ttk import Frame
class serialPlot:
def __init__(self, serialPort='/dev/ttyACM0', serialBaud=38400, plotLength=100, dataNumBytes=2, numPlots=4):
self.port = serialPort
self.baud = serialBaud
self.plotMaxLength = plotLength
self.dataNumBytes = dataNumBytes
self.numPlots = numPlots
self.rawData = bytearray(numPlots * dataNumBytes)
self.dataType = None
if dataNumBytes == 2:
self.dataType = 'h' # 2 byte integer
elif dataNumBytes == 4:
self.dataType = 'f' # 4 byte float
self.data = []
self.privateData = None
for i in range(numPlots): # give an array for each type of data and store them in a list
self.data.append(collections.deque([0] * plotLength, maxlen=plotLength))
self.isRun = True
self.isReceiving = False
self.thread = None
self.plotTimer = 0
self.previousTimer = 0
# self.csvData = []
print('Trying to connect to: ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
try:
self.serialConnection = serial.Serial(serialPort, serialBaud, timeout=4)
print('Connected to ' + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
except:
print("Failed to connect with " + str(serialPort) + ' at ' + str(serialBaud) + ' BAUD.')
def readSerialStart(self):
if self.thread == None:
self.thread = Thread(target=self.backgroundThread)
self.thread.start()
# Block till we start receiving values
while self.isReceiving != True:
time.sleep(0.1)
def getSerialData(self, frame, lines, lineValueText, lineLabel, timeText, pltNumber):
if pltNumber == 0: # in order to make all the clocks show the same reading
currentTimer = time.perf_counter()
self.plotTimer = int((currentTimer - self.previousTimer) * 1000) # the first reading will be erroneous
self.previousTimer = currentTimer
self.privateData = copy.deepcopy(self.rawData) # so that the 3 values in our plots will be synchronized to the same sample time
timeText.set_text('' + str(self.plotTimer) + '')
data = self.privateData[(pltNumber*self.dataNumBytes):(self.dataNumBytes + pltNumber*self.dataNumBytes)]
value, = struct.unpack(self.dataType, data)
self.data[pltNumber].append(value) # we get the latest data point and append it to our array
lines.set_data(range(self.plotMaxLength), self.data[pltNumber])
lineValueText.set_text('[' + lineLabel + '] = ' + str(value))
def backgroundThread(self): # retrieve data
time.sleep(1.0) # give some buffer time for retrieving data
self.serialConnection.reset_input_buffer()
while (self.isRun):
self.serialConnection.readinto(self.rawData)
self.isReceiving = True
#print(self.rawData)
def sendSerialData(self, data):
self.serialConnection.write(data.encode('utf-8'))
def close(self):
self.isRun = False
self.thread.join()
self.serialConnection.close()
print('Disconnected...')
# df = pd.DataFrame(self.csvData)
# df.to_csv('/home/rikisenia/Desktop/data.csv')
class Window(Frame):
def __init__(self, figure, master, SerialReference):
Frame.__init__(self, master)
self.entry = []
self.setPoint = None
self.master = master # a reference to the master window
self.serialReference = SerialReference # keep a reference to our serial connection so that we can use it for bi-directional communicate from this class
self.initWindow(figure) # initialize the window with our settings
def initWindow(self, figure):
self.master.title("Haptic Feedback Grasping Controller")
canvas = FigureCanvasTkAgg(figure, master=self.master)
toolbar = NavigationToolbar2Tk(canvas, self.master)
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
# create out widgets in the master frame
lbl1 = Tk.Label(self.master, text="Distance")
lbl1.pack(padx=5, pady=5)
self.entry = Tk.Entry(self.master)
self.entry.insert(0, '0') # (index, string)
self.entry.pack(padx=5)
SendButton = Tk.Button(self.master, text='Send', command=self.sendFactorToMCU)
SendButton.pack(padx=5)
def sendFactorToMCU(self):
self.serialReference.sendSerialData(self.entry.get() + '%') # '%' is our ending marker
def main():
# portName = 'COM5'
portName = '/dev/ttyACM0'
baudRate = 38400
maxPlotLength = 100 # number of points in x-axis of real time plot
dataNumBytes = 4 # number of bytes of 1 data point
numPlots = 1 # number of plots in 1 graph
s = serialPlot(portName, baudRate, maxPlotLength, dataNumBytes, numPlots) # initializes all required variables
s.readSerialStart() # starts background thread
# plotting starts below
pltInterval = 50 # Period at which the plot animation updates [ms]
xmin = 0
xmax = maxPlotLength
ymin = -(1)
ymax = 200
fig = plt.figure()
ax = plt.axes(xlim=(xmin, xmax), ylim=(float(ymin - (ymax - ymin) / 10), float(ymax + (ymax - ymin) / 10)))
ax.set_title('Strain Gauge/ToF')
ax.set_xlabel("Time")
ax.set_ylabel("Force/Distance")
# put our plot onto Tkinter's GUI
root = Tk.Tk()
app = Window(fig, root, s)
lineLabel = ['W', 'X', 'Y', 'Z']
style = ['y-', 'r-', 'c-', 'b-'] # linestyles for the different plots
timeText = ax.text(0.70, 0.95, '', transform=ax.transAxes)
lines = []
lineValueText = []
for i in range(numPlots):
lines.append(ax.plot([], [], style[i], label=lineLabel[i])[0])
lineValueText.append(ax.text(0.70, 0.90-i*0.05, '', transform=ax.transAxes))
anim = animation.FuncAnimation(fig, s.getSerialData, fargs=(lines, lineValueText, lineLabel, timeText), interval=pltInterval) # fargs has to be a tuple
plt.legend(loc="upper left")
root.mainloop() # use this instead of plt.show() since we are encapsulating everything in Tkinter
s.close()
if __name__ == '__main__':
main()
一个窗口出现,没有数据通过它,即使我有4个传感器的数据来自Arduino。该窗口包含1个图形,其中当前有4个绘图。我想要4个图形,每个图形在一个窗口中有一个绘图。我一直在使用python作为参考来绘制图形。数据传输的代码也在链接中。我试着结合他的两个不同的代码,并调试它,以制作4个图形,每个图形都有一个绘图,以使用一个Tkinter GUI窗口,但它不起作用。我还得到一个TypeError错误:getSerialData()缺少1个必需的位置参数:“pltNumber”。如果pltNumber在代码的括号中,我不知道为什么会出现这个错误。我是python的初学者。我应该改变什么才能使代码正常工作
该脚本可以生成4个单独的图形,每个图形都有一个不在Tkinter GUI中的绘图(可用于4个传感器,但我需要在Tkinter窗口中使用它们):
编辑:被误解的问题,但仍将此保留在此处,因为它对您处理图形很有用 下面是我用来生成5个列表框的一些代码,并将它们附加到字典中,以便稍后在代码中引用它们
self.listboxes = []
for i in range(5):
self.lb = tk.Listbox(self.modeSelect)
self.lb.configure(background='#2f2a2d', exportselection='false', font='{Arial} 12 {}', foreground='#feffff', height='23')
self.lb.configure(relief='flat', width='9', justify='center', selectbackground='#feffff', selectforeground='#000000')
self.lb.pack(side='left')
self.listboxes.append(self.lb)
self.lb.bind("<Double-1>", self.getIndexLB)
这样,您可以为每个图形指定点,还可以通过执行以下操作同时控制所有图形:
for child in self.modeSelect.winfo_children():
if isinstance(child, tk.Listbox):
child.delete(index)
我认为类似的东西可能有用
《华盛顿邮报》也提到了类似的问题
这里我们可以使用matplotlib的后端类,即FigureCanvasTkAgg
。
它的工作原理类似于tkinter画布,但具有将图形绘制到其中的附加功能
这意味着我们可以初始化多个matplotlib图形,在其上绘制图形,然后将这些图形绘制到画布上。
这允许我们在同一tkinter窗口上绘制多个图形
要导入此类-:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
然后,可以使用matplotlib的figure对象在画布上绘制图形,如so-:
from matplotlib.figure import Figure
fig = Figure(...) # Initializing the figure object.
canvas = FigureCanvasTkAgg(fig, master=root) # Initializing the FigureCanvasTkAgg Class Object.
tk_canvas = canvas.get_tk_widget() # Getting the Figure canvas as a tkinter widget.
tk_canvas.pack() # Packing it into it's master window.
canvas.draw() # Drawing the canvas onto the screen.
类似地,可以初始化多个画布并将其打包到tk窗口中,从而生成多个绘制的图形。
地物对象的打印可以使用matplotlib方法完成
两个此类数字的完整代码将变为-:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
# FOR FIRST GRAPH
fig = Figure(...) # Initializing the figure object.
canvas = FigureCanvasTkAgg(fig, master=root) # Initializing the FigureCanvasTkAgg Class Object.
tk_canvas = canvas.get_tk_widget() # Getting the Figure canvas as a tkinter widget.
tk_canvas.pack() # Packing it into it's master window.
canvas.draw() # Drawing the canvas onto the screen.
# FOR SECOND GRAPH
fig_2 = Figure(...) # Initializing the second figure object.
canvas_2 = FigureCanvasTkAgg(fig_2, master=root) # Initializing the second FigureCanvasTkAgg Class Object.
tk_canvas_2 = canvas_2.get_tk_widget() # Getting the second Figure canvas as a tkinter widget.
tk_canvas_2.pack() # Packing it into it's master window.
canvas_2.draw() # Drawing the second canvas onto the screen.
# CAN BE REPEATED FOR MULTIPLE SUCH GRAPHS....
动态生成这4个图是什么意思?我有一个代码,可以制作4个单独的图形,每个图形有一个绘图。虽然这些图形不在Tkinter GUI中,但它有一个文本框和按钮,我也需要它。我将添加代码供您查看。编写的代码没有正确的缩进。图形是否嵌入到tkinter对象中@AshbyS.@MatrixProgrammer我是堆叠的新手,原谅meNo的问题,对不起,我不是有意冒犯你,只是建议你这么说。另外,我建议你看看@BryanOakley回复的帖子
from matplotlib.figure import Figure
fig = Figure(...) # Initializing the figure object.
canvas = FigureCanvasTkAgg(fig, master=root) # Initializing the FigureCanvasTkAgg Class Object.
tk_canvas = canvas.get_tk_widget() # Getting the Figure canvas as a tkinter widget.
tk_canvas.pack() # Packing it into it's master window.
canvas.draw() # Drawing the canvas onto the screen.
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
# FOR FIRST GRAPH
fig = Figure(...) # Initializing the figure object.
canvas = FigureCanvasTkAgg(fig, master=root) # Initializing the FigureCanvasTkAgg Class Object.
tk_canvas = canvas.get_tk_widget() # Getting the Figure canvas as a tkinter widget.
tk_canvas.pack() # Packing it into it's master window.
canvas.draw() # Drawing the canvas onto the screen.
# FOR SECOND GRAPH
fig_2 = Figure(...) # Initializing the second figure object.
canvas_2 = FigureCanvasTkAgg(fig_2, master=root) # Initializing the second FigureCanvasTkAgg Class Object.
tk_canvas_2 = canvas_2.get_tk_widget() # Getting the second Figure canvas as a tkinter widget.
tk_canvas_2.pack() # Packing it into it's master window.
canvas_2.draw() # Drawing the second canvas onto the screen.
# CAN BE REPEATED FOR MULTIPLE SUCH GRAPHS....