How do I make an edit_traits() GUI item responsive to changes in its dependencies? - properties

I'm designing a HasTraits subclass with dependent properties:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Trait, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
#cached_property
def _get_bar(self):
return Trait(list(self._dicts)[self.zap], self._dicts)
traits_view = View(
Item("zap"),
Item("bar"),
Item("baz"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
When I run this code I see:
And if I change the value of Zap:
Note the following:
After changing Zap, the address of Bar has changed.
This means that changes to Bar are being dynamically updated in the GUI, while it's still opened; that's great! However...
The way Bar is displayed in the GUI is not very useful.
I'd love to have Bar displayed as Baz is displayed: selectable by the user.
What I'd like is to have the best of both worlds:
the dynamic GUI updating I see with Bar, and
the display format of Baz.
Does anyone know how I can get this?
I've tried several ways of updating a Baz-like item dynamically, to no avail.
(See this previous SO question.)

I assume you wish both bar and baz to be dict type (in traits Dict). Actually, there are default display widgets for pre-defined trait types, which are more useful than showing address. I believe traitsui doesn't know how to properly display your custom Trait object unless you explicitly assign an editor for it. Note that for baz, although a dropdown menu is generated, it is only displaying the keys, which is not very useful either.
With that said, the following codes might meet your expectations.
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dicts = [
{"zero": 0, "one": 1},
{"zero": 1, "one": 2},
{"zero": 2, "one": 3},
]
zap = Int(0)
bar = Property(Dict, depends_on=["zap"])
baz = Trait(list(_dicts[0])[0], _dicts[0])
#cached_property
def _get_bar(self):
return self._dicts[self.zap]
traits_view = View(
Item("zap"),
Item("bar", style="custom"),
Item("baz"),
width=500,
)

The following code gets me the behavior I want:
#!/usr/bin/env python
# Example for SO question on dynamically changing Dict contents.
from traits.api import HasTraits, Dict, Property, Trait, Int, cached_property, Enum, List
from traitsui.api import View, Item
class Foo(HasTraits):
"Has dependent properties, which I'd like to remain up-to-date in the GUI."
_dict = {
"zero": 0,
"one": 1,
"two": 2,
}
_zaps = [
["zero", "one"],
["one", "two"],
["zero", "two"],
]
zaps = List(_zaps[0])
zap = Enum([0,1,2]) # Selection of `zap` should limit the items of `_dict` available for selection.
bar = Enum(_zaps[0][0], values="zaps")
bar_ = Int(_dict[_zaps[0][0]])
def _zap_changed(self, new_value):
self.zaps = self._zaps[new_value]
self.bar_ = self._dict[self.bar]
def _bar_changed(self, new_value):
self.bar_ = self._dict[self.bar]
traits_view = View(
Item("zap"),
Item("bar"),
Item("bar_", style="readonly"),
width=500,
)
if __name__ == '__main__':
Foo().configure_traits()
Immediately after program start-up:
And after changing to Zap to '1':

Related

Pipeline generation - passing in simple datastructures like lists/arrays

For a code repository project in Palantir Foundry, I am struggling with re-using some of my transformation logic.
It seems almost trivial, but: is there way to send an Input to a Transform that is not a dataset/dataframe reference?
In my case I want to pass in strings or lists/arrays.
This is my code:
from pyspark.sql import functions as F
from transforms.api import Transform, Input, Output
def my_computation(result, customFilter, scope, my_categories, my_mappings):
scope_df = scope.dataframe()
my_categories_df = my_categories.dataframe()
my_mappings_df = my_mappings.dataframe()
filtered_cat_df = (
my_categories_df
.filter(F.col('CAT_NAME').isin(customFilter))
)
# ... more logic
def generateTransforms(config):
transforms = []
for key, value in config.items():
o = {}
for outKey, outValue in value['outputs'].items():
o[outKey] = Output(outValue)
i = {}
for inpKey, inpValue in value['inputs'].items():
i[inpKey] = Input(inpValue)
i['customFilter'] = Input(value['my_custom_filter'])
transforms.append(Transform(my_computation, inputs=i, outputs=o))
return transforms
config = {
"transform_one": {
"my_custom_filter": {
"foo",
"bar"
},
"inputs": {
"scope": "/my-project/input/scope",
"my_categories": "/my-project/input/my_categories",
"my_mappings": "/my-project/input/my_mappings"
},
"outputs": {
"result": "/my-project/output/result"
}
}
}
TRANSFORMS = generateTransforms(config)
The concrete question is: how can I send in the values from my_custom_filter into customFilter in the transformation function my_computation?
If I execute it like above, I get the error "TypeError: unhashable type: 'set'"
This looks like a python issue, any chance you can point out which line is causing the error?
Reading throung your code, I would guess it's this line:
i['customFilter'] = Input(value['my_custom_filter'])
Your python logic is wrong, if we unpack your code you're trying to do this call:
i['customFilter'] = Input({"foo", "bar"})
Edit to answer the comment on how to create a python transform to lock a variable in a closure:
def create_transform(inputs={}, outputs={}, my_other_var):
#transform(**inputs, **outputs)
def compute(input_foo, input_bar, output_foobar, ctx):
df = input_foo.dataframe()
df = df.withColumn("mycol", F.lit(my_other_var))
output_foorbar.write_dataframe(df)
return compute
and now you can call this:
transforms.append(create_tranform(inputs, outptus, "foobar"))

How can one dynamically update a progress indicator while a calculation started by changing an InputField value is running?

I have a Mathematica notebook that employs a fairly complicated user interface for controlling a long-running calculation. Among other things, the interface takes liberal advantage of Button, RadioButtonBar, Checkbox, and InputField.
When the effect of clicking a Button is an intermediate calculation that may take more than a couple seconds to complete, I like to provide a visual indication that the code hasn't crashed and is, in fact, doing something useful. A good way to do this is to start up a ProgressIndicator just before the intermediate calculation starts and then turn it off once the calculation is done. I have found this to be straightforward for calculations started by a Button click.
The same method does not work, however, for calculations that are initiated by changes to an InputField value. The simplified code below was written to do this but fails. The last two rows of the Grid are supposed to change automatically when updatingQ changes to True in the inner Dynamic command and then change back when updatingQ reverts to True, but it never happens. It appears that the outer Dynamic code is being blocked while the inner Dynamic code runs so it never even notices the changes to updatingQ.
On the other hand, the last two lines of the Grid respond as expected if one manually sets updatingQ=True on a separate input line.
(BTW, i) Pause[2] is just a stand-in for the intermediate calculation and ii) I multiply the input value by Pi is just to make it more obvious when the stand-in calculation is done.)
Apparently, the action portion of a Button behaves differently. Other pieces of code within the same Dynamic block can see and quickly respond when flags are changed there. It may be notable that I use Method->"Queued" in such cases. I tried the same with InputField (for which it is not a documented option) but to no effect.
I've tried various other things not shown here also without success.
A way to make this work would be much appreciated.
Clear[ProgressIndicatorTest]
updatingQ = False;
ProgressIndicatorTest = {
TextCell["ProgressIndicatorTest", "Subsubsection", Background -> LightBlue],
DynamicModule[
{filterTypes = {"Max energy", "Max length"}, filterValue, workingOn = "", iter = 0},
Scan[(filterValue[#[[1]]] = #[[2]]) &, Transpose#{filterTypes, {0.1, 100.}}];
Dynamic[
Grid[
Join[
Map[
Function[
filterType,
{filterType,
Dynamic#
InputField[
Dynamic[
filterValue[filterType],
Function[
value,
If[value > 0,
updatingQ = True;
Pause[2];
filterValue[filterType] = \[Pi] value;
updatingQ = False
]
]
], FieldSize -> 5, Alignment -> Right
]
}
], filterTypes
],
{{updatingQ, "-------"}},
{If[updatingQ,
{"Updating ... ",
ProgressIndicator[Appearance -> "Indeterminate"]},
Nothing
]}
], Alignment -> Left,
Background -> {None, {LightGreen, LightGreen, LightYellow, LightYellow}}
]
]
]
};
CellGroup[ProgressIndicatorTest]
As Forrest Gump never said, "Stackoverflow/Stackexchange is like a box of chocolates ... you never know what you'll get". And so today I found this answer which solves my problem.
Adapted to my particular case, the resulting code is as follows:
Clear[ProgressIndicatorTest]
calculation[n_] := Module[{a = .3}, Do[a = a (1 - a), {i, n 10^6}]]
updatingQ = False;
ProgressIndicatorTest = {
TextCell["ProgressIndicatorTest", "Subsubsection", Background -> LightBlue],
DynamicModule[{filterTypes = {"Max energy", "Max length"}, filterValue, upToDateQ = True},
Scan[(filterValue[#[[1]]] = #[[2]]) &, Transpose#{filterTypes, {0.1, 100.}}];
Dynamic[
Grid[
Join[
Map[
Function[
filterType,
{filterType,
DynamicWrapper[
InputField[
Dynamic[
filterValue[filterType],
Function[
value,
If[value > 0,
upToDateQ = False;
filterValue[filterType] = value
]
]
], FieldSize -> 5, Alignment -> Right
],
If[! upToDateQ,
Refresh[
updatingQ = True; calculation[2]; updatingQ = False;
upToDateQ = True,
None
]
],
SynchronousUpdating -> False
]
}
], filterTypes
],
{
If[updatingQ,
{"Updating ... ",
ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 80]},
Nothing
]
}
], Alignment -> Left,
Background -> {None, {LightGreen, LightGreen, LightYellow,}}]
]]
};
CellGroup[ProgressIndicatorTest]
This code does exactly what I want.
The key to success is wrapping DynamicWrapper around InputField and inserting a cleverly constructed second argument that performs the flag reset (upToDate=False in my case) that triggers the ProgressIndicator located elsewhere.
A couple more points.
Pause turns out not to be a good stand-in for a calculation. You may observe that the code behaves differently with a real function such as calculation.
It is interesting to note that upToDateQ can be a local variable whereas updatingQ cannot.
Kudos to Albert Retey for providing the code back in 2013.
The documentation for InputField says
"An InputField of type Expression [the default] replaces its
contents with the fully evaluated form every time the contents are
updated".
This seems to mean that InputField privately evaluates its content and all connected dynamics before releasing value changes, probably to prevent circular evaluations.
The following example condenses the problem. The first part works ok ...
changed = processing = False;
Column[{InputField[Dynamic[x, (changed = True; x = 2 #) &], FieldSize -> 5],
Dynamic[changed],
Dynamic[processing]}]
... until the dynamic below is also evaluated. Then changed never shows True because it is changed back to False before the update concludes.
Dynamic[If[changed,
processing = True;
Pause[2];
changed = processing = False]]
A alternative strategy would be to use a Button, e.g.
changed = False;
processing = Spacer[0];
Column[{InputField[Dynamic[y, (changed = True; y = #) &], FieldSize -> 5],
Button["Enter",
If[changed,
processing = ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 120];
Pause[2];
y = 2 y;
changed = False;
processing = Spacer[0]], Method -> "Queued", Enabled -> Dynamic[changed]],
Dynamic[changed],
Dynamic[processing]}]
This shorter version avoids the need to tab out of the input field.
changed = False;
processing = Spacer[0];
Column[{InputField[Dynamic[y], FieldSize -> 5],
Button["Enter",
processing = ProgressIndicator[Appearance -> "Indeterminate", ImageSize -> 120];
Pause[2];
y = 2 y;
processing = Spacer[0], Method -> "Queued"], Dynamic[processing]}]
Note the use of Method -> "Queued" gives Button the advantage over InputField. Without it Button appears to have the same problem.

How to add a second dataset to a plotly annotated heatmap?

I'm trying to create an annotated heatmap with a dropdown menu to switch between two different sets of data. The datasets have the same format and I have added a working dropdown menu. But I can only add one dataset at a time. I am using
fig = ff.create_annotated_heatmap(data, annotation_text=numbers, showscale=True, colorscale=colorscale, text=hover, hoverinfo='text')
to create the annotated heatmap. Is there a way to add a second dataset to switch between with the dropdown menu?
Resolved. Had to add the second data set to the args of the dropdown menu object
along with any other changes needed (such as hover text)
I just realized how easy it is to switch between two plots with a menu. You can just get the data from each figure to create a list of traces to swithc between
from plotly.offline import init_notebook_mode, iplot
import plotly.figure_factory as ff
init_notebook_mode(connected=True)
fig_1 = ff.create_annotated_heatmap(...)
fig_2 = ff.create_annotated_heatmap(...)
menu_items = ["Heatmap 1", "Heatmap 2"]
trace1 = fig_1.to_dict()["data"][0]
trace2 = fig_2.to_dict()["data"][0]
buttons = []
for i, menu_item in enumerate(menu_items):
visibility = [i==j for j in range(len(menu_items))]
button = dict(
label = menu_item,
method = 'update',
args = [{'visible': visibility},
{ 'title' : menu_item }])
buttons.append(button)
updatemenus = list([
dict(buttons = buttons)
])
layout = dict(updatemenus = updatemenus, title=menu_items[0])
fig = dict(data=[trace1, trace2], layout=layout)
iplot(fig)

trie implementation in python , object reference

im looking at the following implementation of trie in python:
tree = {}
def add_to_tree(root, value_string):
for character in value_string:
root = root.setdefault(character, {})
def main():
tree={}
add_to_tree(tree, 'abc')
print tree
if __name__=="__main__":
main()
What is not clear to me is:
why is it returning {a:{b:{c:{}}}} instead of {a:{},b:{},c:{}} ?
I ran the code through this which gives a visualization of it. After iterating though 'a' I get tree = {'a':{}}, root = {} then after 'b' I get tree = {a:{b:{}}}, root={}. Whats not clear is what variable is holding the reference to {b:{}} which gets assigned to {a:{}} to change it to {a:{b:{}}} ?
You are reassigning root to the newly created dict for every character, change this line:
root = root.setdefault(character, {})
To becomes:
root.setdefault(character, {})
This gives the desired output (note that dict are unordered):
{'a': {}, 'c': {}, 'b': {}}

Ironpython in Spotfire - draw curve and modify chart

I have a code that changes my visualisation type from a Line to a Bar chart based on a property type. I need add a query to draw a straight line based on a property within my visualisation. The code I have so far is this:
from Spotfire.Dxp.Application.Visuals import VisualContent
from Spotfire.Dxp.Application.Visuals import VisualTypeIdentifiers
vc1 = viz1.As[VisualContent]()
Yaxis=vc1.YAxis.Expression
Xaxis=vc1.XAxis.Expression
ColorAxis=vc1.ColorAxis.Expression
if type=="LINE":
viz1.TypeId = VisualTypeIdentifiers.LineChart
if type == "BAR":
viz1.TypeId = VisualTypeIdentifiers.BarChart
vc1.XAxis.Expression=Xaxis
vc1.YAxis.Expression=Yaxis
vc1.ColorAxis.Expression=ColorAxis
Thanks in advance for your help!
from Spotfire.Dxp.Application.Visuals import LineChart
if vis.As[LineChart]().FittingModels[0].Enabled == False:
vis.As[LineChart]().FittingModels[0].Enabled = True
else:
vis.As[LineChart]().FittingModels[0].Enabled = False
This code requires a vis parameter defined as Type: Visualization Value: Page > Line Chart