Newbie here, i am working on a program which allows the users to see the animation of spring extension after adjusting the slider(higher frequency=higher extension). However, based on what i did, the spring will move immediately after the slider is moved. How should i change my code so that the spring will show its extension from 0 to the desired length like a full animation?
from tkinter import *
import math
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.patches as patches
window = Tk()
window.geometry("700x700")
window.configure(bg="white")
fig= Figure(figsize=(3, 3), dpi=100)
pg = fig.add_subplot(111)
def plot_graph():
pg.axis([0,800,-500,1000])
pg.get_yaxis().set_visible(False)
pg.set_title("Mass Spring Simulation ")
pg.set_xlabel("Spring extension (m)")
pg.set_ylabel("Displacement")
pg.grid()
def sine_graph():
pg.clear()
coorx=[]
coory=[]
for x in range(0,round(62.5*frequency)):
coorx.append(x)
y = amplitude*math.sin(x/(frequency))+250
coory.append(y)
rect = patches.Rectangle((coorx[-1], 0), 80,500, color='black')
pg.add_patch(rect)
Line= pg.plot(coorx,coory,label='Spring')
plot_graph()
fig.canvas.draw()
def update_fre(value):
global frequency
frequency = slider2.get()
sine_graph()
amplitude,frequency = 250,1
canvas = FigureCanvasTkAgg(fig, window)
canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas,window)
toolbar.update()
L2 = Label(window,text="Force")
L2.pack()
slider2 = Scale(window,from_=1,to=10,orient=HORIZONTAL,command=update_fre)
slider2.pack()
sc= Label(window,text="Spring Constant = 0.0143N/m")
sc.pack()
sine_graph()
window.mainloop()
what i have tried (clearing the page and redrawing),but my computer will crash automatically when adjusting slider:
def sine_graph():
pg.clear()
coorx=[]
coory=[]
z=1
for i in range (frequency):
for x in range(1,round(62.5*z)):
pg.clear()
coorx.append(x)
y = amplitude*math.sin(x/(frequency))+250
coory.append(y)
z+=1
Related
I am trying to plot data on tkinter canvas using matplotlib "imshow()" function. When I am running the code the data is getting plotted onto the canvas and in the navigation toolbar pixel coordinates (x and y coordinates) are getting displayed along with pixel values (in bracket). Issue is I want to display only the pixel coordinates and hide pixel values which is getting displayed in the navigation toolbar.
The code which I am using is:
import tkinter
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import tkinter
import numpy as np
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
root = tkinter.Tk()
fig = Figure(figsize=(5, 4), dpi=100)
fig.subplots_adjust(bottom=0, right=1, top=1, left=0, wspace=0, hspace=0)
ax = fig.add_subplot(111)
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
return 'x={:.01f}, y={:.01f}'.format(x, y)
data = np.random.random((10,10))
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
canvas1 = FigureCanvasTkAgg(fig, master=root)
canvas1.draw()
toolbar = NavigationToolbar2Tk(canvas1,root)
toolbar.update()
toolbar.pack(side=tkinter.TOP, fill=tkinter.X, padx=8)
canvas1.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1, padx=10, pady=5)
canvas1._tkcanvas.pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=1, padx=10, pady=5)
root.mainloop()
Kindly suggest how to hide pixel values getting displayed in the navigation toolbar (inside brackets) and display only pixel coordinates (x & y coordinates).
The value in the navigation toolbar is created by the images' format_cursor_data method. You can replace that method to return an empty string.
im = ax.imshow(data, interpolation='none')
im.format_cursor_data = lambda e: ""
One way is to override the method mouse_move:
class Navigator(NavigationToolbar2Tk):
def mouse_move(self, event):
self._set_cursor(event)
if event.inaxes and event.inaxes.get_navigate():
try:
s = event.inaxes.format_coord(event.xdata, event.ydata)
self.set_message(s)
except (ValueError, OverflowError):
pass
else:
self.set_message(self.mode)
...
toolbar = Navigator(canvas1,root)
...
I have the following code:
from tkinter import *
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class tkPlot:
def __init__(self,master):
frame = Frame(master)
frame.pack()
canvasSubFrame=Frame(frame)
canvasSubFrame.grid(row=0, column=0)
f = Figure(figsize=(8,8), dpi=100)
subplotA = f.add_subplot(211)
subplotA.set_title("Plot A")
subplotB = f.add_subplot(212)
subplotB.set_title("Plot B")
subplotA.plot([1,2,3,4,5,6],[1,4,9,16,25,36])
subplotB.plot([1,2,3,4,5,6],[1,1/2,1/3,1/4,1/5,1/6])
canvas = FigureCanvasTkAgg(f, master=canvasSubFrame)
canvas.draw()
canvas.get_tk_widget().pack(expand=False)
if __name__ == "__main__":
root = Tk()
app = tkPlot(root)
root.mainloop()
Ideally, i would like to have a textbox or spin control besides every top and bottom of each subplot to set the y axes min and max scales (y limits)
What is the proper way to do this. It should work for any number of subplots.
I'm creating a GUI in Tkinter that has an embedded matplotlib graph. I change the contents of the graph based on a couple of Radiobuttons. These Radiobuttons are linked to a function which redefines the data in the graph, and ends with a call to self.canvas.draw(). I used Unable to update Tkinter matplotlib graph with buttons and custom data substantially, and I got it to work perfectly: the graph is updated with the desired new data.
The problem I run into is that (I'm using dates for the x-tick labels) after the redraw function is called, the x-tick labels get rotated back to horizontal, causing them to overlap. Below is a simplified form of my application, which has no dependencies so can be run directly.
import tkinter
from tkinter import *
from tkinter.ttk import *
import csv
import datetime as dt
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
matplotlib.use('TkAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
class MainApp:
def __init__(self,parent):
self.RightFrame = LabelFrame(parent,text='RightFrame')
self.RightFrame.pack(side='right')
self.plot_fig=plt.figure(figsize=(4,4),dpi=100)
self.fig = Figure()
self.ax = self.fig.add_subplot(111)
self.fig.autofmt_xdate(rotation=30) ### Sets tick rotation Initially
self.ax.fmt_xdata=mdates.DateFormatter('%Y-%m-%d')
self.ax.hold(False)
self.canvas = FigureCanvasTkAgg(self.fig,master=self.RightFrame)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=TOP)
self.plot_radio_frame = LabelFrame(self.RightFrame)
self.plot_radio_frame.pack(side=BOTTOM)
self.plot_time_radio_var = IntVar()
self.plot_time_radio_var.set(7)
modes = [('1 Week',3),('1 Month',4),('1 Year',5),('5 Years',6),('All',0)]
for text,value in modes:
b= Radiobutton(self.plot_radio_frame,text=text,variable=self.plot_time_radio_var,value=value)
b.pack(side=LEFT)
self.plot_time_radio_var.trace('w',self.plotting_function)
def plotting_function(self, varname, elementname, mode):
plot_time = self.plot_time_radio_var.get()
try:
data_hist = [['2016-12-16',116.4700],
['2016-12-19',115.8000],
['2016-12-20',116.7400],
['2016-12-21',116.8000],
['2016-12-22',116.3500],
['2016-12-23',115.5900],
['2016-12-27',116.5200],
['2016-12-28',117.5200],
['2016-12-29',116.4500],
['2016-12-30',116.6500]
]
days = int(plot_time)
spacing = 1
dates = [line[0] for line in data_hist[-days::spacing]]
new_dates = [dt.datetime.strptime(d,'%Y-%m-%d').date() for d in dates]
Y_DATA = [line[1] for line in data_hist[-days::spacing]]
#print(adj_close)
self.curve = self.ax.plot(new_dates,Y_DATA)
self.canvas.draw()
except Exception as e:
print(str(e))
root = Tk()
MainApp(root)
root.mainloop()
I have snippets of code to generate a basemap, as well as the (very rough) start of a GUI. However, I cannot find that "one-liner" will allow me to display the map as a figure in the GUI. Code snippets are as follows; ideally, I would like a couple of things to happen:
An image of the 'basemap' to relace the plot of sin(2*pi*t)
I would really like for the code to record the (pixel) location on the graphic, if I were to click on the plot shown (the hope is that you could click anywhere on the map, and the script would record the latitude and longitude of where you clicked).
Regarding the 1st step, I've tried things such as setting the figure variable, f, equal to the Basemap; this killed the GUI portion altogether, and simply showed an image of the map in another window.
I've tried to address #2 by trying to implement a couple routines I found on stackexchange, but never got it to work fully.
Basemap:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
## User-chosen datapoint
#lons = [10]; lats = [20];
# Define map projection
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\
llcrnrlon=-180,urcrnrlon=180,resolution='c')
m.drawcoastlines()
Rough GUI:
import matplotlib
matplotlib.use('TkAgg')
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
# implement the default mpl key bindings
from matplotlib.backend_bases import key_press_handler
from Tkinter import *
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from mpl_toolkits.basemap import Basemap
from matplotlib.figure import Figure
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
root.wm_title("Embedding in TK")
f = Figure(figsize=(5, 4), dpi=100)
a = f.add_subplot(111)
t = arange(0.0, 3.0, 0.01)
s = sin(2*pi*t)
a.plot(t, s)
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(f, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def on_key_event(event):
print('you pressed %s' % event.key)
key_press_handler(event, canvas, toolbar)
canvas.mpl_connect('key_press_event', on_key_event)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
The question how to include a basemap plot into Tkinter has actually not been answered yet. So the idea is to create an axes ax in a figure and add the Basemap to the axes. This is done with the ax argument,
m = Basemap(..., ax=ax)
The figure is then added to the canvas in the usual way; below is a complete example.
from mpl_toolkits.basemap import Basemap
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import sys
if sys.version_info[0] < 3:
import Tkinter as Tk
else:
import tkinter as Tk
root = Tk.Tk()
root.wm_title("Embedding in TK")
fig = Figure(figsize=(5, 4), dpi=100)
ax = fig.add_subplot(111)
m = Basemap(projection='cyl',llcrnrlat=-90,urcrnrlat=90,\
llcrnrlon=-180,urcrnrlon=180,resolution='c', ax=ax)
m.drawcoastlines()
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
toolbar = NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tk.TOP, fill=Tk.BOTH, expand=1)
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
Note: In newer versions of matplotlib you should use NavigationToolbar2Tk instead of NavigationToolbar2TkAgg.
One simple technique is to define an on_click function, show an image, then overwrite that image. The on_click function will still read the mouse click location, despite the image change.
A sample code follows, demonstrating this is as shown:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
def on_click(event):
if event.inaxes is not None:
print event.xdata, event.ydata
else:
print 'Clicked ouside axes bounds but inside plot window'
fig, ax = plt.subplots()
fig.canvas.callbacks.connect('button_press_event', on_click)
plt.show()
#Define map projection
m = Basemap(projection='cyl', llcrnrlat=-90, urcrnrlat=90,
llcrnrlon=-180, urcrnrlon=180, resolution='c')
m.drawcoastlines()
I was wondering if anyone had an idea as to why the code below does not display a graph with a line in it after the button on the GUI is pressed. I would like to create a program that executes a long list of commands after a set of data is imported by clicking a button. One of these commands would be to display the spectral data on a graph within the same window. Here is what I have so far:
# import modules that I'm using
import matplotlib
matplotlib.use('TKAgg')
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.pyplot as pltlib
import Tkinter
from Tkinter import *
import numpy as np
import scipy as sc
#import matplotlib.pyplot as pltlib
# lmfit is imported becuase parameters are allowed to depend on each other along with bounds, etc.
from lmfit import minimize, Parameters, Minimizer
#Make object for application
class App_Window(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def initialize(self):
button = Tkinter.Button(self,text="Open File",command=self.OnButtonClick).pack(side=Tkinter.TOP)
self.canvasFig=pltlib.figure(1)
Fig = matplotlib.figure.Figure(figsize=(5,4),dpi=100)
FigSubPlot = Fig.add_subplot(111)
x=[]
y=[]
self.line1, = FigSubPlot.plot(x,y,'r-')
self.canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(Fig, master=self)
self.canvas.show()
self.canvas.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
self.canvas._tkcanvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
self.resizable(True,False)
self.update()
def refreshFigure(self,x,y):
self.line1.set_xdata(x)
self.line1.set_ydata(y)
self.canvas.draw()
def OnButtonClick(self):
# file is opened here and some data is taken
# I've just set some arrays here so it will compile alone
x=[]
y=[]
for num in range(0,1000):x.append(num*.001+1)
# just some random function is given here, the real data is a UV-Vis spectrum
for num2 in range(0,1000):y.append(sc.math.sin(num2*.06)+sc.math.e**(num2*.001))
X = np.array(x)
Y = np.array(y)
self.refreshFigure(X,Y)
if __name__ == "__main__":
MainWindow = App_Window(None)
MainWindow.mainloop()
That is because the range of xaxis & yaxis doesn't change to new data's range, change your refreshFigure as following:
def refreshFigure(self,x,y):
self.line1.set_data(x,y)
ax = self.canvas.figure.axes[0]
ax.set_xlim(x.min(), x.max())
ax.set_ylim(y.min(), y.max())
self.canvas.draw()