How can I have a slideshow with matplotlib graphs in tkinter? - python-3.x

I would like to utilize a tkinter window with integrated matplotlib graphs that cycle every x seconds. However, this is what I have and I am stuck. Any help would be greatly appreciated. Thank you!
import time
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import tkinter as tk
root = tk.Tk()
root.geometry('500x500')
x = [1, 2, 3, 4, 5]
y = [[3, 6, 1, 9, 2], [2, 0, 1, 4, 6], [6, 3, 8, 2, 0]]
fig = plt.figure(figsize=(5,5))
i = 0
while i < len(y):
plt.plot(x, y[i])
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack()
time.sleep(1)
plt.clf()
i += 1
root.mainloop()

What you're looking for is after(). You'll likely have an easier time if you wrap everything up in a simple Root class, so I've done that here.
# imports go here as usual...
class Root(tk.Tk):
def __init__(self):
super().__init__() # init tk.Tk()
self.geometry('500x500')
self.x = [1, 2, 3, 4, 5]
self.y = [[3, 6, 1, 9, 2], [2, 0, 1, 4, 6], [6, 3, 8, 2, 0]]
self.fig = plt.figure(figsize=(5, 5))
# set up the canvas
self.canvas = FigureCanvasTkAgg(fig, master=root)
self.canvas.get_tk_widget().pack()
# begin cycling plots, starting with index 0
self.index = 0
self.cycle_plots(self.index)
def cycle_plots(self, index):
plt.clf # clear plot here
self.index += 1 # go to the next index
if self.index >= len(self.y):
self.index = 0 # wrap around
plt.plot(self.x, self.y[self.index])
self.canvas.draw() # update the canvas
# after 1000mS, call this function again
self.after(1000, lambda i=self.index: self.cycle_plots(i))
if __name__ == '__main__': # run the app!
root = Root()
root.mainloop()
Using a class makes it easier to pass your UI objects - like the Canvas - around between functions (methods). Here, self refers to your Root class, so anything inside it can share variables with self.<varname>, like self.canvas

Related

Two sets of arrows in plotly

How to add two sets of arrows with different colours, please? I obtained just green arrows. Are red arrows overplotted? How to suppress that?
When I comment the part between ###, I have red arrows.
The desired result is to have both arrows - red and green.
Thank you
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
d = {'a': [1, 2, 2], 'b': [3, 5, 4], 'c': [0.1, 0.2, 0.6]}
df = pd.DataFrame(data=d)
fig = px.scatter(df, x='a', y='b', error_y='c')
fig.update_xaxes(title_font_family="Trebuchet")
fig.update_layout(yaxis=dict(scaleanchor="x", scaleratio=1),
template = "plotly_white",
title="<b>V</b>",
)
fig.update_layout(xaxis = dict(autorange="reversed"))
x_end = [1, 2, 2]
y_end = [3, 5, 4]
x_start = [0, 1, 3]
y_start = [4, 4, 4]
list_of_all_arrows = []
for x0,y0,x1,y1 in zip(x_end, y_end, x_start, y_start):
arrow = go.layout.Annotation(dict(
x=x0,
y=y0,
xref="x", yref="y",
text="",
showarrow=True,
axref="x", ayref='y',
ax=x1,
ay=y1,
arrowhead=3,
arrowwidth=1.5,
arrowcolor='rgb(255,51,0)',)
)
list_of_all_arrows.append(arrow)
fig.update_layout(annotations=list_of_all_arrows)
###
list_of_all_arrows2 = []
for x0,y0,x1,y1 in zip([i-2 for i in x_end], [i-3 for i in y_end], x_start, y_start):
arrow = go.layout.Annotation(dict(
x=x0,
y=y0,
xref="x", yref="y",
text="",
showarrow=True,
axref="x", ayref='y',
ax=x1,
ay=y1,
arrowhead=3,
arrowwidth=1.5,
arrowcolor='green',)
)
list_of_all_arrows2.append(arrow)
fig.update_layout(annotations=list_of_all_arrows2)
###
# fig.write_html("Fig.html")
fig.show()
The origin of the problem is that in the background figures in plotly are dictionaries. The fact that you are calling two times fig.update_layout(annotations=list_anotation) updates figure's dictionary annotations entry. To check the dictionary of a figure just print the figure print(fig), there you can see the key layout and sub key annotations.
Therefore only calling one the function update_layout works as you want.
Step1: delete this line
fig.update_layout(annotations=list_of_all_arrows) # delete this line
Step2: change last line
fig.update_layout(annotations=list_of_all_arrows2 + list_of_all_arrows)
this is equivalent to appending all arrows to a single list
Total code
import plotly.express as px
import numpy as np
import pandas as pd
import plotly.graph_objects as go
d = {'a': [1, 2, 2], 'b': [3, 5, 4], 'c': [0.1, 0.2, 0.6]}
df = pd.DataFrame(data=d)
fig = px.scatter(df, x='a', y='b', error_y='c')
fig.update_xaxes(title_font_family="Trebuchet")
fig.update_layout(yaxis=dict(scaleanchor="x", scaleratio=1),
template = "plotly_white",
title="<b>V</b>",
)
fig.update_layout(xaxis = dict(autorange="reversed"))
x_end = [1, 2, 2]
y_end = [3, 5, 4]
x_start = [0, 1, 3]
y_start = [4, 4, 4]
list_of_all_arrows = []
for x0,y0,x1,y1 in zip(x_end, y_end, x_start, y_start):
arrow = go.layout.Annotation(dict(
x=x0,
y=y0,
xref="x", yref="y",
text="",
showarrow=True,
axref="x", ayref='y',
ax=x1,
ay=y1,
arrowhead=3,
arrowwidth=1.5,
arrowcolor='rgb(255,51,0)',)
)
list_of_all_arrows.append(arrow)
list_of_all_arrows2 = []
for x0,y0,x1,y1 in zip([i-2 for i in x_end], [i-3 for i in y_end], x_start, y_start):
arrow = go.layout.Annotation(dict(
x=x0,
y=y0,
xref="x", yref="y",
text="",
showarrow=True,
axref="x", ayref='y',
ax=x1,
ay=y1,
arrowhead=3,
arrowwidth=1.5,
arrowcolor='green',)
)
list_of_all_arrows2.append(arrow)
fig.update_layout(annotations=list_of_all_arrows2 + list_of_all_arrows)
The final plot

