如何使Matplotlib跟随周围的tkinter窗口大小更改。

huangapple go评论72阅读模式
英文:

How to get a Mathplotlib to follow size changes of a surrounding tkinter window

问题

以下是您提供的代码的翻译部分:

# 创建一个特定 .csv 文件的小型查看器的想法已经完成开发工作,但是如果主窗口的大小调整,则 mathplotlib 图表不会调整大小。

# 缺少的代码是什么,以使图表跟随主窗口的大小调整?

# 以下是我程序的简化示例:
import tkinter as tk
from tkinter import ttk
import datetime
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)
import pandas as pd

class AppWindow(tk.Tk):

    def __init__(self):
        super().__init__()
        self.notebook = None
        self.df = None
        self.df_1 = None
        self.build_app_window()

    def build_app_window(self):
        self.title("env_viewer")

        btn_refresh = tk.Button(self,
                                text="Refresh",
                                command=self.refresh)
        btn_refresh.grid(row=3, column=0, sticky="new", padx=5)
        self.notebook = ttk.Notebook(self)
        self.notebook.grid(row=2, column=1, rowspan=10, sticky="nsew")

        self.add_voltage_tab()

    def add_voltage_tab(self):
        # 包含绘图的图形
        self.fig_voltage, self.ax_voltage = plt.subplots(figsize=(5, 2.7),
                                                         dpi=50)
        # 创建包含 Matplotlib 图形的 Tkinter 画布
        self.canvas_voltage = FigureCanvasTkAgg(self.fig_voltage,
                                                master=self.notebook)
        self.notebook.add(self.canvas_voltage.get_tk_widget(),
                          text='Voltage')

    def update_voltage_plot(self):
        # 提取数据
        dates = self.df['Date Time']
        voltage = self.df['Voltage[V]']
        # 重新绘制
        self.ax_voltage.clear()
        self.ax_voltage.plot(dates, voltage, color="blue", label="Voltage [V]")
        self.ax_voltage.legend(loc='upper left')
        self.fig_voltage.autofmt_xdate(rotation=45)
        self.fig_voltage.tight_layout()
        self.canvas_voltage.draw()

    def refresh(self):
        dir_path = os.path.dirname(os.path.realpath(__file__))
        self.filepath_1 = os.path.join(dir_path, "2023-07_environment_data.csv")
        self.df_1 = pd.read_csv(str(self.filepath_1), sep=';')
        self.update_df()
        self.update_voltage_plot()

    def update_df(self):
        # 合并数据
        self.df = self.df_1
        # 将日期列转换为 datetime
        self.df['Date Time'] = pd.to_datetime(self.df['Date Time'])
        # 按日期升序排序数据集
        self.df = self.df.sort_values(by='Date Time')

if __name__ == "__main__":
    MainWindow = AppWindow()
    MainWindow.mainloop()

数据示例:

Date Time;in_temp;in_press;in_hum;out_temp;out_hum;Water;Fan1;Fan2;CPU_temp;Voltage[V];Current[mA];Power[W]
2023-07-05 07:15:02;23.39;951.55;52.74;22.12;63.41;898;0;0;48.69;14.30;391.98;5.613
2023-07-05 07:30:01;23.53;951.53;52.08;21.86;63.56;896;0;0;49.17;14.30;366.46;5.258
2023-07-05 07:45:02;23.56;951.46;52.63;21.70;65.56;899;0;0;50.15;13.72;380.67;5.547
2023-07-05 08:00:02;23.58;951.49;52.72;21.67;65.64;892;0;0;46.74;13.72;380.67;5.175
2023-07-05 08:15:02;23.61;951.29;52.81;21.59;65.65;891;0;0;49.17;13.72;379.79;5.198
2023-07-05 08:30:01;23.69;951.40;52.79;21.77;65.80;891;0;0;51.61;13.75;444.48;6.189
2023-07-05 08:45:02;23.69;951.60;52.60;21.80;65.27;899;0;0;52.09;13.75;444.88;6.170
2023-07-05 09:00:02;23.72;951.85;53.18;21.88;65.97;904;0;0;50.15;13.98;642.47;8.929
2023-07-05 09:15:02;23.80;952.10;53.61;21.79;67.41;898;0;0;50.15;13.68;382.56;5.201
英文:

The idea is to create a small viewer for a specific .csv file. All the development work is done, BUT the mathplotlib graph does not resize if the main window is resized.

What is the missing code to make the graph following the size of the main window?

Below is a squeezed down example of my program:

import tkinter as tk
from tkinter import ttk
import datetime
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg)
import pandas as pd
class AppWindow(tk.Tk):
def __init__(self):
super().__init__()
self.notebook = None
self.df = None
self.df_1 = None
self.build_app_window()
def build_app_window(self):
self.title("env_viewer")
btn_refresh = tk.Button(self,
text="Aktualisieren",
command=self.refresh)
btn_refresh.grid(row=3, column=0, sticky="new", padx=5)
self.notebook = ttk.Notebook(self)
self.notebook.grid(row=2, column=1, rowspan=10, sticky="nsew")
self.add_voltage_tab()
def add_voltage_tab(self):
# the figure that will contain the plot
self.fig_voltage, self.ax_voltage = plt.subplots(figsize=(5, 2.7),
dpi=50)
# creating the Tkinter canvas, containing the Matplotlib figure
self.canvas_voltage = FigureCanvasTkAgg(self.fig_voltage,
master=self.notebook)
self.notebook.add(self.canvas_voltage.get_tk_widget(),
text='Spannung')
def update_voltage_plot(self):
# Extract the data
dates = self.df['Date Time']
voltage = self.df['Voltage[V]']
# redraw
self.ax_voltage.clear()
self.ax_voltage.plot(dates, voltage, color="blue", label="Spannung [V]")
self.ax_voltage.legend(loc='upper left')
self.fig_voltage.autofmt_xdate(rotation=45)
self.fig_voltage.tight_layout()
self.canvas_voltage.draw()
def refresh(self):
dir_path = os.path.dirname(os.path.realpath(__file__))
self.filepath_1 = os.path.join(dir_path, "2023-07_environment_data.csv")
self.df_1 = pd.read_csv(str(self.filepath_1), sep=';')
self.update_df()
self.update_voltage_plot()
def update_df(self):
# Merge the data
self.df = self.df_1
# Convert the date column to datetime
self.df['Date Time'] = pd.to_datetime(self.df['Date Time'])
# Sort the dataset in the ascending order of date
self.df = self.df.sort_values(by='Date Time')
if __name__ == "__main__":
MainWindow = AppWindow()
MainWindow.mainloop()

Data sample

Date Time;in_temp;in_press;in_hum;out_temp;out_hum;Water;Fan1;Fan2;CPU_temp;Voltage[V];Current[mA];Power[W]
2023-07-05 07:15:02;23.39;951.55;52.74;22.12;63.41;898;0;0;48.69;14.30;391.98;5.613
2023-07-05 07:30:01;23.53;951.53;52.08;21.86;63.56;896;0;0;49.17;14.30;366.46;5.258
2023-07-05 07:45:02;23.56;951.46;52.63;21.70;65.56;899;0;0;50.15;13.72;380.67;5.547
2023-07-05 08:00:02;23.58;951.49;52.72;21.67;65.64;892;0;0;46.74;13.72;380.67;5.175
2023-07-05 08:15:02;23.61;951.29;52.81;21.59;65.65;891;0;0;49.17;13.72;379.79;5.198
2023-07-05 08:30:01;23.69;951.40;52.79;21.77;65.80;891;0;0;51.61;13.75;444.48;6.189
2023-07-05 08:45:02;23.69;951.60;52.60;21.80;65.27;899;0;0;52.09;13.75;444.88;6.170
2023-07-05 09:00:02;23.72;951.85;53.18;21.88;65.97;904;0;0;50.15;13.98;642.47;8.929
2023-07-05 09:15:02;23.80;952.10;53.61;21.79;67.41;898;0;0;50.15;13.68;382.56;5.201

答案1

得分: 1

你可以使用'bind'方法。

import tkinter as tk
from tkinter import ttk
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd

class AppWindow(tk.Tk):

    def __init__(self):
        super().__init__()
        self.notebook = None
        self.df = None
        self.df_1 = None
        self.build_app_window()

    def build_app_window(self):
        self.title("env_viewer")
        self.geometry("800x600")  # 设置主窗口的初始大小

        # 将主窗口的<Configure>事件绑定到on_resize回调函数
        self.bind("<Configure>", self.on_resize)

        btn_refresh = tk.Button(self,
                                text="Aktualisieren",
                                command=self.refresh)
        btn_refresh.grid(row=3, column=0, sticky="new", padx=5)
        self.notebook = ttk.Notebook(self)
        self.notebook.grid(row=2, column=1, rowspan=10, columnspan=3, sticky="nsew")

        self.add_voltage_tab()

    def on_resize(self, event):
        # 主窗口调整大小时更新图形大小的回调函数
        width = event.width - 100
        height = event.height - 100
        self.fig_voltage.set_size_inches(width / 100, height / 100)
        self.canvas_voltage.draw()

    def add_voltage_tab(self):
        # 包含绘图的图形
        self.fig_voltage, self.ax_voltage = plt.subplots(figsize=(5, 2.7),
                                                         dpi=100)  # 设置图形的初始大小
        # 创建包含Matplotlib图形的Tkinter画布
        self.canvas_voltage = FigureCanvasTkAgg(self.fig_voltage,
                                                master=self.notebook)
        self.notebook.add(self.canvas_voltage.get_tk_widget(),
                          text='Spannung')

# 你的代码的其余部分...

if __name__ == "__main__":
    MainWindow = AppWindow()
    MainWindow.mainloop()
英文:

You can use the 'bind' method.

import tkinter as tk
from tkinter import ttk
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import pandas as pd
class AppWindow(tk.Tk):
def __init__(self):
super().__init__()
self.notebook = None
self.df = None
self.df_1 = None
self.build_app_window()
def build_app_window(self):
self.title(&quot;env_viewer&quot;)
self.geometry(&quot;800x600&quot;)  # Set the initial size of the main window
# Bind the main window&#39;s &lt;Configure&gt; event to the on_resize callback
self.bind(&quot;&lt;Configure&gt;&quot;, self.on_resize)
btn_refresh = tk.Button(self,
text=&quot;Aktualisieren&quot;,
command=self.refresh)
btn_refresh.grid(row=3, column=0, sticky=&quot;new&quot;, padx=5)
self.notebook = ttk.Notebook(self)
self.notebook.grid(row=2, column=1, rowspan=10, columnspan=3, sticky=&quot;nsew&quot;)
self.add_voltage_tab()
def on_resize(self, event):
# Callback function to update graph size on main window resize
width = event.width - 100
height = event.height - 100
self.fig_voltage.set_size_inches(width / 100, height / 100)
self.canvas_voltage.draw()
def add_voltage_tab(self):
# the figure that will contain the plot
self.fig_voltage, self.ax_voltage = plt.subplots(figsize=(5, 2.7),
dpi=100)  # Set the initial size of the graph
# creating the Tkinter canvas, containing the Matplotlib figure
self.canvas_voltage = FigureCanvasTkAgg(self.fig_voltage,
master=self.notebook)
self.notebook.add(self.canvas_voltage.get_tk_widget(),
text=&#39;Spannung&#39;)
# Rest of your code ...
if __name__ == &quot;__main__&quot;:
MainWindow = AppWindow()
MainWindow.mainloop()

答案2

得分: 0

Technoed的答案确实解决了画布大小调整的问题。

但是为了使周围的笔记本也能正确调整大小,需要额外的代码行:

def build_app_window(self):
    ...
    self.columnconfigure(0, weight=0)
    self.columnconfigure(1, weight=1)
    self.rowconfigure(0, weight=1)
    ...

并且宽度和高度应该是整数

```python
self.fig_voltage.set_size_inches(int(width / 100), int(height / 100))
英文:

The answer of Technoed does solve the issue with the canvas resizing.

But additional lines are necessary to get the surrounding notebook resizing properly as well:

def build_app_window(self):
...
self.columnconfigure(0, weight=0)
self.columnconfigure(1, weight=1)
self.rowconfigure(0, weight=1)
...

And the width and heigth should be integers:

self.fig_voltage.set_size_inches(int(width / 100), int(height / 100))

huangapple
  • 本文由 发表于 2023年7月27日 23:09:39
  • 转载请务必保留本文链接:https://go.coder-hub.com/76781127.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定