Problem with LivePlots in Tkinter (Python) - python

I'm still quite new to Python, so please do not be too harsh with me.
I'm trying to implement a live plot in a GUI via Tkinter.
With the help of some example codes and google I got it working.
Now I want to erase/clear the Old Plot with click on a Button.
However I get a raise value error when trying to do so.
in the init I initialize one Plot like this:
self.x = 0
self.i = 0
self.delta_i = 1
self.xy_data = []
self.figure = pyplot.figure()
self.figure.set_size_inches((12,7), forward=True)
self.subplot = self.figure.add_subplot(221)
self.line,=self.subplot.plot([],[], color="blue")
self.line2, = self.subplot.plot([],[], color="red")
In the actual drawing function (which is executed over and over for a specific amount of time) I'm doing this:
self.Timer = Tdiff
self.FixTemp=TargetTemp
self.ActualTemp =Value1
self.xy_data += [[self.Timer,self.ActualTemp]]
self.xy2_data += [[self.Timer,self.FixTemp]]
self.subplot.lines.remove(self.line)
self.subplot.lines.remove(self.line2)
self.line, = self.subplot.plot(
[row[0] for row in self.xy_data[0::self.delta_i]],
[row[1] for row in self.xy_data[0::self.delta_i]],
color="blue")
self.line2, = self.subplot.plot(
[row[0] for row in self.xy2_data[0::self.delta_i]],
[row[1] for row in self.xy2_data[0::self.delta_i]],
color="red")
In the Button Function I have tried to do:
self.subplot.cla() # But that clears even the Subplot title and x/y labels. As I have in total 3
subplots it would not be very code effective to initialize them again, I think.
self.subplot.lines.remove(self.line) and self.subplot.lines.remove(self.line2) # But I'm getting a rase Value Error
I would be glad if someone could help out here.
Thank you

Related

How to I make a PyQtGraph scrolling graph clear the previous line within a loop

I wish to plot some data from an array with multiple columns, and would like each column to be a different line on the same scrolling graph. As there are many columns, I think it would make sense to plot them within a loop. I'd also like to plot a second scrolling graph with a single line.
I can get the single line graph to scroll correctly, but the graph containing the multiple lines over-plots from the updated array without clearing the previous lines.
How do I get the lines to clear within the for loop. I thought that setData, might do the clearing. Do I have to have a pg.QtGui.QApplication.processEvents() or something similar within the loop? I tried to add that call but had it no effect.
My code:
#Based on example from PyQtGraph documentation
import numpy as np
import pyqtgraph as pg
win = pg.GraphicsLayoutWidget(show=True)
win.setWindowTitle('pyqtgraph example: Scrolling Plots')
timer = pg.QtCore.QTimer()
plot_1 = win.addPlot()
plot_2 = win.addPlot()
data1 = np.random.normal(size=(300))
curve1 = plot_1.plot(data1)
data_2d = np.random.normal(size=(3,300))
def update_plot():
global data1, data_2d
data1[:-1] = data1[1:]
data1[-1] = np.random.normal()
curve1.setData(data1)
for idx, n in enumerate(data_2d):
n[:-1] = n[1:]
n[-1] = np.random.normal()
curve2 = plot_2.plot(n,pen=(idx))
curve2.setData(n)
#pg.QtGui.QApplication.processEvents() #Does nothing
timer = pg.QtCore.QTimer()
timer.timeout.connect(update_plot)
timer.start(50)
if __name__ == '__main__':
pg.exec()
You could clear the plot of all curves each time with .clear(), but that wouldn't be very performant. A better solution would be to keep all the curve objects around and call setData on them each time, like you're doing with the single-curve plot. E.g.
curves_2d = [plot_2.plot(pen=idx) for idx, n in enumerate(data_2d)]
# ... in update_plot
curves_2d[idx].setData(n)

"Button_Press_Event" Coordinates Being Called Before I Actually Click

