在 Tkinter [.grid 方法] 中嵌入 MatPlotLib 图形,并自定义 MatPlotLib 的导航工具栏

Embedding a MatPlotLib Graph in Tkinter [.grid method], and Customizing MatPlotLib's Navigation Toolbar

我在将我的 MatPlotLib 图嵌入 Tkinter 时遇到了问题,在 Google 和 MatPlotLib 网站上进行了一些搜索后,我能得到的最好的方法是标准方法:

import tkinter

from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)

fig = Figure(figsize=(5, 4), dpi=100)

canvas = FigureCanvasTkAgg(fig, master=root)

canvas.draw()

canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)

toolbar = NavigationToolbar2Tk(canvas, root) toolbar.update()

canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1)

现在,如果我尝试用 .grid 替换包装布局(并删除 .pack() 参数),我会遇到一堆错误,无论我尝试了多少次 Google 搜索,所有在 Tkinter 中嵌入 MatPlotLib 图的方法都只使用 pack 方法。有人可以帮我解决这个问题吗?我想嵌入图形,但使用网格方法,因为我的 GUI 应用程序的其余布局是 .grid 布局。

我在 Tkinter 中的导航工具栏遇到的另一个问题是导航工具栏显然可以自定义(至少根据 SentDex [5:18])。他似乎没有详细说明我如何做到这一点,这对我来说很困难,因为我对 MatPlotLib 的按钮不是很满意(它们看起来非常陈旧和过时)。

有人可以帮我解决这个问题吗?当我只放入图表时,它似乎工作得很好,但是当我尝试将图表放入导航工具栏时我也遇到了问题。对此的任何帮助将不胜感激。谢谢!

这是一个简单的 plotnavigation toolbar inside tkinter window 仅使用 grid 几何管理器。

import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)

window = tk.Tk()

btn = tk.Label(window, text='A simple plot')
btn.grid(row=0, column=0, padx=20, pady=10)

x = ['Col A', 'Col B', 'Col C']

y = [50, 20, 80]

fig = plt.figure(figsize=(4, 5))
plt.bar(x=x, height=y)

# You can make your x axis labels vertical using the rotation
plt.xticks(x, rotation=90)

# specify the window as master
canvas = FigureCanvasTkAgg(fig, master=window)
canvas.draw()
canvas.get_tk_widget().grid(row=1, column=0, ipadx=40, ipady=20)

# navigation toolbar
toolbarFrame = tk.Frame(master=window)
toolbarFrame.grid(row=2,column=0)
toolbar = NavigationToolbar2Tk(canvas, toolbarFrame)

window.mainloop()

输出GUI

我没有对导航工具栏进行自定义,因此我没有为该部分提供任何解决方案。但我肯定会调查它,如果我发现有用的东西,就会更新你。希望对您有所帮助。

我设法获得了一个简单的应用程序来控制 matplotlib 图形 window 调整大小。

我没有使用过导航栏功能,所以这可能是对这个框架的补充

import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from google.cloud import bigquery
import os, json, sys
from time import time
import pandas as pd


