I'm using PyQt5 to create a GUI and in this GUI I visualize Bokeh graphs using QWebEngineView.
It works fine but when I tried to implement the "muting" legend like this I get an error:
js: Uncaught TypeError: Cannot read property 'pageX' of undefined
If I use the show method, I get the expecting result in my browser. However if I use save and display it to the QWebEngineView I get the mentioned error.
Any ideas?
The slot in my Gui Class to plot and show in the QWebEngineView:
Notes: Ignore the Bar and Pizza plots, it is the scatter and line that is relevant to this matter
def plotGraph(self, df=None):
# Get parameters to plot
x = str(self.ui.comboBox_x_axis.currentText())
y = str(self.ui.comboBox_y_axis.currentText())
# Define axis types
try:
x_axis_type = str(
self.ui.comboBox_plot_scale.currentText()).split('-')[0]
y_axis_type = str(
self.ui.comboBox_plot_scale.currentText()).split('-')[1]
except:
x_axis_type = 'auto'
y_axis_type = 'auto'
# Define kind of graph
kind = str(self.ui.comboBox_plot_style.currentText())
# For bar chart define groups
group = str(self.ui.comboBox_group.currentText())
# Prepare data for plot
if (kind == 'bar' and group != "Don't group"):
data = df[[x, y, group]]
else:
data = df[[x, y]]
data = data.sort_values(x, axis=0)
# Dynamically define plot size
width = round(self.ui.webViewer.frameGeometry().width())
height = round(self.ui.webViewer.frameGeometry().height())
# Plot and save html
self.plot = self.graph.plot(
data, kind, x_axis_type, y_axis_type, width, height)
self.plot_num = 1
# Display it at QWebEngineView
self.ui.webViewer.setUrl(QtCore.QUrl(
"file:///C:/Users/eandrade_brp/Documents/git/tl-data-viewer/plot.html"))
Here is the Graph class that handles all the bokeh plots (I omitted some non necessary code)
class Graph(object):
"""docstring for ClassName"""
def __init__(self, file_name="plot.html"):
super(Graph, self).__init__()
output_file(file_name)
def plot(self, data, kind, x_axis_type, y_axis_type, width, height):
p = None
if kind == 'scatter' or kind == 'line':
layout, p = self.createFigure(
data, kind, x_axis_type, y_axis_type, width, height)
elif kind == 'bar':
layout = self.plot_Bar(data, width, height)
elif kind == 'pizza':
layout = self.plot_Pizza(
data, width, height)
# Show/save
save(layout)
return p
def createFigure(self, data, kind, x_axis_type, y_axis_type, width, height):
source, xdata, ydata, xvalues, yvalues = self.prepare_data(data)
# Define tool
tools = "pan, box_zoom, lasso_select, undo, redo"
wheel_zoom = WheelZoomTool()
hover = HoverTool(
tooltips=[
(data.columns[0], '$x'),
(data.columns[1], '$y')],
mode='mouse')
# Create first figure and customize
fig1 = figure(title="{} vs {}" .format(ydata, xdata), tools=tools,
x_axis_type=x_axis_type, y_axis_type=y_axis_type,
toolbar_location="right", plot_width=round(0.9 * width),
plot_height=round(0.75 * height))
fig1.add_tools(wheel_zoom)
fig1.add_tools(hover)
fig1.toolbar.active_scroll = wheel_zoom
fig1.background_fill_color = "beige"
fig1.background_fill_alpha = 0.4
# Create second figure and customize
fig2 = figure(title='Overview', title_location="left",
x_axis_type=x_axis_type, y_axis_type=y_axis_type,
tools='', plot_width=round(0.9 * width), plot_height=round(0.25 * height))
fig2.xaxis.major_tick_line_color = None
fig2.xaxis.minor_tick_line_color = None
fig2.yaxis.major_tick_line_color = None
fig2.yaxis.minor_tick_line_color = None
fig2.xaxis.major_label_text_color = None
fig2.yaxis.major_label_text_color = None
# Add View box to second figure
rect = Rect(x='x', y='y', width='width', height='height', fill_alpha=0.1,
line_color='black', fill_color='black')
fig2.add_glyph(source, rect)
# Add JS callBacks
self.JS_linkPlots(fig1, source)
# Plots
plots = self.plot_continuous(source, xvalues, yvalues, fig1, kind)
self.plot_continuous(source, xvalues, yvalues, fig2, kind)
s2 = ColumnDataSource(data=dict(ym=[0.5, 0.5]))
fig1.line(x=[0, 1], y='ym', color="orange",
line_width=5, alpha=0.6, source=s2)
# Add legends
legend = Legend(items=[
(ydata, plots)],
location=(0, 0),
click_policy="mute")
# Add legend to fig layout
fig1.add_layout(legend, 'below')
# Layout
layout = col(fig1, fig2)
return layout, fig1
def plot_continuous(self, source, xvalues, yvalues, fig, kind, color=0):
if kind == 'scatter':
s = fig.scatter(
xvalues, yvalues,
fill_color='white', fill_alpha=0.6,
line_color=Spectral10[color], size=8,
selection_color="firebrick",
nonselection_fill_alpha=0.2,
nonselection_fill_color="blue",
nonselection_line_color="firebrick",
nonselection_line_alpha=1.0)
return [s]
elif kind == 'line':
l = fig.line(
xvalues, yvalues, line_width=2, color=Spectral10[color], alpha=0.8,
muted_color=Spectral10[color], muted_alpha=0.2)
s = fig.scatter(
xvalues, yvalues,
fill_color="white", fill_alpha=0.6,
line_color=Spectral10[color], size=8,
selection_color="firebrick",
nonselection_fill_alpha=0.2,
nonselection_fill_color="blue",
nonselection_line_color="firebrick",
nonselection_line_alpha=1.0)
return [s, l]
else:
raise 'Wrong type of plot'
def prepare_data(self, data):
xdata = data.columns[0]
xvalues = data[xdata]
ydata = data.columns[1]
yvalues = data[ydata]
source = ColumnDataSource(data)
return source, xdata, ydata, xvalues, yvalues
First, a disclaimer: Bokeh makes no claim to function, either fully or partially, with Qt browser widgets. We are simply not equipped to be able to maintain that claim rigorously under continuous testing, therefore we cannot make it. If anyone would ever like to step in as a maintainer of that functionality, it's possible in the future that we can make stronger support claims.
Bokeh uses a third party library Hammer.js to provide uniform low-level event handling across different platforms. Bokeh expects that the events that are generated have a pageX and pageY attributes. It appears that Qt's browser widget does not satisfy this expectation, leading to the error you are seeing. It's possible that updating the version of Hammer used by Bokeh might fix the problem. It's possible that a workaround could be introduced. In any case, it would require new work on BokehJS itself.
The short answer is: this interacive legend probably just is not going to work on Qt. As a workaround, use Bokeh widgets or Qt Widgets to high and show glyphs, and do not rely on the interactive legend capability.
Longer term: Wo could look into some of the ideas suggested above. But we would need assistance to do do. We do not have the bandwidth, ability, or experience to build Qt apps ourselves to test potential fixes. If you have the ability to work together with a core dev on finding a solution, please feel free to make an issue on the issue tracker.
Related
I was working on making a graph that displays the full line by keping x axis constant and left axis adjusting. I am calculating the cost to produce egg tray with multiple variables.
Using jupyter notebook with ipywidgets as widgets i was able to get the answer.
jypyter auto adjusting y axis
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib nbagg
x = np.linspace(50000, 80000, 30000)
fig, ax = plt.subplots(1, figsize=(10,4))
plt.suptitle('Cost To Produce')
def production_cost(carton_percent,assorted_percent,white_percent,dry_eggtray_weight,electric_cost,ebiogas_sold,skilled_labor,manual_labor):
ax.clear()
total_paper_weight = x*dry_eggtray_weight/1000
carton_price = total_paper_weight*carton_percent*1.9/100
assorted_price = total_paper_weight*assorted_percent*4.25/100
white_price = total_paper_weight*white_percent*10/100
burner_consumption = (x+8679)/17.3
electric_consumption = (x+11074)/19.1
skilledlabor_cost = skilled_labor*6*346/7+skilled_labor*6*3.5*54/7
manuallabor_cost = manual_labor*6*290/7+manual_labor*6*3.5*36/7
rawmats_price = carton_price + assorted_price + white_price
burner_price = burner_consumption*2.2*ebiogas_sold
electric_price = electric_consumption*electric_cost
labor_price = skilledlabor_cost + manuallabor_cost
cellophane_price = x*13/(140*2)
maintenance_price = 2000
admin_price = 4000
overall_price = rawmats_price + burner_price + electric_price + labor_price + cellophane_price + maintenance_price + admin_price
y = overall_price/x
ax.plot(x,y)
ax.set_xlabel('Egg Tray Production')
ax.set_ylabel('Cost per Tray')
plt.show()
carton_percent = widgets.FloatSlider(min=0, max=100, value=37.5, description='% Carton:')
assorted_percent = widgets.FloatSlider(min=0, max=100, value=37.5, description='% Assorted:')
white_percent = widgets.FloatSlider(min=0, max=100, value=25, description='% White:')
dry_eggtray_weight = widgets.IntSlider(min=0, max=90, value=80, description='Dry Try (g):')
electric_cost = widgets.FloatSlider(min=6, max=20, value=11.37, description='Elec Cost:')
ebiogas_sold = widgets.FloatSlider(min=6, max=20, value=6.7, description='EBio SellP:')
skilled_labor = widgets.IntSlider(min=0, max=12, value=6, description='Skilled C:')
manual_labor = widgets.IntSlider(min=0, max=16, value=12, description='Manual C:')
widgets.interactive(production_cost, carton_percent=carton_percent, assorted_percent=assorted_percent, white_percent=white_percent, dry_eggtray_weight=dry_eggtray_weight, electric_cost=electric_cost, ebiogas_sold=ebiogas_sold, skilled_labor=skilled_labor, manual_labor=manual_labor)
But using python idle, i wasnt able to copy the result
I tried narrowing variables so it wouldnt be hard to trace. So i tried this code with widgets from matplotlib directly. but only the drawing or graph is moving and both x and y axis is steady. I have tried autoscale but it does not work. Like ax.autoscale
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, Button
# The parametrized function to be plotted
def f(x,electric_cost,ebiogas_sold):
burner_consumption = (x+8679)/17.3
electric_consumption = (x+11074)/19.1
burner_price = burner_consumption*2.2*ebiogas_sold
electric_price = electric_consumption*electric_cost
overall_price = burner_price + electric_price
return overall_price/x
x = np.linspace(50000, 80000, 30001)
# Define initial parameters
init_electric_cost = 11.37
init_ebiogas_sold = 6.7
# Create the figure and the line that we will manipulate
fig, ax = plt.subplots()
line, = ax.plot(x, f(x, init_electric_cost, init_ebiogas_sold), lw=2)
ax.autoscale(enable=True, axis="y", tight=True)
ax.set_xlabel('Egg Tray Produced [pcs]')
# adjust the main plot to make room for the sliders
fig.subplots_adjust(left=0.25, bottom=0.25)
# Make a vertically oriented slider to control the cost of electricity
electric_cost = fig.add_axes([0.25, 0.1, 0.65, 0.03])
electric_cost_slider = Slider(
ax=electric_cost,
label="Electric Cost [pesos]",
valmin=0,
valmax=20,
valinit=init_electric_cost,
)
# Make a vertically oriented slider to control the cost of biogas converted to power and then sold
biogas_cost = fig.add_axes([0.1, 0.25, 0.0225, 0.63])
biogas_cost_slider = Slider(
ax=biogas_cost,
label="Biogas Power Sold [pesos]",
valmin=0,
valmax=20,
valinit=init_ebiogas_sold,
orientation="vertical"
)
# The function to be called anytime a slider's value changes
def update(val):
line.set_ydata(f(x, electric_cost_slider.val, biogas_cost_slider.val))
#fig.canvas.draw_idle()
# register the update function with each slider
electric_cost_slider.on_changed(update)
biogas_cost_slider.on_changed(update)
# Create a `matplotlib.widgets.Button` to reset the sliders to initial values.
resetax = fig.add_axes([0.8, 0.025, 0.1, 0.04])
button = Button(resetax, 'Reset', hovercolor='0.975')
def reset(event):
biogas_cost_slider.reset()
electric_cost_slider.reset()
button.on_clicked(reset)
plt.show()
I finally got the answer.
I just have to change the limits of my Y
My changes are highlighted as BOLD
I dont know how to properly construct yet. so ill just paste the screenshot of changes
enter image description here
enter image description here
What I want to do:
I made a dynamic map using bokeh, pandas and geopandas. The data to be displayed is loaded from a table, then mapped to a country per year. The year to be displayed is determined by a bokeh slider. You can also hover over a country and get its value. I now want to be able to change the data source by selecting a radio button. To display the data correctly, I want to change the color palette, rescale it (a range from e.g. 50 to 100 instead of 0 to 4.5), update the scaling on the slider to the new lowest year to highest year, and then display the world-map with the new data. I also want to update the title of the map from e.g. "Fertility" to "Life expectancy".
What I already have:
I have a working dynamic map with Slider and Hover tool. I also have a list from which the data to be used is taken (datapath, title to be used, color palette to be used, highest and lowest year, highest and lowest value). I have a radio button group, with three different data sources to choose from. All paths are relative, the data is provided with a consistent structure. I had the map changing the data below and displaying the new stuff, but than I did something and it stopped working. I also had the Hover tool display the right values, but with a wrong (old) description.
What I need help with:
Updating the color bar to accomodate the new palette and the new range
Updating the slider to accomodate a changed range
Updating the title displayed to show what's actually displayed
What I already tried:
I've put the whole loading and displaying in the function executed when the radio button group is changed. The first thing this function does is clearing the layout and then rebuilding it. Unfortunately, this is neither efficient, nor working, since I only get the radio button Group and an empty space below, no matter what I do. I've searched for a solution, but all I found (and tried) didn't do what I needed.
I can provide the actual code, if needed (though some of the variables and documentations are in German), but since I'm pretty new to the whole python thing, I don't now, what exactly of that about 300 lines of code you need. Just let me now, and I'll try and provide.
Hope you can help me with that.
Thanks in advance,
Asd789
EDIT: As correctly pointed out in the comments, some code to help understand what I did.
I'll cut all the imports for the sake of brevity, as a mistake there would have shown up as error in my terminal. Also leaving out comments.
geoFrame #dataframe for geopanda shapefile
configList = [0, "Fertility", 'YlGnBu', 'Year' ]
df #dataframe for the .csv file
higStep = df['step'].max()
lowStep = df['step'].min()
configList.append(higStep)
higVal = df['valueInterest'].max()
merged = geoFrame.merge(df, left_on = 'country_code', right_on = 'code')
merged_json = json.loads(merged.to_json())
json_data = json.dumps(merged_json)
geosource = GeoJSONDataSource(geojson = json_data)
palette = brewer[configList[2]][8]
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 4)
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal')
p = figure(title = configList[1], plot_height = 600 , plot_width = 950, toolbar_location = None)
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.patches('xs','ys', source = geosource,fill_color = {'field' :'valueInterest', 'transform' : color_mapper},
line_color = 'black', line_width = 0.25, fill_alpha = 1)
p.add_layout(color_bar, 'below')
df_curr = df[df['step'] == higStep]
color_mapper = LinearColorMapper(palette = palette, low = 0, high = 40, nan_color = '#d9d9d9')
def json_data(selectedStep):
st = selectedStep
df_st = df[df['step'] == st]
merged = geoFrame.merge(df_st, left_on = 'country_code', right_on = 'code', how = 'left')
merged_json = json.loads(merged.to_json())
json_data = json.dumps(merged_json)
return json_data
geosource = GeoJSONDataSource(geojson = json_data(higStep))
palette = brewer[configList[2]][8]
palette = palette[::-1]
color_mapper = LinearColorMapper(palette = palette, low = 0, high = higVal/2, nan_color = '#d9d9d9')
hover = HoverTool(tooltips = [ ('Country/region','#country'),(configList[1], '#valueInterest')])
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal')
p = figure(title = configList[1], plot_height = 600 , plot_width = 950, toolbar_location = None, tools = [hover])
p.xgrid.grid_line_color = None
p.ygrid.grid_line_color = None
p.patches('xs','ys', source = geosource,fill_color = {'field' :'valueInterest', 'transform' : color_mapper},
line_color = 'black', line_width = 0.25, fill_alpha = 1)
p.add_layout(color_bar, 'below')
def update_plot(attr, old, new):
st = slider.value
new_data = json_data(st)
geosource.geojson = new_data
p.title.text = configList[1] %st
slider = Slider(title = configList[3],start = 1950, end = 2015, step = 1, value = 2015)
slider.on_change('value', update_plot)
def radHandler(attr, new, old):
if radio.active == 0:
datapath = os.path.join(dataloc, 'children-per-woman-UN.csv')
configList = [0, "Fertility", 'YlGnBu', 'Year']
elif radio.active == 1:
#see above with diferent data
elif radio.active == 2:
#see above with different data
curdoc().clear()
higStep = df['step'].max()
lowStep = df['step'].min()
configList.append(higStep)
update_plot(attr, new, old)
hover = HoverTool(tooltips = [ ('Country/region','#country'),(configList[1], '#valueInterest')])
palette = brewer[configList[2]][8]
palette = palette[::-1]
olor_mapper = LinearColorMapper(palette = palette, low = 0, high = higVal/2, nan_color = '#d9d9d9')
color_bar = ColorBar(color_mapper=color_mapper, label_standoff=8,width = 500, height = 20,
border_line_color=None,location = (0,0), orientation = 'horizontal'
p = figure(title = configList[1]+' '+str(configList[4]), plot_height = 600 , plot_width = 950, toolbar_location = None, tools = [hover])
layout = column(widgetbox(radio),p,widgetbox(slider))
curdoc().add_root(layout)
radio = RadioButtonGroup(labels=['Fertility', 'Life expectancy', 'Covid-19 total cases'], active=0)
radio.on_change('active',radHandler)
layout = column(p, widgetbox(radio),widgetbox(slider))
curdoc().title = configList[1]
curdoc().add_root(layout)
Sorry I could'nt cook it down further, but I don't know what's essential and what's just fancy stuff around.
This code works until I touch the radio button group. After that, the plot itself is just blank, without any prompts anywhere. The code itself is not entirely my fault, I got it to fix and maybe expand on it, the expansion being the ability to switch between data sources as decribed above.
I try to implement a matplotlib figure that updates during the simulation of my environment.
The following Classes works fine in my test but doesn't update the figure when I use it in my environment. During the simulation of the environment, the graph is shown, but no lines are plotted.
My guess is that .draw() is not working how I think it does.
Can anyone figure out the issue here?
class Visualisation:
def __init__(self, graphs):
self.graphs_dict = {}
for graph in graphs:
fig = plt.figure()
ax = fig.add_subplot(111)
line, = ax.plot(graph.x, graph.y, 'r-')
self.graphs_dict[graph.title] = {"fig": fig, "ax": ax, "line": line, "graph": graph}
self.graphs_dict[graph.title]["fig"].canvas.draw()
plt.ion()
plt.show()
def update(self, graph):
graph = self.graphs_dict[graph.title]["graph"]
self.graphs_dict[graph.title]["line"].set_xdata(graph.x)
self.graphs_dict[graph.title]["line"].set_ydata(graph.y)
self.graphs_dict[graph.title]["fig"].canvas.flush_events()
x_lim, y_lim = self.get_lim(graph)
self.graphs_dict[graph.title]["ax"].set_xlim(x_lim)
self.graphs_dict[graph.title]["ax"].set_ylim(y_lim)
self.graphs_dict[graph.title]["fig"].canvas.draw()
#staticmethod
def get_lim(graph):
if graph.x_lim is None:
x = np.array(graph.x)
y = np.array(graph.y)
x_lim = [x.min(), x.max()]
y_lim = [y.min(), y.max()]
else:
x_lim = graph.x_lim
y_lim = graph.y_lim
return x_lim, y_lim
class Graph:
def __init__(self, title, x, y, x_label="", y_label=""):
"""
Sets up a graph for Matplotlib
Parameters
----------
title : String
Title of the plot
x : float
y : float
x_label : String
x Label
y_label : String
y Label
"""
self.title = title
self.x = x
self.y = y
self.x_label = x_label
self.y_label = y_label
self.x_lim, self.y_lim = None, None
def set_lim(self, x_lim, y_lim):
self.x_lim = x_lim
self.y_lim = y_lim
class Environment:
def __init__(self, [..], verbose=0):
"""verbose : int
0 - No Visualisation
1 - Visualisation
2 - Visualisation and Logging"""
self.vis = None
self.verbose = verbose
[......]
def simulate(self):
for _ in range(self.n_steps):
[...]
self.visualize()
def visualize(self):
if self.verbose == 1 or self.verbose == 2:
if self.vis is None:
graphs = [Graph(title="VariableY", x=[], y=[])]
graphs[0].set_lim(x_lim=[0, 100], y_lim=[0, 300])
self.vis = Visualisation(graphs=graphs)
else:
self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)
self.vis.update(self.vis.graphs_dict["VariableY"]["graph"])
When I run the code I more or less just write: env.simulate().
The code runs fine here:
class TestSingularVisualisation(unittest.TestCase):
def setUp(self):
self.graph = Graph(title="Test", x=[0], y=[0])
self.vis = Visualisation(graphs=[self.graph])
class TestSingleUpdate(TestSingularVisualisation):
def test_repeated_update(self):
for i in range(5):
self.graph.x.append(i)
self.graph.y.append(np.sin(i))
self.vis.update(self.graph)
time.sleep(1)
Turns out your code works the way it is set up. Here is the sole problem with the code you provided:
self.vis.graphs_dict["VariableY"]["graph"].x.append(self.internal_step)
self.vis.graphs_dict["VariableY"]["graph"].y.append(150)
You are plotting a line and correctly updating the canvas, however, you keep appending the exact same (x, y) coordinate. So the simulation does update the line, but the line simplifies to a point. Your test case does not do this. You can run a dummy example with your code by simply adding a line like this:
self.internal_step += 5
before adding the new x point, and you will produce a horizontal line.
Let me know if this solves your problem.
Probably not the most elegant, but I use plt.pause(0.1) when I want to update plots during execution. It pauses for 0.1s and forces all plots to be actually displayed. (It work in %debug in ipython as a bonus)
I'm trying to change plot width when taptool selects something.
I invoke this using bokeh serve, and navigating to the localhost website
when I click on one of the rectangles, my console prints out "callback" and "callback2" but my plot doesn't change width.
What am I doing wrong?
counts = [1*10**7,2*10**7,3*10**7] #dummy data
l_edge = [x for x in range(len(counts))]
r_edge = [x + .85 for x in range(len(counts))]
data = {
'height': counts,
'leftEdges': l_edge,
'rightEdges': r_edge,
}
p = figure()
s = ColumnDataSource(data)
p.add_tools(TapTool())
def callbackfcn(attr,old,new):
global p
print('callback')
p.width = np.random.choice([100,200,300,1000,10000])
p.height = np.random.choice([100,200,300,1000,10000])
print('callback2')
r = p.quad(top='height',bottom=0,
left = 'leftEdges',right = 'rightEdges',
source = s)
r.data_source.on_change('selected',callbackfcn)
layout = column(p)
curdoc().add_root(layout)
Have you tried instead including sizing_mode in your layout? Take a look at the Bokeh docs for an example.
So I've created several charts using the matplotlib library in python 3.5, but I want to be able to have the flexibility to utilize a button to alternate between the views I created within a single window. I've been trying to experiment with an example here, but have not succeeded in doing so. I was curious in how to have the flexibility to click through different views that I created.
My code is sort of organized like this:
def plot1(data1, 'name1'):
...
ax.plot(x,y)
plt.draw()
def plot2(data2, 'name2'):
...
ax2.plot(x,y)
plt.draw()
def plot3(data3, 'name3'):
...
ax3.plot(x,y)
plt.draw()
plot1(data1,'name1')
plot2(data2,'name2')
plot3(data3,'name3')
plt.show()
Currently it will show up in three different windows. Now when I try to make this all into one view accessible via buttons, I'm unable to do so because quite frankly I'm unfamiliar with how to pass on the variables in my methods to create my desired subplots with the callback function. Is there a way to sort of structure my code to have them all run under one matplotlib window?
The following would be a class that uses the functions that you create. Those would not actually plot anything, but provide the required data. They should be put in a list called funcs, and when you click next or prev the corresponding graph would pop up. This should get you started.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.2)
x = range(-50,50)
y = range(-50,50)
l, = plt.plot(x, y, lw=2)
ax.title.set_text('y = x')
class Index(object):
ind = 0
global funcs # used so yu can access local list, funcs, here
def next(self, event):
self.ind += 1
i = self.ind %(len(funcs))
x,y,name = funcs[i]() # unpack tuple data
l.set_xdata(x) #set x value data
l.set_ydata(y) #set y value data
ax.title.set_text(name) # set title of graph
plt.draw()
def prev(self, event):
self.ind -= 1
i = self.ind %(len(funcs))
x,y, name = funcs[i]() #unpack tuple data
l.set_xdata(x) #set x value data
l.set_ydata(y) #set y value data
ax.title.set_text(name) #set title of graph
plt.draw()
def plot1():
x = range(-20,20)
y = x
name = "y = x"
return (x,y, name)
def plot2():
x = range(-20,20)
y = np.power(x, 2)
name = "y = x^2"
return (x,y,name)
def plot3():
x = range(-20,20) # sample data
y = np.power(x, 3)
name = "y = x^3"
return (x,y, name)
funcs = [plot1, plot2, plot3] # functions in a list so you can interate over
callback = Index()
axprev = plt.axes([0.7, 0.05, 0.1, 0.075])
axnext = plt.axes([0.81, 0.05, 0.1, 0.075])
bnext = Button(axnext, 'Next')
bnext.on_clicked(callback.next)
bprev = Button(axprev, 'Previous')
bprev.on_clicked(callback.prev)
plt.show()