I am plotting a fits image of a field of stars. I am then displaying circular apertures across the image on sufficiently bright stars. Next, I am trying to click on the star that I am interested in, and getting the brightness measurement out of the nearest circle. Finally, I want to use that brightness measurement for other calculations.
My problem is that the variable I declare to store the x,y coordinates of my click ("coords") seems to be getting called before I actually click, resulting in an empty array and errors.
A co-worker sent it to me many months ago, and once upon a time it worked flawlessly. But it seems that it's stopped working. It may be a result of updating some libraries/modules, but I can't be sure.
I have tried various combinations, including declaring "coords" in other function, in other locations, etc. I have tried changing the order of certain lines to perhaps get "coords" to be called later. I've also tried writing the code from the ground up, and have had many of the same errors. Because this specific code once worked, I attach it instead of my attempts.
To be honest, I don't fully understand the code, as I didn't write it. As a result, I don't understand what is being called when, or what any changes I've made actually do.
def plot_image(file, vmin=5, vmax=99, threshold=5, radius=25):
hdu=fits.open(file)
image = hdu[0].data
exptime = hdu[0].header['EXPTIME']
band = hdu[0].header['FILTERS']
airmass = hdu[0].header['AIRMASS']
readnoise = hdu[0].header['RN_01']
gain = hdu[0].header['GAIN_01']
obj = hdu[0].header['OBJECT']
sub = image[1500:2000,1500:2000]
bkg_sigma = mad_std(sub)
mean, median, std = sigma_clipped_stats(sub, sigma=3.0, maxiters=5)
daofind = photutils.DAOStarFinder(fwhm=2., threshold=threshold*bkg_sigma)
sources = daofind(sub - median)
positions = (sources['xcentroid'], sources['ycentroid'])
apertures = photutils.CircularAperture(positions, r=radius)
phot_table = photutils.aperture_photometry(sub - median, apertures)
pix = select(image, apertures)
print(pix)
if len(pix) == 2 or len(coords) == 2:
distance = np.sqrt((np.array(phot_table['xcenter'])-pix[0])**2 + (np.array(phot_table['ycenter'])-pix[1])**2)
star = np.argmin(dist)
counts = phot_table[star]['aperture_sum']
fluxfile = open('testfile.txt')
signal = (counts * gain) / exptime
err = np.sqrt(counts*gain + (readnoise**2*np.pi*radius**2))
else:
print('Pix length = 0')
def select(image, apertures, vmin = 5, vmax = 99):
global coords
coords = []
fig = plt.figure(figsize = (9,9))
ax = fig.add_subplot(111)
ax.imshow(image, cmap = 'gist_gray_r', origin='lower', vmin = np.percentile(image, vmin), vmax = np.percentile(image, vmax), interpolation='none')
apertures.plot(color='blue', lw=1.5, alpha=0.5, ax = ax)
ax.set_title('Hello')#label='Object: '+obj+'\nFilter: '+band)
cid = fig.canvas.mpl_connect('button_press_event', onclick)
plt.show()
fig.canvas.mpl_disconnect(cid)
if None in coords:
return [np.nan,np.nan]
else:
return np.round(coords)
def onclick(event):
x = event.xdata
y = event.ydata
global coords
coords = [x, y]
plt.close()
return
def closeonclick(event):
print('Close On Click')
plt.close()
return
Expected Result: The image is displayed with blue apertures overlaid. Then, I click on the desired star and the coordinates I click are stored to "coords" and printed to the console. The window displaying the image is closed alongside the previous step, as well. Finally, using those coordinates, it finds the nearest aperture and does some science with the resulting brightness.
Actual Result: "coords" is immediately printed (an empty list). Immediately after, the image is displayed. Clicking it does nothing. It doesn't change the value of "coords", nothing else is printed, nor does the window close.
I'll come back and delete this if it isn't right (I'd comment if I had the reputation), but it looks like you have to define a global variable outside any functions first, then use the keyword before the variable name inside a function to change its scope. Try moving "coords = []" outside your functions (the list will no longer be empty after the first call to "onclick", but each new click should replace the coordinates so it shouldn't be an issue).
Ref: https://www.programiz.com/python-programming/global-keyword

clear datapoints from bokeh plot

