How to make matplotlib animate in a while loop in tkinter - python

I'm trying to live plot EEG recording in my tkinter app. However, it does not plot while the data is updating in the while loop, and plots only at the end.
import tkinter as tk
from tkinter import ttk, Button, LabelFrame, N, W, messagebox as box
import time
import datetime as dt
from datetime import date
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
import matplotlib.animation as animation
import matplotlib.pyplot as plt
class NFB_HomePage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.nfbprotocol = 0
self.metricstime = 0
self.nfbmetric = 0
self.df = []
self.xList = []
self.yList = []
# Start Neurofeedback
self.StartNFB = Button(self, text="Start NFB", width=25, command=self.onStartNFB)
self.StartNFB.grid(row=7, column=0, padx=5, pady=3, sticky=W)
# LabelFrame for EEG Graph
labelframe3 = LabelFrame(self, text="EEG Plot")
labelframe3.grid(row=4, column=2, padx=5, pady=2, sticky=N, rowspan=5, columnspan=4)
self.fig = plt.Figure(figsize=(10, 5), dpi=100)
self.ax1 = self.fig.add_subplot(111)
self.line, = self.ax1.plot([], [], lw=2)
self.canvas = FigureCanvasTkAgg(self.fig, labelframe3)
self.canvas.draw()
self.canvas.get_tk_widget().grid()
self.canvas._tkcanvas.grid(row=0, column=0, padx=5, pady=3, sticky=W)
def createDF(self):
df_timestamp = []
df_data = np.empty([0, 5])
# metrics
df_alpha = np.empty([0, 1])
df_beta = np.empty([0, 1])
df_theta = np.empty([0, 1])
df_delta = np.empty([0, 1])
df_alpha_theta = np.empty([0, 1])
df_metricstime = np.empty([0, 1])
return df_timestamp, df_data, df_alpha, df_beta, df_theta, df_delta, df_alpha_theta, df_metricstime
def onStartNFB(self):
try:
self.start()
# Initialize dataframes
df_timestamp, df_data, df_alpha, df_beta, df_theta, df_delta, df_alpha_theta, df_metricstime = self.createDF()
# Start recording here!
timestart = time.time()
while time.time() < timestart + int(4):
""" 3.1 ACQUIRE DATA """
self.metricstime = dt.datetime.now().strftime('%H:%M:%S.%f')
""" 3.3 COMPUTE NEUROFEEDBACK METRICS """
alpha_metric, beta_metric, theta_metric, delta_metric, alphatheta_metric = float(np.random.normal(size=1)), float(np.random.normal(size=1)), \
float(np.random.normal(size=1)), float(np.random.normal(size=1)), \
float(np.random.normal(size=1))
self.nfbmetric = alpha_metric
# Save the bands to data frame
df_alpha = np.append(df_alpha, [[alpha_metric]], axis=0)
df_beta = np.append(df_beta, [[beta_metric]], axis=0)
df_theta = np.append(df_theta, [[theta_metric]], axis=0)
df_delta = np.append(df_delta, [[delta_metric]], axis=0)
df_alpha_theta = np.append(df_alpha_theta, [[alphatheta_metric]], axis=0)
df_metricstime = np.append(df_metricstime, [[self.metricstime]], axis=0)
# Update graph
self.df = np.concatenate([df_metricstime, df_alpha, df_theta, df_alpha_theta],
axis=1)
print(self.nfbmetric)
except:
raise RuntimeError("Error with Neurofeedback.")
def start(self):
self.ani = animation.FuncAnimation(self.fig, self.animate, interval=int(500))
self.running = True
self.ani._start()
def animate(self,i):
df = self.df
for eachline in df:
if len(eachline) > 1:
dfX = eachline[0]
dfY = eachline[1]
self.xList.append(dfX)
self.yList.append(dfY)
self.ax1.clear()
self.ax1.plot(self.xList, self.yList)
title = "Neurofeedback Band Power"
self.ax1.set_title(title)
app = ExpGUI()
app.geometry("1280x720")
app.mainloop()
Does somebody know what is wrong with my code? I know how to get it to run outside the while loop, but I have problems getting it to run while the data is updating.

Related

How to plot an automatic graph using mouse without clicking MATPLOTLIB

