Improve speed of resizing plot (matplotlib) inside GTK-window - python-3.x

I have some heavy plot and it's ok that it calculating couple seconds, but when i resize window/chart - its hang up for some time and that is not obviously behavior for me. How i can fix this? There is no changes in plot data, just resizing.
Demo:
import numpy as np
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
import matplotlib
matplotlib.use('GTKCairo')
from matplotlib.figure import Figure
from matplotlib.backends.backend_gtk3cairo import FigureCanvasGTK3Cairo as FigureCanvas
class MyApp:
def __init__(self):
self.Window = Gtk.Window()
graphFig = Figure()
graphCanvas = FigureCanvas(graphFig)
self.Window.add(graphCanvas)
# just example of heavy chart
subplot = graphFig.add_subplot(111)
for n in range(100):
x = np.arange(0, 100, 0.01)
y = np.sin(x) * n
subplot.plot(x, y)
return
def Run(self):
self.Window.show_all()
Gtk.main()
return
App = MyApp()
App.Run()
What options exist of fixing this issue? I wanna redraw chart only when its updated or when user pan/zoom it.

To speed up plotting, consider plotting lower resolution lines. 100 lines at 1000 samples each is a lot of drawing.
Barring that, this post has a very detailed answer that could help you improve speed.

Related

python real time plot using funcanimation giving blank graph

I am trying to plot a realtime graph using matplotlib's FuncAnimation class. The data is coming from arduino using pyserial library. I have tried the following code to plot in real time but the graph is empty, I can see the axes, ticks etc but not the plot. Can anyone help me what I am doing wrong here?
Thank you in advance.
from matplotlib import pyplot as plt
from matplotlib.animation import FuncAnimation
import serial
import os
import numpy as np
import datetime
import pandas as pd
fig = plt.figure()
port= 'COM10'
baudrate = 9600
timeout = 2
ser = serial.Serial(port=port, baudrate=baudrate, timeout=timeout)
def animate(i):
j = 0
while True:
try:
loadcells = ser.readline()
loadcells = loadcells.decode('utf-8').rstrip().replace('\t','').split(',')
print(loadcells)
loadcell1 = float(loadcells[0])
# loadcell2 = loadcells[1]
plt.cla()
plt.plot(loadcell1)
plt.pause(0.001)
j+=1
# plt.plot(loadcell2)
except Exception as e:
print(e)
continue
except KeyboardInterrupt as e1:
print(e1)
ser.close()
anim = FuncAnimation(fig, animate,
frames = 200,
interval = 0.1,
blit = False)
plt.tight_layout()
plt.show()
You should not have an infinite loop in animate(). FuncAnimation works by calling animate() repeatedly at a pre-defined interval (interval=0.1).
You need to rewrite your animate() function where you read the serial port, plot the result, and return (no pause() either).

My Tkinter won't allow me to click the button that will show the sine wave

