Open excel file in Tkinter and plot graphs - excel

I've just started writing Python a month ago and now getting into Tkinter. I'm trying to build a program that a user can open an excel file into Tkinter, display the dataframe and the user can choose a certain graph type, insert the variables for the X-axis and Y-axis and plot it. I've build the general layout of the GUI using PAGE but having problems on how to give commands to the widgets on displaying the dataframe and plotting the graph.
Here's my code for the GUI:
import sys
from tkinter.filedialog import askopenfilename
try:
from Tkinter import *
except ImportError:
from tkinter import *
try:
import ttk
py3 = 0
except ImportError:
import tkinter.ttk as ttk
py3 = 1
def vp_start_gui():
'''Starting point when module is the main routine.'''
global val, w, root
root = Tk()
top = New_Toplevel_1 (root)
root.mainloop()
w = None
def create_New_Toplevel_1(root, *args, **kwargs):
'''Starting point when module is imported by another program.'''
global w, w_win, rt
rt = root
w = Toplevel (root)
top = New_Toplevel_1 (w)
return (w, top)
def destroy_New_Toplevel_1():
global w
w.destroy()
w = None
class New_Toplevel_1:
def __init__(self, top=None):
'''This class configures and populates the toplevel window.
top is the toplevel containing window.'''
self._bgcolor = '#d9d9d9' # X11 color: 'gray85'
self._fgcolor = '#000000' # X11 color: 'black'
self._compcolor = '#d9d9d9' # X11 color: 'gray85'
self._ana1color = '#d9d9d9' # X11 color: 'gray85'
self._ana2color = '#d9d9d9' # X11 color: 'gray85'
top.geometry("757x1037+832+67")
top.title("New Toplevel 1")
top.configure(background="#d9d9d9")
top.configure(highlightbackground="#d9d9d9")
top.configure(highlightcolor="black")
self.Canvas1 = Canvas(top)
self.Canvas1.place(relx=0.04, rely=0.58, relheight=0.4, relwidth=0.92)
self.Canvas1.configure(background="white")
self.Canvas1.configure(borderwidth="2")
self.Canvas1.configure(highlightbackground="#d9d9d9")
self.Canvas1.configure(highlightcolor="black")
self.Canvas1.configure(insertbackground="black")
self.Canvas1.configure(relief=RIDGE)
self.Canvas1.configure(selectbackground="#c4c4c4")
self.Canvas1.configure(selectforeground="black")
self.Canvas1.configure(width=695)
self.Button2 = Button(top)
self.Button2.place(relx=0.75, rely=0.52, height=42, width=138)
self.Button2.configure(activebackground="#d9d9d9")
self.Button2.configure(activeforeground="#000000")
self.Button2.configure(background="#d9d9d9")
self.Button2.configure(disabledforeground="#a3a3a3")
self.Button2.configure(foreground="#000000")
self.Button2.configure(highlightbackground="#d9d9d9")
self.Button2.configure(highlightcolor="black")
self.Button2.configure(pady="0")
self.Button2.configure(text='''Generate Graph''')
self.Labelframe1 = LabelFrame(top)
self.Labelframe1.place(relx=0.05, rely=0.39, relheight=0.18
, relwidth=0.44)
self.Labelframe1.configure(relief=GROOVE)
self.Labelframe1.configure(foreground="black")
self.Labelframe1.configure(text='''Type of Graph''')
self.Labelframe1.configure(background="#d9d9d9")
self.Labelframe1.configure(highlightbackground="#d9d9d9")
self.Labelframe1.configure(highlightcolor="black")
self.Labelframe1.configure(width=330)
self.Radiobutton1 = Radiobutton(self.Labelframe1)
self.Radiobutton1.place(relx=0.06, rely=0.22, relheight=0.2
, relwidth=0.31)
self.Radiobutton1.configure(activebackground="#d9d9d9")
self.Radiobutton1.configure(activeforeground="#000000")
self.Radiobutton1.configure(background="#d9d9d9")
self.Radiobutton1.configure(disabledforeground="#a3a3a3")
self.Radiobutton1.configure(foreground="#000000")
self.Radiobutton1.configure(highlightbackground="#d9d9d9")
self.Radiobutton1.configure(highlightcolor="black")
self.Radiobutton1.configure(justify=LEFT)
self.Radiobutton1.configure(text='''Bar Chart''')
self.Radiobutton2 = Radiobutton(self.Labelframe1)
self.Radiobutton2.place(relx=0.06, rely=0.38, relheight=0.2
, relwidth=0.35)
self.Radiobutton2.configure(activebackground="#d9d9d9")
self.Radiobutton2.configure(activeforeground="#000000")
self.Radiobutton2.configure(background="#d9d9d9")
self.Radiobutton2.configure(disabledforeground="#a3a3a3")
self.Radiobutton2.configure(foreground="#000000")
self.Radiobutton2.configure(highlightbackground="#d9d9d9")
self.Radiobutton2.configure(highlightcolor="black")
self.Radiobutton2.configure(justify=LEFT)
self.Radiobutton2.configure(text='''Histogram''')
self.Radiobutton3 = Radiobutton(self.Labelframe1)
self.Radiobutton3.place(relx=0.06, rely=0.54, relheight=0.2
, relwidth=0.37)
self.Radiobutton3.configure(activebackground="#d9d9d9")
self.Radiobutton3.configure(activeforeground="#000000")
self.Radiobutton3.configure(background="#d9d9d9")
self.Radiobutton3.configure(disabledforeground="#a3a3a3")
self.Radiobutton3.configure(foreground="#000000")
self.Radiobutton3.configure(highlightbackground="#d9d9d9")
self.Radiobutton3.configure(highlightcolor="black")
self.Radiobutton3.configure(justify=LEFT)
self.Radiobutton3.configure(text='''Scatter Plot''')
self.Button3 = Button(top)
self.Button3.place(relx=0.28, rely=0.05, height=52, width=122)
self.Button3.configure(activebackground="#d9d9d9")
self.Button3.configure(activeforeground="#000000")
self.Button3.configure(background="#d9d9d9")
self.Button3.configure(disabledforeground="#a3a3a3")
self.Button3.configure(foreground="#000000")
self.Button3.configure(highlightbackground="#d9d9d9")
self.Button3.configure(highlightcolor="black")
self.Button3.configure(pady="0")
self.Button3.configure(text='''Browse''')
self.Button3.configure(width=122)
self.Button3.configure(command=askopenfilename)
self.Label5 = Label(top)
self.Label5.place(relx=0.03, rely=0.06, height=31, width=147)
self.Label5.configure(activebackground="#f9f9f9")
self.Label5.configure(activeforeground="black")
self.Label5.configure(background="#d9d9d9")
self.Label5.configure(disabledforeground="#a3a3a3")
self.Label5.configure(foreground="#000000")
self.Label5.configure(highlightbackground="#d9d9d9")
self.Label5.configure(highlightcolor="black")
self.Label5.configure(text='''Upload File:''')
self.Label5.configure(width=147)
self.Label3 = Label(top)
self.Label3.place(relx=0.05, rely=0.13, height=31, width=111)
self.Label3.configure(background="#d9d9d9")
self.Label3.configure(disabledforeground="#a3a3a3")
self.Label3.configure(foreground="#000000")
self.Label3.configure(text='''Data Frame :''')
self.Text1 = Text(top)
self.Text1.place(relx=0.05, rely=0.16, relheight=0.21, relwidth=0.9)
self.Text1.configure(background="white")
self.Text1.configure(font="TkTextFont")
self.Text1.configure(foreground="black")
self.Text1.configure(highlightbackground="#d9d9d9")
self.Text1.configure(highlightcolor="black")
self.Text1.configure(insertbackground="black")
self.Text1.configure(selectbackground="#c4c4c4")
self.Text1.configure(selectforeground="black")
self.Text1.configure(width=684)
self.Text1.configure(wrap=WORD)
self.Labelframe2 = LabelFrame(top)
self.Labelframe2.place(relx=0.5, rely=0.39, relheight=0.12
, relwidth=0.45)
self.Labelframe2.configure(relief=GROOVE)
self.Labelframe2.configure(foreground="black")
self.Labelframe2.configure(text='''Labelframe''')
self.Labelframe2.configure(background="#d9d9d9")
self.Labelframe2.configure(width=340)
self.Label1 = Label(self.Labelframe2)
self.Label1.place(relx=0.03, rely=0.24, height=31, width=67)
self.Label1.configure(background="#d9d9d9")
self.Label1.configure(disabledforeground="#a3a3a3")
self.Label1.configure(foreground="#000000")
self.Label1.configure(text='''X-axis :''')
self.Label2 = Label(self.Labelframe2)
self.Label2.place(relx=0.03, rely=0.56, height=31, width=66)
self.Label2.configure(background="#d9d9d9")
self.Label2.configure(disabledforeground="#a3a3a3")
self.Label2.configure(foreground="#000000")
self.Label2.configure(text='''Y-axis :''')
self.Entry1 = Entry(self.Labelframe2)
self.Entry1.place(relx=0.24, rely=0.24, relheight=0.29, relwidth=0.72)
self.Entry1.configure(background="white")
self.Entry1.configure(disabledforeground="#a3a3a3")
self.Entry1.configure(font="TkFixedFont")
self.Entry1.configure(foreground="#000000")
self.Entry1.configure(insertbackground="black")
self.Entry1.configure(width=244)
self.Entry2 = Entry(self.Labelframe2)
self.Entry2.place(relx=0.24, rely=0.56, relheight=0.29, relwidth=0.72)
self.Entry2.configure(background="white")
self.Entry2.configure(disabledforeground="#a3a3a3")
self.Entry2.configure(font="TkFixedFont")
self.Entry2.configure(foreground="#000000")
self.Entry2.configure(insertbackground="black")
self.Entry2.configure(width=244)
if __name__ == '__main__':
vp_start_gui()
I know how to save the data into a dataframe and plot graphs in Python, but i'm not sure where to write those codes when GUI is involved. All i was able to do was give a command to the 'Browse' button to search for the excel file.
self.Button3.configure(command=askopenfilename)
Can someone help me:
How to save the excel file chosen from the command into a dataframe
How to display the dataframe into the textbox below
How to plot the graph with the criteria (type of graph and axes) chosen onto the canvas at bottom
Thanks so much in advance.

