Python - Tkinter 中的 Matplotlib - 重绘刚刚退出(或 Rasberry 中的段错误)
Python - Matplotlib in Tkinter - Redrawing just quits (or segfault in Rasberry)
美好的一天,
所以我有点反对在 Tkinter 中刷新 Matplotlib。总的来说,代码工作正常,但是一旦你按下“向上”键,程序就会在 figure.canvas.draw() 处崩溃——我也在 rasberry 上 运行 这段代码,它只是说内存访问失败。
我知道你可以通过 FuncAnimation 制作动画,但我遇到了一个大问题,它会随着时间的推移增加内存直到崩溃,所以我不想使用它(参见 Python3 - Matplotlib FuncAnimation continuous memoryleak)
所以我想我可以用老式的方式重画它,但这似乎也行不通。
最小代码:
import tkinter as tk
from threading import Thread
from numpy import sin, cos, pi
from pynput.keyboard import Listener
import keyboard
reDraw = False
U1ausg = U2ausg = U3ausg = Counter_3 = 50
Counter_1 = 120
Counter_2 = 240
#--Mainwindow--
class Drehstromdemonstrator(tk.Tk):
def __init__(self):
#Predefine
tk.Tk.__init__(self)
tk.Tk.wm_title(self, "Minimal")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frame = plotSine(self,(tk.Tk.winfo_screenwidth(self)*0.7,tk.Tk.winfo_screenheight(self)*0.7))
def reDrawFrame(self):
self.frame.drawStuff()
import numpy,matplotlib
matplotlib.use('TkAgg')
from numpy import sin, cos, pi, deg2rad, linspace, arange
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.ticker as tck
import time
import math
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from math import ceil
#global U1ausg, U2ausg, U3ausg, Counter_1, Counter_2, Counter_3
class plotSine:
def __init__(self,masterframe,size):
self._running = True
global U1ausg
global U2ausg
global U3ausg
(w,h) = size
inchsize = (w/25.5, h/25.4)
fig = self.figure = Figure(inchsize)
self.axes = fig.add_subplot(111)
self.axes.xaxis.set_ticks(arange(0,390,30))
self.axes.margins(x=0)
self.axes.yaxis.set_ticks(arange(-120,130,20))
self.axes.set_ylim(-120,120)
#create canvas as matplotlib drawing area
self.canvas = FigureCanvasTkAgg(fig, master=masterframe)
self.canvas.get_tk_widget().pack()
#self.canvas.mpl_connect('key_press_event',on_press)
self.x = linspace(0,360,1000)
self.axes.grid()
self.ysin = int(ceil(U1ausg))*sin(50/Counter_3 * deg2rad(self.x))
self.lineU1, = self.axes.plot(self.x, self.ysin, "-r",label="U1")
#self.drawStuff()
#Draw the plot
def drawStuff(self,*args):
ax = self.axes
#ax.clear()
ax.legend(["U1"])
#Changed everything to degree instead of PI, better looking
self.lineU1.set_ydata(int(ceil(U1ausg))*sin(50/Counter_3 * deg2rad(self.x)))
print("Redrawing!")
#---------------------------------------------------------------------
#---------------------------------------------------------------------
#---------------------------------------------------------------------
self.figure.canvas.draw() #First run is fine, crashes here afterwards
print("Flush!")
self.figure.canvas.flush_events()
print("ReDraw Done!")
#--Run Mainprog
app = Drehstromdemonstrator()
def on_press(event):
global Counter_3
Counter_3 = Counter_3 + 5
print("up")
app.reDrawFrame()
try:
keyboard.on_press_key("up", on_press)
app.mainloop()
# Aufraeumarbeiten nachdem das Programm beendet wurde
except UnicodeDecodeError:
print("Interrupt aswell!")
lampensteuerung.terminate()
except KeyboardInterrupt:
print("Interrupt")
lampensteuerung.terminate()
except Exception as e:
print(e)
此代码只是将一个简单的正弦波 (matplotlib) 放入 Tkinter 叠加层(必须有一个)- 在按钮上按“UP”可以将频率提高 +5。
我正在使用 keyboard.on_press_key("up", on_press)
因为我必须在我使用 GPIO.add_event_detect(channel, GPIO.RISING)
的树莓上尽可能地模拟它
(使用 fig.canvas.mpl_connect('key_press_event', on_press) 它工作正常但我不能在 PI 上这样做)
谢谢和问候!
所以我自己得到了 - 解决方案:将 draw() 更改为 draw_idle()
对于任何需要 Tinker 中可更改的 Matplotlib 以响应 Keyinputs / 或 GPIO.INPUTS 的人,这里是代码:
# coding=utf-8
import tkinter as tk
import math as m
from numpy import sin
#-----------------------------------Ausgabe:-----------------------------------------
#Predefine
app = None
freq = 50
# Create Main Window
class Application(tk.Tk):
def __init__(self):
#Predefine
tk.Tk.__init__(self)
tk.Tk.wm_title(self, "Title")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#Add the MatPlotLib
self.frame = plotSine(self,(tk.Tk.winfo_screenwidth(self)*0.7,tk.Tk.winfo_screenheight(self)*0.7))
def reDrawFrame(self):
self.frame.reDraw()
self.frame.reDraw() ## For whatever reason it can happen that the Draw Function has to be called twice for a good refresh?
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
import numpy,matplotlib
matplotlib.use('TkAgg')
from numpy import sin, deg2rad, linspace
import math
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from math import ceil
import keyboard
class plotSine:
def __init__(self,masterframe,size):
self._running = True
(w,h) = size
inchsize = (w/25.5, h/25.4)
fig = self.figure = Figure(inchsize)
self.axes = fig.add_subplot(111)
#create canvas as matplotlib drawing area
self.canvas = FigureCanvasTkAgg(fig, master=masterframe)
self.canvas.draw_idle()
self.canvas.get_tk_widget().pack()
self.x = linspace(0,360,1000)
self.axes.grid()
self.ysin = int(ceil(120))*sin(50/freq * deg2rad(self.x))
self.lineU1, = self.axes.plot(self.x, self.ysin, "-r",label="U1")
#Draw the plot
def reDraw(self,*args):
ax = self.axes
self.ysin = int(ceil(120))*sin(50/freq * deg2rad(self.x))
self.lineU1.set_ydata(self.ysin)
self.figure.canvas.draw_idle()
self.figure.canvas.flush_events()
#------------------------------------------------------------------------------------
#Keyboard Click - Changeable to any other event i.e. GPIO event detect
def up_press(event):
global freq
freq = freq + 5
app.reDrawFrame()
def down_press(event):
global freq
if(freq > 5):
freq = freq - 5
app.reDrawFrame()
#-------------------------------------------------------------------------------------
# Start Tkinter
try:
app = Application()
keyboard.on_press_key("up", up_press)
keyboard.on_press_key("down", down_press)
app.mainloop()
except Exception as e:
print(e)
美好的一天,
所以我有点反对在 Tkinter 中刷新 Matplotlib。总的来说,代码工作正常,但是一旦你按下“向上”键,程序就会在 figure.canvas.draw() 处崩溃——我也在 rasberry 上 运行 这段代码,它只是说内存访问失败。
我知道你可以通过 FuncAnimation 制作动画,但我遇到了一个大问题,它会随着时间的推移增加内存直到崩溃,所以我不想使用它(参见 Python3 - Matplotlib FuncAnimation continuous memoryleak)
所以我想我可以用老式的方式重画它,但这似乎也行不通。
最小代码:
import tkinter as tk
from threading import Thread
from numpy import sin, cos, pi
from pynput.keyboard import Listener
import keyboard
reDraw = False
U1ausg = U2ausg = U3ausg = Counter_3 = 50
Counter_1 = 120
Counter_2 = 240
#--Mainwindow--
class Drehstromdemonstrator(tk.Tk):
def __init__(self):
#Predefine
tk.Tk.__init__(self)
tk.Tk.wm_title(self, "Minimal")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.frame = plotSine(self,(tk.Tk.winfo_screenwidth(self)*0.7,tk.Tk.winfo_screenheight(self)*0.7))
def reDrawFrame(self):
self.frame.drawStuff()
import numpy,matplotlib
matplotlib.use('TkAgg')
from numpy import sin, cos, pi, deg2rad, linspace, arange
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.ticker as tck
import time
import math
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from math import ceil
#global U1ausg, U2ausg, U3ausg, Counter_1, Counter_2, Counter_3
class plotSine:
def __init__(self,masterframe,size):
self._running = True
global U1ausg
global U2ausg
global U3ausg
(w,h) = size
inchsize = (w/25.5, h/25.4)
fig = self.figure = Figure(inchsize)
self.axes = fig.add_subplot(111)
self.axes.xaxis.set_ticks(arange(0,390,30))
self.axes.margins(x=0)
self.axes.yaxis.set_ticks(arange(-120,130,20))
self.axes.set_ylim(-120,120)
#create canvas as matplotlib drawing area
self.canvas = FigureCanvasTkAgg(fig, master=masterframe)
self.canvas.get_tk_widget().pack()
#self.canvas.mpl_connect('key_press_event',on_press)
self.x = linspace(0,360,1000)
self.axes.grid()
self.ysin = int(ceil(U1ausg))*sin(50/Counter_3 * deg2rad(self.x))
self.lineU1, = self.axes.plot(self.x, self.ysin, "-r",label="U1")
#self.drawStuff()
#Draw the plot
def drawStuff(self,*args):
ax = self.axes
#ax.clear()
ax.legend(["U1"])
#Changed everything to degree instead of PI, better looking
self.lineU1.set_ydata(int(ceil(U1ausg))*sin(50/Counter_3 * deg2rad(self.x)))
print("Redrawing!")
#---------------------------------------------------------------------
#---------------------------------------------------------------------
#---------------------------------------------------------------------
self.figure.canvas.draw() #First run is fine, crashes here afterwards
print("Flush!")
self.figure.canvas.flush_events()
print("ReDraw Done!")
#--Run Mainprog
app = Drehstromdemonstrator()
def on_press(event):
global Counter_3
Counter_3 = Counter_3 + 5
print("up")
app.reDrawFrame()
try:
keyboard.on_press_key("up", on_press)
app.mainloop()
# Aufraeumarbeiten nachdem das Programm beendet wurde
except UnicodeDecodeError:
print("Interrupt aswell!")
lampensteuerung.terminate()
except KeyboardInterrupt:
print("Interrupt")
lampensteuerung.terminate()
except Exception as e:
print(e)
此代码只是将一个简单的正弦波 (matplotlib) 放入 Tkinter 叠加层(必须有一个)- 在按钮上按“UP”可以将频率提高 +5。
我正在使用 keyboard.on_press_key("up", on_press)
因为我必须在我使用 GPIO.add_event_detect(channel, GPIO.RISING)
的树莓上尽可能地模拟它
(使用 fig.canvas.mpl_connect('key_press_event', on_press) 它工作正常但我不能在 PI 上这样做)
谢谢和问候!
所以我自己得到了 - 解决方案:将 draw() 更改为 draw_idle()
对于任何需要 Tinker 中可更改的 Matplotlib 以响应 Keyinputs / 或 GPIO.INPUTS 的人,这里是代码:
# coding=utf-8
import tkinter as tk
import math as m
from numpy import sin
#-----------------------------------Ausgabe:-----------------------------------------
#Predefine
app = None
freq = 50
# Create Main Window
class Application(tk.Tk):
def __init__(self):
#Predefine
tk.Tk.__init__(self)
tk.Tk.wm_title(self, "Title")
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
#Add the MatPlotLib
self.frame = plotSine(self,(tk.Tk.winfo_screenwidth(self)*0.7,tk.Tk.winfo_screenheight(self)*0.7))
def reDrawFrame(self):
self.frame.reDraw()
self.frame.reDraw() ## For whatever reason it can happen that the Draw Function has to be called twice for a good refresh?
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
import numpy,matplotlib
matplotlib.use('TkAgg')
from numpy import sin, deg2rad, linspace
import math
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from math import ceil
import keyboard
class plotSine:
def __init__(self,masterframe,size):
self._running = True
(w,h) = size
inchsize = (w/25.5, h/25.4)
fig = self.figure = Figure(inchsize)
self.axes = fig.add_subplot(111)
#create canvas as matplotlib drawing area
self.canvas = FigureCanvasTkAgg(fig, master=masterframe)
self.canvas.draw_idle()
self.canvas.get_tk_widget().pack()
self.x = linspace(0,360,1000)
self.axes.grid()
self.ysin = int(ceil(120))*sin(50/freq * deg2rad(self.x))
self.lineU1, = self.axes.plot(self.x, self.ysin, "-r",label="U1")
#Draw the plot
def reDraw(self,*args):
ax = self.axes
self.ysin = int(ceil(120))*sin(50/freq * deg2rad(self.x))
self.lineU1.set_ydata(self.ysin)
self.figure.canvas.draw_idle()
self.figure.canvas.flush_events()
#------------------------------------------------------------------------------------
#Keyboard Click - Changeable to any other event i.e. GPIO event detect
def up_press(event):
global freq
freq = freq + 5
app.reDrawFrame()
def down_press(event):
global freq
if(freq > 5):
freq = freq - 5
app.reDrawFrame()
#-------------------------------------------------------------------------------------
# Start Tkinter
try:
app = Application()
keyboard.on_press_key("up", up_press)
keyboard.on_press_key("down", down_press)
app.mainloop()
except Exception as e:
print(e)