首页 > 解决方案 > 使用用户 tkinter 输入作为 Matplotlib 坐标绘制一条线

问题描述

我在我的 Tkinter GUI 环境中嵌入了一个 Matplotlib 折线图。我的“实验”基本上从用户那里获取两个输入(数字),然后它应该使用它们来画一条线。但是,我不知道如何根据用户输入绘制折线图。有人可以解释一下怎么做吗?这是我的代码:

from tkinter import *
import tkinter as tk
from tkinter import ttk
import matplotlib.pyplot as plt
import numpy as np

root = tk.Tk()
root.geometry('500x700')

first_input = StringVar()
second_input = StringVar()

class Graph():
    def linechart():
        x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
        y = [1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5]
        plt.plot(x, y)
        plt.xticks(np.arange(min(x), max(x)+1, 1.0))
        plt.show()

class Random(Graph):
    def __init__(self):
        self.label1 = None
        self.label2 = None
        self.userEntry = None
    def first_input(self):
        self.label1 = ttk.Label(text="Insert:")
        self.label1.pack()
        self.userEntry = ttk.Entry(textvariable=first_input)
        self.userEntry.pack()
        self.button_2 = Button(root, text="Enter", command=self.second_input)
        self.button_2.pack()
    def second_input(self):
        self.label2 = ttk.Label(text="Insert:")
        self.label2.pack()
        self.userEntry = ttk.Entry(textvariable=second_input)
        self.userEntry.pack()
        self.button_3 = Button(root, text="Enter", command=self.create_window)
        self.button_3.pack()
    def create_window(self):
        Graph.linechart()

random = Random()

button_1 = Button(root, text="random", command=lambda:random.first_input())
button_1.pack()

root.mainloop()

标签: pythonpython-3.xmatplotlibtkintertkinter-canvas

解决方案


以下是如何在 tkinter 框架中嵌入 matplotlib 图,根据用户输入的坐标在此图上绘制线条,并直接在 tkinter 级别覆盖轴:

另请参阅matplotlib 文档以获取更简单的示例。

彩色线条绘制在嵌入在 tkinter 画布中的 matplotlib 图形上,一条线从 绘制p0=(0, 0), p1=(500, 500),另一条线从p0=(0, 500), p1=(500, 0). (从弹出窗口输入)
黑白叠加层直接绘制在 tkinter 画布上

在此处输入图像描述

import matplotlib as mpl
import tkinter as tk
import matplotlib.backends.tkagg as tkagg
from matplotlib.backends.backend_agg import FigureCanvasAgg


def draw_figure(canvas, figure, loc=(0, 0)):
    """ Draw a matplotlib figure onto a Tk canvas

    loc: location of top-left corner of figure on canvas in pixels.
    Inspired by matplotlib source: lib/matplotlib/backends/backend_tkagg.py
    """
    figure_canvas_agg = FigureCanvasAgg(figure)
    figure_canvas_agg.draw()
    figure_x, figure_y, figure_w, figure_h = figure.bbox.bounds
    figure_w, figure_h = int(figure_w), int(figure_h)
    photo = tk.PhotoImage(master=canvas, width=figure_w, height=figure_h)

    canvas.create_image(loc[0] + figure_w/2, loc[1] + figure_h/2, image=photo)
    tkagg.blit(photo, figure_canvas_agg.get_renderer()._renderer, colormode=2)

    return photo


class LineCoordsEntry(tk.Toplevel):
    def __init__(self, master):
        self.master = master
        super().__init__(self.master)
        self.label_x0 = tk.Label(self, text='x0:')
        self.label_x0.grid(row=0, column=0)
        self.entry_x0 = tk.Entry(self, width=6)
        self.entry_x0.grid(row=0, column=1)
        self.label_y0 = tk.Label(self, text='y0:')
        self.label_y0.grid(row=0, column=2)
        self.entry_y0 = tk.Entry(self, width=6)
        self.entry_y0.grid(row=0, column=3)

        self.label_x1 = tk.Label(self, text='x1:')
        self.label_x1.grid(row=1, column=0)
        self.entry_x1 = tk.Entry(self, width=6)
        self.entry_x1.grid(row=1, column=1)
        self.label_y1 = tk.Label(self, text='y1:')
        self.label_y1.grid(row=1, column=2)
        self.entry_y1 = tk.Entry(self, width=6)
        self.entry_y1.grid(row=1, column=3)

        self.quit_button = tk.Button(self, text='quit', command=self.destroy)
        self.quit_button.grid(row=2, column=0)

        self.validate_button = tk.Button(self, text='validate', command=self.send_data)
        self.validate_button.grid(row=2, column=1, columnspan=3)

    def send_data(self):
        p0 = float(self.entry_x0.get()), float(self.entry_y0.get())
        p1 = float(self.entry_x1.get()), float(self.entry_y1.get())
        self.master.retrieve_line_data(p0, p1)


class App(tk.Frame):
    def __init__(self, master, w=500, h=500):
        self.master = master
        super().__init__(self.master)
        self.w = w
        self.h = h
        self.canvas = tk.Canvas(self.master, width=self.w, height=self.h)
        self.canvas.pack()

        self.enter_line_coordinates_button = tk.Button(self, text='make new line', command=self.spawn_entry_coordinates)
        self.enter_line_coordinates_button.pack()

        self.draw_lines_button = tk.Button(self, text='draw lines', command=self.draw_lines)
        self.draw_lines_button.pack()

        self.draw_overlay_button = tk.Button(self, text='draw overlaid axis', command=self.draw_overlay)
        self.draw_overlay_button.pack()

        self.erase_overlay_button = tk.Button(self, text='remove overlaid axis', command=self.erase_overlay)
        self.erase_overlay_button.pack()

        self.lines = []

    def spawn_entry_coordinates(self):
        LineCoordsEntry(self)

    def retrieve_line_data(self, p0, p1):
        self.lines.append((p0, p1))
        print(self.lines)

    def draw_lines(self):
        """draw the lines on the matplotlib canvas
        """
        fig = mpl.figure.Figure(figsize=(5, 5))
        ax = fig.add_axes([0, 0, 1, 1])
        for p0, p1 in self.lines:
            x0, y0, x1, y1 = *p0, *p1
            X = x0, x1
            Y = y0, y1
            print(X, Y)
            ax.plot(X, Y)

        self.fig_x, self.fig_y = 0, 0 #self.w, self.h
        self.fig_photo = draw_figure(self.canvas, fig, loc=(self.fig_x, self.fig_y))
        self.fig_w, self.fig_h = self.fig_photo.width(), self.fig_photo.height()

    def draw_overlay(self):
        """draw lines on the tkinter canvas, overlaid on the matplotlib canvas
        """
        self.canvas.create_line(0, self.h//2, self.w, self.h//2, tags=('overlay',))
        self.canvas.create_line(self.w//2, 0, self.w//2, self.h, tags=('overlay',))

    def erase_overlay(self):
        self.canvas.delete('overlay')


root = tk.Tk()
App(root).pack()
root.mainloop()

推荐阅读