In class use some self.variable to keep information and then you can use it in other methods in class.
In example I use method load() to load file and create dataframe, and method display() to display this dataframe in text widget.
try:
# Python 2
import Tkinter as tk
import ttk
from tkFileDialog import askopenfilename
except ImportError:
# Python 3
import tkinter as tk
from tkinter import ttk
from tkinter.filedialog import askopenfilename
import pandas as pd
# --- classes ---
class MyWindow:
def __init__(self, parent):
self.parent = parent
self.filename = None
self.df = None
self.text = tk.Text(self.parent)
self.text.pack()
self.button = tk.Button(self.parent, text='LOAD DATA', command=self.load)
self.button.pack()
self.button = tk.Button(self.parent, text='DISPLAY DATA', command=self.display)
self.button.pack()
def load(self):
name = askopenfilename(filetypes=[('CSV', '*.csv',), ('Excel', ('*.xls', '*.xlsx'))])
if name:
if name.endswith('.csv'):
self.df = pd.read_csv(name)
else:
self.df = pd.read_excel(name)
self.filename = name
# display directly
#self.text.insert('end', str(self.df.head()) + '\n')
def display(self):
# ask for file if not loaded yet
if self.df is None:
self.load()
# display if loaded
if self.df is not None:
self.text.insert('end', self.filename + '\n')
self.text.insert('end', str(self.df.head()) + '\n')
# --- main ---
if __name__ == '__main__':
root = tk.Tk()
top = MyWindow(root)
root.mainloop()