class SliderGraph:

    def __init__(self, master):
        self.master = master
        # with open(self.resource_path(config_file)) as fp:
        #     self.config = json.load(fp)
        # os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = self.resource_path(creds_file)
        # self.projectID = self.config['projectID']
        # self.datasetID = self.config['datasetID']
        # last_used_freq = self.config['last_used_frequency']
        # last_used_delta = self.config['last_used_delta']

        self.bounds = 1
        self.frame = tk.Frame(master)
        self.fig = Figure()
        row = 0

        self.ax = self.fig.add_subplot(111)
        self.ax.set_xlabel("Run Numbers")
        self.ax.set_ylabel("UDD")
        self.ax.set_ylim([-self.bounds,self.bounds])

        self.canvas = FigureCanvasTkAgg(self.fig, master=master)  # , width=win_width, height=(win_height-50))
        self.canvas.draw()
        self.canvas.get_tk_widget().grid(row=row, columnspan=2, sticky='nsew')
        row+=1

        self.table_label = tk.Label(master, text="Enter BigQuery Table Name")
        self.table_label.grid(row=row, column=0)
        # row += 1

        self.table_name = tk.Entry(master)
        # self.table_name.insert(0,self.config['last_used_table'])
        self.table_name.grid(row=row, column=1, sticky='ew')
        row += 1

        self.get_table_button = tk.Button(master, text="Get Table Data and Plot", command=self.plot_data)
        self.get_table_button.grid(row=row, columnspan=2)
        row += 1

        self.frequency_slider = tk.Scale(master, from_=400, to=4500, orient=tk.HORIZONTAL, command=self.update_plot)
        # self.frequency_slider.set(last_used_freq)
        self.frequency_slider.grid(row=row,columnspan=2, sticky="nsew")
        row += 1

        self.frequency_entry = tk.Entry(master)
        # self.frequency_entry.insert(0,last_used_freq)
        self.frequency_entry.grid(row=row, columnspan=2)
        row += 1

        self.delta_slider = tk.Scale(master, from_=-500, to=500, orient=tk.HORIZONTAL, command=self.update_plot)
        # self.delta_slider.set(last_used_delta)
        self.delta_slider.grid(row=row, columnspan=2, sticky="ensw")
        row += 1

        self.delta_entry = tk.Entry(master)
        # self.delta_entry.insert(0, last_used_delta)
        self.delta_entry.grid(row=row, columnspan=2)
        row += 1

        self.get_table_button = tk.Button(master, text="Autoscale", command=self.autoscale)
        self.get_table_button.grid(row=row,columnspan=2)
        row += 1

        tk.Grid.columnconfigure(master, 0, weight=1)
        tk.Grid.columnconfigure(master, 1, weight=1)
        tk.Grid.rowconfigure(master, 0, weight=5)
        for x in range(1,row):
            tk.Grid.rowconfigure(master, x, weight=0)

        master.protocol('WM_DELETE_WINDOW', self.close)

        self.df = None
        self.frequency_list = []
        self.series = None
        self.elapsed = time()*1000

    def plot_data(self):
        self.ax.clear()
        self.client = bigquery.Client(project=self.projectID)
        self.tableID = f"`{self.datasetID}.{self.table_name.get()}`"

        QUERY = (
            f"SELECT * EXCEPT (testCount,elapsed_run_time_ms_,moleculeValue,onboardTemp1,onboardTemp2,temp,tempControl,\
             logamp, txrx,timestamp) FROM {self.tableID} ORDER BY runCount ASC;"
        )
        # query_job = self.client.query(QUERY)
        # rows = query_job.result()
        self.df = pd.read_gbq(QUERY,self.projectID)

        for col in self.df.columns:
            if 'runCount' in col:
                continue
            self.frequency_list.append(int(col.replace('_','')))
        self.frequency_slider.configure(from_=min(self.frequency_list),to=max(self.frequency_list))
        self.delta_slider.configure(from_=-max(self.frequency_list),to=max(self.frequency_list))

        freq = f'_{self.frequency_slider.get()}'
        freq2 = f'_{self.frequency_slider.get() + self.delta_slider.get()}'
        self.series = self.df[freq] - self.df[freq2]
        self.series.plot(ax=self.ax)
        self.ax.set_ylim([-self.bounds,self.bounds])

        self.fig.canvas.draw()
        self.fig.canvas.flush_events()
        pass

    def update_plot(self, newslider):
        try:

            self.ax.clear()
            freq = f'_{self.frequency_slider.get()}'
            freq2 = f'_{self.frequency_slider.get() + self.delta_slider.get()}'
            self.series = self.df[freq] - self.df[freq2]
            self.series.plot(ax=self.ax)
            zero = self.series.mean()
            self.ax.set_ylim([zero-self.bounds, zero+self.bounds])
            self.fig.canvas.draw()
            self.fig.canvas.flush_events()
            # self.master.update_idletasks()

            if ((time()*1000)-self.elapsed > 100):
                self.elapsed = time()*1000
                self.frequency_entry.delete(0,'end')
                self.frequency_entry.insert(0,freq.replace('_',''))
                self.delta_entry.delete(0,'end')
                self.delta_entry.insert(0,self.delta_slider.get())
        except:
            pass

    def play_runs(self):
        pass

    def autoscale(self):
        self.ax.clear()
        self.series.plot(ax=self.ax)
        self.ax.relim()
        self.ax.autoscale()
        zero = self.series.mean()
        self.bounds = self.series.max() - self.series.min()
        self.ax.set_ylim([zero-self.bounds,zero+self.bounds])
        self.fig.canvas.draw()
        self.fig.canvas.flush_events()

    def close(self):

        self.master.destroy()

if __name__=="__main__":
    root = tk.Tk()
    slider_fig = SliderGraph(root)
    root.mainloop()