I'm still somewhat new to Bokeh, and I've run into a problem I haven't been able to solve.
I have a Bokeh plot visualizing some streaming data in two separate figures. For various reasons the users of the plot may want to clear the two plots of the current datapoints upon clicking a button.
What would be the good way to clear the figures? I am yet to come upon a good solution.
My code looks something like:
#Defining plots
plot_data = ColumnDataSource(dict(x=[],y=[],z=[]))
p = figure(plot_height = 600, plot_width = 800,
x_axis_label = 'X',
y_axis_label = 'Y')
p2 = figure(plot_height = 600, plot_width = 800,
x_axis_label = 'X',
y_axis_label = 'Z')
doc = curdoc()
The data source is getting updated in an async loop:
async def loop():
while True:
data = await socket.recv_pyobj()
new_data = get_last_data(data)
#update ColumnDataSource
doc.add_next_tick_callback(partial(update,new_data))
doc.add_root(column(gridplot([p,p2], plot_width=1000)))
try:
testloop = IOLoop.current()
testloop.spawn_callback(loop)
except KeyboardInterrupt:
testloop.close()
and the ColumnDataSource is getting updated through the following function when new datapoints appear in the stream (parsed as a dataframe)
def update(new_data):
input_data = dict(x=new_data['x'], y=new_data['y'], z=new_data['z'])
plot_data.stream(input_data, rollover=500)
My initial idea for clearing the figures through a button click is the following:
#Defining button for clearing plot
button = Button(label="CLEAR PLOT", button_type="danger")
def clear_plot(event):
plot_data = ColumnDataSource(dict(x=[],y=[],z=[]))
button.on_event(ButtonClick,clear_plot)
This is not working, and if I understand the stream method correctly, that is at the heart of the problem, as new data is continuously getting appended to the source and the above clear_plot function will not really clear the stream data source. How would one go about clearing the stream data source such that the figures are cleared?
By assigning a new value to plot_data, you're just changing the variable itself. Anything that got the reference to the previous value of plot_data will still have that old reference.
Instead, try changing the data attribute of the data source:
def clear_plot(event):
plot_data.data = {k: [] for k in plot_data.data}

pan buttons + jump to location in matplotlib

I am creating a PyQt applicaton with a matplotlib figure containing 4 subplots (screenshot of the figure):
I would like to create 2 buttons "pan left" and "pan right", which will pan the 4 subplots at a selected rate on the x axis.
I would also like to include a button in the GUI - "jump to location", which will read a value from a textbox in the GUI and jump to that location on the x axis - on all 4 subplots.
I haven't found a way to manipulate the existing pan feature in these ways.
Any suggestions would be appreciated. Thanks.
OK,
I ended up using a much simpler solution than I thought I would.
attached here is the code to my solution:
def jump_to_loc(self):
global dataArrays
for j in range(len(dataArrays)):
ax = self.figure.axes[j]
yBoundary = self.figure.yBoundaries[j]
ax.axis([self.jmpToLocSpinner.value(), self.jmpToLocSpinner.value()+100, yBoundary[0], yBoundary[1]])
self.figure.draw()
def pan(self, dir):
global dataArrays
dir_val = 0
if dir == 'left':
dir_val = 10
else:
dir_val = -10
for j in range(len(dataArrays)):
ax = self.figure.axes[j]
yBoundary = self.figure.yBoundaries[j]
x_axis = ax.get_xlim()
ax.axis([x_axis[0] + dir_val, x_axis[1] + dir_val, yBoundary[0], yBoundary[1]])
self.figure.draw()
def pan_left(self):
self.pan('right')
def pan_right(self):
self.pan('left')
I am basically utilizing the 'axis' function in the axes component, in order to set the x axis limits.

Creating the proper axis labels and understanding them

I had to create a contour graph (in python) based on a formula and several other parameters. My graph came out fine. However, my axis labels will not show. I have tried changing the code several times but I am actually a little lost as how to what my real problem is. I know it deals with the command to create the labels but understand the error message
Also, this is my first post and if you have recommendations for how I should ask questions, I would appreciate the help.
def contourf_plot():
T = np.linspace(0,30,50)
P = np.linspace(600,1000,50)
X, Y = np.meshgrid(T,P)
Z = (Y/100)*np.e**((12*X)/(X+243))
Z.shape
plt.figure()
CF = plt.contourf(T,P,Z,50)
plt.colorbar(CF)
plt.set_Tlabel("Temperature[$\degree$C]")
plt.set_Plabel("Pressure[Pa]")
plt.show()
return
if __name__ == "__main__":
contourf_plot()
Error message: 'module' object has no attribute 'set_Xlabel'
All you need to do is a slight change in your code. You are currently trying to add a label to the axes T and P, though they do not exist (it is still the x and y axes). T and P are just the data that you are trying to plot.
def contourf_plot():
T = np.linspace(0,30,50)
P = np.linspace(600,1000,50)
X, Y = np.meshgrid(T,P)
Z = (Y/100)*np.e**((12*X)/(X+243))
Z.shape
fig,ax = plt.subplots() #add this line
CF = plt.contourf(T,P,Z,50)
plt.colorbar(CF)
ax.set_xlabel("Temperature[$\degree$C]") #sets the x and y label
ax.set_ylabel("Pressure[Pa]")
plt.show()
return
if __name__ == "__main__":
contourf_plot()
This gives the image

Categories

Resources