I am trying to write a simple program that reads the values of three different widgets and plots a graph of a function depending on what the input values are. I run into the following problems:
1) On the button press I get the error "App_Window instance has no attribute 'refreshFigure'"
2)On exiting ipython (I use ipython-listener in conjunction with a gedit plugin) I get the error "Exception RuntimeError: 'main thread is not in main loop' in del of
I've based this mostly on this thread - Interactive plot based on Tkinter and matplotlib and this thread - How do I refresh a matplotlib plot in a Tkinter window?
#!/usr/bin/python
from Tkinter import Tk, Frame
from Tkinter import *
import Tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#this bit I added just because the NVC13 module is in another directory
import sys
for i in range(len(sys.path)):
if sys.path[i]!='/home/captain-pants/NV_centri':
if i==len(sys.path)-1:
sys.path.append('/home/captain-pants/NV_centri')
continue
else:
break
import multiprocessing as mp
import numpy as np
import matplotlib.pyplot as plt
#This module contains all the code necessary to return a list of values that depend on the input parameters
import NVC13_1lev_advanced_ex_nocoh as C13
from matplotlib.figure import Figure
#this function takes a list of parameters and returns a list of values dependant on those parameters.
def plotfunc(c13,Eg,Bz):
Epam=[]
nv = C13.NVC13(c13,[2580,1423],[Eg,Eg],0,0,Bz,0)
evals = nv.intH()
for i in range(len(evals[0])):
Epam.append(np.real_if_close(evals[0][i]))
return Epam
class App_Window:
def __init__(self, parent):
frame = Tkinter.Frame(parent)
self.Egdata=DoubleVar()
self.c13input=IntVar()
self.Bzdata=DoubleVar()
parent.title("Simple")
Tkinter.Button(text='lulz',command=self.main1).grid(row=3,column=0)
Tkinter.Label(textvariable=self.Egdata).grid(row=5,column=0)
self.c13 = Tkinter.Entry(textvariable=self.c13input)
self.Eg = Tkinter.Scale(orient='horizontal')
self.Bz = Tkinter.Scale(orient='horizontal')
self.c13.grid(row=6,column=0)
self.Bz.grid(row=5,column=0)
self.Eg.grid(row=4,column=0)
Fig = Figure()
FigSubPlot = Fig.add_subplot(111)
self.line, = FigSubPlot.plot(range(10),'bo')
x=[]
y=[]
self.canvas = FigureCanvasTkAgg(Fig, master=parent)
self.canvas.show()
self.canvas.get_tk_widget().grid(row=0,column=0,columnspan=2)
frame.grid(row=0,column=0)
def refreshFigure(self,y):
x=np.arange(len(y))
self.line.set_xdata(x)
self.line.set_ydata(y)
self.canvas.draw()
def main1(self):
self.c13input=int(self.c13.get())
self.Egdata=self.Eg.get()
self.Bzdata=self.Bz.get()
values = plotfunc(self.c13input,self.Egdata,self.Bzdata)
#Just to see whether I actually get the right thing in the input.
print self.c13input
self.refreshFigure(values)
root = Tkinter.Tk()
app = App_Window(root)
root.mainloop()
Related
I have 3 sheets in a notebook that do the same thing as I show in the code below. (This code works fine here but not in my code)
All sheets have the function checkbox_O and g_checkbox with the same name and all use the global variable lines
When I press the graph button, the graphs are displayed but the checkboxes only work fine on sheet 1.
I did code tests and I realize that, in sheet 2 and 3, the g_checkbox function does not have access to the lines array (I don't know why)
This is what I tried to fix it and it didn't work:
tried to write g_checkbox function as a class method
I tried to place a global lines variable for each sheet
I changed the name of the function checkbox_O in each sheet, so that it is different in each one of them
I need help please to know how to make the checkboxes work in all three sheets.
I don't know what I'm doing wrong and I don't know how to fix it.
Thanks in advance
import numpy as np
import tkinter as tk
from tkinter import ttk
import matplotlib
from matplotlib import pyplot as plt
matplotlib.use('TkAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import(FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib import style
from matplotlib.widgets import CheckButtons, Button, TextBox, RadioButtons
x = np.arange(-15, 15, .01)
lines = []
class root(tk.Tk):
def __init__(self):
super().__init__()
self.geometry('750x618')
self.my_book()
def my_book(self):
#I left it out so the code doesn't get too long
class myFrame(ttk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.my_widgets()
def my_widgets(self):
self.f = Figure(figsize=(7, 5.5), dpi=100)
self.plott = self.f.add_subplot()
self.canvas = FigureCanvasTkAgg(self.f, self)
self.canvas.draw()
self.canvas.get_tk_widget().pack()
self.button_ax = self.f.add_axes([0.76, 0.95, 0.11, 0.04])
self.button_graph = Button(self.button_ax, 'Graph', color='darkgrey')
self.button_graph.on_clicked(self.callback_button)
plt.show()
def callback_button(self, event):
self.graph_1()
self.graph_2()
def checkbox_O(self, lines, f):
self.check_ax = f.add_axes([0.76, 0.085, 0.21, 0.16])
labels = [str(line.get_label()) for line in lines]
labels_visual = ['graph1', 'graph2']
visibility = [line.get_visible() for line in lines]
self.check = CheckButtons(self.check_ax, labels_visual, visibility)
def g_checkbox(label):
selec_index = [ind for ind, lab in enumerate(self.check.labels) if label in lab._text]
for index, laabel in enumerate(labels):
if laabel==label:
lines[index].set_visible(not lines[index].get_visible())
if (lines[index].get_visible()):
self.check.rectangles[selec_index[0]].set_fill(True)
else:
self.check.rectangles[selec_index[0]].set_fill(False)
f.canvas.draw()
self.check.on_clicked(g_checkbox)
plt.show()
def graph_1(self):
global lines
for i in range(0, 15):
V = np.sin(i/10*x) + 2*i
l0, = self.plott.plot(x, V, label='graph1')
lines.append(l0)
self.checkbox_O(lines, self.f)
def graph_2(self):
global lines
for i in range(0, 15):
l0, = self.plott.plot([i, i], [0, 10], label='graph2')
lines.append(l0)
self.checkbox_O(lines, self.f)
if __name__ == "__main__":
app = root()
app.mainloop()
PS: By the way, I took the code from stackoverflow and modified it so that it works like this
I'm trying to implement matplotlib toolmanager custom tools from this Tool Manager Example in my tkinter app (which is based partly on this other MPL Example), but I'm running into issues when trying to access fig.canvas.manager.toolmanager.
Here's a pared-down version of my tkinter application
import matplotlib as mpl
import matplotlib.backends.backend_tkagg as mptk
import matplotlib.pytlot as plt
import numpy as np
import tkinter as tk
from matplotlib.backend_tools import ToolBase # for custom tools, as per linked TM example
from tkinter import ttk
mpl.use('TkAgg') # do I need this? What is this doing?
plt.rcParams['toolbar'] = 'toolmanager' # as per the linked TM example
class Root(tk.Tk):
def __init__(self):
super().__init__()
self.title('Plot App')
self.geometry('1920x1080')
self.main_frame = MainFrame(self)
self.main_frame.pack(expand=True, fill=tk.BOTH)
class MainFrame(tk.Frame):
def __init__(self, parent):
super().__init__(parent)
self.parent = parent
self.label_frame = ttk.LabelFrame(self, text='Data Viewer')
self.label_frame.pack(expand=True, fill=tk.BOTH)
self.plot() # just plot immediately at init
def plot(self):
# nonsense data to plot
t = np.arange(0, 10, 0.1)
a = np.sin(8 * np.pi * t)
b = np.cos(4 * np.pi * t)
self.fig, self.ax = plt.subplots()
self.ax.plot(t, a, t, b)
self.tk_canvas = mptk.FigureCanvasTkAgg(self.fig, master=self.label_frame)
self.tk_canvas.draw()
self.tk_canvas.get_tk_widget().pack(expand=True, fill=tk.BOTH)
# init custom toolbar
self.toolbar = Toolbar(self.fig, self.label_frame)
self.toolbar.update()
class Toolbar(mptk.NavigationToolbar2Tk): # custom toolbar inherits from NavigationToolbar2Tk
def __init__(self, fig, parent):
super().__init__(fig.canvas, parent)
self.fig = fig
self.tb = self.fig.canvas.manager.toolbar # here is where I run into trouble
self.tm = self.fig.canvas.manager.toolmanager # and likewise, here
if __name__ == '__main__':
app = Root()
app.mainloop()
The line assigning self.tb throws the following exception at run time:
AttributeError: 'NoneType' has no attribute 'toolbar'
I understand that this is because self.fig.canvas.manager is None - what I don't understand is why that's the case when the example does this without issues.
For reference, if I print(fig.canvas.manager.toolmanager) from the Tool Manager Example code, I get (as expected):
<matplotlib.backend_managers.ToolManager object at ~ID~>
It's worth pointing out (though perhaps not surprising) that the assignment to self.tm throws the same exception for much the same reason.
Informational Edit:
A little more digging led me to the method plt.get_current_fig_manager(), which in my case is unfortunately None; I assume this has something to do with using the Tk backend.
What am I doing wrong? How can I use toolmanager effectively with NavigationToolbar2Tk? I'd like to be able to use tm.add_tool() and tm.remove_tool(), but I can't since my toolmanager doesn't exist.
Any help is appreciated much appreciated.
I'm trying to plot data from serial port in a tkinter gui. The plot should be plotted and updated only when a specific pack comes from serial.
I can parse the new incoming data and update the GUI (text area). But when I call the "plot()" function from the "update_gui" thread, the program quit and I get the
"Process finished with exit code -1073741819 (0xC0000005)"
message.
Instead, if I call "plot()" from somewhere else (command button, or simply before mainloop()), the plot is generated and shown.
Here is the relevant part of the code:
import threading
import tkinter as tk
import tkinter.scrolledtext as st
import rx_seriale as rx_ser
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
import queue
running = True
serial_data = ''
filter_data = ''
update_period = 5
serial_object = None
gui = tk.Tk()
gui.title("UART Interface")
my_queue = queue.Queue()
t1 = ''
def plot(valori):
global frame_plot1
try:
# the figure that will contain the plot
fig = Figure(figsize=(4.5, 4), dpi=100)
# adding the subplot
plot1 = fig.add_subplot(111)
#dummy values
y = [i ** 2 for i in range(101)]
# plotting the graph
plot1.plot(y)
# creating the Tkinter canvas
# containing the Matplotlib figure
canvas = FigureCanvasTkAgg(fig, master=frame_plot1)
canvas.draw()
# placing the canvas on the Tkinter window
canvas.get_tk_widget().pack()
# creating the Matplotlib toolbar
#toolbar = NavigationToolbar2Tk(canvas, frame_plot1)
#toolbar.update()
# placing the toolbar on the Tkinter window
#canvas.get_tk_widget().pack()
except Exception as e:
print('Errore:' + str(e))
def update_gui():
global filter_data
global update_period
global my_queue
global type_test, test_status,flag
while (1):
data = my_queue.get(block=True)
text.insert('end', test_status[data[0]] + " - " + type_test[data[1]])
text.insert('end', '\n')
text.see('end')
if (data[1] == 6):
plot(1)
if __name__ == "__main__":
'''
...
all the stuff for design TK windows
...
'''
# threads
t2 = threading.Thread(target=update_gui)
t2.daemon = True
t2.start()
# mainloop
gui.geometry('1000x500')
gui.mainloop()
What goes wrong? Thank you.
EDIT2:
So after some more work I created the smallest sample which reproduces the Memoryleak pretty decent on every machine.
This code just creates a TKinter Window and a matplotlib canvas with 3 Sine waves, than tries to animate then. See that blit is true, so it shoudnt do anything because the drawing does not change.
I also tried following ways from this post
How can I release memory after creating matplotlib figures
gc.collect() - did nothing
axes.clear - it did stop endless memory to be leaked but also completely screws the picture
figure.clf - same as clear but worse.
Any other idea why FuncAnimation increases in memory usage over time?????
Thanks
import tkinter as tk
from threading import Thread
from numpy import sin, cos, pi
#--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))
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
U1ausg = U2ausg = U3ausg = Counter_3 = 50
Counter_1 = 120
Counter_2 = 240
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.x = linspace(0,360,1000)
self.axes.grid()
#self.drawStuff()
#Draw the plot
def drawStuff(self,*args):
ax = self.axes
self.axes.legend(["U1","U2","U3"])
#Changed everything to degree instead of PI, better looking
ysin = int(ceil(U1ausg))*sin(50/Counter_3 * deg2rad(self.x))
ysin2 = int(ceil(U2ausg))*sin(50/Counter_3 * deg2rad(self.x) + deg2rad(Counter_1))
ysin3 = int(ceil(U3ausg))*sin(50/Counter_3 * deg2rad(self.x) + deg2rad(Counter_2))
lineU1 = ax.plot(self.x, ysin, "-r",label="U1")
lineU2 = ax.plot(self.x, ysin2, "-g",label="U2")
lineU3 = ax.plot(self.x, ysin3, "-b",label="U3")
return [*lineU1,*lineU2,*lineU3]
#Animation to redraw when value changed
def animate(self):
self.ani = animation.FuncAnimation(self.figure, self.drawStuff, interval=10, blit=True)
#--Run Mainprog
try:
app = Drehstromdemonstrator()
plt.show()
app.frame.animate()
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)
EDIT1:
By try-and-error I could figure out that the problem seems to be in my matplotlib, as if I disable it the memory increase stops.
So my task is to finish this project from someone else - it simulates a 3-phased system via a raspberry and some lights. When I disable the GUI the code works splendid, no problems, fast reaction. Sometimes the PI says he has low voltage but I just think that is some weird bug because the sourcepower is really enough to power it.
Full Code removed because not needed.
I stuck on my code. I want to have GUI on PyQt5 to show my graph. I have working code in matplot, but I cannot transform it to PyQT. I'm a beginner at this. I tried to find working similar code but I only did this. This works on matplotlib, but i want to have it on my GUI.
import serial
import numpy
import matplotlib.pyplot as plt
from drawnow import *
distance =[]
arduinoData=serial.Serial('COM4',115200)
plt.ion()
cnt=0
def makeFig():
plt.ylim(2,20)
plt.title('Plot ')
plt.grid(True)
plt.ylabel('Distance cm')
plt.plot(distance, 'ro-', label='Distance ')
plt.legend(loc='upper left')
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
arduinoString = arduinoData.readline() #read the line of text from the serial port
measurement = int(arduinoString) #Convert first element to floating number and put in measurement
distance.append(measurement) #Build our distance array by appending temp readings
drawnow(makeFig) #Call drawnow to update our live graph
plt.pause(.000001) #Pause Briefly. Important to keep drawnow from crashing
cnt=cnt+1
if(cnt>50): #If you have 50 or more points, delete the first one from the array
distance.pop(0) #This allows us to just see the last 50 data points
#This allows us to just see the last 50 data points
My PyQt and Matplot. I want to click on the button and show this graph.
I got error that " in line 51, in plot
measurement=int(arduinoString)
ValueError: invalid literal for int() with base 10: b'\n' " and
"Backend TkAgg is interactive backend. Turning interactive mode on."
I'm sending data that are simple distance like 5, 10 and so on from my sensor.
import sys
from PyQt5.QtWidgets import QDialog, QApplication, QPushButton, QVBoxLayout
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar
import matplotlib.pyplot as plt
import random
import serial
import numpy
from drawnow import *
arduinoData=serial.Serial('COM4',115200)
cnt=0
distance =[]
class Window(QDialog):
def __init__(self, parent=None):
super(Window, self).__init__(parent)
# a figure instance to plot on
self.figure = plt.figure()
# this is the Canvas Widget that displays the `figure`
# it takes the `figure` instance as a parameter to __init__
self.canvas = FigureCanvas(self.figure)
# this is the Navigation widget
# it takes the Canvas widget and a parent
self.toolbar = NavigationToolbar(self.canvas, self)
# Just some button connected to `plot` method
self.button = QPushButton('Plot')
self.button.clicked.connect(self.plot)
# set the layout
layout = QVBoxLayout()
layout.addWidget(self.toolbar)
layout.addWidget(self.canvas)
layout.addWidget(self.button)
self.setLayout(layout)
def plot(self):
while True:
while (arduinoData.inWaiting()==0): #wait for data
pass
arduinoString=arduinoData.readline() #read the line of text from the serial port
measurement=int(arduinoString)
distance.append(measurement)
plt.pause(0.00001)
global cnt
cnt=cnt+1
if (cnt>50):
distance.pop(0)
self.figure.clear()
ax = self.figure.add_subplot(111)
ax.plot(distance, '*-')
self.canvas.draw()
if __name__ == '__main__':
app = QApplication(sys.argv)
main = Window()
main.show()
sys.exit(app.exec_())
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
This doesn't actually wait, it executes whatever's below it regardless of your condition check. Try it as follows:
while True:
if (arduinoData.inWaiting()==0):
continue
#rest of your code