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....