PyVisa and Printing New Data - python

I am trying to use Pyvisa to capture data from one of my channels of my Keithly 2701 DMM.
I get a static onetime response via temp = keithly.ask('SCPI COmmand'), but what I want to do is constantly print new data without setting any predefined size, i.e capture 300 data points.
I want to determine when to stop the capture if I have seen a trend over 10000 data points, or, in another experiment, I might see a trend after 2500 data points.
from pylab import *
from visa import instrument
inst = SerialInstument(args)
while new data:
print inst.aks('channel')

while True:
print inst.ask('channel')
time.sleep(1)
You can then ctrl-c to stop the loop when you see fit.
The above script is simple - it just puts numbers on the screen until you kill it. I find it useful to plot the data from PyVISA in real time, using matplotlib. I found this to be buggy in pyplot mode (I got lots of blank screens when I turned off interactive mode, ymmv) so I embedded it into a tkinter window, as follows:
import matplotlib
matplotlib.use('TkAgg') # this has to go before the other imports
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
import Tkinter as Tk
import visa
# set up a PyVISA instrument and a list for the data
data = []
keithley = visa.instrument('GPIB0::whatever')
# make a Tkinter window
root = Tk.Tk()
# add a matplotlib figure to the Tk window
fig = Figure()
ax = fig.add_subplot(111)
canv = FigureCanvasTkAgg(fig, master=root)
canv.show()
canv.get_tk_widget().pack(fill='both', expand=True)
# a function that is called periodically by the event loop
def plot_update():
# add a new number to the data
data.append(keithley.ask('SCPI:COMM:AND?'))
# replot the data in the Tk window
ax.clear()
ax.plot(data)
fig.tight_layout()
canv.draw()
# wait a second before the next plot
root.after(1000, plot_update)
root.after(1000, plot_update)
root.mainloop()
It may not seem like much, but we gradually developed a short script like this into a rather capable instrument control program ;)

Related

Graph papers black in python GUI

I try to make a line graph in tkinter using the data in the database but then the graph appears as black only in the GUI.
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
###------Show Information Using Graph-------###
graquery = '''SELECT Date, Amount FROM balance'''
CURSOR.execute(graquery)
graresults = CURSOR.fetchall()
Date = [result[0] for result in graresults]
Amount = [result[1] for result in graresults]
figure = plt.figure()
plt.plot(Date, Amount)
plt.xlabel('Date')
plt.ylabel('Amount')
plt.title('Balance graph Graph')
gracanvas = Canvas(main_WINDOW, width=1070, height=452)
gracanvas.pack()
gracanvas.place(x=356, y=270)
figure_canvas = FigureCanvasTkAgg(figure, canvas)
figure_canvas.draw()
change:
figure_canvas = FigureCanvasTkAgg(figure, canvas)
to the following:
figure_canvas = FigureCanvasTkAgg(figure, gracanvas)
Also change this:
figure_canvas.draw()
to this:
gracanvas.create_window(0, 0, window=figure_canvas.get_tk_widget())
It appears that you are attempting to show a graph created using matplotlib within a tkinter interface. The cause of the black graph could be due to a few factors.
It is necessary to confirm that the appropriate backend is being utilized to display the graph in tkinter. In the code provided, you have imported the correct backend, FigureCanvasTkAgg, which is used for displaying matplotlib graphs in tkinter.
It is important to ensure that the figure_canvas object is placed in the correct tkinter widget. In the code provided, you have created a Canvas widget and are attempting to display the figure_canvas object within it, but it seems that you are using the variable canvas instead of gracanvas. It is suggested to use gracanvas instead of canvas.
Lastly, in order to show the graph in your GUI, you should call the get_tk_widget() method on the figure_canvas object to obtain the tkinter widget that can be displayed.

Closing tkinter windows doesn't kill the python program when using matplotlib to plot graph