negative numbers in red tkinter and pandastable

I would like to change the color of negative numbers to red but I can't find anything in the documenation that works.
import pandastable as pt
import tkinter as tk
import pandas as pd
window = tk.Tk()
frame1 = tk.Frame(window)
frame1.grid(row=0, column=0)
d = {'col1':[1, 2, -3, 4, 5, 5], 'col2':[1, -0.5, -1, 4, 2, -2]
,'col3': [-0.345, 1, 2, 3, 3, 4]}
df = pd.DataFrame(data=d)
pt = pt.Table(frame1, dataframe = df, width=300, height=300, rows=8, cols=8)
pt.show()
window.mainloop()

Drawing edge weights don't work properly in networkx Python 3

My edge and weight data is like this:
{(0, 1): 7, (0, 2): 3, (1, 4): 6, (1, 2): 1, (1, 3): 2, (2, 3): 2, (3, 4): 4}
But i got an image like this. Which is showing wrong edge weights. What am i missing?
Here 0->1 has a weight of 7 but it shows 2. What is happening?
I have tried the following code:
import networkx as nx
from matplotlib import pyplot as plt
class DRAW:
def __init__(self):
self.G=nx.Graph()
def draw(self,node,track):
# node data is a list containing nodes like [0,1,2,3,4]
# track is edge and weight dict like {(0, 1): 7, (0, 2): 3, (1, 4): 6}
[self.G.add_node(k) for k in node]
[self.G.add_edge(m[0],m[1],weight=m[2]) for m in track]
# label list data for the weight show
label={(k[0],k[1]):k[2] for k in track}
nx.draw_networkx(self.G,pos=nx.spring_layout(self.G))
nx.draw_networkx_edge_labels(self.G, pos=nx.spring_layout(self.G),edge_labels=label,font_size=25)
plt.show()
Clean your code this way:
import networkx as nx
from matplotlib import pyplot as plt
track = {(0, 1): 7,
(0, 2): 3,
(1, 4): 6,
(1, 2): 1,
(1, 3): 2,
(2, 3): 2,
(3, 4): 4}
class Draw:
def __init__(self):
self.G=nx.Graph()
def draw(self,node,track):
# node data is a list containing nodes like [0,1,2,3,4]
# track is edge and weight dict like {(0, 1): 7, (0, 2): 3, (1, 4): 6}
[self.G.add_node(k) for k in node]
[self.G.add_edge(k[0],k[1],weight=v) for k,v in track.items()]
# label list data for the weight show
labels = nx.get_edge_attributes(self.G,'weight')
options = {'font_size':20,
'node_color':'red',
'label_pos':0.5,#(0=head, 0.5=center, 1=tail)
'node_size':1200,
'style':'solid',#(solid|dashed|dotted,dashdot)
'width':2}
pos = nx.spring_layout(self.G)
nx.draw(self.G,
pos,
with_labels=True,
**options)
nx.draw_networkx_edge_labels(self.G,
pos,
edge_labels=labels,
**options)
plt.savefig("graph.png")
plt.show()
d = Draw()
d.draw([*range(5)],track)
and you get:

How to process different row in tensor based on the first column value in tensorflow

let's say I have a 4 by 3 tensor:
sample = [[10, 15, 25], [1, 2, 3], [4, 4, 10], [5, 9, 8]]
I would like to return another tensor of shape 4: [r1,r2,r3,r4] where r is either equal to tf.reduce_sum(row) if row[0] is less than 5, or r is equal to tf.reduce_mean(row) if row[0] is greater or equal to 5.
output:
output = [16.67, 6, 18, 7.33]
I'm not an adept to tensorflow, please do assist me on how to achieve the above in python 3 without a for loop.
thank you
UPDATES:
So I've tried to adapt the answer given by #Onyambu to include two samples in the functions but it gave me an error in all instances.
here is the answer for the first case:
def f(x):
c = tf.constant(5,tf.float32)
def fun1():
return tf.reduce_sum(x)
def fun2():
return tf.reduce_mean(x)
return tf.cond(tf.less(x[0],c),fun1,fun2)
a = tf.map_fn(f,tf.constant(sample,tf.float32))
The above works well.
The for two samples:
sample1 = [[10, 15, 25], [1, 2, 3], [4, 4, 10], [5, 9, 8]]
sample2 = [[0, 15, 25], [1, 2, 3], [0, 4, 10], [1, 9, 8]]
def f2(x1,x2):
c = tf.constant(1,tf.float32)
def fun1():
return tf.reduce_sum(x1[:,0] - x2[:,0])
def fun2():
return tf.reduce_mean(x1 - x2)
return tf.cond(tf.less(x2[0],c),fun1,fun2)
a = tf.map_fn(f2,tf.constant(sample1,tf.float32), tf.constant(sample2,tf.float32))
The adaptation does give errors, but the principle is simple:
calculate the tf.reduce_sum of sample1[:,0] - sample2[:,0] if row[0] is less than 1
calculate the tf.reduce_sum of sample1 - sample2 if row[0] is greater or equal to 1
Thank you for your assistance in advance!
import tensorflow as tf
def f(x):
y = tf.constant(5,tf.float32)
def fun1():
return tf.reduce_sum(x)
def fun2():
return tf.reduce_mean(x)
return tf.cond(tf.less(x[0],y),fun1,fun2)
a = tf.map_fn(f,tf.constant(sample,tf.float32))
with tf.Session() as sess: print(sess.run(a))
[16.666666 6. 18. 7.3333335]
If you want to shorten it:
y = tf.constant(5,tf.float32)
f=lambda x: tf.cond(tf.less(x[0], y), lambda: tf.reduce_sum(x),lambda: tf.reduce_mean(x))
a = tf.map_fn(f,tf.constant(sample,tf.float32))
with tf.Session() as sess: print(sess.run(a))

Iterating through a list and outputting it through create_text

I'm having some trouble with tkinter's create_text. I'm trying to iterate through a list and have create_text output each item in the list one by one. I can't figure this out, as every time I've tried, it does not work the way I want it to. Here's some code that exemplifies the issue:
class GUI(Frame):
def __init__(self, master):
self.test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.c = Canvas(master, width=300, height=300)
self.c.pack()
for items in self.test_list:
items = items
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)
root = Tk()
root.title("Test")
root.geometry("300x300")
GUI(root)
mainloop()
Thank you and I appreciate the help.
Your code had severe indentation problems.
Further, you did not call mainloop on any object.
Then, the position of the objects on the canvas was outside the visible window:
I fixed the code so it runs, and displays something on the canvas; from there, you can modify it to suit your needs.
import tkinter as tk
class GUI(tk.Frame):
def __init__(self, master):
self.test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.c = tk.Canvas(master, width=300, height=300)
self.c.pack()
for idx, elt in enumerate(self.test_list):
row = (idx // 3 + 5) * 20
col = (idx % 3 + 5) * 20
self.c.create_text(row, col, text=elt)
if __name__ == '__main__':
root = tk.Tk()
root.title("Test")
root.geometry("300x300")
GUI(root)
root.mainloop()
This has two outer loops.
# iterates, items == 9 now
for items in self.test_list:
items = items
# uses 9 over and over
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)
Maybe try this instead.
for items in self.test_list:
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)

Resources