我有一个使用 PySimpleGUI 的 GUI,其中包含多个图,可以从蓝牙设备接收数据。我想实时绘制接收到的数据,理想情况下与接收到点的速度一样快。现在大约是每 20 毫秒 1 点。然而,在目前的状态下,它的速度慢得令人痛苦。在 GUI 赶上之前,硬件早已完成了测量。整个 GUI 陷入困境,甚至没有注册设备已完成其任务。
class DataPlot:
#stuff
def update(self, plot_func):
self.ax.cla()
self.ax.grid()
plot_func()
self.ax.set_title(self.title)
self.ax.set_xlabel(self.x_label)
self.ax.set_ylabel(self.y_label)
plt.legend(loc="lower right", fontsize="5", ncol=2)
self.figure_agg.draw()
class View:
#stuff
def update_demo_plots(
self, calibration_sweeps: List[SensorSweep], test_sweeps: List[SensorSweep]
):
def demo_well1_update(calibration_sweeps, test_sweeps):
for c_num in range(8):
cal_x = [sweep.applied_voltages[c_num] for sweep in calibration_sweeps]
cal_y = [sweep.calculated_resistances[c_num] for sweep in calibration_sweeps]
self.well1_plot.ax.plot(
cal_x,
cal_y,
"s",
color="blue",
markersize=2,
label="Calibration" if c_num == 0 else None,
)
self.well1_plot.update(
lambda: demo_well1_update(calibration_sweeps, test_sweeps)
)
#other plotting
每次收到一个点,都会调用 update_demo_plots() 。这完全清除并重新绘制每个点的所有数据。
我已经确定,仅调用 self.figure_agg.draw() 并注释掉其他所有内容就足以显着减慢 GUI 的速度。我该如何改进并解决这个问题?
您的数据速度太快,该代码无法绘制,因为它对收到的每个数据执行了很多操作。以下是解决您问题的一些解决方案:
ax.cla()
的使用,以防止不必要的绘图清除。Line2D
对象。在初始化期间创建一次并在所有更新中重复使用它。set_xdata()
和 set_ydata()
直接用新数据更新现有线。这显着减少了处理时间。self.figure_agg.draw()
,因为它可能会阻塞主线程,导致 GUI 变慢。请使用 self.figure_agg.draw_idle()
来代替。奖励:使用
set_xlim()
和 set_ylim()
进行动态调整的轴限制,使绘图以最新数据为中心,从而增强视觉体验。
我没有适当的设置来从蓝牙设备生成这些数据,因此我仅在 AI 的帮助下使用 Python 模拟数据。这是包含我上面提到的所有要点的最终代码。
# Importing necessary libraries
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import PySimpleGUI as sg
import random
import threading
import time
# Class for managing data plotting
class DataPlot:
def __init__(self, ax, title, x_label, y_label, window_size=50):
# Setting up the plot with given parameters
self.ax = ax
self.title = title
self.x_label = x_label
self.y_label = y_label
self.window_size = window_size # Controls the number of points shown in the moving window
self.line, = self.ax.plot([], [], 'b-', linewidth=1) # Initialize the plot line
self.ax.grid()
self.ax.set_title(self.title)
self.ax.set_xlabel(self.x_label)
self.ax.set_ylabel(self.y_label)
# Initialize empty lists to hold data
self.x_data = []
self.y_data = []
def update(self, new_x, new_y):
# Add new data points to the current list
self.x_data.extend(new_x)
self.y_data.extend(new_y)
# Keep only the latest `window_size` number of points
if len(self.x_data) > self.window_size:
self.x_data = self.x_data[-self.window_size:]
self.y_data = self.y_data[-self.window_size:]
# Update the line data
self.line.set_xdata(self.x_data)
self.line.set_ydata(self.y_data)
# Adjust the plot limits to keep the new data centered
self.ax.set_xlim(min(self.x_data), max(self.x_data))
self.ax.set_ylim(min(self.y_data) - 10, max(self.y_data) + 10) # Dynamically adjust y-limits
# Redraw the plot without blocking the GUI
self.line.figure.canvas.draw_idle()
class View:
def __init__(self, figure, ax):
self.figure = figure
self.ax = ax
self.well1_plot = DataPlot(ax, "Moving Line Plot", "Time", "Value")
def update_demo_plots(self, calibration_sweeps):
# Prepare data for plotting
cal_x = [i for i in range(len(calibration_sweeps))] # Simulating time on the x-axis
cal_y = [sweep['resistance'] for sweep in calibration_sweeps] # Plotting resistance values
self.well1_plot.update(cal_x, cal_y)
# Function to simulate incoming data
def data_generator(view):
calibration_sweeps = [] # This list will store incoming data points
while True:
time.sleep(0.02) # Simulate data arrival every 20 milliseconds
# Generate random data points
new_data = {'voltage': random.uniform(0, 5), 'resistance': random.uniform(10, 100)}
calibration_sweeps.append(new_data)
# Limiting the number of data points to keep the plot responsive
if len(calibration_sweeps) > 50: # Keep the last 50 points
calibration_sweeps.pop(0)
# Updating the plot with new data
view.update_demo_plots(calibration_sweeps)
# Setting up the GUI window with PySimpleGUI
layout = [[sg.Canvas(key='-CANVAS-')], [sg.Button('Exit')]]
window = sg.Window('Real-time Plotting', layout, finalize=True)
fig, ax = plt.subplots()
view = View(fig, ax)
figure_agg = FigureCanvasTkAgg(fig, window['-CANVAS-'].TKCanvas)
figure_agg.draw()
figure_agg.get_tk_widget().pack(side='top', fill='both', expand=1)
thread = threading.Thread(target=data_generator, args=(view,), daemon=True)
thread.start()
while True:
event, values = window.read(timeout=10)
if event == sg.WIN_CLOSED or event == 'Exit':
break
window.close()
这更新情节非常快。如果您在实施过程中有任何不清楚或出现问题,请告诉我。