I am currently working on a GUI using python tkinter and matplotlib. However, after adding the matplotlib features to my GUI, I couldn't kill the program by closing all the windows anymore. I was able to do that previously when I experimented with some simple matplotlib plots. I would really appreciate if anyone could help me by giving some suggestions to try. Thank you! Here is the code that is relevant to my problem:
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.backend_bases import MouseEvent, key_press_handler
import sys
sys.path.append('/Users/YangQing/Desktop/eMPRess')
import empress
class App:
def plot_cost_landscape(self):
"""Plots the cost landscape using matplotlib and embeds the graph in a tkinter window."""
# creates a new tkinter window
plt_window = tk.Toplevel(self.master)
plt_window.geometry("550x550")
plt_window.title("Matplotlib Graph DEMO")
# creates a new frame
plt_frame = tk.Frame(plt_window)
plt_frame.pack(fill=tk.BOTH, expand=1)
plt_frame.pack_propagate(False)
recon_input = empress.read_input("./examples/heliconius.newick")
cost_region = empress.compute_cost_region(recon_input, 0.5, 10, 0.5, 10) # create
cost_region.draw_to_file('./examples/cost_poly.png') # draw to a file
fig = cost_region.draw() # draw to figure (creates matplotlib figure)
canvas = FigureCanvasTkAgg(fig, plt_frame)
canvas.draw()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
toolbar = NavigationToolbar2Tk(canvas, plt_frame)
toolbar.update()
canvas.get_tk_widget().pack(side=tk.TOP)
# prints the x,y coordinates clicked by the user inside the graph otherwise prints an error message
fig.canvas.callbacks.connect('button_press_event', self.get_xy_coordinates)
Try adding os._exit(0) at the end of the script!! For this you also need to import the os module into your script.os._exit() method in Python is used to exit the process with specified status.

Slow FuncAnimation Script

