placement issue with iPython Notebook sever widgets - widget

I am not able to understand why a particular piece of code doesn't work the way I want it to work. Here is a simple set of widgets for the iPython Notebook server.
from IPython.html import widgets # Widget definitions
from IPython.display import display # Used to display widgets in the notebook
def returnParameter(parameterName = 'Specify Name'):
parameter = [ widgets.CheckboxWidget(),
widgets.TextWidget(value=parameterName),
widgets.DropdownWidget(values=['A', 'B']), ]
h_container = widgets.ContainerWidget(children=parameter)
return h_container
v_container = widgets.ContainerWidget( children=[ returnParameter(str(i)) for i in range(10) ] )
display(v_container) # <<- here it works
for h in v_container.children:
h.remove_class('vbox')
h.add_class('hbox')
# display(v_container) # <<- moving it here does not change 'hbox'
For some reason, the property of h_container within the returnParameter() function does not change if I do a display(v_container) after the point at which I do change the parameter. However, it does seem to change if I place this line before. I am not certain why this is. Any help? I would ideally want to push the property changes within the function returnParameter() but for the reason stated above, this apparently is not possible. Any suggestions?

Related

How to show Jinja2 top level template code?

I'm using Jinja2 with a custom loader that creates (complex) templates dynamically. This means that error messages do not contain a template filename but instead refer to the "top-level template code". To inspect errors, I would like to print the top level template code to the console (for example by calling render in a try-catch block). How can this be achieved?
One (not so elegant) way to achieve this is to cache all responses of the custom loader:
import os
_template_by_fn = dict()
def print_template(template_fn):
print("Template: %s" % template_fn)
print(os.linesep.join(_template_by_fn[str(template_fn)]))
def custom_load_template(template_fn):
# Do work to create the template.
template_code = ""
# When done, cache the result.
_template_by_fn[str(template_fn)] = template_code
return template_code

In Jupyter Notebook, how to show output within function without using print

I want the code to show "123" without using built-in print function, but it does not. What should I do?
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
def myf():
"123"
myf()
If I got right, you just want to print the "123", right?
def myf():
print("123")
myf()
If you want to receive the "123" as a result of your def, would be something like this :
def myf():
x = "123"
return x
Z = myf()
print (Z)
"123"
You can use the display function:
Although, I don't think that's what you want. The settings you're enabling:
from IPython.core.interactiveshell import InteractiveShell
InteractiveShell.ast_node_interactivity = "all"
Only apply to returned values; that's why you don't see it printed. If you return the value and call the function a few times, you'll see them:
I don't have a direct answer to this, but I have some pointers to the problem.
Normal output is written either in stdout or stderr by some Python provided method. However, when utilizing the IPython feature of checking a value by using either direct value ("123") or variable (first line a = "123", second line a). This output stream can not be captured with simple %%capture magic in Jupyter; the output vanishes in the scope of the function definition.
I do agree that this would be useful; in machine learning we sometimes use dependency inversion like structures, where we modify functions instead of line-by-line code, where debugging gets hard since we can not capture some of the outputs without injecting a print or display. However, not using display might have unwanted and hard to predict consequences as some of the models can be rather verbose in what they write. However, capturing some outputs without extra prints and displays from user defined cells might be nice feature.
Notice that sometimes print doesn't work, but display does. Print might not always understand how our utilities in pandas or matplotlib works.

Add a TensorBoard metric from my PettingZoo environment