Here i have fetch the excel data and store it in the graph, but i want my graph should plot data after 5 second,and the graph should run in the Run time.
from openpyxl import load_workbook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
# read from excel file
wb = load_workbook('C:\\Users\\Acer-i5-607\\Desktop\\case1.xlsx')
sheet_1 = wb.get_sheet_by_name('case1')
plt.figure(figsize=(6, 4), facecolor='Grey')
G = gridspec.GridSpec(6, 2)
axes_1 = plt.subplot(G[0, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=4).value
#print x
#print y
# create the plot
plt.xlabel('time')
plt.ylabel('HR')
plt.plot(x, y, color='cyan', label='HR')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
#plt.title('Reading values from an Excel file'
axes_1 = plt.subplot(G[1, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=6).value
#print a
#print b
# create the plot
plt.xlabel('time')
plt.ylabel('Pulse')
plt.plot(x, y, color='red', label='Pulse')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
axes_1 = plt.subplot(G[2, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=7).value
#print x
#print y
# create the plot
plt.xlabel('time')
plt.ylabel('SpO2')
plt.plot(x, y, color='magenta', label='SpO2')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
axes_1 = plt.subplot(G[3, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=8).value
#print x
#print y
# create the plot
plt.xlabel('time')
plt.ylabel('Perf')
plt.plot(x, y, color='blue', label='Perf')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
axes_1 = plt.subplot(G[4, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=9).value
#print x
#print y
# create the plot
plt.xlabel('time')
plt.ylabel('etCO2')
plt.plot(x, y, color='yellow', label='etCO2')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
axes_1 = plt.subplot(G[5, :])
x = np.zeros(sheet_1.max_row)
y = np.zeros(len(x))
for i in range(1, sheet_1.max_row):
x[i] = sheet_1.cell(row=i + 1, column=2).value
y[i] = sheet_1.cell(row=i + 1, column=10).value
#print x
#print y
# create the plot
plt.xlabel('time')
plt.ylabel('imCO2')
plt.plot(x, y, color='green', label='imCO2')
plt.legend(loc='upper right', fontsize='small')
plt.grid(True)
plt.xlim(0, 60000)
plt.ylim(0, 100)
plt.show()

import tkinter as tk
from tkinter import filedialog
import pandas as pd
import matplotlib.pyplot as plt
root= tk.Tk()
canvas1 = tk.Canvas(root, width = 300, height = 300, bg = 'lightsteelblue')
canvas1.pack()
def getExcel ():
global df
import_file_path = filedialog.askopenfilename()
df = pd.read_excel (import_file_path)
df["Year"] = pd.to_datetime(df["Year"], format="%Y")
ax = df.plot("Year", "Accidents",marker='o',color='r')
plt.grid()
plt.title('Yearly Graph')
ax.figure.autofmt_xdate()
plt.show()
browseButton_Excel = tk.Button(text='Import Excel File', command=getExcel, bg='green', fg='white', font=('helvetica', 12, 'bold'))
canvas1.create_window(150, 150, window=browseButton_Excel)
root.mainloop()

Related

AttributeError: 'list' object has no attribute 'set_canvas'

I am a begginer to python.
I create a graph as below to view on tkinter window.
But It gave me some error.
Error :
figure.set_canvas(self)
AttributeError: 'list' object has no attribute 'set_canvas'
This is my coding part:
import numpy as np
import tkinter as tk
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def f(x, a, b, c):
return a*x**2+b*x+c
xlist = np.linspace(-10, 10, num = 1000)
##print(xlist)
a = 5
b = 1
c = 4
ylist = f(xlist, a, b, c)
plt.figure(num = 0, dpi = 120)
ax = plt.plot(xlist, ylist, label = 'f(x)')
print(type(ax))
plt.title('Plotting Example')
plt.xlabel('Distance / ft')
plt.ylabel('Height / ft')
plt.legend()
plt.grid(True)
# plt.show()
# __________________________________________________________________
my_w = tk.Tk()
my_w.geometry('1000x820+50+50')
my_w.title('data view on tk graph')
wrapper1 = tk.LabelFrame(my_w)
wrapper2 = tk.LabelFrame(my_w)
wrapper1.pack(fill="both", expand="yes", padx=5, pady=5)
wrapper2.pack(fill="both", padx=5, pady=5)
canvas = tk.Canvas(wrapper1, bg="#595656", height=200)
canvas.pack(fill="both", expand=True)
# __________________________________________________________________
plot1 = FigureCanvasTkAgg(ax, canvas)
plot1.draw()
plot1.get_tk_widget().pack(fill="both", expand="yes", padx=5, pady=5)
ctrl_frame = tk.Frame(wrapper2, bg='green', height=100)
ctrl_frame.pack(fill="both")
tk.Button(ctrl_frame, text='View', width=15, command=lambda:None).place(relx=0.9, rely=0.5, anchor=tk.E)
my_w.mainloop()
__________________________________________________________________
Please help me to set the graph on tkinter window
import numpy as np
import tkinter as tk
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
# ax = sns.set()
def f(x, a, b, c):
return a * x ** 2 + b * x + c
xlist = np.linspace(-10, 10, num=1000)
a = 5
b = 1
c = 4
ylist = f(xlist, a, b, c)
figure = plt.figure(figsize = (5,4), num = 0, dpi = 120)
figure.add_subplot(111).plot(xlist, ylist)
plt.title('Plotting Example')
plt.xlabel('Distance / ft')
plt.ylabel('Height / ft')
plt.grid(True)
# __________________________________________________________________
my_w = tk.Tk()
my_w.geometry('1000x820+50+50')
my_w.title('data view on tk graph')
wrapper1 = tk.LabelFrame(my_w)
wrapper2 = tk.LabelFrame(my_w)
wrapper1.pack(fill="both", expand="yes", padx=5, pady=5)
wrapper2.pack(fill="both", padx=5, pady=5)
# __________________________________________________________________
plot1 = FigureCanvasTkAgg(figure, wrapper1)
plot1.get_tk_widget().pack(fill="both", expand="yes", padx=5, pady=5)
ctrl_frame = tk.Frame(wrapper2, bg='green', height=100)
ctrl_frame.pack(fill="both")
tk.Button(ctrl_frame, text='View', width=15, command=lambda: None).place(relx=0.9, rely=0.5, anchor=tk.E)
my_w.mainloop()

Matplotlib Scatter plot interactivity not working

Until this morning I was able to display labels information when hovering the dots on a scatter plot.
Now, if I run the following code it does not display any error but the interactivity is not working and it looks like mplconnect or mlpcursors are completely ignored.
I've tried the same code under windows and Fedora.
Not understanding what's going on.
from matplotlib.pyplot import figure, show
import numpy as npy
from numpy.random import rand
x, y, c, s = rand(4, 100)
def onpick3(event):
ind = event.ind
print('onpick3 scatter:', ind, npy.take(x, ind), npy.take(y, ind))
fig = figure()
ax1 = fig.add_subplot(111)
col = ax1.scatter(x, y, 100*s, c, picker=True)
#fig.savefig('pscoll.eps')
fig.canvas.mpl_connect('pick_event', onpick3)
show()
Or
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(1)
x = np.random.rand(15)
y = np.random.rand(15)
names = np.array(list("ABCDEFGHIJKLMNO"))
c = np.random.randint(1,5,size=15)
norm = plt.Normalize(1,4)
cmap = plt.cm.RdYlGn
fig,ax = plt.subplots()
sc = plt.scatter(x,y,c=c, s=100, cmap=cmap, norm=norm)
annot = ax.annotate("", xy=(0,0), xytext=(20,20),textcoords="offset points",
bbox=dict(boxstyle="round", fc="w"),
arrowprops=dict(arrowstyle="->"))
annot.set_visible(False)
def update_annot(ind):
pos = sc.get_offsets()[ind["ind"][0]]
annot.xy = pos
text = "{}, {}".format(" ".join(list(map(str,ind["ind"]))),
" ".join([names[n] for n in ind["ind"]]))
annot.set_text(text)
annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
annot.get_bbox_patch().set_alpha(0.4)
def hover(event):
vis = annot.get_visible()
if event.inaxes == ax:
cont, ind = sc.contains(event)
if cont:
update_annot(ind)
annot.set_visible(True)
fig.canvas.draw_idle()
else:
if vis:
annot.set_visible(False)
fig.canvas.draw_idle()
fig.canvas.mpl_connect("motion_notify_event", hover)
plt.show()
This is not my code, I've copied and pasted it from a website but the behavior is the same.
Plotly express solves the problem
import plotly.express as px
alpha = data[data['Ticker']==focus].V1
gamma = data[data['Ticker']==focus].V2
fig = px.scatter(data, x='V1', y='V2', color=Colors.Market_Cap, hover_data=["Ticker"] )
fig.add_shape(type="circle",
xref="x", yref="y",
x0=int(alpha-3), y0=int(gamma-3), x1=int(alpha+3), y1=int(gamma+3),
line_color="LightSeaGreen",
)
fig.show()

Moving the plot regrading to the offset

Dears,
I wrote this code to calculate the distance between two mouse clicks on the plot. Now I am trying to move the plot to the left or to the right with regards to the calculated offset so that the two plots are exactly match. any idea how to achieve that? I tried to add and subtract normally but it did not work.
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
import mplcursors
from math import sqrt
import numpy as np
import tkinter as tk
from tkinter import simpledialog
class DistancePlot:
def __init__(self):
ROOT = tk.Tk()
ROOT.withdraw()
ROOT.geometry("500x200")
#my_frame = Frame(ROOT)
#my_frame.pack(fill="both", expand=True)
USER_INP = (simpledialog.askinteger(title="Plot dialig",
prompt="Enter the number of the plots "))
if USER_INP is not None:
def f(x):
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
self.x = np.linspace(1, 10)
self.fig, self.ax= plt.subplots()
for i in range(USER_INP):
plt.plot(self.x, f(self.x))
self.ax.set_xlabel('X-axis')
self.ax.set_ylabel('Y-axis')
self.d1 = (0.0, 0.0)
self.d2 = (0.0, 0.0)
self.first_click = True
self.cursor=Cursor(self.ax, horizOn=True, vertOn=True, color='black', linewidth=1.0)
self.fig.canvas.mpl_connect('button_press_event', self.onclick)
mplcursors.cursor(hover=True)
plt.show()
else:
def quit(self):
self.ROOT.destroy()
def onclick(self, event):
z1, r1 = event.xdata, event.ydata
print(z1, r1)
if self.first_click:
self.first_click = False
self.d1 = (z1, r1)
else:
self.first_click = True
self.d2 = (z1, r1)
distance = sqrt((((self.d1[0]) - (self.d2[0])) ** 2) + (((self.d1[1]) - (self.d2[1])) ** 2))
print("The shift between ", self.d1, "and", self.d2, "is", distance)
dp = DistancePlot()
the answer in the comment was helpful but this is not exactly what I want, I tried to use the same logic to get to my solution but it didn't work and I will share it with you.
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
import mplcursors
import numpy as np
import tkinter as tk
#from tkinter import simpledialog
class DistancePlot:
def __init__(self):
ROOT = tk.Tk()
ROOT.withdraw()
ROOT.geometry("500x200")
# USER_INP = 1
#random sine wave
x = np.linspace(1, 10)
def f(x):
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
#fixed sine wave
time= np.arange(0, 10, 0.1)
amplitude = np.sin(time)
self.fig, self.ax = plt.subplots()
self.ax.plot(x, f(x))
self.ax.plot(time, amplitude)
self.ax.set_xlabel('X-axis')
self.ax.set_ylabel('Y-axis')
self.d1 = np.zeros(2)
self.d2 = np.zeros(2)
self.first_click = True
self.cursor = Cursor(self.ax, horizOn=True, vertOn=True, color='black', linewidth=1)
self.fig.canvas.mpl_connect('button_press_event', self.onclick)
mplcursors.cursor(hover=True)
plt.show()
def onclick(self, event):
z1, r1 = event.xdata, event.ydata
print(z1, r1)
if self.first_click:
self.first_click = False
self.d1 = np.array((z1, r1))
else:
self.first_click = True
self.d2 = np.array((z1, r1))
#distance = sqrt((((self.d1[0]) - (self.d2[0])) ** 2) + (((self.d1[1]) - (self.d2[1])) ** 2))
#print("The distance between ", self.d1, "and", self.d2, "is", distance)
delta = self.d2 - self.d1
print("The delta between ", self.d1, "and", self.d2, "is", delta)
if (abs(self.d2[0]) > abs(self.d1[0])).all():
self.ax.lines[0].set_data(self.ax.lines[0].get_data() - delta.reshape(2,1))
self.ax.relim()
self.ax.autoscale_view()
plt.draw()
else:
self.ax.lines[0].set_data(self.ax.lines[0].get_data() + delta.reshape(2,1))
self.ax.relim()
self.ax.autoscale_view()
plt.draw()
dp = DistancePlot()
what I want is use a reference graph and match it with another graph, if the graph I am adding is leading I want it to be subtracted and if it lagging I want it to move forward so adding it to the delta.
Here is an approach, moving the first curve over the given distance. Numpy arrays are used to simplify the loops. relim() and autoscale_view() recalculate the x and y limits to fit everything again inside a margin (this step can be skipped if the expected displacement is small).
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
import mplcursors
from math import sqrt
import numpy as np
import tkinter as tk
from tkinter import simpledialog
class DistancePlot:
def __init__(self):
ROOT = tk.Tk()
ROOT.withdraw()
ROOT.geometry("500x200")
USER_INP = 2
# USER_INP = (simpledialog.askinteger(title="Plot dialig", prompt="Enter the number of the plots "))
if USER_INP is not None:
def f(x):
return np.sin(x) + np.random.normal(scale=0.1, size=len(x))
self.x = np.linspace(1, 10)
self.fig, self.ax = plt.subplots()
for i in range(USER_INP):
self.ax.plot(self.x, f(self.x))
self.ax.set_xlabel('X-axis')
self.ax.set_ylabel('Y-axis')
self.d1 = np.zeros(2)
self.d2 = np.zeros(2)
self.first_click = True
self.cursor = Cursor(self.ax, horizOn=True, vertOn=True, color='black', linewidth=1)
self.fig.canvas.mpl_connect('button_press_event', self.onclick)
mplcursors.cursor(hover=True)
plt.show()
else:
def quit(self):
self.ROOT.destroy()
def onclick(self, event):
z1, r1 = event.xdata, event.ydata
if self.first_click:
self.first_click = False
self.d1 = np.array((z1, r1))
else:
self.first_click = True
self.d2 = np.array((z1, r1))
distance = sqrt((((self.d1[0]) - (self.d2[0])) ** 2) + (((self.d1[1]) - (self.d2[1])) ** 2))
delta = self.d2 - self.d1
self.ax.lines[0].set_data(self.ax.lines[0].get_data() + delta.reshape(2,1))
self.ax.relim()
self.ax.autoscale_view()
plt.draw()
dp = DistancePlot()
If you only want to move left-right, you can use set_xdata to only change the x-positions. The following example code moves the second curve with the given displacement. If you want to move to the left, the second click should be to the left of the first.
def onclick(self, event):
z1, r1 = event.xdata, event.ydata
if self.first_click:
self.first_click = False
self.d1 = np.array((z1, r1))
else:
self.first_click = True
self.d2 = np.array((z1, r1))
delta_x = self.d1[0] - self.d2[0]
self.ax.lines[1].set_xdata(self.ax.lines[1].get_xdata() + delta_x)
self.ax.relim()
self.ax.autoscale_view()
plt.draw()

Python - Tkinter - Paint: How to Paint smoothly and save images with a different names?

I made a paint program, but I can not draw smoothly and save the images each time with a different names. Please help!
from tkinter import *
# by Canvas I can't save image, so i use PIL
import PIL
from PIL import Image, ImageDraw
def save():
filename = 'image.png'
image1.save(filename)
def paint(event):
x1, y1 = (event.x), (event.y)
x2, y2 = (event.x + 1), (event.y + 1)
cv.create_oval((x1, y1, x2, y2), fill='black', width=10)
# --- PIL
draw.line((x1, y1, x2, y2), fill='black', width=10)
root = Tk()
cv = Canvas(root, width=640, height=480, bg='white')
# --- PIL
image1 = PIL.Image.new('RGB', (640, 480), 'white')
draw = ImageDraw.Draw(image1)
# ----
cv.bind('<B1-Motion>', paint)
cv.pack(expand=YES, fill=BOTH)
btn_save = Button(text="save", command=save)
btn_save.pack()
root.mainloop()
You could use continuous drawing instead of drawing separate small circles.
The following example stores the last values of the position of the mouse to draw a line to the current value.
You need to click, and move the mouse to draw; release the click to stop.
The image name includes a number that is incremented by 1 each time you save; you can therefore save all the intermediate images as you draw the full picture.
from tkinter import *
import PIL
from PIL import Image, ImageDraw
def save():
global image_number
filename = f'image_{image_number}.png' # image_number increments by 1 at every save
image1.save(filename)
image_number += 1
def activate_paint(e):
global lastx, lasty
cv.bind('<B1-Motion>', paint)
lastx, lasty = e.x, e.y
def paint(e):
global lastx, lasty
x, y = e.x, e.y
cv.create_line((lastx, lasty, x, y), width=1)
# --- PIL
draw.line((lastx, lasty, x, y), fill='black', width=1)
lastx, lasty = x, y
root = Tk()
lastx, lasty = None, None
image_number = 0
cv = Canvas(root, width=640, height=480, bg='white')
# --- PIL
image1 = PIL.Image.new('RGB', (640, 480), 'white')
draw = ImageDraw.Draw(image1)
cv.bind('<1>', activate_paint)
cv.pack(expand=YES, fill=BOTH)
btn_save = Button(text="save", command=save)
btn_save.pack()
root.mainloop()
Allegedly not less terrible than yours, but the lines are continuous...
You can also use Save As dialog if you want to save image as other name and extension:
from tkinter import *
from tkinter.filedialog import asksaveasfilename as saveAs
import PIL
from PIL import Image, ImageDraw
def save():
filename=saveAs(title="Save image as...",filetype=(("PNG images","*.png"),("JPEG images","*.jpg"),("GIF images","*.gif")))
image1.save(filename)
def activate_paint(e):
global lastx, lasty
cv.bind('<B1-Motion>', paint)
lastx, lasty = e.x, e.y
def paint(e):
global lastx, lasty
x, y = e.x, e.y
cv.create_line((lastx, lasty, x, y), width=1)
draw.line((lastx, lasty, x, y), fill='black', width=1)
lastx, lasty = x, y
def clear():
cv.delete('all')
def exitt():
exit()
win = Tk()
win.title("Paint - made in Python")
win.iconbitmap('Paint.ico') # Delete this line if you don't have file "Paint.ico" in this folder
lastx, lasty = None, None
cv = Canvas(win, width=640, height=480, bg='white')
image1 = PIL.Image.new('RGB', (640, 480), 'white')
draw = ImageDraw.Draw(image1)
cv.bind('<1>', activate_paint)
cv.pack(expand=YES, fill=BOTH)
save_ = Button(text="Save image", command=save)
save_.pack()
reset=Button(text='Reset canvas',command=clear)
reset.pack(side=LEFT)
_exit=Button(text='Exit',command=exitt)
_exit.pack(side=RIGHT)
win.mainloop()
Paint.pyw - Drawing window

Update matplotlib plot with Gtk+ button event

I've encapsulated a matplotlib plot in a Gtk+ window and I'm trying to update that plot when a button is clicked (it's Gauss' circle problem). Trouble is, I'm not exactly sure how to get the plot to update with an event. So far I have the following.
#! /usr/bin/env python3.4
# -*- coding: utf-8 -*-
""" Main application--embed Matplotlib figure in window with UI """
import gi
gi.require_version('Gtk', '3.0')
import numpy as np
from gi.repository import Gtk, GObject
from matplotlib.figure import Figure
# make sure cairocffi is installed, pycairo doesn't support FigureCanvasGTK3Agg
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg \
as FigureCanvas
from matplotlib.patches import Ellipse
from typing import List, Tuple
from math import sqrt
class Main(Gtk.Window):
""" Main window UI """
SIGMA = 10
def __init__(self):
Gtk.Window.__init__(self, title='Gauss\' Circle Problem')
self.connect('destroy', lambda _: Gtk.main_quit())
self.set_border_width(10)
self.set_default_size(600, 450)
# Set up the l/r box layout
self.box = Gtk.Box(spacing=10)
self.add(self.box)
# Set up the right column
self.rcolumn = Gtk.Grid()
self.box.pack_end(self.rcolumn, False, False, 1)
# Set up spin button
adjustment = Gtk.Adjustment(10, 3, 100, 1, 0, 0)
self.spinbutton = Gtk.SpinButton()
self.spinbutton.set_adjustment(adjustment)
self.rcolumn.attach(self.spinbutton, 0, 0, 1, 1)
# Set up update button
self.update_plot_button = Gtk.Button(label='Update')
self.update_plot_button.connect('clicked', self.update_sigma_event)
self.rcolumn.attach_next_to(self.update_plot_button,
self.spinbutton, Gtk.PackDirection.BTT, 1, 1)
self._add_plot()
def update_sigma_event(self, button) -> None:
""" Update sigma and replot """
self.SIGMA = self.spinbutton.get_value()
self._add_plot()
def _add_plot(self) -> None:
""" Add the plot to the window """
fig = Figure(figsize=(5, 4))
ax = fig.add_subplot(111, aspect='equal')
arr = np.zeros([self.SIGMA * 2 + 1] * 2)
points = self.collect(int(self.SIGMA), int(self.SIGMA), self.SIGMA)
# flip pixel value if it lies inside (or on) the circle
for p in points:
arr[p] = 1
# plot ellipse on top of boxes to show their centroids lie inside
circ = Ellipse(\
xy=(int(self.SIGMA), int(self.SIGMA)),
width=2 * self.SIGMA,
height=2 * self.SIGMA,
angle=0.0
)
ax.add_artist(circ)
circ.set_clip_box(ax.bbox)
circ.set_alpha(0.2)
circ.set_facecolor((1, 1, 1))
ax.set_xlim(-0.5, 2 * self.SIGMA + 0.5)
ax.set_ylim(-0.5, 2 * self.SIGMA + 0.5)
# Plot the pixel centers
ax.scatter(*zip(*points), marker='.', color='white')
# now plot the array that's been created
ax.imshow(-arr, interpolation='none', cmap='gray')
# add it to the window
canvas = FigureCanvas(fig)
self.box.pack_start(canvas, True, True, 0)
#staticmethod
def collect(x: int, y: int, sigma: float =3.0) -> List[Tuple[int, int]]:
""" create a small collection of points in a neighborhood of some
point
"""
neighborhood = []
X = int(sigma)
for i in range(-X, X + 1):
Y = int(pow(sigma * sigma - i * i, 1/2))
for j in range(-Y, Y + 1):
neighborhood.append((x + i, y + j))
return neighborhood
if __name__ == '__main__':
window = Main()
window.show_all()
Gtk.main()
I'm not exactly sure where to go from here, I just know that updating the SpinButton indeed adjusts self.SIGMA, but I don't know how to tell matplotlib to update the plot in the window.
Also, this is what it looks like currently if you aren't able to run it (I'm also trying to vertically center the two button widgets in the right column :P):
This is a solution I've found to my problem:
#! /usr/bin/env python3.4
# -*- coding: utf-8 -*-
""" Main application--embed Matplotlib figure in window with UI """
import gi
gi.require_version('Gtk', '3.0')
import numpy as np
from gi.repository import Gtk, GObject
from matplotlib.figure import Figure
# make sure cairocffi is installed, pycairo doesn't support FigureCanvasGTK3Agg
from matplotlib.backends.backend_gtk3agg import FigureCanvasGTK3Agg \
as FigureCanvas
from matplotlib.patches import Ellipse
from typing import List, Tuple, Union
from math import sqrt
class Main(Gtk.Window):
""" Main window UI """
SIGMA = 10
INVERT = -1
def __init__(self) -> None:
Gtk.Window.__init__(self, title='Gauss\' Circle Problem')
self.connect('destroy', lambda _: Gtk.main_quit())
self.set_border_width(10)
self.set_default_size(650, 500)
# Set up the l/r box layout
self.box = Gtk.Box(spacing=10)
self.add(self.box)
# Set up the right column
self.rcolumn = Gtk.VBox(spacing=0)
self.rcolumn.set_spacing(10)
self.box.pack_end(self.rcolumn, False, False, 20)
# Set up spin button
adjustment = Gtk.Adjustment(self.SIGMA, 1, 30, 1, 0, 0)
self.spinbutton = Gtk.SpinButton()
self.spinbutton.set_adjustment(adjustment)
self.rcolumn.pack_start(self.spinbutton, False, False, 0)
# Set up invert checkbox
self.invertbutton = Gtk.CheckButton('Invert')
self.invertbutton.set_active(True)
self.invertbutton.connect('toggled', self.switch_toggle_parity, 'invert')
self.rcolumn.add(self.invertbutton)
# Set up update button
self.update_plot_button = Gtk.Button(label='Update')
self.update_plot_button.connect('clicked', self.update_sigma_event)
self.rcolumn.add(self.update_plot_button)
self.initial_plot()
def calculate(self) -> None:
""" Re-calculate using the formula """
arr = np.zeros([self.SIGMA * 2 + 1] * 2)
points = self.collect(int(self.SIGMA), int(self.SIGMA), self.SIGMA)
# flip pixel value if it lies inside (or on) the circle
for p in points:
arr[p] = 1
# plot ellipse on top of boxes to show their centroids lie inside
circ = Ellipse(
xy=(int(self.SIGMA), int(self.SIGMA)),
width=2 * self.SIGMA,
height=2 * self.SIGMA,
angle=0.0
)
self.ax.clear()
self.ax.add_artist(circ)
circ.set_clip_box(self.ax.bbox)
circ.set_alpha(0.2)
circ.set_facecolor((1, 1, 1))
self.ax.set_xlim(-0.5, 2 * self.SIGMA + 0.5)
self.ax.set_ylim(-0.5, 2 * self.SIGMA + 0.5)
# Plot the pixel centers
self.ax.scatter(*zip(*points), marker='.',
color='white' if self.INVERT == -1 else 'black')
# now plot the array that's been created
self.ax.imshow(self.INVERT * arr, interpolation='none', cmap='gray')
def initial_plot(self) -> None:
""" Set up the initial plot; only called once """
self.fig = Figure(figsize=(5, 4))
self.canvas = FigureCanvas(self.fig)
self.box.pack_start(self.canvas, True, True, 0)
self.ax = self.fig.add_subplot(111, aspect='equal')
self.calculate()
self.draw_plot()
def update_sigma_event(self, button: Union[Gtk.Button, None] =None) -> None:
""" Update sigma and trigger a replot """
self.SIGMA = int(self.spinbutton.get_value())
self.calculate()
self.draw_plot()
def switch_toggle_parity(self, button: Union[Gtk.CheckButton, None] =None,
name: str ='') -> None:
""" Switch the parity of the plot before update """
self.INVERT *= -1
def draw_plot(self) -> None:
""" Draw or update the current plot """
self.fig.canvas.draw()
#staticmethod
def collect(x: int, y: int, sigma: float =3.0) -> List[Tuple[int, int]]:
""" create a small collection of points in a neighborhood of some
point
"""
neighborhood = []
X = int(sigma)
for i in range(-X, X + 1):
Y = int(pow(sigma * sigma - i * i, 1/2))
for j in range(-Y, Y + 1):
neighborhood.append((x + i, y + j))
return neighborhood
if __name__ == '__main__':
window = Main()
window.show_all()
Gtk.main()
I've also added a button that swaps the parity of the binary image plot and re-structured the method calls.
It's a slow/simple start, but I suppose we all have to start somewhere! Comments and suggestions welcome.
Might be not entirely adequate to what you're doing, but there's a similarly simple yet faster algorithm for Gauss circle problem (with some Java source code and an ugly but handy illustration): https://stackoverflow.com/a/42373448/5298879
It's around 3.4x faster than counting points in one of the quarters, plus the center, plus the ones on the axis, that you're doing now, while taking just one more line of code.
You simply imagine an inscribed square and count only one-eighth of what's outside that square inside that circle.

Resources