python - 由于 tkinter GUI 中的 tkraise() 导致位置混乱
问题描述
我正在尝试使用 .csv 文件中的给定值绘制图表。正如预期的那样,情节似乎没有引起任何问题。问题是画布实际上扰乱了前一帧的位置。
请注意,我正在使用具有 tkraise 功能的两个框架,其中另一个框架 ID 使用按钮调用,从现在开始显示框架。这发生得很顺利,但位置有点受到干扰。有什么办法可以保留帧中数据在第一帧中按要求居中的位置?
我想强调这个特殊的 DataStored 类,其中图表的情节正在发生。
这是我的代码:
import tkinter as tk
from tkinter import messagebox
import serial
from serial.tools import list_ports
from os import path
import continuous_threading
import csv
from datetime import datetime
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
FONT = ("Helvetica", 20)
attributes = {'padx': 20, 'pady': 20}
th = None
class App(tk.Tk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.title('DAS')
self.geometry('600x450')
container = tk.Frame(self)
container.pack(fill=tk.BOTH, expand=True)
self.frames = {}
self.frames[0] = Display(container, self)
self.frames[0].grid(row=0, column=0, sticky='nsew')
self.frames[1] = DataStored(container, self)
self.frames[1].grid(row=0, column=0, sticky='nsew')
self.show_frame(0)
def show_frame(self, frame):
frame = self.frames[frame]
frame.tkraise()
class Display(tk.Frame):
def __init__(self, container, controller):
super().__init__(container)
name = tk.Label(self, text='Data Acquistion System', font=FONT)
name.pack(fill='x')
left_wrapper = tk.Frame(self)
left_wrapper.pack(side='left', **attributes)
right_wrapper = tk.Frame(self)
right_wrapper.pack(side='left', **attributes)
port_label = tk.Label(left_wrapper, text='Port:', font=FONT)
port_label.grid(row=0, column=0)
port_option = tk.StringVar(left_wrapper, value='None')
ports_availiable = self.get_arduino_ports()
port_value = tk.OptionMenu(left_wrapper, port_option, *ports_availiable)
port_value.grid(row=0, column=1)
baud_label = tk.Label(left_wrapper, text='Baudrate:', font=FONT)
baud_label.grid(row=1, column=0)
baud_value = tk.Entry(left_wrapper)
baud_value.grid(row=1, column=1)
baud_value.insert(0, 9600)
connect_button = tk.Button(left_wrapper, text='Connect', command=lambda: self.connect_to_port(port_option.get(), baud_value.get()))
connect_button.grid(row=2, column=1)
playback_button = tk.Button(left_wrapper, text='Playback', command=lambda: controller.show_frame(1))
playback_button.grid(row=3, column=1)
size = {'height': 4, 'width': 15}
temperature_label = tk.Label(right_wrapper, text='Temperature(in °C):', font=FONT)
self.temperature_val = tk.Text(right_wrapper, **size)
self.temperature_val.configure(state='disabled')
temperature_label.grid(row=0, column=0)
self.temperature_val.grid(row=0, column=1)
def get_arduino_ports(self):
"""
Returns port in which Arduino is connected. If multiple ports are found it returns first one.
"""
arduino_ports = [
# grep returns same list of ports as comports. It also filter out devices using regex.
p.device for p in list_ports.grep(r'Arduino')
]
if not arduino_ports:
return ['None']
return arduino_ports
def connect_to_port(self, port, baudrate=9600):
"""
Function to establish connection with port.
"""
global BOARD
global DATA_FILE
if not port:
return None
try:
BOARD = serial.Serial(port, baudrate)
BOARD.flush()
except serial.SerialException:
print("Not able to connect to the board.")
messagebox.showwarning("Serial Error", "Not able to connect to board")
return
DATA_FILE = open("data.csv", "a")
DATA_FILE.write("time, temperature\n")
global th
th = continuous_threading.PeriodicThread(0.5, target=self.read_data)
th.start()
def read_data(self):
"""
Read data from the connected board.
"""
if serial.in_waiting > 0:
time_now = datetime.now().strftime("%H:%M:%S")
received = BOARD.read().decode('utf-8')
DATA_FILE.write(','.join((time_now, received))+'\n')
self.temperature_val.delete(0, tk.END)
self.temperature_val.insert(0, received)
class DataStored(tk.Frame):
def __init__(self, container, controller):
super().__init__(container)
tk.Label(self, text='Playback Data', font=FONT).pack(fill=tk.X)
top_frame = tk.Frame(self)
top_frame.pack(fill=tk.X)
bottom_frame = tk.Frame(self)
bottom_frame.pack(fill=tk.X)
update = tk.Button(bottom_frame, text='Update', command=lambda: self.update_playback(top_frame))
update.pack()
back = tk.Button(bottom_frame, text='Back', command=lambda: controller.show_frame(0))
back.pack()
def update_playback(self, container):
time = []
temperature = []
if not path.exists("data.csv"):
messagebox.showwarning("Warning", "No data found to plot.")
return
with open("data.csv", "r") as file:
reader = csv.reader(file)
next(reader)
for row in reader:
time_now, temperature_now = row[0], row[1]
time.append(time_now)
temperature.append(temperature_now)
for widget in container.winfo_children():
widget.destroy()
fig = Figure(figsize=(3, 3), dpi=100)
plot1 = fig.add_subplot(111)
plot1.plot(time, temperature, 'b-o')
canvas = FigureCanvasTkAgg(fig, master=container)
canvas.draw()
canvas.get_tk_widget().pack()
toolbar = NavigationToolbar2Tk(canvas, container)
toolbar.update()
canvas.get_tk_widget().pack()
BOARD = None
DATA_FILE = None
if __name__ == "__main__":
window = App()
window.mainloop()
解决方案
推荐阅读
- c# - 如何使用接受 FromBody 参数的 API GET 方法
- node.js - 无法通过管道将媒体流对象或 Mediarecorder blob 传输到谷歌语音到文本流接口
- hadoop - 警告:您的 Hadoop 安装不包括来自 HDFS-11644 的 StreamCapabilities 类
- symfony - Symfony:集合类型中的索引
- python - 如何通过按下按钮隐藏部分框架
- javascript - 如何让 div 有一个单独的 dom,就像使用 IFRAME 时一样
- javascript - 如果用户单击浏览器后退按钮,如何在 Django 中清除 self.add_error?
- google-app-engine - 我应该为安全灵活的应用程序引擎设置哪些服务帐户权限 -> 云功能通信
- python - Matplotlib 内存不足
- python - 如何在pyspark中分别旋转多个列