I use python 3.6 (the newest one) with the spider interpreter on a windows 10 PC with more-than-average hardware and wrote a script that enables me to continuously measure and plot the frequencies of two channels on an Agilent frequency counter and saves the data to a txt file. I furthermore have to convert the script to a .exe file (using pyinstaller) in order to distribute it to several measuring PCs.
Everything works fine, even from the .exe file, until the measuring time reaches about 2000 seconds. The software then starts to become very slow until it even show the "window not answering" thing that windows does while plotting.
I tried to activate the bliting-function of FuncAnimate, but when I do so, it only shows a white window.
Therefore I am now looking for options to enhance the speed of my software, especially at high data amounts, without cutting of the lots and lots of data (they are needed to be seen from t=0 to t=whatever).
Why does the blit=True kill my animation? Or is there a better way to plot these amounts of data fast and over and over again?
My code:
#Importing all required additional Python packages and sub-packages/functions
import visa, time, tkinter,sys
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas
from tkinter import messagebox, filedialog
from matplotlib.figure import Figure
import matplotlib.animation as animation
#Initialize the tkinter main window (graphical user interface window)
root=tkinter.Tk()
root.geometry('1200x900')
#Initialize the VISA resource manager and define a list object
rm=visa.ResourceManager('C:\\Windows\\System32\\visa32.dll')
liste=[]
#lists all available VISA resources and adds them to the list object
#one entry per VISA instrument
string=str(rm.list_resources())[1:-1]
liste.append(string.split('\'')[1:-1])
#opens a message box for each object in the list (each instrument)
#if user chosses "yes" for a VISA resource, the software tries to access the device
#if the device is present, the for-loop is left, otherwise the whole software quits
for i in liste[0]:
box=messagebox.askyesno('VISA Resources','Is this the correct VISA-Resource?'+'\n'+str(i))
if box==True:
try: inst=rm.open_resource(i)
except:
messagebox.showerror('Wrong Resource','The VISA resource was wrong!')
root.destroy()
sys.exit()
break
elif box==False: continue
#checks if the VISA resource is actually existent and present
try: inst
except:
messagebox.showerror('No Resource found','No VISA Resource was chosen.')
root.destroy()
sys.exit()
#opens a file dialog window and safes the chosen location as "filename"
#furthermore checks if the user specified a valid path or quited by using "cancel"
#if the user clicked "cancel", the software quits
filename=filedialog.asksaveasfilename()
if len(filename)==0:
messagebox.showerror('No File','No file location was specified.')
root.destroy()
sys.exit()
#definition of variables as well as the update function
x_data,y_data1, y_data2, y_data3=[],[],[],[]
def update(frame):
#create X-data, seconds since .clock was first called
x_data.append(time.clock())
#read out current freq on channel1 and float it (originaly returned as string)
value1=float(inst.query('MEAS:FREQ? 10 MHz, 0.1 Hz, (#1)'))
#add data to list
y_data1.append(value1)
#define and automatically adjust subplot limits
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
#define subplot title and labels
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
#same as above for second channel
value2=float(inst.query('MEAS:FREQ? 10 MHz, 0.1 Hz, (#2)'))
y_data2.append(value2)
subplot2.set_ylim(min(y_data2)-1,max(y_data2)+1)
subplot2.set_xlim(min(x_data)-1,max(x_data)+1)
subplot2.set_title('Channel 2')
subplot2.set_xlabel('Time')
subplot2.set_ylabel('Frequency')
#calculates and plots the difference of the upper two channels
y_data3.append(value1-value2)
subplot3.set_ylim(min(y_data3)-1,max(y_data3)+1)
subplot3.set_xlim(min(x_data)-1,max(x_data)+1)
subplot3.set_title('Difference')
subplot3.set_xlabel('Time')
subplot3.set_ylabel('Frequency')
#plots the subplots in the main plot frame
subplot1.plot(x_data,y_data1,'b')
subplot2.plot(x_data, y_data2,'r')
subplot3.plot(x_data, y_data3,'g')
#writes all data do a new file defined before
newfile.write(str(time.clock())+', '+str(value1)+', ' +str(value2)+'\n')
#enables the code to make use of the defined variables/data
return x_data, y_data1, y_data2, y_data3
#create a global boolean variable and set it to "True"
global boolean
boolean=True
#define a Pause function using the global boolean variable
def Pause():
global boolean
#if the boolean is True, the animation stops and the variable is set to False
if boolean==True:
anim.event_source.stop()
boolean=False
#if the boolean is False, the animation continues and the variable is set to True
elif boolean==False:
anim.event_source.start()
boolean=True
#define a Quit function that quits the application and closes the created file
def Quit():
newfile.close()
root.destroy()
#define a function that applies the user input data aquisition time to the animation
def SpeedApply():
anim.event_source.interval=int(float(Interv.get())*1000)
#create and place different buttons that call the defined functions upon buttonclick
QuitBut=tkinter.Button(text='Quit', command=Quit)
QuitBut.place(x=15,y=15)
StartBut=tkinter.Button(text='Pause/Resume',command=Pause)
StartBut.place(x=55, y=15)
Interv=tkinter.Spinbox(root,values=(0.1,0.2,0.5,1,1.5,2), width=8)
Interv.place(x=160, y=17)
Interv.delete(0,'end')
Interv.insert(0,1)
Speedbut=tkinter.Button(text='Apply Speed', command=SpeedApply)
Speedbut.place(x=250, y=15)
#create the figure needed to plot the animated data
figure=Figure(figsize=(8,8), dpi=100)
subplot1=figure.add_subplot(311)
subplot2=figure.add_subplot(312)
subplot3=figure.add_subplot(313)
figure.subplots_adjust(hspace=0.6)
#create a tkinter canvas, needed to embedd the figure into a tkinter root window
canvas=FigureCanvas(figure,root)
canvas.draw()
#canvas.start_event_loop(0.001)
canvas.get_tk_widget().place(x=25,y=50, height=850, width=1150)
#create the newfile where the data will be stored lateron
newfile=open(filename+time.strftime('%d')+time.strftime('%m')+time.strftime('%y')+'.txt','w')
newfile.write('Time, Channel 1, Channel 2\n')
#animation calling the update function upon the figure in the canvas with an interval of 1 second
anim=animation.FuncAnimation(figure,update, blit=False, interval=1000)
#tkinter mainloop, needed to react to user input in tkinter GUI
root.mainloop()
Okay, as requested, a simpler script showing my problem:
import time, tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.animation as animation
#Initialize the tkinter main window (graphical user interface window)
root=tkinter.Tk()
root.geometry('1200x900')
value1=1
value2=2
#definition of variables as well as the update function
x_data,y_data1, y_data2, y_data3=[],[],[],[]
def update(frame):
#create X-data, seconds since .clock was first called
x_data.append(time.clock())
global value1, value2, value3
value1=value1+2
#add data to list
y_data1.append(value1)
#define and automatically adjust subplot limits
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
#define subplot title and labels
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
#same as above for second channel
value2=value2+1
y_data2.append(value2)
subplot2.set_ylim(min(y_data2)-1,max(y_data2)+1)
subplot2.set_xlim(min(x_data)-1,max(x_data)+1)
subplot2.set_title('Channel 2')
subplot2.set_xlabel('Time')
subplot2.set_ylabel('Frequency')
#calculates and plots the difference of the upper two channels
y_data3.append(value1-value2)
subplot3.set_ylim(min(y_data3)-1,max(y_data3)+1)
subplot3.set_xlim(min(x_data)-1,max(x_data)+1)
subplot3.set_title('Difference')
subplot3.set_xlabel('Time')
subplot3.set_ylabel('Frequency')
#plots the subplots in the main plot frame
subplot1.plot(x_data,y_data1,'b')
subplot2.plot(x_data, y_data2,'r')
subplot3.plot(x_data, y_data3,'g')
#enables the code to make use of the defined variables/data
return x_data, y_data1, y_data2, y_data3
#create a global boolean variable and set it to "True"
global boolean
boolean=True
#define a Quit function that quits the application and closes the created file
def Quit():
root.destroy()
#create and place different buttons that call the defined functions upon buttonclick
QuitBut=tkinter.Button(text='Quit', command=Quit)
QuitBut.place(x=15,y=15)
#create the figure needed to plot the animated data
figure=Figure(figsize=(8,8), dpi=100)
subplot1=figure.add_subplot(311)
subplot2=figure.add_subplot(312)
subplot3=figure.add_subplot(313)
figure.subplots_adjust(hspace=0.6)
#create a tkinter canvas, needed to embedd the figure into a tkinter root window
canvas=FigureCanvas(figure,root)
canvas.draw()
#canvas.start_event_loop(0.001)
canvas.get_tk_widget().place(x=25,y=50, height=850, width=1150)
#animation calling the update function upon the figure in the canvas with an interval of 1 second
anim=animation.FuncAnimation(figure,update, blit=False, interval=100)
#tkinter mainloop, needed to react to user input in tkinter GUI
root.mainloop()
I left the "beautyful-makings" like titles in the code.
To actually see the problem I am having you would have to run that script for at least 600 seconds, the problem becomes "stronger" after 2000 seconds.
It seems that a large part of your issues is having to recompute the entire lists min and max each time,
e.g. your subplot x and y min/max values, rather than processing your entire appended list, you should use a variable to hold the lowest and highest values so far compare the current piece of input data to them, and if it meets condition update them,
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
You also appear to be resetting up the subplot labels every single update, now I am not as familiar with GUI's in TKinter, but i feel this could be covered in an initilisation definition,
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
The first suggestion should keep your program running in a more consistent time, rather than incrementally slowing down, the other should be able to reduce how much your doing per update.