This code won't work and I have no idea why. I'm pretty new to programming and am using a Mac, so I don't know a whole lot. I read that it might be backends of matplotlib but all the other articles were kind of inconclusive. I would like to display the wave in the GUI and would like to be able to update it with new data.
I've tried installing and uninstalling matplotlib, downloading Quartz11
import tkinter as tk
from tkinter import Frame, Label, Entry, Button
import pyaudio
from matplotlib.figure import Figure
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import matplotlib. animation as animation
def insert_number():
f=int(entry1.get())
t=float(entry2.get())
samples=(np.sin((f*2*np.pi*np.arange(fs*t)/(fs)).astype(np.float32)))
stream=p.open(format=pyaudio.paFloat32,
channels=1,
rate=fs,
output=True)
stream.write(volume*samples)
fig, ax = plt.subplots()
def dopp():
x = np.arange(0, 2*np.pi, 0.01)
line, = ax.plot(x, np.sin((x)/10))
def animate(i):
line.set_ydata(np.sin(f(x + i/10.0)))
return line,
def init():
line.set_ydata(np.ma.array(x, mask=True))
return line,
ani = animation.FuncAnimation(fig, animate, np.arange(1, 200), init_func=init,
interval=25, blit=True)
plt.show()
canvas = FigureCanvasTkAgg(fig, master=self.window)
canvas.get_tk_widget().pack()
canvas.draw()
start= mclass (window)
window.mainloop()
tk.mainloop()
button2 = tk.Button (root, text='Click to see Waves ',command=insert_number)
canvas1.create_window(97, 270, window=button2)`
No error messages, but I know something is wrong. I appreciate any help. Thanks!
So I found a very similar question as to what I was asking, and I think I am supposed to define a 'PlotWindow' like a window that has the math and can just transfer the data to be displayed... If anyone could give me an idea of how that function works or kind of a general idea of what it would look like it would help a lot, I don't think I am very familiar with that. Thanks to all who replied to my original post.

multiprocessing show matplotlib plot

I'm trying to open up multiple plots but I ran into a few problems. When I tried to create plots using threading, python would first open a number of windows, then close all but the first.
In another question it was recommended that I use multiprocessing which I have tried. The code runs without error, it just doesn't show any plot.
I'm trying to get something very simple to work before moving on to my main project.
# Import the necessary packages and modules
import matplotlib.pyplot as plt
import numpy as np
#from threading import Thread
import multiprocessing
def plot(datax, datay, name):
# Prepare the data
x = datax
y = datay**2
# Plot the data
plt.scatter(x, y, label=name)
# Add a legend
plt.legend()
# Show the plot
plt.show()
#plot(3,3,)
'''
for i in range(10):
t = Thread(target=plot, args=(i,i+1,i,))
t.start()
'''
for i in range(2):
p = multiprocessing.Process(target=plot, args=(i, i, i))
p.start()
update:
for some reason, multiprocessing stopped working again. I tried creating a function multiP() only to open the processes but it didn't work when I added the input('value: '). scine I can't figure out how to send data to a specific thread I'm going to save data like this: dat = [[x,y0,y1,...yn],[x,y0,y1,...yn],...] and each plot process with check the if something was added to dat.
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing
#multiprocessing.freeze_support() # <- may be required on windows
def plot(datax, datay, name):
x = datax
y = datay**2
plt.scatter(x, y, label=name)
plt.legend()
plt.show()
def multiP():
if __name__ == "__main__":
for i in range(2):
p = multiprocessing.Process(target=plot, args=(i, i, i))
p.start()
if True:
#input('Vlaue: ') # while commented plots are shown
multiP()
The following code produces two figures as desired.
import matplotlib.pyplot as plt
import numpy as np
import multiprocessing
#multiprocessing.freeze_support() # <- may be required on windows
def plot(datax, datay, name):
x = datax
y = datay**2
plt.scatter(x, y, label=name)
plt.legend()
plt.show()
def multiP():
for i in range(2):
p = multiprocessing.Process(target=plot, args=(i, i, i))
p.start()
if __name__ == "__main__":
input('Value: ')
multiP()
Taking ImportanceOfBeingErnest answer, I leave below an implementation which only shows one window and waits for the window to close, which can be very handy. Every time it is called, it displays a new window with the corresponding image (a new pocess will be started for each image). I used a lot to view images when stopped at some breakpoint during debug.
# Library packages needed
import numpy as np
import datetime
import sys
import queue
import multiprocessing
# Plot related packages
import matplotlib.pyplot as plt
def showImage(img: np.ndarray, title: str = str(datetime.datetime.today())):
"""Show an image in a new process without blocking. Usefull for debugging.
Args:
img (np.ndarray): Image to be shown
title (str, optional): Title to be shown. Defaults to
str(datetime.datetime.today()).
"""
def plot(q, title):
fig = plt.figure()
fig.suptitle(title)
try:
q.get(True, 2.0) # Wait a couple of seconds
except queue.Empty:
print('Not image received to plot...quitting')
sys.exit()
plt.imshow(img)
plt.show()
sys.exit()
# Create a queue to share data between process
q = multiprocessing.Queue()
# Create and start the process
proc = multiprocessing.Process(None, plot, args=(q, title))
proc.start()
q.put(img)
To run it, just save this to a show_image.py file and call
from show_image.py import showImage
show_image(img, title)

Matplotlib.animation.FuncAnimation using pcolormesh

Python 3.5, windows 10 Pro.
I'm trying to continuously plot an 8x8 array of pixels (for the sake of the question I'll just use random data, but in the real thing I'm reading from a serial port).
I can do it using a while loop, but I need to switch over to matplotlib.animation.FuncAnimation and I can't get it to work. I've tried looking at the help files and tried to follow examples from matplotlib.org here, but I've not been able to follow it.
Can someone help me figure out how to continuously plot an 8x8 array of pixels using FuncAnimation and pcolormesh? Here is what I've got so far:
import scipy as sp
import matplotlib.pyplot as plt
from matplotlib import animation
plt.close('all')
y = sp.rand(64).reshape([8,8])
def do_something():
y = sp.rand(64).reshape([8,8])
fig_plot.set_data(y)
return fig_plot,
fig1 = plt.figure(1,facecolor = 'w')
plt.clf()
fig_plot = plt.pcolormesh(y)
fig_ani = animation.FuncAnimation(fig1,do_something)
plt.show()
If you want to see the while loop code, just so you know exactly what I'm trying to reproduce, see below.
import scipy as sp
import matplotlib.pyplot as plt
plt.figure(1)
plt.clf()
while True:
y = sp.rand(64).reshape([8,8])
plt.pcolormesh(y)
plt.show()
plt.pause(.000001)
I was able to find a solution using imshow instead of pcolormesh. In case anyone else is struggling with the same issues I had, I've posted the working code below.
import scipy as sp
import matplotlib.pyplot as plt
import matplotlib.animation as animation
Hz = sp.rand(64).reshape([8,8]) # initalize with random data
fig = plt.figure(1,facecolor='w')
ax = plt.axes()
im = ax.imshow(Hz)
im.set_data(sp.zeros(Hz.shape))
def update_data(n):
Hz = sp.rand(64).reshape([8,8]) # More random data
im.set_data(Hz)
return
ani = animation.FuncAnimation(fig, update_data, interval = 10, blit = False, repeat = False)
fig.show()

pyqtgraph : using AxisItem to plot time axis displaying extra axis

When I implemented a time axis class overriding AxisItem class it is displaying an extra axis on the top of the window. How can I make that axis disappear? You can see image of this issue on google group.
Or here is the simple code
from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
from PySide.QtCore import QTime
class TimeAxisItem(pg.AxisItem):
def tickStrings(self, values, scale, spacing):
return [QTime().addMSecs(value).toString('mm:ss') for value in values]
app = QtGui.QApplication([])
win = pg.GraphicsWindow()
axis = TimeAxisItem('bottom')
plot = win.addPlot(title = "sdf",axisItems={'bottom':axis})
curve = plot.plot()
QtGui.QApplication.instance().exec_()
I'd be very glad for your help.
Regards,
It's a bug!
This has been fixed in the latest develop branch on github; will appear in the next release.

Resources