I'm using Tensorboard to see the progress of the PettingZoo environment that my agents are playing. I can see the reward go up with time, which is good, but I'd like to add other metrics that are specific to my environment. i.e. I'd like TensorBoard to show me more charts with my metrics and how they improve over time.
The only way I could figure out how to do that was by inserting a few lines into the learn method of OnPolicyAlgorithm that's part of SB3. This works and I got the charts I wanted:
(The two bottom charts are the ones I added.)
But obviously editing library code isn't a good practice. I should make the modifications in my own code, not in the libraries. Is there currently a more elegant way to add a metric from my PettingZoo environment into TensorBoard?
You can add a callback to add your own logs. See the below example. In this case the call back is called every step. There are other callbacks that you case use depending on your use case.
import numpy as np
from stable_baselines3 import SAC
from stable_baselines3.common.callbacks import BaseCallback
model = SAC("MlpPolicy", "Pendulum-v1", tensorboard_log="/tmp/sac/", verbose=1)
class TensorboardCallback(BaseCallback):
"""
Custom callback for plotting additional values in tensorboard.
"""
def __init__(self, verbose=0):
super(TensorboardCallback, self).__init__(verbose)
def _on_step(self) -> bool:
# Log scalar value (here a random variable)
value = np.random.random()
self.logger.record('random_value', value)
return True
model.learn(50000, callback=TensorboardCallback())

Embedding Altair htmls in google sites, how to `mark_image` using private google drive links?

I am trying to embed an interactive plot made using Altair into google-site. In this plot, I want to interactively display an image at a time that is stored on google-drive. When I gave this an attempt, mark_image failed silently, presumably because it did not read the image. This is not a surprise because google-drive images were private. With publically shared images I won't have this issue. For the purpose of this plot, I would like to keep the images private. Plus, there are a lot of images in total (~1K), so I probably should not encode them in data/bytes. I suspect that would probably make my HTML file very big and slow. Please correct me if I am wrong on this.
I wonder if mark_image could read the images from the google-drive links, probably using a "reader" of some sort (an upstream JS or python library), and then feed the read image to mark_image. If anybody has experience with this, solutions/suggestions/workarounds would be greatly helpful.
Here's a demo code to test this:
Case 1: Publically accessible image (no problem). Displayed using mark_image, saved in HTML format.
import altair as alt
import pandas as pd
path="https://vega.github.io/vega-datasets/data/gimp.png"
source = pd.DataFrame([{"x": 0, "y": 0, "img": path},])
chart=alt.Chart(source).mark_image(width=100,height=100,).encode(x='x',y='y',url='img')
chart.save('test.html')
Then I embed the HTML in a google-site (private, not shared to the public), using this option and then paste the content of the HTML file in the Embed code tab.
Case 2: Image on Google-drive (problem!). The case of an image stored on google-drive (private, not shared to the public).
# Please use the code above with `path` variable generated like this:
file_id='' # google drive file id
path=f"https://drive.google.com/uc?export=view&id={file_id}"
In this case, apparently mark_image fails silently and the image is not shown on the plot. ​
After searching for an optimal solution, I decided to rely on a sort of a workaround of encoding the images in data/bytes. This eliminates the issue of reading the URLs from google drive, which I could not find a solution for.
Encoding images in data/bytes, as I suspected, made the HTML big in size, however, surprisingly (to me) not slow to load at all. I guess that's the best thing I could do for what I wanted to do.
In the example below, get_data function obtains the data/bytes of an image. I put that into a column of the dataframe that is taken by Altair as input.
def plot_(images_from):
import altair as alt
import pandas as pd
import numpy as np
np.random.seed(0)
n_objects = 20
n_times = 50
# Create one (x, y) pair of metadata per object
locations = pd.DataFrame({
'id': range(n_objects),
'x': np.random.randn(n_objects),
'y': np.random.randn(n_objects)
})
def get_data(p):
import base64
with open(p, "rb") as f:
return "data:image/jpeg;base64,"+base64.b64encode(f.read()).decode()
import urllib.request
if images_from=='url':
l1=[f"https://vega.github.io/vega-datasets/data/{k}.png" for k in ['ffox','7zip','gimp']]
elif images_from=='data':
l1=[get_data(urllib.request.urlretrieve(f"https://vega.github.io/vega-datasets/data/{k}.png",f'/tmp/{k}.png')[0]) for k in ['ffox','7zip','gimp']]
np.random.seed(0)
locations['img']=np.random.choice(l1, size=len(locations))
# Create a 50-element time-series for each object
timeseries = pd.DataFrame(np.random.randn(n_times, n_objects).cumsum(0),
columns=locations['id'],
index=pd.RangeIndex(0, n_times, name='time'))
# Melt the wide-form timeseries into a long-form view
timeseries = timeseries.reset_index().melt('time')
# Merge the (x, y) metadata into the long-form view
timeseries['id'] = timeseries['id'].astype(int) # make merge not complain
data = pd.merge(timeseries, locations, on='id')
# Data is prepared, now make a chart
selector = alt.selection_single(empty='none', fields=['id'])
base = alt.Chart(data).properties(
width=250,
height=250
).add_selection(selector)
points = base.mark_point(filled=True, size=200).encode(
x='mean(x)',
y='mean(y)',
color=alt.condition(selector, 'id:O', alt.value('lightgray'), legend=None),
)
timeseries = base.mark_line().encode(
x='time',
y=alt.Y('value', scale=alt.Scale(domain=(-15, 15))),
color=alt.Color('id:O', legend=None)
).transform_filter(
selector
)
images=base.mark_image(filled=True, size=200).encode(
x='x',
y='y',
url='img',
).transform_filter(
selector
)
chart=points | timeseries | images
chart.save(f'test/chart_images_{images_from}.html')
# generate htmls
plot_(images_from='url') # generate the HTML using URLs
plot_(images_from='data') # generate the HTML using data/bytes
The HTML made using the data was ~78 times bigger than the one made using URLs (~12Mb vs ~0.16Kb), but not noticeably slower.
Update: As I later found out google site does not allow embedding an HTML file of more than 1Mb size. So in the end, encoding the images did not really help.