I'm looking to plot Data automatically using mouse without clicking From a DZT file.
i Created a program in order to plot data as you can see in this graph:
As you can see in this picture, x axes going from 0 to 3525 ans every x is a signal so i have more than 3500 signals making this graph.
for exemple if i want to see the signal of x=999, it looks like this.
what i want really to do is every time when i pass my mouse without clicking on the graph it should plot it's signal automatically .
i try to use a lot of methods but rally i dont know how to do it.
i'll be grateful for your help
this is my file:
https://www.grosfichiers.com/mvabGETFfna
This is my program:
from tkinter import *
from tkinter import messagebox, filedialog
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import os
from readgssi import readgssi
root = Tk()
root.title("IHM")
root.geometry("1000x800")
Resources_frame = LabelFrame(root, bd=2, relief=GROOVE, text="Conversion Area")
Resources_frame.place(x=5, y=5, width=250, height=80)
lbl_ra = Label(Resources_frame, text="Select your File ")
lbl_ra.grid(row=0, column=0, sticky=W)
lbl_ra = Label(Resources_frame, text="Convert Selected file ")
lbl_ra.grid(row=1, column=0, sticky=W)
NumberOfSimples = ''
DataAScan = ''
Data = ''
xData = ''
xAxes = ''
def ReadDZT():
file_path = filedialog.askopenfilename()
file_name, file_extension = os.path.splitext(file_path)
print(file_extension)
if file_extension == '.DZT':
messagebox.showinfo("INFO", "Your DZT File Has Been Selected Successfully")
hdr, arrs, gps = readgssi.readgssi(infile=file_path, verbose=True)
TimeRange = hdr['rhf_range']
Samples = hdr['rh_nsamp']
X_Axes = np.array(range(0, Samples))
xAxes = X_Axes[2::1]
df = pd.DataFrame(arrs[0])
Data = df.iloc[2::1, 0::1]
fig2 = plt.figure()
plt.plot(xAxes, Data[999])
plt.show()
fig = plt.figure()
plt.imshow(Data, aspect='auto', cmap='bone')
plt.show()
elif file_extension == '.csv':
messagebox.showinfo("INFO", "Your CSV File Has Been Selected Successfully")
df = pd.read_csv(file_path)
NumberOfSimples = df.iloc[2::1, 0]
Data = df.iloc[::1, ::1]
DataAScan = df.iloc[2::1, 999]
fig1 = plt.figure()
plt.plot(NumberOfSimples, DataAScan)
plt.show()
fig2 = plt.figure()
plt.imshow(Data, aspect='auto', cmap='bone')
plt.show()
else:
messagebox.showwarning("WARNING", "You Have Been Selected a Different Format")
# =============Resources Buttons================#
btn_rs = Button(Resources_frame, relief=GROOVE, padx=8, pady=1, text="Browse", command=ReadDZT).grid(row=0,
column=1)
root.mainloop()
As indicated in the previous question Ploting a graph automatically using mouse coordinate (and I think this question should have been an edit of it), the mouse motion can be monitored with plt.connect('motion_notify_event', mouse_move). The slice of Data to be plotted in fig2 simply corresponds to the x-coordinate of the mouse, that is Data[int(event.xdata)] in mouse_move(event).
Therefore, in mouse_move(event), you:
Clear the axis: ax2.clear() (where ax2 is the AxesSubplot object of fig2)
Plot the slice ax2.plot(self.xAxes, self.Data[int(event.xdata)])
Update the figure fig2.canvas.draw_idle()
However, I had issues with using simultaneously the matplotlib figures and the tkinter GUI because of the separate event loops. Therefore I embedded the figures in the tkinter GUI directly (see e.g. https://matplotlib.org/3.4.0/gallery/user_interfaces/embedding_in_tk_sgskip.html).
I also put the graph related code in a class to keep the loaded data in attributes to avoid using global variables.
import tkinter as tk
from tkinter import messagebox, filedialog
import pandas as pd
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import numpy as np
import os
from readgssi import readgssi
class Graphs(tk.Toplevel):
def __init__(self, master=None, **kw):
tk.Toplevel.__init__(self, master, **kw)
self.rowconfigure(0, weight=1)
self.rowconfigure(2, weight=1)
# data
self.Data = []
self.xData = []
self.xAxes = []
self.line = None
# figure1 : 2D data map
self.fig1 = Figure()
self.ax1 = self.fig1.add_subplot(111)
self.canvas1 = FigureCanvasTkAgg(self.fig1, self)
self.canvas1.draw()
self.canvas1.get_tk_widget().grid(sticky='ewsn')
self.toolbar1 = NavigationToolbar2Tk(self.canvas1, self, pack_toolbar=False)
self.toolbar1.grid(sticky="ew")
# figure 2: slice plot
self.fig2 = Figure()
self.ax2 = self.fig2.add_subplot(111)
self.canvas2 = FigureCanvasTkAgg(self.fig2, self)
self.canvas2.draw()
self.canvas2.get_tk_widget().grid(sticky='ewsn')
self.toolbar2 = NavigationToolbar2Tk(self.canvas2, self, pack_toolbar=False)
self.toolbar2.grid(sticky="ew")
# bind plotting to mouse motion
self.canvas1.mpl_connect('motion_notify_event', self.mouse_move)
def mouse_move(self, event):
x = event.xdata
if len(self.Data) and x is not None: # there is something to plot
self.ax2.clear()
x = int(x)
self.ax2.plot(self.xAxes, self.Data[x])
self.line.set_data([x, x], [len(self.Data), 0])
self.canvas1.draw_idle()
self.canvas2.draw_idle()
def readDZT(self):
file_path = filedialog.askopenfilename()
file_name, file_extension = os.path.splitext(file_path)
if file_extension == '.DZT':
messagebox.showinfo("INFO", "Your DZT File Has Been Selected Successfully")
hdr, arrs, gps = readgssi.readgssi(infile=file_path, verbose=True)
Samples = hdr['rh_nsamp']
X_Axes = np.array(range(0, Samples))
self.xAxes = X_Axes[2::1]
df = pd.DataFrame(arrs[0])
self.Data = df.iloc[2::1, 0::1]
# clear plots
self.ax1.clear()
self.ax2.clear()
# plot slice
self.ax2.plot(self.xAxes, self.Data[999])
self.canvas2.draw_idle()
# plot 2D map
self.ax1.imshow(self.Data, aspect='auto', cmap='bone')
self.line = self.ax1.plot([999, 999], [len(self.Data), 0], 'r')[0]
self.ax1.set_ylim(len(self.Data), 0)
self.canvas1.draw_idle()
else:
messagebox.showwarning("WARNING", "You Have Been Selected a Different Format")
root = tk.Tk()
root.title("IHM")
root.geometry("1000x800")
Resources_frame = tk.LabelFrame(root, bd=2, relief=tk.GROOVE, text="Conversion Area")
Resources_frame.place(x=5, y=5, width=250, height=80)
tk.Label(Resources_frame, text="Select your File ").grid(row=0, column=0, sticky=tk.W)
tk.Label(Resources_frame, text="Convert Selected file ").grid(row=1, column=0, sticky=tk.W)
graphs = Graphs(root)
btn_rs = tk.Button(Resources_frame, relief=tk.GROOVE, padx=8, pady=1, text="Browse",
command=graphs.readDZT).grid(row=0, column=1)
root.mainloop()

Multiple matplotlib instances in tkinter GUI

I have build simple tkinter GUI.
Now, I am trying to visualise 3 different graphs (by calling the same function with different variables) and place them in 3 different rows of the GUI.
When I do that I get 2 problems:
Every time I run the script (interface.py) I get 2 windows - both GUI and external graph's window. How to get rid of the second one?
I am not able to visualize all the 3 graphs. The script stops after showing the first one. I believe this is because of that the first graph works in a loop (iterates through plenty of data points). Is there any work around it?
Interface:
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 6 10:24:35 2020
#author: Dar0
"""
from tkinter import * #import tkinter module
from visualizer import main #import module 'visualizer' that shows the graph in real time
class Application(Frame):
''' Interface for visualizing graphs, indicators and text-box. '''
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
# Label of the 1st graph
Label(self,
text='Hook Load / Elevator Height / Depth vs Time'
).grid(row = 0, column = 0, sticky = W)
# Graph 1 - Hook Load / Elevator Height / Depth vs Time
# button that displays the plot
#plot_button = Button(self,2
# command = main,
# height = 2,
# width = 10,
# text = "Plot"
# ).grid(row = 1, column = 0, sticky = W)
self.graph_1 = main(root, 1, 0)
# place the button
# in main window
# Label of the 2nd graph
Label(self,
text = 'Hook Load / Elevator Height vs Time'
).grid(row = 3, column = 0, sticky = W)
# Graph 2 - Hook Load / Elevator Height vs Time
self.graph_2 = main(root, 4, 0)
#Label of the 3rd graph
Label(self,
text = 'Hook Load vs Time'
).grid(row = 6, column = 0, sticky = W)
#Graph 3 - Hook Load vs Time
#Label of the 1st indicator
Label(self,
text = '1st performance indicator'
).grid(row = 0, column = 1, sticky = W)
#1st performance indicator
#Label of 2nd performance indicator
Label(self,
text = '2nd performance indicator'
).grid(row = 3, column = 1, sticky = W)
#2nd performance indicator
#Label of 3rd performance indicator
Label(self,
text = '3rd performance indicator'
).grid(row = 6, column = 1, sticky = W)
#Text-box showing comments based on received data
self.text_box = Text(self, width = 50, height = 10, wrap = WORD)
self.text_box.grid(row = 9, column = 0, columnspan = 1)
self.text_box.delete(0.0, END)
self.text_box.insert(0.0, 'My message will be here.')
#Main part
root = Tk()
root.title('WiTSML Visualizer by Dar0')
app = Application(root)
root.mainloop()
Visualizer:
#WiTSML visualizer
#Created by Dariusz Krol
#import matplotlib
#matplotlib.use('TkAgg')
#from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
#from matplotlib.figure import Figure
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import random
class Visualizer(object):
""" Includes all the methods needed to show streamed data. """
def __init__(self):
self.file_path = 'C:/Anaconda/_my_files/witsml_reader/modified_witsml.csv' #Defines which file is streamed
self.datetime_mod = []
self.bpos_mod = []
self.woh_mod = []
self.torq_mod = []
self.spp_mod = []
self.depth_mod = []
self.flow_in_mod = []
self.rpm_mod = []
def open_file(self):
self.df = pd.read_csv(self.file_path, low_memory = False, nrows = 300000) #Opens the STREAMED file (already modified so that data convert is not required)
self.df = self.df.drop(0)
self.df = pd.DataFrame(self.df)
return self.df
def convert_dataframe(self):
self.df = self.df.values.T.tolist() #Do transposition of the dataframe and convert to list
#Columns are as following:
# - DATETIME
# - BPOS
# - WOH
# - TORQ
# - SPP
# - DEPTH
# - FLOW_IN
# - RPM
self.datetime_value = self.df[0]
self.bpos_value = self.df[1]
self.woh_value = self.df[2]
self.torq_value = self.df[3]
self.spp_value = self.df[4]
self.depth_value = self.df[5]
self.flow_in_value = self.df[5]
self.rpm_value = self.df[7]
return self.datetime_value, self.bpos_value, self.woh_value, self.torq_value, self.spp_value, self.depth_value, self.flow_in_value, self.rpm_value
#print(self.bpos_value)
def deliver_values(self, no_dp, columns):
''' Method gets no_dp amount of data points from the original file. '''
self.no_dp = no_dp #defines how many data points will be presented in the graph
val_dict = {
'datetime': [self.datetime_value, self.datetime_mod],
'bpos': [self.bpos_value, self.bpos_mod],
'woh': [self.woh_value, self.woh_mod],
'torq': [self.torq_value, self.torq_mod],
'spp': [self.spp_value, self.spp_mod],
'depth': [self.depth_value, self.depth_mod],
'flow_in': [self.flow_in_value, self.flow_in_mod],
'rpm': [self.rpm_value, self.rpm_mod]
}
for item in columns:
if self.no_dp > len(val_dict[item][0]):
dp_range = len(val_dict[item][0])
else:
dp_range = self.no_dp
for i in range(dp_range):
val_dict[item][1].append(val_dict[item][0][i])
return self.datetime_mod, self.bpos_mod, self.woh_mod, self.torq_mod, self.spp_mod, self.depth_mod, self.flow_in_mod, self.rpm_mod
def show_graph2(self, tr_val, row, column):
from pylive_mod import live_plotter, live_plotter2
self.open_file()
self.convert_dataframe()
self.deliver_values(no_dp = 100000, columns = ['datetime', 'depth', 'bpos', 'woh'])
fst_p = 0
size = 300 # density of points in the graph (100 by default)
x_vec = self.datetime_mod[fst_p:size]
y_vec = self.depth_mod[fst_p:size]
y2_vec = self.bpos_mod[fst_p:size]
y3_vec = self.woh_mod[fst_p:size]
line1 = []
line2 = []
line3 = []
for i in range(self.no_dp):
#print(self.datetime_mod[i:6+i])
#print('Ostatni element y_vec: ', y_vec[-1])
#print(x_vec)
x_vec[-1] = self.datetime_mod[size+i]
y_vec[-1] = self.depth_mod[size+i]
y2_vec[-1] = self.bpos_mod[size+i]
y3_vec[-1] = self.woh_mod[size+i]
line1, line2, line3 = live_plotter2(tr_val, row, column, x_vec, y_vec, y2_vec, y3_vec, line1, line2, line3)
x_vec = np.append(x_vec[1:], 0.0)
y_vec = np.append(y_vec[1:], 0.0)
y2_vec = np.append(y2_vec[1:], 0.0)
y3_vec = np.append(y3_vec[1:], 0.0)
def main(tr_val, row, column):
Graph = Visualizer()
Graph.open_file() #Opens the streamed file
Graph.convert_dataframe() #Converts dataframe to readable format
Graph.show_graph2(tr_val, row, column)
#Show us the graph
#main()
Function that creates the graph:
def live_plotter2(tr_val, row, column, x_data, y1_data, y2_data, y3_data, line1, line2, line3, identifier='',pause_time=1):
if line1 == [] and line2 == [] and line3 == []:
# this is the call to matplotlib that allows dynamic plotting
plt.ion()
fig = plt.figure(figsize = (5, 4), dpi = 100)
fig.subplots_adjust(0.15)
# -------------------- FIRST GRAPH --------------------
host = fig.add_subplot()
ln1 = host
ln2 = host.twinx()
ln3 = host.twinx()
ln2.spines['right'].set_position(('axes', 1.))
ln3.spines['right'].set_position(('axes', 1.12))
make_patch_spines_invisible(ln2)
make_patch_spines_invisible(ln3)
ln2.spines['right'].set_visible(True)
ln3.spines['right'].set_visible(True)
ln1.set_xlabel('Date & Time') #main x axis
ln1.set_ylabel('Depth') #left y axis
ln2.set_ylabel('Elevator Height')
ln3.set_ylabel('Weight on Hook')
#
x_formatter = FixedFormatter([x_data])
x_locator = FixedLocator([x_data[5]])
#ln1.xaxis.set_major_formatter(x_formatter)
ln1.xaxis.set_major_locator(x_locator)
#
ln1.locator_params(nbins = 5, axis = 'y')
ln1.tick_params(axis='x', rotation=90) #rotates x ticks 90 degrees down
ln2.axes.set_ylim(0, 30)
ln3.axes.set_ylim(200, 250)
line1, = ln1.plot(x_data, y1_data, color = 'black', linestyle = 'solid', alpha=0.8, label = 'Depth')
line2, = ln2.plot(x_data, y2_data, color = 'blue', linestyle = 'dashed', alpha=0.8, label = 'Elevator Height')
line3, = ln3.plot(x_data, y3_data, color = 'red', linestyle = 'solid', alpha=0.8, label = 'Weight on Hook')
fig.tight_layout() #the graphs is not clipped on sides
plt.title('WiTSML Visualizer')
plt.grid(True)
#Shows legend
lines = [line1, line2, line3]
host.legend(lines, [l.get_label() for l in lines], loc = 'lower left')
#Shows the whole graph
#plt.show()
#-------------------- Embedding --------------------
canvas = FigureCanvasTkAgg(fig, master=tr_val)
canvas.draw()
canvas.get_tk_widget().grid(row=row, column=column, ipadx=40, ipady=20)
# navigation toolbar
toolbarFrame = tk.Frame(master=tr_val)
toolbarFrame.grid(row=row,column=column)
toolbar = NavigationToolbar2Tk(canvas, toolbarFrame)
# after the figure, axis, and line are created, we only need to update the y-data
mod_x_data = convert_x_data(x_data, 20)
line1.axes.set_xticklabels(mod_x_data)
line1.set_ydata(y1_data)
line2.set_ydata(y2_data)
line3.set_ydata(y3_data)
#Debugging
#rint('plt.lim: ', ln2.axes.get_ylim())
# adjust limits if new data goes beyond bounds
# limit for line 1
if np.min(y1_data)<=line1.axes.get_ylim()[0] or np.max(y1_data)>=line1.axes.get_ylim()[1]:
plt.ylim(0, 10)
line1.axes.set_ylim([np.min(y1_data)-np.std(y1_data),np.max(y1_data)+np.std(y1_data)])
# limit for line 2
if np.min(y2_data)<=line2.axes.get_ylim()[0] or np.max(y2_data)>=line2.axes.get_ylim()[1]:
plt.ylim([np.min(y2_data)-np.std(y2_data),np.max(y2_data)+np.std(y2_data)])
#plt.ylim(0, 25)
# limit for line 3
if np.min(y3_data)<=line3.axes.get_ylim()[0] or np.max(y3_data)>=line3.axes.get_ylim()[1]:
plt.ylim([np.min(y3_data)-np.std(y3_data),np.max(y3_data)+np.std(y3_data)])
#plt.ylim(0, 25)
# Adds lines to the legend
#host.legend(lines, [l.get_label() for l in lines])
# this pauses the data so the figure/axis can catch up - the amount of pause can be altered above
plt.pause(pause_time)
# return line so we can update it again in the next iteration
return line1, line2, line3
The key is to not use pyplot when you want to plot within tkinter as shown in the official example. Use matplotlib.figure.Figure instead (see this for added info).
Below is a minimum sample that plots 3 independent graphs along a Text widget which I see in your code:
import pandas as pd
import numpy as np
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
class Graph(tk.Frame):
def __init__(self, master=None, title="", *args, **kwargs):
super().__init__(master, *args, **kwargs)
self.fig = Figure(figsize=(4, 3))
ax = self.fig.add_subplot(111)
df = pd.DataFrame({"values": np.random.randint(0, 50, 10)}) #dummy data
df.plot(ax=ax)
self.canvas = FigureCanvasTkAgg(self.fig, master=self)
self.canvas.draw()
tk.Label(self, text=f"Graph {title}").grid(row=0)
self.canvas.get_tk_widget().grid(row=1, sticky="nesw")
toolbar_frame = tk.Frame(self)
toolbar_frame.grid(row=2, sticky="ew")
NavigationToolbar2Tk(self.canvas, toolbar_frame)
root = tk.Tk()
for num, i in enumerate(list("ABC")):
Graph(root, title=i, width=200).grid(row=num//2, column=num%2)
text_box = tk.Text(root, width=50, height=10, wrap=tk.WORD)
text_box.grid(row=1, column=1, sticky="nesw")
text_box.delete(0.0, "end")
text_box.insert(0.0, 'My message will be here.')
root.mainloop()
Result:

Matplotlib rcParams ignored when plotting

I am working on an interactive plotting script using matplotlib version 3.2.2 and tkinter.
When the script is run, the first window looks like this:
Furthermore, the rcParams are updated and plotted once the Plot figure button is clicked:
If I now hit the button Change plot settings and change for instance the markersize parameter -> Plot figure, the plot is updated like so:
if I, however, try changing the label sizes to 20 px and then validate that the rcParams['axes.labelsize'] is changed, they are. But the size of the x and y labels are never updated in the actual plot.
The plot title (text input field all the way to the top in the plot window) fontsize can be changed after it has been plotted.
The minimal code:
"""
This is a script for interactively plotting a scatterplot and changing the plot params.
"""
import numpy as np
import matplotlib as mpl
import matplotlib.style
import random
mpl.use('TkAgg')
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from tkinter import *
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from pandas.api.types import is_numeric_dtype
def center_tk_window(window, height, width):
# Helper method for centering windows
screen_width = window.winfo_screenwidth()
screen_height = window.winfo_screenheight()
x_coordinate = int((screen_width / 2) - (width / 2))
y_coordinate = int((screen_height / 2) - (height / 2))
window.geometry("{}x{}+{}+{}".format(width, height, x_coordinate, y_coordinate))
def plot_data(data, chosen_columns, ax=None, initial_box=None, fig=None):
if fig is None:
fig = Figure()
if ax is None:
# Create a new subplot
ax = fig.add_subplot(111)
# Selected x-coordinates
x_data = data[chosen_columns['x_col']]
# Selected y-coordinates
if 'y_col' in chosen_columns:
y_data = data[chosen_columns['y_col']]
filled_markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd', 'P', 'X')
# Category column
if 'category_col' in chosen_columns:
category_data = data[chosen_columns['category_col']]
# Plotting it all
sns.scatterplot(ax=ax, x=x_data, y=y_data, hue=category_data, style=category_data,
markers=filled_markers
)
# Shrink current axis's height by 20% on the bottom
if initial_box is None:
initial_box = ax.get_position()
ax.set_position([initial_box.x0, initial_box.y0 + initial_box.height * 0.2,
initial_box.width, initial_box.height * 0.80])
# Put a legend below current axis
ax.legend(bbox_to_anchor=(0.5, -0.15), ncol=6)
else: # Normal scatterplot without any categorical values
sns.scatterplot(ax=ax, x=x_data, y=y_data)
ax.set_ylabel(chosen_columns['y_col'])
ax.set_xlabel(chosen_columns['x_col'])
return fig, ax, initial_box
class GenericPlot:
def __init__(self, data):
# Parameters window selection
self.canvas = None
self.fig = None
self.ax = None
self.chosen_columns = None
self.initial_box = None
self.updated_rc_params = None
self.set_plot_params(data)
# Plot window
self.save_plot_bool = False
self.plot_window = Tk()
self.interactive_plot(data)
self.plot_window.mainloop()
def set_plot_params(self, data):
def plot_with_settings():
def format_input(input):
if input == '':
# Use the default value
return mpl.rcParams[input]
if ',' in input:
return float(input.replace(',', '.'))
else:
return float(input)
# Figure size
figure_params = {}
if figsize_width.get() != '' and figsize_height.get() != '':
figure_params['figsize'] = (format_input(figsize_width.get()), format_input(figsize_height.get()))
# label sizes
axes_params = {}
if label_size.get() != '':
axes_params['labelsize'] = format_input(label_size.get())
if title_size.get() != '':
axes_params['titlesize'] = format_input(title_size.get())
legend_params = {}
if legend_title_fontsize.get() != '': legend_params['title_fontsize'] = format_input(
legend_title_fontsize.get())
legend_additional = {'loc': 'upper center',
'fancybox': False,
'shadow': False
}
legend_params.update(legend_additional)
if marker_size.get() != '': lines_params = {'markersize': format_input(marker_size.get())}
legend_params['markerscale'] = format_input(legend_markerscale.get())
mpl.rc('figure', **figure_params)
mpl.rc('axes', **axes_params)
mpl.rc('lines', **lines_params)
mpl.rc('legend', **legend_params)
self.updated_rc_params = mpl.rcParams
# Update canvas if the params were changed after it was drawn:
if self.ax is not None:
self.ax.clear()
mpl.rcParams.update(self.updated_rc_params)
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax,
self.initial_box, self.fig)
self.canvas.draw()
custom_params_window.destroy() # Close the tk window
# Create a new window
custom_params_window = Tk()
center_tk_window(custom_params_window, 300, 400) # window, height, width
custom_params_window.title('Set plot parameters')
# Set up GUI
custom_params_window.columnconfigure(0, weight=1)
custom_params_window.columnconfigure(1, weight=1)
n_rows = 8
for r in range(n_rows):
custom_params_window.rowconfigure(r, weight=1)
row_num = 0
# Figsize
Label(custom_params_window, text="Figure width (px)").grid(row=row_num, column=0, sticky="e")
figsize_width = Entry(custom_params_window)
placeholder_width = self.updated_rc_params['figure.figsize'][
0] if self.updated_rc_params is not None else 7.0
figsize_width.insert(0, placeholder_width)
figsize_width.grid(row=row_num, column=1)
row_num += 1
Label(custom_params_window, text="Figure height (px)").grid(row=row_num, column=0, sticky="e")
figsize_height = Entry(custom_params_window)
placeholder_height = self.updated_rc_params['figure.figsize'][
1] if self.updated_rc_params is not None else 6.0
figsize_height.insert(0, placeholder_height)
figsize_height.grid(row=row_num, column=1)
# User input label size
row_num += 1
Label(custom_params_window, text="Label sizes (px)").grid(row=row_num, column=0, sticky="e")
label_size = Entry(custom_params_window)
placeholder_label_size = self.updated_rc_params[
'axes.labelsize'] if self.updated_rc_params is not None else 10.0
label_size.insert(0, placeholder_label_size)
label_size.grid(row=row_num, column=1)
# User input title size
row_num += 1
Label(custom_params_window, text="Title font size (px)").grid(row=row_num, column=0, sticky="e")
title_size = Entry(custom_params_window)
placeholder_axes_titlesize = self.updated_rc_params[
'axes.titlesize'] if self.updated_rc_params is not None else 14.0
title_size.insert(0, placeholder_axes_titlesize)
title_size.grid(row=row_num, column=1)
print(" self.updated_rc_params STATUS:", self.updated_rc_params)
# Marker_size
row_num += 1
Label(custom_params_window, text="Marker size (px)").grid(row=row_num, column=0, sticky="e")
marker_size = Entry(custom_params_window)
placeholder_legend_markersize = self.updated_rc_params[
'lines.markersize'] if self.updated_rc_params is not None else 6.0
marker_size.insert(0, placeholder_legend_markersize)
marker_size.grid(row=row_num, column=1)
# Legend markerscale
row_num += 1
Label(custom_params_window, text="Legend markerscale\n(Relative size to marker size) ").grid(
row=row_num, column=0,
sticky="e")
legend_markerscale = Entry(custom_params_window)
placeholder_legend_markerscale = self.updated_rc_params[
'legend.markerscale'] if self.updated_rc_params is not None else 1.0
legend_markerscale.insert(0, placeholder_legend_markerscale)
legend_markerscale.grid(row=row_num, column=1)
# Legend title size
row_num += 1
Label(custom_params_window, text="Legend title font size").grid(row=row_num, column=0, sticky="e")
legend_title_fontsize = Entry(custom_params_window)
placeholder_legend_title_size = self.updated_rc_params[
'legend.title_fontsize'] if self.updated_rc_params is not None else 1.0
legend_title_fontsize.insert(0, placeholder_legend_title_size)
legend_title_fontsize.grid(row=row_num, column=1)
row_num += 1
Button(custom_params_window, text="Plot figure", command=lambda: plot_with_settings(), height=2,
width=8).grid(row=row_num, column=0)
custom_params_window.mainloop()
def interactive_plot(self, data):
"""
Input :
window : tkinter window
data : DataFrame object
"""
def close_plot_window():
self.plot_window.destroy()
def set_save_plot_bool():
self.save_plot_bool = True
self.plot_window.destroy()
center_tk_window(self.plot_window, 750, 600)
# Drop-down variables (3 drop-downs)
dropdown_choice_x = StringVar(self.plot_window) # Variable holding the dropdown selection for the x column
dropdown_choice_y = StringVar(self.plot_window) # Variable holding the dropdown selection for the y column
dropdown_choice_category = StringVar(
self.plot_window) # Variable holding the dropdown selection for the category column
# Create set of column names in the dataset
choices = data.columns.values
# Find numeric and string columns
string_columns = []
numeric_columns = []
[numeric_columns.append(col) if is_numeric_dtype(data[col]) else string_columns.append(col) for col in
data.columns]
if len(numeric_columns) < 1:
raise Exception("Unable to plot, there are too few numerical columns.")
if len(numeric_columns) == 1:
raise Exception(
"Unable to create scatter plot- need more than two numerical columns in the imported dataset.")
# GUI setup
self.plot_window.columnconfigure(0, weight=1)
self.plot_window.columnconfigure(1, weight=1)
n_rows = 6
for r in range(n_rows):
self.plot_window.rowconfigure(r, weight=1)
def update_ax_title(title):
self.ax.set_title(title.get())
self.canvas.draw()
title = StringVar()
title.trace("w", lambda name, index, mode, title=title: update_ax_title(title))
# Set title
Label(self.plot_window, text="Set plot title:").grid(row=0, column=0, sticky="e")
e = Entry(self.plot_window, textvariable=title, width=23)
e.grid(row=0, column=1, sticky="w")
# Drop-down 1: x-value selection
if len(numeric_columns) >= 1:
x_values_column = numeric_columns[0] # Select the first numeric column as the default x values to plot
dropdown_choice_x.set(x_values_column) # Set the default option in the dropdown with the first column
Label(self.plot_window, text="Select x column:").grid(row=1, column=0, sticky="e")
choices_numeric = numeric_columns # Only show numeric columns in the drop-down for x and y
dropdown_menu_x = OptionMenu(self.plot_window, dropdown_choice_x, *choices_numeric)
dropdown_menu_x.grid(row=1, column=1, sticky="w")
dropdown_menu_x.config(width=16)
self.chosen_columns = {'x_col': x_values_column}
# Drop-down 2: y-value selection
if len(numeric_columns) >= 2:
y_values_column = numeric_columns[1] # Select the second alternative in the dropdown list for the y values
dropdown_choice_y.set(y_values_column) # Set the default option in the dropdown with the first column
l2 = Label(self.plot_window, text="Select y column:")
l2.grid(row=2, column=0, sticky='e')
dropdown_menu_y = OptionMenu(self.plot_window, dropdown_choice_y, *choices_numeric)
dropdown_menu_y.config(width=16)
dropdown_menu_y.grid(row=2, column=1, sticky='w')
self.chosen_columns = {'x_col': x_values_column,
'y_col': y_values_column}
if len(data.columns) > 2: # There exist a third columns as well -> include drop-down for category selection
# Drop-down 3: Category selections
category_column = string_columns[0] if (len(string_columns) > 0) else numeric_columns[2]
dropdown_choice_category.set(
category_column) # Set the default option in the dropdown with the first column
l3 = Label(self.plot_window, text="Select category column:")
l3.grid(row=3, column=0, sticky='e')
dropdown_menu_category = OptionMenu(self.plot_window, dropdown_choice_category, *choices, 'Set title above')
dropdown_menu_category.config(width=16)
dropdown_menu_category.grid(row=3, column=1, sticky='w')
self.chosen_columns = {'x_col': x_values_column,
'y_col': y_values_column,
'category_col': category_column}
# Plot the initially selected columns
self.fig, self.ax, self.initial_box = plot_data(data, self.chosen_columns)
self.canvas = FigureCanvasTkAgg(self.fig, master=self.plot_window)
self.canvas.get_tk_widget().grid(row=4, columnspan=2, rowspan=True)
self.canvas.draw()
def change_dropdown_x(*args):
# This function is triggered once a dropdown selection is made
selected_x_col = dropdown_choice_x.get()
self.chosen_columns['x_col'] = selected_x_col
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
# chosen columns might not be updated...
def change_dropdown_y(*args):
# This function is triggered once a dropdown selection is made
selected_y_col = dropdown_choice_y.get()
self.chosen_columns['y_col'] = selected_y_col
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
def change_dropdown_category(*args):
# This function is triggered once a dropdown selection is made
selected_category = dropdown_choice_category.get()
self.chosen_columns['category_col'] = selected_category
# Create a new plot now
self.ax.clear() # Clearing the previous plot
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax, self.initial_box,
self.fig)
self.canvas.draw()
# Link functions to change dropdown
dropdown_choice_x.trace('w',
lambda *args: change_dropdown_x(
*args))
dropdown_choice_y.trace('w',
lambda *args: change_dropdown_y(
*args))
dropdown_choice_category.trace('w', lambda *args: change_dropdown_category(
*args))
def change_settings():
self.plot_params_type = 'customize'
self.set_plot_params(data)
# self.plot_window.destroy()
# Save and close buttons
Button(self.plot_window, text="<- Change plot settings", command=change_settings, height=2, width=20).grid(
row=5, columnspan=2)
Button(self.plot_window, text="CLOSE", command=close_plot_window, height=2, width=8).grid(row=6, column=0)
Button(self.plot_window, text="SAVE PLOT ->", command=set_save_plot_bool, height=2, width=8).grid(row=6,
column=1)
# Create dummy data to plot
df = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
# Add a category column to the DataFrame
labels = ['q', 'r', 's', 't']
df['Category'] = [labels[random.randint(0, len(labels) - 1)] for i in range(100)]
GenericPlot(df)
I have tried to change the x and y label sizes in the function called update_ax_title (just to debug:
And run the function
def update_ax_title(title):
self.ax.set_title(title.get()) # Correct size (5.0)
self.ax.set_xlabel('gdsgsdgsdgsdgdsg') # Incorrect size...
print(mpl.rcParams['axes.labelsize']) # prints 5.0
print(mpl.rcParams['axes.titlesize']) # prints 5.0
self.canvas.draw()
Only the title size is updated, even though the rcParams are updated globally. The x and y label sizes change after running specifically self.ax.set_xlabel('gdsgsdgsdgsdgdsg',fontsize=5)
How can this issue be solved? Thanks!
Although I don't fully understand why it doesn't work, you can solve it by applying the labelsize manually to each axis using set_size:
...
# Update canvas if the params were changed after it was drawn:
if self.ax is not None:
self.ax.clear()
mpl.rcParams.update(self.updated_rc_params)
# NEW!
self.ax.xaxis.label.set_size(self.updated_rc_params['axes.labelsize'])
self.ax.yaxis.label.set_size(self.updated_rc_params['axes.labelsize'])
# End of New
self.fig, self.ax, _ = plot_data(data, self.chosen_columns, self.ax,
self.initial_box, self.fig)
self.canvas.draw()
custom_params_window.destroy() # Close the tk window
...

How to define a choice (symbol) from combobox widget as a (global) variable?

I would like to come directly to the point, so I will firstly just ask all my questions which I have.
In Class Win1 I have a combobox, my question is: Is there a possibility to define the entry/chosen symbol as a variable and is there a possibility to define the variable as global, without having to redefine classes?
This is my second question, which is at the moment less relevant than the first one.
When this code is running, first one window shows up, then a second one and so on. When turning this code into a software/app/..., will the software then also show one window than a second one or will everything be in one window?
import math
import matplotlib
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
matplotlib.use('TkAgg')
#------------------------------------------------------------------------------
import tkinter
import tkinter as tk
from tkinter import ttk
import tkinter.font as tkFont
import tkinter.ttk as ttk
from tkinter import *
from tkinter.ttk import Combobox
#------------------------------------------------------------------------------
import numpy as np
#------------------------------------------------------------------------------
import pandas as pd
from pandas import DataFrame
from pandas.compat import StringIO
from pandas_datareader import data as web
#------------------------------------------------------------------------------
import yfinance as yf
#------------------------------------------------------------------------------
from statistics import mean
#------------------------------------------------------------------------------
import requests
#------------------------------------------------------------------------------
import time
from datetime import datetime
from datetime import date
from datetime import timedelta
import calendar
#------------------------------------------------------------------------------
import random
#------------------------------------------------------------------------------
from alpha_vantage.timeseries import TimeSeries
from alpha_vantage.techindicators import TechIndicators
#------------------------------------------------------------------------------
import sys
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk)
# Implement the default Matplotlib key bindings.
from matplotlib.backend_bases import key_press_handler
#------------------------------------------------------------------------------
#symbol = 'TSLA'
global_symbol = ""#None
#combobox = ""
def onSymbolSelected(self,event = None):
global global_symbol
global_symbol = self.combobox.get() # this assume combo is a property of Win1
def config_plot():
fig, ax = plt.subplots()
return(fig, ax)
class Win1:
def __init__(self, master):
self.master = master
self.master.geometry("400x400")
self.frame = tk.Frame(self.master)
#symbol = Entry(root, text ='', bd=5)
#userInput1 = StringVar(None)
#self.master = Entry(root, textvariable=userInput1)
#self.master.pack()
SMI = ['ROG.SW','NOVN.SW','NESN.SW','SRAIL.SW','ABB','ADEN.SW','ALC.SW','CSGN.SW','GEBN.SW','GIVN.SW',
'LONN.SW','CFR.SW','SIKA.SW','UHR.SW','SLHN.SW','SREN.SW','SCMN.SW','UBSG.SW','ZURN.SW']
SMI.sort()
NYSE = ['NYSE:MMM','NYSE:AXP','NYSE:AAPL','NYSE:BA','NYSE:CAT','NYSE:CVX','NYSE:CSCO','NYSE:KO','NYSE:DOW','NYSE:XOM',
'NYSE:GS','NYSE:HD','NYSE:IBM','NYSE:INTC','NSYE:JNJ','NYSE:JPM','NYSE:MCD','NYSE:MRK','NYSE:MSFT','NYSE:NKE',
'NYSE:PFE','NYSE:PG','NYSE:RTX','NYSE:TRV','NYSE:UNH','NYSE:VZ','NYSE:V','NYSE:WMT','NYSE:WBA','NYSE:DIS']
NYSE.sort()
DAX = ['ETR:ADS','ETR:ALV','ETR:BAS','ETR:BAYN','ETR:BEI','ETR:BMW','ETR:CON','ETR:1COV','ETR:DAI','ETR:DBK',
'ETR:LHA','ETR:DPW','ETR:DTE','ETR:EOAN','ETR:FRE','ETR:FME','ETR:HEI','ETR:HEN3','ETR:IFX','ETR:MTX',
'ETR:MUV2','ETR:RWE','ETR:SAP','ETR:SIE','ETR:VOW3','ETR:VNA','ETR:WDI']
DAX.sort()
Stockxx = []
Stockxx.sort()
symbol = ['ROG.SW','SMI','NYSE','DAX','Stockxx']
symbol.sort()
#symbol.current()
#root= Tk()
self.combobox = StringVar()
self.combobox = ttk.Combobox(master, values = symbol)
#def onSymbolSelected(self, Event = None):
# global global_symbol
# global_symbol = self.combobox.get()
self.combobox.bind("<<ComboboxSelected>>", onSymbolSelected)#onSymbolSelected)
#cb = ttk.Combobox(master, values = symbol)
#print(var)
# note that this is the StringVar ICUS, not the combobox ICUSTOMER.
#label_var = tk.Label( textvariable= var)
#print(label_var)
#label_ICustVar.grid(row = 3, column = 3)
self.combobox.place(x=100, y=50)
button1 = Button(root, text="Click Here", fg = 'blue', width=20, command = lambda: self.new_window("2", Win2))
button1.pack(side='bottom',padx=15,pady=15)
# this assume combo is a property of Win1
#self.butnew("Hauptfunktionen", "2", Win2)
#self.butnew("Weitere Funktionen", "3", Win3)
self.quit = tk.Button(self.frame, text = f"Close this window", command = self.close_window)
self.quit.pack()
self.frame.pack()
print(symbol,"asfjöasldkjflödas")
print(global_symbol,"ioöbajbo")
def close_window(self):
self.master.destroy()
#def onSymbolSelected(self,event = None):
# global global_symbol
# global_symbol = self.combo.get() # this assume combo is a property of Win1
def butnew(self, text, number, _class):
tk.Button(self.frame, text = text, command= lambda: self.new_window(number, _class)).pack(side='left')
def new_window(self, number, _class):
self.new = tk.Toplevel(self.master)
_class(self.new, number)
class Win2:
def __init__(self, master, number):
self.master = master
self.master.geometry("1080x540")
self.frame = tk.Frame(self.master)
self.frame = Frame(self.master)
self.fig, self.ax = config_plot()
self.graphIndex = 0
self.canvas = FigureCanvasTkAgg(self.fig, self.master)
self.config_window()
self.quit = tk.Button(self.frame, text = f"Close this window", command = self.close_window)
self.quit.pack(side=LEFT)
self.draw_graph_one()
self.butnew("Weitere Funktionen", "3", Win3)
self.frame.pack(expand=YES, fill=BOTH)
def butnew(self, text, number, _class):
tk.Button(self.frame, text = text, command= lambda: self.new_window(number, _class)).pack(side='left')
def new_window(self, number, _class):
self.new = tk.Toplevel(self.master)
_class(self.new, number)
def draw_graph_one(self):
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
data1, meta_data1 = ts.get_monthly(symbol=global_symbol)
df1 = data1['4. close']
self.ax.clear()
self.ax.plot(df1)
self.ax.set(title='Historical Chart')
self.canvas.draw()
#------------------------------------------------------------------------fertig
def draw_graph_two(self):
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
data2, meta_data2 = ts.get_intraday(symbol=global_symbol, interval='1min', outputsize='compact')
df2 = data2['4. close']
self.ax.clear()
self.ax.plot(df2)
self.ax.set(title='Intraday')
self.canvas.draw()
#------------------------------------------------------------------------fertig
def draw_graph_three(self):
ti = TechIndicators(key='YOUR_API_KEY',output_format='pandas')
data3, meta_data3 = ti.get_bbands(symbol=global_symbol, interval='15min', time_period=30)
df3 = data3
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
data2, meta_data2 = ts.get_intraday(symbol=global_symbol, interval='15min', outputsize='full')
df2 = data2['4. close']
self.ax.clear()
self.ax.plot(df2, linestyle='-.')
self.ax.plot(df3)
self.ax.set(title='Bollinger Bands')
self.canvas.draw()
#------------------------------------------------------------------------fertig
def draw_graph_four(self):
ti = TechIndicators(key='YOUR_API_KEY',output_format='pandas')
data44, meta_data44 = ti.get_sma(symbol=global_symbol, interval='30min', time_period=60)
df44 = data44
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
data1, meta_data1 = ts.get_intraday(symbol=global_symbol, interval='30min', outputsize='full')
df1 = data1['4. close']
self.ax.clear()
self.ax.plot(df1)#, color='yellow')
self.ax.plot(df44)#, color='black')
self.ax.set(title='Simple Moving Average')
self.canvas.draw()
#------------------------------------------------------------------------fertig
def draw_graph_five(self):
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
data1, meta_data1 = ts.get_monthly(symbol=global_symbol)#, interval='1min', outputsize='full')
df1 = data1['4. close']
ts = TimeSeries(key='YOUR_API_KEY',output_format='pandas')
dataG, meta_dataG = ts.get_monthly(symbol='UCITS ETF')#, interval='1min', outputsize='full')
dG = dataG['4. close']
self.ax.clear()
self.ax.plot(df1)
self.ax.plot(dG)
self.ax.set(title='Comparision with Global Index')
self.canvas.draw()
#------------------------------------------------------------------------------
def draw_graph_six(self):
ti = TechIndicators(key='YOUR_API_KEY',output_format='pandas')
data23, meta_data23 = ti.get_cci(symbol=global_symbol, interval='30min', time_period=60)
df23 = data23
self.ax.clear()
self.ax.plot(df23)
self.ax.set(title='Commodity Channel Index')
self.canvas.draw()
#------------------------------------------------------------------------fertig
def draw_graph_seven(self):
ti = TechIndicators(key='YOUR_API_KEY',output_format='pandas')
data13, meta_data23 = ti.get_dx(symbol=global_symbol, interval='30min', time_period=60)
df13 = data13
self.ax.clear()
self.ax.plot(df13)
self.ax.set(title='Directional Movement Index')
self.canvas.draw()
#------------------------------------------------------------------------------
def draw_graph_eight(self):
self.ax.clear()
self.ax.plot()
self.ax.set(title='Further Information')
self.canvas.draw()
#------------------------------------------------------------------------------
def draw_graph_nine(self):
self.ax.clear()
self.ax.plot()
self.ax.set(title='Further Information')
self.canvas.draw()
def on_key_press(event):
print("you pressed {}".format(event.key))
key_press_handler(event, self.canvas, toolbar)
def config_window(self):
self.canvas.mpl_connect("key_press_event", self.on_key_press)
toolbar = NavigationToolbar2Tk(self.canvas, self.master)
toolbar.update()
self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
self.button_switch1 = Button(self.master, text="Historical Chart", command=self.draw_graph_one)
self.button_switch1.pack(side=LEFT)
self.button_switch2 = Button(self.master, text="Intraday", command=self.draw_graph_two)
self.button_switch2.pack(side=LEFT)
self.button_switch3 = Button(self.master, text="Bollinger Bands", command=self.draw_graph_three)
self.button_switch3.pack(side=LEFT)
self.button_switch4 = Button(self.master, text="SMA", command=self.draw_graph_four)
self.button_switch4.pack(side=LEFT)
self.button_switch5 = Button(self.master, text="Comparison with Index", command=self.draw_graph_five)
self.button_switch5.pack(side=LEFT)
self.button_switch6 = Button(self.master, text="CCI", command=self.draw_graph_six)
self.button_switch6.pack(side=LEFT)
self.button_switch7 = Button(self.master, text="DX", command=self.draw_graph_seven)
self.button_switch7.pack(side=LEFT)
self.button_switch8 = Button(self.master, text="Technical Analysis", command=self.draw_graph_eight)
self.button_switch8.pack(side=LEFT)
self.button_switch9 = Button(self.master, text="Technical Analysis", command=self.draw_graph_nine)
self.button_switch9.pack(side=LEFT)
def close_window(self):
self.master.destroy()
class Win3:
def __init__(self, master, number):
self.master = master
self.master.geometry("1080x540")
self.frame = tk.Frame(self.master)
#self.frame = Frame(self.master)
self.fig, self.ax = config_plot()
self.graphIndex = 0
self.canvas = FigureCanvasTkAgg(self.fig, self.master)
self.config_window()
self.quit = tk.Button(self.frame, text = f"Close this window", command = self.close_window)
self.quit.pack(side=LEFT)
self.frame.pack(expand=YES, fill=BOTH)
def on_key_press(event):
print("you pressed {}".format(event.key))
key_press_handler(event, self.canvas, toolbar)
def config_window(self):
self.canvas.mpl_connect("key_press_event", self.on_key_press)
toolbar = NavigationToolbar2Tk(self.canvas, self.master)
toolbar.update()
self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
def close_window(self):
f.master.destroy()
root = tk.Tk()
app = Win1(root)
root.mainloop()
Sorry for asking so many questions, unfortunately I didn't completely understand the answers from my previous question. Thank you very much for your help.
Ok, so I instead of opening a new thread, I decided just to expand my program directly here, since my new question is based on the old one. So I changed the program and my it doesn't work totally. The problem is that global_symbol doesn't change its value and I don't understand where the problem lies, I tried many things (leaving some lines, adding, etc) but none of them really worked. So does anyone of you guys know where the problem lies?
For question 1/ What you can do (worst case) is add a callback to your combobox. This callback can then update a "global variable"
Callback setting on combobox :
self.combo = ttk.Combobox(master, values = symbol)
self.combo.bind("<<ComboboxSelected>>", onSymbolSelected)
symbol declaration outside of Win1
global_symbol = None
callback implementation in Win1
def onSymbolSelected(self,event = None)
global global_symbol
global_symbol = self.combo.get() # this assume combo is a property of Win1

Can interact only with the last graph

I'm practicing tkinter and matplotlib.
I wrote this piece of code which creates an entered number of frames and in each frame a different plot is embedded. I can then go from plot to plot by switching between the created frames and turn a drawing mode on. What the drawing mode do is enable me to click on the plot and create a horizontal line on it.
But when I turn drawing mode on I can only interact with the last graph.
Any ideas why this is happening?
The piece of code:
from tkinter import *
from random import *
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
numberOfFrames = eval(input('Number of frames to be crated: '))
x = []
y = []
class app(Tk):
def __init__(self):
Tk.__init__(self)
self.geometry('640x400')
self.frames = []
self.currentPage = 0
def nextpage():
try:
frame = self.frames[self.currentPage+1]
frame.tkraise()
self.currentPage += 1
except:
pass
def backpage():
if self.currentPage == 0:
pass
else:
frame = self.frames[self.currentPage-1]
frame.tkraise()
self.currentPage -= 1
def DrawOn():
def onclick(event):
plt.hlines(event.ydata,event.xdata-0.1,event.xdata+0.1,
colors='r',linestyle='solid')
canvas.show()
fig.canvas.mpl_connect('button_press_event', onclick)
for i in range(numberOfFrames):
frame = Frame(self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames.append(frame)
fig = plt.figure()
for j in range(2):
x.append(randint(1,10))
y.append(randint(1,10))
plt.plot(x,y)
canvas = FigureCanvasTkAgg(fig, self.frames[i])
canvas.get_tk_widget().pack(fill='both', expand=True)
toolbar = NavigationToolbar2TkAgg(canvas, self.frames[i])
toolbar.update()
canvas._tkcanvas.pack(fill='both', expand=True)
label = Label(self.frames[i], text='Page %d'%(i+1))
label.pack()
Next = Button(self.frames[i], text='Next', command = nextpage)
Next.pack(side=LEFT)
Back = Button(self.frames[i], text='Back', command = backpage)
Back.pack(side=LEFT)
Draw = Button(self.frames[i], text='Draw', command = DrawOn)
Draw.pack(side=LEFT)
self.frames[0].tkraise()
run = app()
run.mainloop()
That's the alteration I made to my code. It made possible to interact with all the embedded graphs. I'm not sure if it is the best way to do it, but it seems to work.
from tkinter import *
from random import *
import matplotlib
matplotlib.use('TkAgg')
from matplotlib import pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
numberOfFrames = eval(input('Number of frames to be crated: '))
x = []
y = []
class app(Tk):
def __init__(self):
Tk.__init__(self)
self.geometry('640x400')
self.frames = []
self.currentPage = 0
self.figs = []
self.axeslist = []
self.canvaslist = []
def nextpage():
try:
frame = self.frames[self.currentPage+1]
frame.tkraise()
self.currentPage += 1
except:
pass
def backpage():
if self.currentPage == 0:
pass
else:
frame = self.frames[self.currentPage-1]
frame.tkraise()
self.currentPage -= 1
def DrawOn():
def onclick(event):
self.axeslist[self.currentPage].hlines(event.ydata,event.xdata-0.1,event.xdata+0.1,
colors='r',linestyle='solid')
self.canvaslist[self.currentPage].show()
print(event.xdata,event.ydata)
for i in self.figs:
i.canvas.mpl_connect('button_press_event', onclick)
for i in range(numberOfFrames):
frame = Frame(self)
frame.grid(row=0, column=0, sticky="nsew")
self.frames.append(frame)
fig = plt.figure()
self.figs.append(fig)
ax = self.figs[i].add_subplot(111)
self.axeslist.append(ax)
for j in range(2):
x.append(randint(1,10))
y.append(randint(1,10))
plt.plot(x,y)
canvas1 = FigureCanvasTkAgg(self.figs[i], self.frames[i])
self.canvaslist.append(canvas1)
self.canvaslist[i].get_tk_widget().pack(fill='both', expand=True)
toolbar = NavigationToolbar2TkAgg(self.canvaslist[i], self.frames[i])
toolbar.update()
self.canvaslist[i]._tkcanvas.pack(fill='both', expand=True)
label = Label(self.frames[i], text='Page %d'%(i+1))
label.pack()
Next = Button(self.frames[i], text='Next', command = nextpage)
Next.pack(side=LEFT)
Back = Button(self.frames[i], text='Back', command = backpage)
Back.pack(side=LEFT)
Draw = Button(self.frames[i], text='Draw', command = DrawOn)
Draw.pack(side=LEFT)
self.frames[0].tkraise()
run = app()
run.mainloop()

Categories

Resources