Python plots graph into button instead of figure - python-3.x

I'm a total beginner to python (started some days ago).
This little program is supposed to draw sinus waves, and the buttons at the bottom should increase / decrease the frequency of the plotted sinus. But my program plots the sinus into the button, for whatever reason. Where is the mistake? I've tried so much already... Thanks in advance ❤
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
class Function:
def __init__(self, amplitude, frequency):
self.amplitude = amplitude
self.frequency = frequency
self.time = np.arange(0.0, 1.0, 0.001)
self.signal= amplitude*np.sin(2*np.pi*frequency*self.time)
def status(self):
print("The frequency is: ", self.frequency)
print("The amplitude is: ", self.amplitude)
def setFrequency(self, frequency):
self.frequency = frequency
def setAmplitue(self, amplitude):
self.amplitude = amplitude
def show(self):
plt.plot(self.time, self.signal, linewidth = 2)
plt.ylabel("Sinus")
plt.xlabel("x")
pass
func = Function(1, 20)
func.show()
def IncreaseFrequency(event):
global func
if (func.frequency < 100):
func.setFrequency(func.frequency + 10)
else:
func.setFrequency(10)
func.show()
plt.draw()
pass
def LowerFrequency(event):
global func
if (func.frequency > 10):
func.setFrequency(func.frequency - 10)
else:
func.setFrequency(100)
func.show()
plt.draw()
pass
buttonIncrSize = plt.axes([0.7, 0.01, 0.1, 0.05])
buttonLowerSize = plt.axes([0.81, 0.01, 0.1, 0.05])
buttonIncrFreq = Button(buttonIncrSize, 'Next')
buttonLowerFreq = Button(buttonLowerSize, 'Previous')
buttonIncrFreq.on_clicked(IncreaseFrequency)
buttonLowerFreq.on_clicked(LowerFrequency)
plt.show()

First what looks kind of weird is that after lowering or increasing your frequency, you never update the self.signal, which should change if your frequency changes.
Secondly, from what I could see on this link, plt.plot returns an Line2D object which you can use to set the y data on your plot using line2D.set_ydata(new_ydata).
After changing the y data, just update the plot using plt.draw() and that should work.
EDIT: Indeed I didn't have access to my PC but ran this now and didn't work. After some search:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import matplotlib
class Function:
def __init__(self, amplitude, frequency):
self.amplitude = amplitude
self.frequency = frequency
self.time = np.arange(0.0, 1.0, 0.001)
self.signal = amplitude * np.sin(2 * np.pi * frequency * self.time)
def status(self):
print("The frequency is: ", self.frequency)
print("The amplitude is: ", self.amplitude)
def setFrequency(self, frequency):
self.frequency = frequency
self.signal = self.amplitude * np.sin(2 * np.pi * frequency * self.time)
def setAmplitude(self, amplitude):
self.amplitude = amplitude
self.signal = self.amplitude * np.sin(2 * np.pi * frequency * self.time)
def show(self):
plt.cla()
plt.plot(self.time,self.signal,lw=2)
plt.draw()
plt.ylabel("Sinus")
plt.xlabel("x")
def IncreaseFrequency(event):
global func
if (func.frequency < 100):
func.setFrequency(func.frequency + 10)
else:
func.setFrequency(10)
func.show()
def LowerFrequency(event):
global func
if (func.frequency > 10):
func.setFrequency(func.frequency - 10)
else:
func.setFrequency(100)
func.show()
fig, ax = plt.subplots()
func = Function(1, 20)
func.show()
buttonIncrSize = plt.axes([0.7, 0.01, 0.1, 0.05])
buttonLowerSize = plt.axes([0.81, 0.01, 0.1, 0.05])
buttonIncrFreq = Button(buttonIncrSize, 'Next')
buttonLowerFreq = Button(buttonLowerSize, 'Previous')
buttonIncrFreq.on_clicked(IncreaseFrequency)
buttonLowerFreq.on_clicked(LowerFrequency)
plt.sca(fig.axes[0])
plt.show()
Main change being the plt.sca(fig.axes[0]) which allows to select the current axis for plt. When you run plt.axes() it sets the current axes of plt to the button, hence subsequently plotting your graph in that button.
Also added plt.cla() which clears the current plotting area.

