I am building a chart using Bokeh. I need to create an empty Figure and when the page is loaded, if I click on a button, data is loaded and displayed on the chart.
I wrote this code but it doesn't work.
def _op_produttivitaRidge1day(db, custom_attrs, start_date, end_date, postazione, stazione: str, mongo_coll, hours: list, hours2: list, stazione2: str, stazione3: str, cod_avaria: str):
def crosscorr(datax, datay, lag=0, wrap=False):
""" Lag-N cross correlation.
Shifted data filled with NaNs
Parameters
----------
lag : int, default 0
datax, datay : pandas.Series objects of equal length
Returns
----------
crosscorr : float
"""
if wrap:
shiftedy = datay.shift(lag)
shiftedy.iloc[:lag] = datay.iloc[-lag:].values
return datax.corr(shiftedy)
else:
return datax.corr(datay.shift(lag))
# custom data
custom_attrs = {
"OP30": ["ST7_LETTURA_INI_TRASD", "ST7_LETTURA_TRASD", "ST3_CONT_SCARTI_CONS", "ST4_CONT_SCARTI_CONS",
"ST7_CONT_SCARTI_CONS"],
"OP40": ["ST2_VAL_CRIMPATURA", "ST2_VAL_INI_CRIMPATURA", "ST5_CNT_SCARTI_CONS_CHECKER", "ST1_CONT_SCARTI_CONS",
"ST2_CONT_SCARTI_CONS", "ST5_CONT_SCARTI_CONS"],
"OP50": ["ST3_CNT_VETRINO_SALD", "ST1_VAL_PRESSIONE", "ST1_VAL_PERDITA", "ST2_CONT_SCARTI_CONS",
"ST3_CONT_SCARTI_CONS"],
"OP60": ["ST2_LETTURA_INI_TRASD", "ST3_COUNT_CONTROLLO_VETRINO", "ST4_CONT_FOTO1_COGNEX",
"ST4_CONT_FOTO2_COGNEX", "ST4_RIPROVE_SCARTO", "ST1_CONT_SCARTI_CONS", "ST2_CONT_SCARTI_CONS",
"ST3_CONT_SCARTI_CONS", "ST4_CONT_SCARTI_CONS"],
"OP70": ["ST1_CONT_SCARTI_CONS"],
"OP80": ["ST1_COUNT_CONTROLLO_VETRINO", "ST1_CONT_SCARTI_CONS", "ST2_CONT_SCARTI_CONS"],
"OP90": ["ST1_VAL_TRASD_DAMPER", "ST1_VAL_TRASD_INI_DAMPER", "ST1_VAL_TRASD_CUP_INI_DAMPER",
"ST1_VAL_TRASD_CUP_DAMPER", "ST1_CONT_SCARTI_CONS"],
"OP100": [],
"OP110": [],
"OP120": ["ST1_VAL_MISURA_PISTON", "ST1_VAL_TRASD_INI", "ST1_CONT_SCARTI_CONS"]
}
# ProduttivitĂ Ridge Graph
# day, hour, number of items produced
attr_name = stazione + "_PZ_IN"
attr_value = "$" + attr_name
c5 = db[mongo_coll].aggregate([
{"$match": {"$and": [{"Timestamp": {"$gte": start_date}}, {"Timestamp": {"$lte": end_date}}]}},
# {"$match": {{"$substr": ["$Timestamp" , 0, 10]} : {"$in": days_list}}},
{"$project": {"DAY": {"$substr": ["$Timestamp", 0, 10]}, "HOUR": {"$substr": ["$Timestamp", 11, 4]},
attr_name: 1}},
{"$group": {"_id": {"DAY": "$DAY", "HOUR": "$HOUR"}, "ITEM_COUNT": {"$sum": attr_value}}},
{"$project": {"_id": 0, "DAY": "$_id.DAY", "HOUR": "$_id.HOUR", "ITEM_COUNT": 1}},
{"$sort": {"DAY": 1, "HOUR": 1}}
])
c5_df = pd.DataFrame(list(c5))
days = sorted(c5_df["DAY"].unique())
PR = figure(title="Produzione " + postazione + " " + stazione, y_range=days, plot_width=900, x_range=(-2, 208),
toolbar_location=None)
bt = Button(
label="Show data",
button_type="success",
width=50
)
def createPR(FIG, rm_num_values):
c5_df['ITEM_COUNT_RM'] = c5_df.iloc[:, 0].rolling(window=rm_num_values).mean().fillna(0)
for d in days:
y = []
for h in hours:
try:
# hf = str(int(h)).zfill(2)
hf = h
num_items = (c5_df.loc[c5_df["HOUR"] == hf].loc[c5_df["DAY"] == d].iloc[0]["ITEM_COUNT_RM"]) / 80
# print(hf,d,num_items)
if num_items > 1:
num_items = 1
except:
num_items = 0
y.append([d, num_items])
# print(y)
s5 = ColumnDataSource(data=dict(x=range(146)))
s5.add(y, d)
FIG.patch('x', d, source=s5)
FIG.outline_line_color = None
FIG.background_fill_color = "#efefef"
FIG.y_range.range_padding = 0.12
FIG.xaxis.ticker = FixedTicker(
ticks=[0, 6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96, 102, 108, 114, 120, 126, 132, 138])
FIG.xaxis.major_label_overrides = {0: '0', 6: '1', 12: '2', 18: '3', 24: '4', 30: '5', 36: '6', 42: '7', \
48: '8', 54: '9', 60: '10', 66: '11', 72: '12', 78: '13', 84: '14', 90: '15', \
96: '16', 102: '17', 108: '18', 114: '19', 120: '20', 126: '21', 132: '22',
138: '23'}
FIG.outline_line_color = background
FIG.border_fill_color = background
FIG.background_fill_color = background
return FIG
#PR = createPR(PR, 3)
bt.on_click(createPR(PR, 3))
return column(bt, PR)
This code was written for Bokeh v2.1.1. Run it with bokeh serve -show main.py.
Replace data = dict(x=[1, 2, 3, 2], y=[6, 7, 2, 2]) with your data acquisition function: data = _op_produttivitaRidge1day(db, custom_attrs, start_date, end_date, ...) Everything should work if your function returns a dictionary with x and y vectors of equal length.
The approach is to first pass an empty data_source to the figure and then after a button click, download the data from your MongoDB and assign it as dictionary to the patch.data_source.data.
main.py:
from bokeh.plotting import curdoc, figure
from bokeh.models import Column, Button
plot = figure()
patch = plot.patch(x=[], y=[])
def callback():
data = dict(x=[1, 2, 3, 2], y=[6, 7, 2, 2]) # <-- your data acquisition function comes here
patch.data_source.data = data
button = Button()
button.on_click(callback)
curdoc().add_root(Column(plot, button))
Related
I am trying to plot several scatter glyphs to one figure, using a loop. The goal is to use an own glyph for each brand and to update the values if another value is chosen by the Select widgets.
But the figure shows an empty graph. I assume that the problem is the ColumnDataSource and the update in the function "update()".
This is an executable example. Do you have any idea?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool
from bokeh.plotting import figure, show
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
df = pd.DataFrame(zip(brands, product, price, size, value, id), columns=col)
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#id"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#x'),
(select_y_axis.value, '#y')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
source = {}
plots = {}
for brand in brands:
# Create Column Data Source that will be used by the plot
source[brand] = ColumnDataSource(data=dict(x=[], y=[], id=[], product=[], brand=[]))
plots[brand] = fig.scatter(x='x', y='y', size=5, source=source[brand])
def update():
x_name = select_x_axis.value
y_name = select_y_axis.value
fig.xaxis.axis_label = x_name
fig.yaxis.axis_label = y_name
for brand in brands:
df1 = df.loc[df['brand'] == brand]
source[brand].data = dict(
x=df1[x_name],
y=df1[y_name],
id=df1['ID'],
product=df1['product'],
brand=df1['brand']
)
# Set up layouts and add to document
controls = [select_x_axis, select_y_axis]
for control in controls:
control.on_change('value', lambda attr, old, new: update())
inputs = column(select_x_axis, select_y_axis)
update() # initial load of the data
show(row(inputs, fig, width=1000))
#curdoc().add_root(row(inputs, fig, width=1000))
#curdoc().title = "xyz"
resultig plot
Something like that ?
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
import pandas as pd
def make_data() :
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
ident = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
return pd.DataFrame(zip(brands, product, price, size, value, ident), columns=col)
if __name__ == "__main__" :
df = make_data()
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Scatter plot
hover = HoverTool(tooltips=[(name, "#" + name) for name in df.columns])
figure = figure(tools=[hover, 'reset'])
# Source
source = ColumnDataSource(df)
figure.scatter("xvalues", "yvalues", source=source)
# Selects
options = ["product", "price", "size", "value"]
select_x_axis = Select(title="x-Axis:", value=default_xcol, options=options)
select_y_axis = Select(title="y-Axis:", value=default_ycol, options=options)
# callback
callback = CustomJS(args={"source":source, "axis":figure.xaxis[0]}, code="""
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change("value", callback)
callback = CustomJS(args={"source":source, "axis":figure.yaxis[0]}, code="""
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
axis.axis_label = cb_obj.value;
""")
select_y_axis.js_on_change("value", callback)
show(row(
column(select_x_axis, select_y_axis),
figure, width=1000))
#jsgounot Thanks for your help. I figured something out that works quiet well at the moment:
from bokeh.layouts import column, row
from bokeh.models import ColumnDataSource, Select, HoverTool, CustomJS
from bokeh.plotting import figure, show
from bokeh.palettes import d3
import pandas as pd
brands = ['a', 'b', 'c', 'a', 'b', 'b', 'c']
product = ['v1', 'v2', 'v3', 'v4', 'v5', 'v6', 'v7']
price = [2, 3, 54, 48, 9, 2, 4]
size = [10, 11, 12, 13, 14, 15, 16]
value = [5, 4, 3, 8, 1, 0, 1]
id = [1, 2, 3, 4, 5, 6, 7]
col = ['ID', 'brand', 'product', 'price', 'size', 'value']
label = ['price', 'size', 'value']
colors = d3["Category20c"][len(brands)]
markers = ['circle', 'square', 'triangle', 'asterisk', 'circle_x', 'square_x', 'inverted_triangle', 'x', 'circle_cross', 'square_cross', 'diamond', 'cross']
df = pd.DataFrame(zip(id, brands, product, price, size, value), columns=col)
default_xcol = "price"
default_ycol = "size"
df["xvalues"] = df[default_xcol]
df["yvalues"] = df[default_ycol]
# Widgets:
select_x_axis = Select(title="x-Axis:", value=label[0], options=label)
select_y_axis = Select(title="y-Axis:", value=label[1], options=label)
# Set up figure
hover = HoverTool(tooltips=[
("index", "#ID"),
('Brand', '#brand'),
('Product', '#product'),
(select_x_axis.value, '#xvalues'),
(select_y_axis.value, '#yvalues')
])
# Set up plots:
fig = figure(plot_height=400, plot_width=800, title="xyz",
# tooltips=TOOLTIPS,
tools=[hover, 'reset'],
x_axis_label=select_x_axis.value,
y_axis_label=select_y_axis.value)
df_brand = []
sources = []
plots = {}
for index, brand in enumerate(df.brand.unique()):
df_brand.append(df.loc[df['brand'] == brand])
sources.append(ColumnDataSource(df_brand[index]))
plots[brand] = fig.scatter(x='xvalues', y='yvalues', source=sources[index],
legend_label=brand,
marker=markers[index],
color=colors[index])
fig.legend.click_policy="hide"
callback_x = CustomJS(args={'sources': sources, 'axis': fig.xaxis[0], 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['xvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
callback_y = CustomJS(args={'sources': sources, 'axis': fig.yaxis[0], 'hov': fig.hover, 'brands': df.brand.unique()}, code="""
for (var i = 0; i <= 2; i++){
var source = sources[i]
source.data['yvalues'] = source.data[cb_obj.value];
source.change.emit();
}
axis.axis_label = cb_obj.value;
""")
select_x_axis.js_on_change('value', callback_x)
select_y_axis.js_on_change('value', callback_y)
show(row(
column(select_x_axis, select_y_axis),
fig, width=1000))
Any ideas what's supposed to go where the triple '?'s are?
import pandas as pd
from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider, Select
import bokeh.plotting as bp
from bokeh.plotting import Figure, output_file, show
from bokeh.models import HoverTool, DatetimeTickFormatter
# Create an output file
bp.output_file('columnDataSource.html')
# Create your plot as a bokeh.figure object
myPlot = bp.figure(height = 600,
width = 800,
y_range=(0,3))
x_values = [1, 2, 3, 4, 5]
y_values = [1, 2, 3, 4, 5]
myPlot.line(x = x_values, y= y_values, line_width=2)
callback = CustomJS(args={
'source1': {'x': [1,2,3,4], 'y':[1,1,1,1]},
'source2': {'x': [0,0,0,0], 'y':[2,2,2,2]},
'source3': {'x': [1,2,3,4], 'y':[1,1,1,1]}},
code="""
var data1 = source1;
var data2 = source2;
var data3 = source3;
var f = cb_obj.value;
if(f == 'A'){
console.log("A selected from dropdown.");
data1.x = data1.x;
data1.y = data1.y;
}
else if(f == 'B'){
// Substitute all old data1 values in with data2 values
console.log("B selected from dropdown.");
data1.x = data2.x;
data1.y = data2.y;
}
else{
console.log("C selected.");
// Substitute all old data1 values in with data3 values
data1.x = data3.x;
data1.y = data3.y;
}
// Problematic line!
???.change.emit();
""")
select = Select(title='Choose', value='A', options=['A','B','C'])
select.js_on_change('value', callback)
layout = column(select, myPlot)
show(layout) # et voilĂ .
I expect my x and y values to change and plot accordingly to my Bokeh graph.
Nothing is changing at the moment as I don't know what object's "trigger" function I'm supposed to be calling. Please help, I'm new to Bokeh.
You do ColumnDataSource.change.emit() if you updated the data source fields by reference e.g. when you update only x or only y:
ColumnDataSource.data['x'] = [4, 3, 2, 1]
ColumnDataSource.change.emit()
When you update them both you do:
ColumnDataSource.data = new_data
Where new_data is a new json object like {'x': [1], 'y':[2]}.
The reason for this is that JS can automatically detect a change when existing object is replaced with a new one but it cannot detect changes by reference so in those cases you need explicitly to call: ColumnDataSource.change.emit() to update the BokehJS model.
Here is your modified code:
from bokeh.models import CustomJS, ColumnDataSource, Select, Column
from bokeh.plotting import figure, show
myPlot = figure(y_range = (0, 4))
data = {'A': {'x': [1, 2, 3, 4], 'y':[1, 1, 1, 1]},
'B': {'x': [1, 2, 3, 4], 'y':[2, 2, 2, 2]},
'C': {'x': [1, 2, 3, 4], 'y':[3, 3, 3, 3]} }
source = ColumnDataSource(data['A'])
myPlot.line('x', 'y', line_width = 2, source = source)
callback = CustomJS(args = {'source': source, 'data': data},
code = """source.data = data[cb_obj.value]; """)
select = Select(title = 'Choose', value = 'A', options = ['A', 'B', 'C'])
select.js_on_change('value', callback)
layout = Column(select, myPlot)
show(layout)
I would like to create a standalone document, similar to the server app example 'Crossfilter': to select different columns for coloring the circles and to update the colorbar as well.
I define a CustomJS with the code below, where I create a new LinearColorMapper with the calculated low and high values. (For debugging purposes, I intentionally add different palette than the one set via the Python code).
var low = Math.min.apply(Math,source.data[cb_obj.value]);
var high = Math.max.apply(Math,source.data[cb_obj.value]);
var color_mapper = new Bokeh.LinearColorMapper({palette:'Viridis5', low:low, high:high});
cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
color_bar.color_mapper = color_mapper;
source.change.emit();
As a result, when selecting the column, the circles become white, the line black, the ticks of the color bar change correctly, but the palette does not change.
Could you help me setting the proper attributes in the callback? Thank you in advance.
Original state, color mapper set from Python code
After selecting the column 'd'
I created a "minimal working example" to show how far I got. The full project with the template it can be found here: https://github.com/pintergreg/bokehjscolormapperexample
It looks like you cannot reference a colour pallet like this in BokehJS. Just pass Viridis5 variable to JS callback and it works (tested on Bokeh v1.0.4):
import pandas as pd
from bokeh.models import ColumnDataSource, ColorBar, Select, CustomJS
from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.palettes import Spectral5, Viridis5
from bokeh.transform import linear_cmap
from bokeh.embed import components
from jinja2 import Environment, FileSystemLoader
df = pd.DataFrame({"a": [2, 6, 5, 3, 7, 8, 1, 9, 2, 4],
"b": [3, 5, 7, 1, 0, 6, 5, 4, 2, 9],
"c": [11, 12, 13, 14, 11, 13, 15, 14, 15, 12],
"d": [21, 23, 24, 25, 21, 22, 23, 24, 25, 22]})
source = ColumnDataSource(df)
mapper = linear_cmap(field_name = "c", palette = Spectral5,
low = min(df["c"]), high = max(df["c"]))
fig = figure(plot_width = 400, plot_height = 400)
cir = fig.circle(x = "a", y = "b", size = 12,
source = source, line_color = mapper, color = mapper)
color_bar = ColorBar(color_mapper = mapper["transform"], width = 8,
location = (0, 0))
fig.add_layout(color_bar, "right")
codec = """
var low = Math.min.apply(Math,source.data[cb_obj.value]);
var high = Math.max.apply(Math,source.data[cb_obj.value]);
var color_mapper = new Bokeh.LinearColorMapper({palette:viridis5, low:low, high:high});
cir.glyph.fill_color = {field: cb_obj.value, transform: color_mapper};
cir.glyph.line_color = {field: cb_obj.value, transform: color_mapper};
color_bar.color_mapper.low = low;
color_bar.color_mapper.high = high;
color_bar.color_mapper.palette = viridis5;
source.change.emit();
"""
cb_cselect_c = CustomJS(args = dict(cir = cir, source = source, color_bar = color_bar, viridis5 = Viridis5),
code = codec)
c_select = Select(title = "Select variable for color: ", value = "None",
options = ["c", "d"], callback = cb_cselect_c)
layout = gridplot([[fig], [c_select]])
show(layout)
# env = Environment(loader=FileSystemLoader("."))
# template = env.get_template("template.html")
#
# script, div = components(layout)
#
# with open("output.html", "w") as f:
# print(template.render(script=script, div=div), file=f)
You also need to manually add this line to header section of the generated HTML file:
<script type="text/javascript" src="http://cdn.bokeh.org/bokeh/release/bokeh-api-1.0.4.min.js"></script>
I'm trying to do some really basic scatterplots. I'm following instructions from here: https://plot.ly/julia/subplots/
using Plotly
trace1 = [
"x" => [1, 2, 3],
"y" => [4, 5, 6],
"type" => "scatter"
]
trace2 = [
"x" => [20, 30, 40],
"y" => [50, 60, 70],
"xaxis" => "x2",
"yaxis" => "y2",
"type" => "scatter"
]
data = [trace1, trace2]
layout = [
"xaxis" => ["domain" => [0, 0.45]],
"yaxis2" => ["anchor" => "x2"],
"xaxis2" => ["domain" => [0.55, 1]]
]
response = Plotly.plot(data, ["layout" => layout, "filename" => "simple-subplot", "fileopt" => "overwrite"])
plot_url = response["url"]
I get:
ERROR: MethodError: no method matching PlotlyJS.Plot(::Array{Array{Pair{String,Any},1},1}, ::Array{Pair{String,Any},1})
Closest candidates are:
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:Union{AbstractString, Date, Number, Symbol},1}, ::AbstractArray{T,1} where T, ::PlotlyJS.Layout; kind, style, kwargs...) where T<:Union{AbstractString, Date, Number, Symbol} at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:56
PlotlyJS.Plot(::AbstractArray{T<:(AbstractArray{T,1} where T),1}, ::AbstractArray{T,2} where T) where T<:(AbstractArray{T,1} where T) at /Users/username/.julia/v0.6/PlotlyJS/src/convenience_api.jl:68
...
Stacktrace:
[1] #plot#36(::Array{Any,1}, ::Function, ::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
[2] plot(::Array{Array{Pair{String,Any},1},1}, ::Vararg{Any,N} where N) at /Users/username/.julia/v0.6/PlotlyJS/src/display.jl:76
I see the errors invoke PlotlyJS vs regular Plotly. I copy/pasted the example code into a fresh Julia shell. I've done Pkg.add("PlotlyJS"), Pkg.add("Plotly"), and Pkg.update(). Still no luck...
My reading of the documentation suggests that this is the kind of approach to try:
using PlotlyJS
t1 = scatter(;x=[1, 2, 3, 4, 5],
y=[1, 6, 3, 6, 1],
mode="lines",
name="Team A",
text=["A-1", "A-2", "A-3", "A-4", "A-5"])
t2 = scatter(;x=[1, 2, 3, 4, 5],
y=[10, 60, 30, 60, 10],
mode="lines",
name="Team B",
text=["B-1", "B-2", "B-3", "B-4", "B-5"])
layout = Layout(;title="Data Labels Hover",
xaxis_range=[0, 10],
yaxis_range=[0, 100])
data = [t1, t2]
PlotlyJS.plot(data, layout)
I think I'd recommend using Plots.jl for local plotting, though (which can use PlotlyJS.jl). Presumably Plotly.jl is for plotting to the cloud?
Now, I'm writing a code to change all the buttons and labels color when a button is pressed.
But I get a different error every time.
windows = []
global windows
buttons = []
global buttons
labels = []
global labels
A bunch of code... Then...
def flavor(c):
if c != 0:
c = 0
for w in windows:
w.config(bg = 'black')
for l in labels:
l.config(bg = 'black', fg = 'white')
for b in buttons:
b.config(bg = 'black', fg = 'white')
elif c != 1:
c = 1
for w in windows:
w.config(bg = 'dark green')
for l in labels:
l.config(bg = 'dark green', fg = 'light green')
for b in buttons:
b.config(bg = 'dark green', fg = 'light green')
The error I got nearly all the time and most of time was :
Traceback (most recent call last):
File "C:\Users\Ahmet\Desktop\An interesting experiment\FOBBY.py", line 153, in <module>
main()
File "C:\Users\Ahmet\Desktop\An interesting experiment\FOBBY.py", line 118, in main
thememenu.add_command(label="Plain",command = flavor(0))
File "C:\Users\Ahmet\Desktop\An interesting experiment\FOBBY.py", line 79, in flavor
l.config(bg = 'dark green', fg = 'light green')
AttributeError: 'NoneType' object has no attribute 'config'
Thanks for help
I am willing to bet you are creating your widgets like this:
l = Label(,,,).pack(...)
labels.append(l)
When you do something like foo().bar(), the result is whatever the last function returns. In your case pack (or maybe grid) is the last function to be called and it always returns None. Thus, your lists contain nothing but Nones.