I want a picker on plot_date but it is not responding on clicks. even other events will not connect to the graphs.
This class will get tweets from a local database for sentiment analyse.
import matplotlib
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.dates as md
from matplotlib.dates import DayLocator, HourLocator, DateFormatter, drange
import matplotlib.pyplot as plt
import tkinter as Tk
from Core.Database import Database
from numpy import arange
matplotlib.use('TkAgg')
plt.style.use('ggplot')
class SentimentGraph:
figure = None
axes = None
timeStamps = []
sentiment_score = []
def __init__(self, create_figure=True):
# get data from database.
self.get_data()
# create figure.
if create_figure:
self.figure = plt.figure()
# draw graph in figure
self.draw_graph(self.figure)
def draw_graph(self, figure):
neutral = 0
negative = 0
positive = 0
for score in self.sentiment_score:
if score == 0:
neutral += 1
elif score > 0:
positive += 1
elif score < 0:
negative += 1
self.figure, axes = plt.subplots(ncols=2, nrows=1)
ax1, ax2 = axes.ravel()
# The slices will be ordered and plotted counter-clockwise.
labels = 'neutral', 'Negative', 'Positive'
sizes = [neutral, positive, negative]
colors = ['yellowgreen', 'lightcoral', 'lightskyblue']
explode = (0, 0.1, 0.1) # only "explode" the 2nd slice (i.e. 'Hogs')
ax1.pie(sizes, explode=explode, labels=labels, colors=colors,
autopct='%1.1f%%', shadow=True, startangle=90,
radius=0.25, center=(0, 0), frame=True)
# Set aspect ratio to be equal so that pie is drawn as a circle.
ax1.axis('equal')
ax1.axis('off')
ax2.plot_date(self.timeStamps, self.sentiment_score, alpha=0.5, picker=True)
def onclick(event):
index = event.ind
xy = event.artist.get_offsets()
print('--------------')
print(xy[index])
self.figure.canvas.mpl_connect('pick_event', onclick)
ax2.set_title("Sentiment score")
ax2.set_ylabel("Sentiment score")
xfmt = md.DateFormatter('%Y-%m-%d %H:%M')
ax2.xaxis.set_minor_locator(HourLocator(arange(0, 25, 6)))
ax2.xaxis.set_major_formatter(DateFormatter('%H:%M'))
ax2.xaxis.set_major_formatter(xfmt)
ax2.fmt_xdata = md.DateFormatter('%Y-%m-%d %H:%M')
self.figure.autofmt_xdate()
def get_data(self):
db = Database()
result = db.query(
''' select sentiment_score, posted_at / 1000 as timestamp from tweets ''')
rows = result.fetchall()
for row in rows:
self.sentiment_score.append(row[0])
# convert unix timestamp to matplotlib compatible
date = matplotlib.dates.epoch2num(row[1])
self.timeStamps.append(date)
return True
if __name__ == "__main__":
# change config db file location
import config
config.DB_FILE = "../tweets.db"
# create window
root = Tk.Tk()
root.wm_title("time line")
graph = SentimentGraph()
def _quit():
root.quit() # stops mainloop
root.destroy() # this is necessary on Windows to prevent
# Fatal Python Error: PyEval_RestoreThread: NULL tstate
# a tk.DrawingArea
canvas = FigureCanvasTkAgg(graph.figure, 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)
button = Tk.Button(master=root, text='Quit', command=_quit)
button.pack(side=Tk.BOTTOM)
Tk.mainloop()
# If you put root.destroy() here, it will cause an error if
# the window is closed with the window manager.
The root of your problem is that you're not embedding your figure in your Tkinter application.
Instead, you're creating an entirely new tkinter widget and window when you call plt.figure or plt.subplots. You're then "piggybacking" another canvas on top of that pre-existing figure and using it in your application.
Because matplotlib thinks the plot belongs to the original figure you created with plt.subplots, it's not registering any mouse events.
When you're embedding a figure in another application you must use the figure that you create manually. You cannot call plt.figure or plt.subplots (or plt.anything, really).
To solve your problem, set up the canvas inside your application using a manually-created Figure object, similar to the embedding examples in the documentation.
Related
I am trying to plot a figure that has many lines where each line represents a specifc temperature!
An example of what I want is here:
However, I bulit the following code:
x=pd.DataFrame(df1, columns =[0])
J = set(x.iloc[:,0])
print ('Length Temperature',len(J))
O = len(J)
M = len(df1.index)
print('Indexxxxx: ',df1.iloc[0:12+0,5])
for i in range(0,M,O):
figure3 = plt.Figure(figsize=(8, 6), dpi=80)
ax1 = figure3.add_subplot(111)
ax1.scatter(df1.iloc[i+1:M+i,5],df1.iloc[i+1:M+i,6], label = "Temperature " + str((df1.iloc[i, 0])))
scatter1 = FigureCanvasTkAgg(figure3, GraphWindow)
scatter1.get_tk_widget().pack(side=tk.LEFT, fill=tk.BOTH)
ax1.set_xlabel('Reduced Frequency [Hz]')
ax1.set_ylabel('Complex Shear Modulus G*')
ax1.set_yscale('log')
ax1.set_xscale('log')
ax1.set_title('MasterCurve ')
ax1.set_facecolor('whitesmoke')
figure3.patch.set_facecolor('whitesmoke')
ax1.spines['bottom'].set_color('black')
ax1.spines['top'].set_color('black')
ax1.spines['left'].set_color('black')
ax1.spines['right'].set_color('black')
toobar = NavigationToolbar2Tk(scatter1, GraphWindow)
ax1.legend(['(Temperature)' + str((df1.iloc[i, 0]))])
hold(True)
Everything is fine in this code but I am obtaining the lines in blue and the legend is the same for all of them.. This is what I obtained:
My question is, how can I change the color of each line and add new legend in each iteration in the above for loop.
Thanks in advance!
You want a single Figure, a single Axes and a single Canvas, and plot the different curves inside of them. In other words, you do too much inside the cycle…
import tkinter
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg, NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import numpy as np
# frequencies and a poor man's dataframe
freq = np.logspace(-1.5, 1.5, 12)
G = {'ABCDE'[n-1]:np.logspace(n-4, n-2-n/4, 12) for n in range(1, 6)}
root = tkinter.Tk()
root.wm_title("Embedding in Tk")
fig = Figure(figsize=(6, 4), dpi=100, layout='constrained')
ax = fig.add_subplot()
# this is our loop on the different curves
lines = [ax.plot(freq, G[letter], '-o', label=letter)[0] for letter in G]
# titles, log axes, legend
ax.set_xlabel('Reduced Frequency [Hz]')
ax.set_ylabel('Complex Shear Modulus G*')
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_title('MasterCurve ')
ax.set_facecolor('whitesmoke')
ax.legend()
connect the figure to a tkinter Canvas --- once
canvas = FigureCanvasTkAgg(fig, master=root) # A tk.DrawingArea.
canvas.draw()
# boilerplate
toolbar = NavigationToolbar2Tk(canvas, root, pack_toolbar=False)
toolbar.update()
canvas.mpl_connect("key_press_event", key_press_handler)
button_quit = tkinter.Button(master=root, text="Quit", command=root.destroy)
button_quit.pack(side=tkinter.BOTTOM)
toolbar.pack(side=tkinter.BOTTOM, fill=tkinter.X)
canvas.get_tk_widget().pack(side=tkinter.TOP, fill=tkinter.BOTH, expand=True)
# let's show our curves
tkinter.mainloop()
I have one, two, or three plots in a canvas of Tkinter. Let's assume I don't have any Mouse. There only is a keyboard. I want a cursor on these plots that moves at the same horizontal axis on all graphs in the canvas and of course when the Tk window shows up the yellow cursors (mplcursors) on all graphs be at the first point of graphs without any mouse movement and clicks(if you run the following code there are two cursors yellow and green, yellow appears by clicking on the line). The yellow cursors must move together (the same x-axes point) by right/left arrow keys on the keyboard.
from matplotlib.widgets import MultiCursor
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import mplcursors
root = Tk()
fig = Figure(figsize=(8,6))
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(-np.pi, np.pi, 256, endpoint=True)
y = np.sin(x)
z = np.cos(x)
ax1.plot(x, y, label="sin function")
ax1.legend(loc="upper right")
ax2.plot(x, z, label="cos function")
canvas = FigureCanvasTkAgg(fig,root)
c1 = mplcursors.cursor(ax1, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
c2 = mplcursors.cursor(ax2, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
multi = MultiCursor(fig.canvas, (ax1, ax2), color='g', lw=0.5, horizOn=True, vertOn=True)
ax2.legend(loc="upper left")
canvas.draw()
#pack canavs
canvas.get_tk_widget().pack(side = BOTTOM, fill=BOTH, expand=True)
#create Navigation Toolbar
toolbar = NavigationToolbar2Tk(canvas, root) #add to canvas
canvas._tkcanvas.pack(side = TOP, fill=BOTH, expand=True)
root.mainloop()
Do you have any suggestions? Can I also make the Multi-Cursor (green lines) to move with mplcursors on plots and stick to them too?
I have tried multi-threading from this link, used mouse controlling from this link and getting pixels of first plot points by this question but I couldn't handle the first display event and it needed the mouse to be clicked on both plots separately to show the cursors.
My plots are more complicated, they are signals and I have multiple pages in my application.
The following code does what I wanted But couldn't solve to keep the green Multi-Cursor on the yellow ones! (Exploring yet)
the following code uses MouseEvent and _process_event which clicks on the plot by default, when calling it you should declare when this process happens?, on which plot, in which points!
from matplotlib.widgets import MultiCursor
import matplotlib.pyplot as plt
import numpy as np
from tkinter import *
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
import mplcursors
import subprocess
from matplotlib.backend_bases import KeyEvent, MouseEvent
import copy
def _process_event(name, ax, coords, *args):
ax.viewLim # unstale viewLim.
if name == "__mouse_click__":
# So that the dragging callbacks don't go crazy.
_process_event("button_press_event", ax, coords, *args)
_process_event("button_release_event", ax, coords, *args)
return
display_coords = ax.transData.transform(coords)
if name in ["button_press_event", "button_release_event",
"motion_notify_event", "scroll_event"]:
event = MouseEvent(name, ax.figure.canvas, *display_coords, *args)
elif name in ["key_press_event", "key_release_event"]:
event = KeyEvent(name, ax.figure.canvas, *args, *display_coords)
else:
raise ValueError(f"Unknown event name {name!r}")
ax.figure.canvas.callbacks.process(name, event)
root = Tk()
fig = Figure(figsize=(8,6))
fig, (ax1, ax2) = plt.subplots(nrows=2, sharex=True)
x = np.linspace(-np.pi, np.pi, 256, endpoint=True)
y = np.sin(x)
z = np.cos(x)
ax1.plot(x, y, label="sin function")
ax1.legend(loc="upper right")
ax2.plot(x, z, label="cos function")
canvas = FigureCanvasTkAgg(fig,root)
c1 = mplcursors.cursor(ax1, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
c2 = mplcursors.cursor(ax2, multiple=False,hover=False,highlight=False,bindings={"toggle_visible": "h", "toggle_enabled": "e","left": "left","right": "right"})
multi = MultiCursor(fig.canvas, (ax1, ax2), color='g', lw=0.5, horizOn=True, vertOn=True)
ax2.legend(loc="upper left")
_process_event("__mouse_click__", ax1, (x[0], y[0]), 1)
sel, = c1.selections
c1.add_selection(copy.copy(sel))
_process_event("__mouse_click__", ax2, (x[0], z[0]), 1)
sel2, = c2.selections
c2.add_selection(copy.copy(sel2))
canvas.draw()
#pack canavs
canvas.get_tk_widget().pack(side = BOTTOM, fill=BOTH, expand=True)
#create Navigation Toolbar
toolbar = NavigationToolbar2Tk(canvas, root) #add to canvas
canvas._tkcanvas.pack(side = TOP, fill=BOTH, expand=True)
root.mainloop()
I'm new to coding and i'm trying to create a Tkinter window using Matplotlib classes that allows me to manipulate a line graph depicting the share prices for 4 companies over two years in a separate window, this is the code I've written:
from tkinter import *
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg,NavigationToolbar2Tk)
from matplotlib.backend_bases import key_press_handler
from matplotlib.figure import Figure
import pandas as pd
root = Tk ()
data = pd.read_csv('C:\\Users\\Admin\\Desktop\\shares.csv')
df = data.iloc[:,:5]
print(df)
print()
fig = Figure( figsize = (20,5) , dpi = 100 )
ax1 = fig.add_subplot()
df.plot('Date',figsize = (20,5), ax = ax1)
canvas = FigureCanvasTkAgg ( fig , root )
canvas.draw()
canvas.get_tk_widget().pack(side = TOP, fill = BOTH, expand = 1)
df.plot(kind = 'bar' , ax = ax1)
toolbar = NavigationToolbar2Tk(canvas, root)
toolbar.update()
canvas.get_tk_widget().pack(side = TOP, fill = BOTH, expand = 1)
def on_key(event):
print('You Pressed {}'.format(event.key))
key_press_handler(event, canvas, toolbar)
canvas.mpl_connect('key_press_event' , on_key)
def Quit():
root.quit()
root.destroy()
return
button = Button(root, text = 'QUIT' , command = Quit )
button.pack ()
root.mainloop()
This is the output I have obtained:
Whereas this is the desired graph:
I would appreciate any input as to how I can fix this, and make my code better.
Thanks :)
Try this it may work
df.plot(kind = 'line' , ax = ax1)
You are ploting bar and your expected output is a line graph
I have put together this piece of code that asks for an input in one Tkinter entry. I want the 3rd entry to be the graph, but I can't seem to get the graph to be put into window, rather than it's own.
Ignoring what goes into the first 2 rows of the Tkinter grid, I don't know where to go from here. I'm assuming I need to use some canvas which is why it's been imported but I don't know if I can reproduce this graph in that way.
#Import matplotlib modules
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from numpy import arange, sin, pi
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
#Put image under graph
img = plt.imread("graph.png")
fig, ax1 = plt.subplots()
ax1.imshow(img, extent=[-10,10,-10,10], aspect='equal')
#Set graph ranges
plt.ylim(-10, 10)
plt.xlim(-10, 10)
#Set axis & labels
ax1.set_xlabel('NEGATIVE')
ax1.set_ylabel('HAPPY')
ax2 = ax1.secondary_xaxis('top')
ax2.set_xlabel('POSITIVE')
ax3 = ax1.secondary_yaxis('right')
ax3.set_ylabel('SAD', rotation=270, labelpad=12.5)
#Remove ticks/values
for ax in (ax1, ax2, ax3):
ax.tick_params(left=False, labelleft=False, top=False, labeltop=False,
right=False, labelright=False, bottom=False, labelbottom=False)
#Calculate score
##TESTING##
import random
posNeg=0
hapSad=0
posNeg = random.randint(-12, 12)
hapSad = random.randint(-12, 12)
if posNeg < (-10):
posNeg = (-10)
if posNeg > 10:
posNeg = 10
if hapSad < (-10):
hapSad = (-10)
if hapSad > 10:
hapSad = 10
##TESTING##
#Plot point on graph
plt.plot([posNeg], [hapSad], marker = 'o', markersize = 15, color = 'red')
from tkinter import *
#testing input function
def print_input():
user_input = entry_1.get()
label_2["text"] = user_input
#window
window = Tk()
label_1 = Label(window, text = "What's your Twitter handle?")
entry_1 = Entry(window)
button_1 = Button(window, text = "go", command = print_input)
label_2 = Label(window)
label_1.grid(row = 0, column = 0)
entry_1.grid(row = 0, column = 1)
button_1.grid(row = 0, column = 2)
label_2.grid(row = 1, column = 0)
graph_1 = plt.show()
graph_1.grid(row = 3, column = 0)
window.mainloop()
#\window
You need to embed the graph using FigureCanvasTkAgg. It is a canvas on which matplotlib graphs can be drawn.
You have imported it, but have not used it. To display it, do the following:
graph_1 = FigureCanvasTkAgg(fig, master=window)
graph_1.get_tk_widget().grid(row = 3, column = 0)
graph_1.draw()
I am trying to graph readings received from a weight scale connected to Arduino. The Arduino using bluetooth sends data to RPi and I successfully made a live GUI. The issue is, I am trying to have a SpinBox or some sort of counter that can adjust the interval of at which the graph updates. I have tried using the variables for the interval in the FuncAnimation but it does not seem to work.
I have brute forced and noticed that once the code is executed, the interval is set to the initial value it receives and sticks to it. While loop does not work well with the objective I have to achieve. I have attached the code below too. Please note I am a beginner in Python.
import serial
from tkinter import *
from tkinter import ttk
from matplotlib import pyplot as plt
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib import style
from matplotlib.figure import Figure
import time
import read_arduino
ser = serial.Serial('/dev/rfcomm0', 115200)
ser.flushInput()
root = Tk()
root.geometry('800x400+200+100')
root.title('This is my root window')
tab_control = ttk.Notebook(root)
tab1 = ttk.Frame(tab_control)
tab2 = ttk.Frame(tab_control)
tab_control.add(tab1, text='First')
tab_control.add(tab2, text='Second')
# var =IntVar()
# var.set(300)
# incr=1000
# spin = Spinbox(tab1, from_=200, to=1000, width=5, textvariable=var)
xar = [0]
yar = [0]
style.use('ggplot')
fig = plt.figure(figsize=(3, 2.5))
plt.xlabel("Time")
plt.ylabel("Weight")
ax1 = fig.add_subplot(1,1,1)
ax1.set_aspect("auto")
fig.subplots_adjust(wspace=0, hspace=0)
ax1.set_ylim(0, 100)
line, = ax1.plot(xar, yar, 'blue',marker='x')
plt.tight_layout()
dum= StringVar()
lbl = Label(tab1, textvariable=dum).grid(row=2,column=2)
def animate(i):
x=get_avg()
yar.append(x)
xar.append(i)
line.set_data(xar, yar)
ax1.set_xlim(0, i+1)
dum.set(x)
def get_avg():
n = 60
val=str(0.0)
for n in range(1,60):
val=str(ser.readline(),"utf-8").strip("\n")
return val
plotcanvas = FigureCanvasTkAgg(fig, tab1)
plotcanvas.get_tk_widget().grid(row=5,column=0)
ani = animation.FuncAnimation(fig, animate, interval=1000, blit=False)
tab_control.pack(expand=1, fill='both')
root.mainloop()