Related

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()

Matplotlib: animation saved with trace

In the following Python code, I am trying to make an animation of a 2 rotating vectors around the origin. I am using matplotlib 3.2.1 and Python 3.8.2 on Ubuntu 20.04.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
r = 2.0
def circle(phi):
return np.array([r*np.cos(phi), r*np.sin(phi)])
fig, ax = plt.subplots(figsize=(10,6))
ax.axis([-3.5*r,3.5*r,-2.5*r,2.5*r])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# set equal aspect
ax.set_aspect("equal")
point, = ax.plot(0, r, marker="o")
traj = plt.Circle((0,0), r, fill=False, color='black')
ax.add_artist(traj) # draw the circular trajectory
def update(phi):
x, y = circle(phi)
point.set_data([x], [y])
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er = plt.text(1.7*x, 1.7*y, r'$\mathbf{e}_r$', fontsize=11)
annot_eθ = plt.text(1.1*(x-0.5*y), 1.1*(y+0.5*x), r'$\mathbf{e}_\theta$', fontsize=11)
ax.add_artist(er_arr)
ax.add_artist(eθ_arr)
ax.add_artist(annot_er)
ax.add_artist(annot_eθ)
return point, er_arr, eθ_arr, annot_er, annot_eθ
anim = FuncAnimation(fig, update, interval=10, blit=True, repeat=False, frames=np.linspace(0, 2.0*np.pi, 360, endpoint=False))
plt.show()
The code above runs smoothly and without any issues.
This is a screenshot of the animation:
However, when I try to save the animation to an mp4 video:
anim.save('anim-issue.mp4', writer='ffmpeg')
the animation in the video appears with traces which, something like this screenshot:
Could someone help me fix that issue with the video animation?
I appreciate your help.
Edit 1: According to this answer this is due to blit=True. But that doesn't solve the issue here, since the arrows have no set_position method.
Edit 2: I found another related question with the same issue I described above but I don't know how to adapt my code to make it work as expected in both cases (plt.show, anim.save).
Updating the position of an arrow would be quite tricky.
The easiest solution is indeed to create new arrows at each frame, but you have to make sure you remove the previous arrows first
full code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
r = 2.0
def circle(phi):
return np.array([r*np.cos(phi), r*np.sin(phi)])
fig, ax = plt.subplots(figsize=(10,6))
ax.axis([-3.5*r,3.5*r,-2.5*r,2.5*r])
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
# set equal aspect
ax.set_aspect("equal")
point, = ax.plot(0, r, marker="o")
traj = plt.Circle((0,0), r, fill=False, color='black')
ax.add_artist(traj) # draw the circular trajectory
er_arr = None
eθ_arr = None
annot_er = None
annot_eθ = None
def init():
global er_arr, eθ_arr, annot_er, annot_eθ
x,y = 0,0
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er = plt.text(1.7*x, 1.7*y, r'$\mathbf{e}_r$', fontsize=11)
annot_eθ = plt.text(1.1*(x-0.5*y), 1.1*(y+0.5*x), r'$\mathbf{e}_\theta$', fontsize=11)
return er_arr, eθ_arr, annot_er, annot_eθ
def update(phi):
global er_arr, eθ_arr, annot_er, annot_eθ
x, y = circle(phi)
point.set_data([x], [y])
er_vec = np.array([0.5*x, 0.5*y])
eθ_vec = np.array([-0.5*y, 0.5*x])
#remove previous artists
er_arr.remove()
eθ_arr.remove()
er_arr = plt.arrow(x, y, dx=er_vec[0], dy=er_vec[1], head_width=0.1, head_length=0.2, color='gray')
eθ_arr = plt.arrow(x, y, dx=eθ_vec[0], dy=eθ_vec[1], head_width=0.1, head_length=0.2, color='grey')
annot_er.set_position((1.7*x, 1.7*y))
annot_eθ.set_position((1.1*(x-0.5*y), 1.1*(y+0.5*x)))
return point, er_arr, eθ_arr, annot_er, annot_eθ
anim = FuncAnimation(fig, update, init_func=init, interval=10, blit=True, repeat=False, frames=np.linspace(0, 2.0*np.pi, 360, endpoint=False))
plt.show()

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.