Plot not updaing correctly using draw() (duplicated axes) in wxPython

I am new to Python and wxPython I am trying to simulate particles in a box with random velocities in random directions.
I created a simple GUI in wxFormBuilder where I have a panel to show a plot of the paricles. The particles are set to a position and plotted onto the panel, then I start the simulation and update the x and y positons of the particles. When redrawing the positions The axes appear 'thicker' as before, it looks like if there are several axes ontop of eachother.
I cant find anything about this problem, I hope somebody could help me with this?
This is the code that creates the Plot:
import wx
import particles
import random
import matplotlib
matplotlib.use('WXAgg')
from matplotlib.figure import Figure
from matplotlib.backends.backend_wxagg import \
FigureCanvasWxAgg as FigCanvas, \
NavigationToolbar2WxAgg as NavigationToolbar
matplotlib.rcParams.update({'font.size': 8})
class MyFrameSub( particles.GUI_MainFrame ):
def __init__( self, parent ):
particles.GUI_MainFrame.__init__( self, parent )
def InitData(self):
self.npart = int(self.m_npart.GetValue())
self.nsteps = int(self.m_steps.GetValue())
self.ndt = float(self.m_dt.GetValue())
self.x= [random.random() for I in range(self.npart)]
self.y= [2*random.random()-1 for I in range(self.npart)]
self.vx= [self.ndt*(2*random.random()-1) for I in range(self.npart)]
self.vy= [self.ndt*(2*random.random()-1) for I in range(self.npart)]
return
def CreatePlot(self):
panelsize = self.m_PlotPanel.GetClientSize()
self.figure = Figure(figsize=(panelsize[0]/100.0,panelsize[1]/100.0), dpi=100, frameon=False)
self.canvas = FigCanvas(self.m_PlotPanel, wx.ID_ANY, self.figure)
self.axes = self.figure.add_subplot(111)
self.axes.axis((-1,1,-1,1))
self.partplot, = self.axes.plot(self.x, self.y, 'ro')
self.canvas.draw()
return
def UpdateData(self):
for i in range(self.nsteps):
for j in range(self.npart):
self.x[j]=self.x[j]+self.vx[j]
self.y[j]=self.y[j]+self.vy[j]
if abs(self.x[j])>1:
self.vx[j]=-self.vx[j]
if abs(self.y[j])>1:
self.vy[j]=-self.vy[j]
self.partplot.set_xdata(self.x)
self.partplot.set_ydata(self.y)
self.canvas.draw()
return
followed by the button definitions, it looks like this:
Before running the simulation: www.merlinvs.de/before.jpg
and after running the simulation: www.merlinvs.de/after.jpg
As you see the axes got ugly and I have no idea why.
Another question I was thinking about is the following:
When I run a loop that takes a while the UI is unresponsive, is it possible to have the UI active to cancel a loop if desired?
As for the unresponsive UI, I used greenlets for my Matplotlib stuff while updating it
from gevent.greenlet import Greenlet
from gevent import sleep
import matplotlib.plot as plt
# Plot stuff
def event_handler():
# Can update plot
sleep(1) # Simulate handling or number crunching (numpy WILL block)
g = Greenlet(event_handler)
g.start()
plt.plot(0,0) # Works immediately and updates
Some things of note is that for serious applications you need to add some protection against race coditions with the plot. Numpy and external science libraries typically cause the entire application to become unresponsive (in my experience) because they are blocking system calls outside of the greenlet context switcher's reach. For something simple though the above pattern works well.

