【发布时间】:2019-06-12 16:36:51
【问题描述】:
我编写了一段代码,其中有一个带有画布的简单 GUI。在这个画布上,我画了一个 Matplot。 Matplot 每秒更新一次,其中包含来自 SQ Lite DB 的数据,我在其中填充了一些虚假的 Sensor 信息(目前仅用于测试)。
我的问题是画布的重绘导致我的窗口/gui 每秒都滞后。我什至试图在另一个线程中更新情节。但即使在那里我也有滞后。
使用我最新的代码,我的大部分工作都正常进行了。线程有助于防止我的 GUI/窗口在 Canvas 更新时冻结。
我最不想做的就是让它线程安全。
这是我收到的消息:
RuntimeError: main thread is not in main loop
这是我最新的线程工作代码:
from tkinter import *
import random
from random import randint
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import time
import threading
from datetime import datetime
continuePlotting = False
def change_state():
global continuePlotting
if continuePlotting == True:
continuePlotting = False
else:
continuePlotting = True
def data_points():
yList = []
for x in range (0, 20):
yList.append(random.randint(0, 100))
return yList
def app():
# initialise a window and creating the GUI
root = Tk()
root.config(background='white')
root.geometry("1000x700")
lab = Label(root, text="Live Plotting", bg = 'white').pack()
fig = Figure()
ax = fig.add_subplot(111)
ax.set_ylim(0,100)
ax.set_xlim(1,30)
ax.grid()
graph = FigureCanvasTkAgg(fig, master=root)
graph.get_tk_widget().pack(side="top",fill='both',expand=True)
# Updated the Canvas
def plotter():
while continuePlotting:
ax.cla()
ax.grid()
ax.set_ylim(0,100)
ax.set_xlim(1,20)
dpts = data_points()
ax.plot(range(20), dpts, marker='o', color='orange')
graph.draw()
time.sleep(1)
def gui_handler():
change_state()
threading.Thread(target=plotter).start()
b = Button(root, text="Start/Stop", command=gui_handler, bg="red", fg="white")
b.pack()
root.mainloop()
if __name__ == '__main__':
app()
这里的想法没有线程:
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import sqlite3
from datetime import datetime
from random import randint
class MainApplication(tk.Frame):
def __init__(self, parent, *args, **kwargs):
tk.Frame.__init__(self, parent, *args, **kwargs)
self.parent = parent
root.update_idletasks()
f = Figure(figsize=(5,5), dpi=100)
x=1
ax = f.add_subplot(111)
line = ax.plot(x, np.sin(x))
def animate(i):
# Open Database
conn = sqlite3.connect('Sensor_Data.db')
c = conn.cursor()
# Create some fake Sensor Data
NowIs = datetime.now()
Temperature = randint(0, 100)
Humidity = randint(0, 100)
# Add Data to the Database
c = conn.cursor()
# Insert a row of data
c.execute("insert into Sensor_Stream_1 (Date, Temperature, Humidity) values (?, ?, ?)",
(NowIs, Temperature, Humidity))
# Save (commit) the changes
conn.commit()
# Select Data from the Database
c.execute("SELECT Temperature FROM Sensor_Stream_1 LIMIT 10 OFFSET (SELECT COUNT(*) FROM Sensor_Stream_1)-10")
# Gives a list of all temperature values
x = 1
Temperatures = []
for record in c.fetchall():
Temperatures.append(str(x)+','+str(record[0]))
x+=1
# Setting up the Plot with X and Y Values
xList = []
yList = []
for eachLine in Temperatures:
if len(eachLine) > 1:
x, y = eachLine.split(',')
xList.append(int(x))
yList.append(int(y))
ax.clear()
ax.plot(xList, yList)
ax.set_ylim(0,100)
ax.set_xlim(1,10)
ax.grid(b=None, which='major', axis='both', **kwargs)
label = tk.Label(root,text="Temperature / Humidity").pack(side="top", fill="both", expand=True)
canvas = FigureCanvasTkAgg(f, master=root)
canvas.get_tk_widget().pack(side="left", fill="both", expand=True)
root.ani = animation.FuncAnimation(f, animate, interval=1000)
if __name__ == "__main__":
root = tk.Tk()
MainApplication(root).pack(side="top", fill="both", expand=True)
root.mainloop()
这是我的数据库架构:
CREATE TABLE `Sensor_Stream_1` (
`Date` TEXT,
`Temperature` INTEGER,
`Humidity` INTEGER
);
【问题讨论】:
-
我将构建一个单独的类来处理数据的绘制,然后将画布传递给该类,同时在其自己的线程中运行它。这应该可以防止您看到的滞后。也就是说我之前使用过 matplotlib 并且没有延迟问题,因此您的代码中可能存在导致延迟的内容。
-
你有这方面的例子吗?我以前从未使用过线程。
-
这里有很多关于堆栈溢出的线程示例。我现在正在查看您的代码,以查看是否有任何可能导致此延迟的突出内容。
-
好的。感谢您查看我的代码。
-
@Mike-SMT 我编辑了我的代码。尝试使用多线程。似乎效果不佳。
标签: python multithreading performance canvas tkinter