Open excel file in Tkinter and plot graphs

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()

Draw line: retrieve the covered pixels

I want to draw a line on a widget:
from PyQt4 import QtGui, QtCore
class LineLabel(QtGui.QLabel):
def __init__(self,parent=None):
super(LineLabel,self).__init__(parent)
self.setMinimumSize(100,100)
self.setMaximumSize(100,100)
def paintEvent(self,e):
painter=QtGui.QPainter(self)
pen = QtGui.QPen()
pen.setWidth(5)
painter.setPen(pen)
painter.drawLine(10,10,90,90)
painter.end()
def test():
form = QtGui.QWidget()
label = LineLabel(form)
form.show()
return form
import sys
app = QtGui.QApplication(sys.argv)
window =test()
sys.exit(app.exec_())
What is the best way to get a list of the pixels that are covered by the line?
Update from the comments:
I don't need to know the pixels directly in between the start and end point but all those pixels that are changed to black (which are more pixels because the line has a certain width).
My overall goal is a fast way to know which pixels on the widget are black. Iterating over the pixels of the image and querying the color is much slower than reading the color value from a list in which the colors are stored: For me 1.9 seconds for an image with 1 million pixels to 0.23 seconds for a list with 1 million entries. Therefore I must update that list after every change of the image on the widget such as by drawing a line.
Answers that refer to a QGraphicsItem in a QGraphicsScene are also helpful.
You may use a linear equation to find the point you want in the line. I think that there is no reference to a draw line.
from PyQt4 import QtGui
from PyQt4.QtGui import QColor, QPaintEvent
m_nInitialX = 0.0
m_nInitialY = 0.0
# my line abstraction
class MyLine:
x1, y1, x2, y2 = .0, .0, .0, .0
width = .0
px, py = (.0, .0)
draw_point = False
def __init__(self, x1, y1, x2, y2, width):
self.x1, self.y1, self.x2, self.y2 = (x1, y1, x2, y2)
self.width = width
def is_in_line(self, x, y):
# mark a position in the line
m = (self.y2 - self.y1) / (self.x2 - self.x1)
print(m*(x-self.x1)-(y-self.y1))
if abs((m*(x-self.x1) - (y-self.y1))) <= self.width/2:
self.draw_point = True
return True
else:
return False
def add_red_point(self, x, y):
self.px, self.py = (x, y)
def draw(self, widget):
painter = QtGui.QPainter(widget)
pen = QtGui.QPen()
pen.setWidth(self.width)
painter.setPen(pen)
painter.drawLine(self.x1, self.y1, self.y2, self.y2)
if self.draw_point:
pen.setColor(QColor(255, 0, 0))
painter.setPen(pen)
painter.drawPoint(self.px, self.py)
painter.end()
line = MyLine(10, 10, 90, 90, width=10) # <-- my line abstraction
class LineLabel(QtGui.QLabel):
def __init__(self, parent=None):
super(LineLabel, self).__init__(parent)
self.setMinimumSize(100, 100)
self.setMaximumSize(100, 100)
# always redraw when needed
def paintEvent(self, e):
print("draw!")
line.draw(self)
def mousePressEvent(self, event):
# mark clicked position in line
m_nInitialX = event.pos().x()
m_nInitialY = event.pos().y()
if line.is_in_line(m_nInitialX, m_nInitialY):
line.add_red_point(m_nInitialX, m_nInitialY)
self.repaint()
def test():
form = QtGui.QWidget()
label = LineLabel(form)
form.show()
return form
import sys
app = QtGui.QApplication(sys.argv)
window = test()
sys.exit(app.exec_())

Resources