Creating a matplotlib interactive plotting window for an existing figure

I am writing a program that fits curves to large sets of xy coordinate data. It is often helpful to watch the progress of the algorithm by plotting and displaying each iteration as the fitting progresses. I'm using matplotlib for plotting.
What I'd like to do is create the figure in the main thread, then pass it into a child thread that displays it. That way I have access to all the figure's methods and attributes in the main thread. I can plot by calling fig.gca().plot() and draw by calling fig.canvas.draw().
I can't figure out how to create an interactive plotting window that shows only the figure I pass to it. Right now I'm using matplotlib.pyplot.show(), which does display my figure, but it also displays any other figures that may have been defined in the program. Is there an object oriented way to create an interactive window for a specific figure? I am looking for a solution that does not rely on unsupported interfaces in matplotlib.
Here is a post that's similar, but it still doesn't answer my question: Interactive figure with OO Matplotlib
I've never understood why matplotlib always seems to use current objects (current figure, current axes, etc.) rather than specific objects (for example, why not have matplotlib.pyplot.show(fig) rather than just show()?) I think I'm missing something. If anyone could shed some light on why matplotlib is designed this way, or how I'm misunderstanding and/or misusing it, that would also be appreciated.
Here's my code:
import matplotlib.pyplot
import threading
import time
class Plotter():
def __init__(self,fig):
t = threading.Thread(target=self.PlottingThread,args=(fig,))
t.start()
def PlottingThread(self,fig):
#This line shows fig1 AND fig2 from below. I want it to show fig ONLY.
matplotlib.pyplot.show()
if __name__ == "__main__":
fig1 = matplotlib.pyplot.figure()
fig2 = matplotlib.pyplot.figure()
Plotter(fig1)
fig1.gca().clear()
fig1.gca().plot([1,2,3])
fig1.canvas.draw()
I think I got it:
import Tkinter
import threading
import matplotlib.backends.backend_tkagg
root = Tkinter.Tk()
class Plotter():
def __init__(self,fig):
t = threading.Thread(target=self.PlottingThread,args=(fig,))
t.start()
def PlottingThread(self,fig):
canvas = matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(fig, master=root)
canvas.show()
canvas.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
toolbar = matplotlib.backends.backend_tkagg.NavigationToolbar2TkAgg(canvas, root)
toolbar.update()
canvas._tkcanvas.pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)
Tkinter.mainloop()
if __name__ == "__main__":
import time
fig1 = matplotlib.figure.Figure(figsize=(5,4), dpi=100)
fig1.gca().plot([1,2,3])
fig2 = matplotlib.figure.Figure(figsize=(5,4), dpi=100)
fig2.gca().plot([3,2,1])
#Shows fig1 and not fig2, just like it's supposed to
Plotter(fig1)
time.sleep(1)
#I can still plot to fig1 from my main thread
fig1.gca().clear()
fig1.gca().plot([5,2,7])
fig1.canvas.draw()
The only thing is if you try to create two instances of Plotter the whole thing crashes. That isn't too important for my application, but it probably means I'm using Tkinter wrong. Suggestions/corrections are welcome.

Categories