Tkinter button and entry

I have a couple of defined functions that I want to create buttons for in my GUI. A couple of these functions require one or two arguments(numbers) and that is what's causing problems for me. I have thought about a combination between a button and an entry where when I click the specific button(for one of my functions), an entry will pop up where I type in a number. Then when I press enter I want this number to be used as the argument for the function I have binded to my button and then the function should be executed.
1 function I want to bind to a button:
def move(power, tacho_units):
MOTOR_CONTROL.cmd(5, power, tacho_units, speedreg=0, smoothstart=1, brake=0)
is_ready(5)
We are working with Lego Mindstorms, so Im pretty sure that for example the function above could be a bit confusing for some people.
from Tkinter import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame, text="Move", command=!_______!)
self.button.pack(side=LEFT)
root = Tk()
app = App(root)
root.mainloop()
root.destroy()
Does someone have any suggestions/solutions for me? I would appreciate if someone could help me. Do I create a function(that will open a new window with an entry) that I call when I click the Move button? The numbers(power and tacho_units in this function) that I type into the entry is what I want to be used for the function Move when I press enter.
Typically, the way to pass arguments to a function associated with a widget is to use lambda or functools.partial (note: these aren't the only ways). Both of these are somewhat advanced topics, but if you just follow an example, it's fairly safe to use them without fully understanding them.
Here's an example using lambda:
b = tk.Button(..., command=lambda power=1, tacho_units="x": move(power, tacho_units)
While not technically correct, just think of a lambda as a "def" but without a name. It takes arguments and can call functions.
Here is the same thing, using functools.partial:
b = tk.Button(..., command=functools.partial(move, power=1, tacho_units="x"))
Note: you'll have to add an import statement for functools.
functools.partial in essence copies the function (in this case, move) and provides default values for the arguments. Thus, when you call it with no arguments (as tkinter does by default), the parameters will have these default values.
HOWEVER...
Often it's easier to write a function to call your function. The purpose of this extra function is to gather the inputs -- presumably from other widgets -- and then call the final function. For example:
def do_move():
power = power_input.get()
tacho_units = tacho_input.get()
move(power, tacho_units)
b = tk.Button(..., command=do_move)
Whether you use this third method depends on where the argument values come from. If you know the values at the time you create the widget, using lambda or functools.partial works because you can embed the arguments right there. If you're going to be getting the parameters from other widgets, the third form is preferable.
Use lambda function to assign function with arguments
some_power = ... # set value
some_tacho_units = ... # set value
self.button = Button(frame, text="Move", command=lambda a=some_power,b=some_tacho_units:move(a, b) )
or
self.button = Button(frame, text="Move", command=lambda:move(5, 10))