Self-destructive Button inside Column - wolfram-mathematica

How to create a Button which will be displayed only when the value of some global FrontEnd setting is False and will self-destruct with entire row of the Column after pressing it setting this value to True?
I need something like this:
Column[{"Item 1", "Item 2",
Dynamic[If[
Last#Last#Options[$FrontEnd, "VersionedPreferences"] === False,
Button["Press me!",
SetOptions[$FrontEnd, "VersionedPreferences" -> True]],
Sequence ## {}]]}]
But with this code the Button does not disappear after pressing it. Is it possible to make it self-destructive?
The final solution based on ideas by belisarius and mikuszefski:
PreemptProtect[SetOptions[$FrontEnd, "VersionedPreferences" -> False];
b = True];
Dynamic[Column[
Join[{"Item 1", "Item 2"},
If[Last#Last#Options[$FrontEnd, "VersionedPreferences"] === False &&
b == True, {Button[
Pane[Style[
"This FrontEnd uses shared preferences file. Press this \
button to set FrontEnd to use versioned preferences file (all the \
FrontEnd settings will be reset to defaults).", Red], 300],
AbortProtect[
SetOptions[$FrontEnd, "VersionedPreferences" -> True];
b = False]]}, {}]], Alignment -> Center],
Initialization :>
If[! Last#Last#Options[$FrontEnd, "VersionedPreferences"], b = True,
b = False]]
The key points are:
introducing additional Dynamic variable b and binding it with the value of Options[$FrontEnd, "VersionedPreferences"],
wrapping entire Column construct
with Dynamic instead of using
Dynamic inside Column.

Perhaps
PreemptProtect[SetOptions[$FrontEnd, "VersionedPreferences" -> False]; b = True];
Column[{"Item 1", "Item 2", Dynamic[
If[Last#Last#Options[$FrontEnd, "VersionedPreferences"]===False && b == True,
Button["Here!", SetOptions[$FrontEnd, "VersionedPreferences"->True];b=False],
"Done"]]}]
Edit
Answering your comment. Please try the following. Encompassing the Column[ ] with Dynamic[ ] allows resizing it:
PreemptProtect[SetOptions[$FrontEnd, "VersionedPreferences" -> False]; b = True];
Dynamic[
Column[{
"Item 1",
"Item 2",
If[Last#Last#Options[$FrontEnd, "VersionedPreferences"] === False && b == True,
Button["Press me!", SetOptions[$FrontEnd, "VersionedPreferences" -> True]; b=False],
Sequence ## {}]}]]

Hmm, dunno if I get it right, but maybe this:
x = True;
Dynamic[Column[{Button["reset", x = True],
If[x, Button["Press me", x = False]]}]
]

Related

Data Store - dash_table conditional formatting failing

#dashapp.callback(
Output(component_id='data-storage', component_property='data'),
Input(component_id='input', component_property='n_submit')
.
.
.
return json_data
#dashapp.callback(
Output('table', component_property='columns'),
Output('table', component_property='data'),
Output('table', component_property='style_cell_conditional'),
Input(component_id='data-storage', component_property='data'),
.
.
.
column_name = 'Target Column'
value = 'This value is a string'
table_columns = [{"name": i, "id": i} for i in df.columns]
table_data = df.to_dict("records")
conditional_formatting = [{
'if': {
'filter_query': f'{{{column_name}}} = {value}'
},
'backgroundColor': 'white',
'color' : 'black',
}
]
return table_columns, table_data, conditional_formatting
When the code above is used WITH the conditional_formatting part - it works for some 'value's, and does not work for other 'value's
When the code above is used WITHOUT the conditional_formatting part - it works as expected for all 'value's
To be noted that when the conditional_formatting part is used, all callbacks are triggered twice. After this happens, the Data Store acts as if it has been infected by the "sick" value and does not allow new data.
Example:
Step 1. Use working input -> All callbacks triggered once -> Data Store is populated -> Data is displayed as expected
Step 2. Use working input -> All callbacks triggered once -> Data Store is populated -> Data is displayed as expected
Step 3. Use not working input -> All callbacks triggered once -> All callbacks are triggered again -> Data related to Input from b) is displayed
Step 4. Use working input -> All callbacks triggered once -> All callbacks are triggered again -> Data related to Input from b) is displayed
Any ideas why does this happen?
Any feedback is appreciated!
conditional_formatting = [{
'if': {
'filter_query': f'{{{column_name}}} = "{value}"'
},
'backgroundColor': 'white',
'color' : 'black',
}
]
Issue was because the failing values had empty space (e.g. San Francisco). Adding quotes around solved the issue.

How do I interop with this Javascript code, from Fable F#?

I want to create a binding of the Plotly.js library to Fable.
I am looking at this js code
import React from 'react';
import Plot from 'react-plotly.js';
class App extends React.Component {
render() {
return (
<Plot
data={[
{
x: [1, 2, 3],
y: [2, 6, 3],
type: 'scatter',
mode: 'lines+points',
marker: {color: 'red'},
},
{type: 'bar', x: [1, 2, 3], y: [2, 5, 3]},
]}
layout={ {width: 320, height: 240, title: 'A Fancy Plot'} }
/>
);
}
}
and my (faulty) attempt of creating a simple test binding looks like this
open Fable.Core
open Fable.Core.JsInterop
open Browser.Types
open Fable.React
// module Props =
type Chart =
|X of int list
|Y of int List
|Type of string
type IProp =
| Data of obj list
let inline plot (props: IProp) : ReactElement =
ofImport "Plot" "react-plotly.js" props []
let myTrace = createObj [
"x" ==> [1,2,3]
"y" ==> [2,6,3]
"type" ==> "scatter"
"mode" ==> "lines"
]
let myData = Data [myTrace]
let testPlot = plot myData
But obviously it does not work. How do I get it to work? Also, what does {[...]} mean? I am new to Javascript, and as far as I know {...} denotes an object which must contain name value pairs, and [...] denotes an array. So {[...]} seems to denote an object with a single nameless member that is an array, but as far as I know, there are no objects with nameless members.
I have been able to reproduce the example you linked. Please note that I don't Plotly and that I went the empiric way and so things can probably be improved :)
I have created the code as I would probably have done it if I had to use it in my production app. So there is a bit more code than in your question because I don't use createObj.
If you don't like the typed DSL you can always simplify it, remove it and use createObj or anonymous record like I did for the marker property :)
You need to install both react-plotly.js plotly.js in your project.
open Fable.Core.JsInterop
open Fable.Core
open Fable.React
// Define props using DUs this helps create a typed version of the React props
// You can then transform a list of props into an object using `keyValueList`
[<RequireQualifiedAccess>]
type LayoutProps =
| Title of string
| Width of int
| Height of int
// GraphType is marked as a `StringEnum` this means
// the value will be replace at compile time with
// their string representation so:
// `Scatter` becomes `"scatter"`
// You can customise the output by using `[<CompiledName("MyCustomName")>]
[<RequireQualifiedAccess; StringEnum>]
type GraphType =
| Scatter
| Bar
[<RequireQualifiedAccess; StringEnum>]
type GraphMode =
| Lines
| Points
| Markers
| Text
| None
[<RequireQualifiedAccess>]
type DataProps =
| X of obj array
| Y of obj array
| Type of GraphType
| Marker of obj
// This is an helpers to generate the `flagList` waited by Plotly, if you don't like it you can just remove
// member and replace it with `| Mode of string` and so you have to pass the string by yourself
static member Mode (modes : GraphMode seq) : DataProps =
let flags =
modes
|> Seq.map unbox<string> // This is safe to do that because GraphMode is a StringEnum
|> String.concat "+"
unbox ("mode", flags)
[<RequireQualifiedAccess>]
type PlotProps =
| Nothing // Should have real props here is there exist more than Data and Layout
// Here notes that we are asking for an `Array` or Data
// Array being the type expected by the JavaScript library
// `DataProps seq` is our way to represents props
static member Data (dataList : (DataProps seq) array) : PlotProps =
let datas =
dataList
|> Array.map (fun v ->
keyValueList CaseRules.LowerFirst v // Transform the list of props into a JavaScript object
)
unbox ("data", datas)
static member Layout (props : LayoutProps seq) : PlotProps =
unbox ("layout", keyValueList CaseRules.LowerFirst props)
// All the example I saw from react-plotly was using this factory function to transform the plotly library into a React component
// Even, the example you shown if you look at the Babel tab in the live example
let createPlotlyComponent (plotly : obj) = import "default" "react-plotly.js/factory"
// Immport the plotly.js library
let plotlyLib : obj = import "default" "plotly.js"
// Apply the factory on the plotly library
let Plot : obj = createPlotlyComponent plotlyLib
// Helper function to instantiate the react components
// This is really low level, in general we use `ofImport` like you did but if I use `ofImport` then I got a React error
let inline renderPlot (plot : obj) (props : PlotProps list) =
ReactBindings.React.createElement(plot, (keyValueList CaseRules.LowerFirst props), [])
let root =
// Here we can render the plot using our Typed DSL
renderPlot
Plot
[
PlotProps.Data
[|
[
DataProps.X [| 1; 2; 3 |]
DataProps.Y [| 2; 6; 3 |]
DataProps.Type GraphType.Scatter
DataProps.Mode
[
GraphMode.Lines
GraphMode.Points
]
DataProps.Marker {| color = "red" |}
]
[
DataProps.Type GraphType.Bar
DataProps.X [| 1; 2; 3 |]
DataProps.Y [| 2; 5; 3 |]
]
|]
PlotProps.Layout
[
LayoutProps.Width 640
LayoutProps.Height 480
LayoutProps.Title "A Fancy Plot"
]
]
I'm a bit late to the party here, but wanted to give you a different option if you're still looking to use plotly.js with Fable.
I've been working on bindings for plotly.js for the past month or so, and it's in a pretty usable state as of now. That being said, I wouldn't say it's production ready.
This is what the example you want to convert would look like written with Feliz.Plotly:
open Feliz
open Feliz.Plotly
let chart () =
Plotly.plot [
plot.traces [
traces.scatter [
scatter.x [ 1; 2; 3 ]
scatter.y [ 2; 6; 3 ]
scatter.mode [
scatter.mode.lines
scatter.mode.markers
]
scatter.marker [
marker.color color.red
]
]
traces.bar [
bar.x [ 1; 2; 3 ]
bar.y [ 2; 5; 3 ]
]
]
plot.layout [
layout.width 320
layout.height 240
layout.title [
title.text "A Fancy Plot"
]
]
]
You can find more information out here.

Check OptionMenu selection and update GUI

I'm working on a class project and I'm trying to take it beyond the requirements a little here (I'm doing my own homework, just need help improving it!) so I want to update the GUI based on certain selections the user makes instead of just having all irrelevent options available all the time (requirements are to just present the options).
I'm still new to Python and even more new to Tkinter so my only attempt has been the following:
#Step Type
ttk.Label(mainframe, text = "Step Type").grid(column = 1, row = 16)
type_entry = OptionMenu(mainframe, StepType, "Kill", "Explore" , "Conversation")
type_entry.grid(column = 2, row = 16, sticky = (E))
#Step Goal
if StepType.get() == "Kill":
ttk.Label(mainframe, text = "Required Kills").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Explore":
ttk.Label(mainframe, text = "Location ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
elif StepType.get() == "Conversation":
ttk.Label(mainframe, text = "NPC ID").grid(column = 1, row = 17)
goal_entry = ttk.Entry(mainframe, width = 20, textvariable = StepGoal)
goal_entry.grid(column = 2, row = 17, sticky = (E))
Obviously what I want to do here is when the user selects one of the options from the menu, to display the corresponding entry box and label instead of having all 3 all the time.
Also looking for the same situation for CheckButton
Full working example: tested od 2.7.5 and 3.3.2
It use command= in OptionMenu to call function when user changed option.
import tkinter as ttk
#----------------------------------------------------------------------
def on_option_change(event):
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
# show label and entry
#goal_label.grid(column=1, row=17)
#goal_entry.grid(column=2, row=17, sticky='E')
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
type_entry = ttk.OptionMenu(mainframe, step_type, "Kill", "Explore" , "Conversation", command=on_option_change)
type_entry.grid(column=2, row=16, sticky='E')
step_type.set("Kill")
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# hide label and entry
#goal_label.grid_forget()
#goal_entry.grid_forget()
# --- star the engine ---
mainframe.mainloop()
BTW: you can use grid() and grid_forget() to show and hide elements.
EDIT: example with Radiobutton using trace on StringVar
import tkinter as ttk
#----------------------------------------------------------------------
def on_variable_change(a,b,c): # `trace` send 3 argument to `on_variable_change`
#print(a, b, c)
selected = step_type.get()
if selected == "Kill":
goal_label['text'] = "Required Kills"
elif selected == "Explore":
goal_label['text'] = "Location ID"
elif selected == "Conversation":
goal_label['text'] = "NPC ID"
#----------------------------------------------------------------------
mainframe = ttk.Tk()
# Step Type
step_type = ttk.StringVar() # there is the rule: variable name lowercase with _
ttk.Label(mainframe, text="Step Type").grid(column=1, row=16)
ttk.Radiobutton(mainframe, text="Kill", value="Kill", variable=step_type).grid(column=2, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Explore", value="Explore", variable=step_type).grid(column=3, row=16, sticky='E')
ttk.Radiobutton(mainframe, text="Conversation", value="Conversation", variable=step_type).grid(column=4, row=16, sticky='E')
step_type.set("Kill")
# use `trace` after `set` because `on_variable_change` use `goal_label` which is not created yet.
step_type.trace("w", on_variable_change)
# Step Goal
step_goal = ttk.StringVar()
goal_label = ttk.Label(mainframe, text="Required Kills")
goal_label.grid(column=1, row=17)
goal_entry = ttk.Entry(mainframe, width=20, textvariable=step_goal)
goal_entry.grid(column=2, row=17, sticky='E')
# --- star the engine ---
mainframe.mainloop()

PlotLegends` default options

I'm trying to redefine an option of the PlotLegends package after having loaded it,
but I get for example
Needs["PlotLegends`"]
SetOptions[ListPlot,LegendPosition->{0,0.5}]
=> SetOptions::optnf: LegendPosition is not a known option for ListPlot.
I expect such a thing as the options in the PlotLegends package aren't built-in to Plot and ListPlot.
Is there a way to redefine the default options of the PlotLegends package?
The problem is not really in the defaults for PlotLegends`. To see it, you should inspect the ListPlot implementation:
In[28]:= Needs["PlotLegends`"]
In[50]:= DownValues[ListPlot]
Out[50]=
{HoldPattern[ListPlot[PlotLegends`Private`a:PatternSequence[___,
Except[_?OptionQ]]|PatternSequence[],PlotLegends`Private`opts__?OptionQ]]:>
PlotLegends`Private`legendListPlot[ListPlot,PlotLegends`Private`a,
PlotLegend/.Flatten[{PlotLegends`Private`opts}],PlotLegends`Private`opts]
/;!FreeQ[Flatten[{PlotLegends`Private`opts}],PlotLegend]}
What you see from here is that options must be passed explicitly for it to work, and moreover, PlotLegend option must be present.
One way to achieve what you want is to use my option configuration manager, which imitates global options by passing local ones. Here is a version where option-filtering is made optional:
ClearAll[setOptionConfiguration, getOptionConfiguration, withOptionConfiguration];
SetAttributes[withOptionConfiguration, HoldFirst];
Module[{optionConfiguration}, optionConfiguration[_][_] = {};
setOptionConfiguration[f_, tag_, {opts___?OptionQ}, filterQ : (True | False) : True] :=
optionConfiguration[f][tag] =
If[filterQ, FilterRules[{opts}, Options[f]], {opts}];
getOptionConfiguration[f_, tag_] := optionConfiguration[f][tag];
withOptionConfiguration[f_[args___], tag_] :=
f[args, Sequence ## optionConfiguration[f][tag]];
];
To use this, first define your configuration and a short-cut macro, as follows:
setOptionConfiguration[ListPlot,"myConfig", {LegendPosition -> {0.8, -0.8}}, False];
withMyConfig = Function[code, withOptionConfiguration[code, "myConfig"], HoldAll];
Now, here you go:
withMyConfig[
ListPlot[{#, Sin[#]} & /# Range[0, 2 Pi, 0.1], PlotLegend -> {"sine"}]
]
LegendsPosition works in ListPlot without problems (for me at least). You don't happen to have forgotten to load the package by using Needs["PlotLegends"]`?
#Leonid, I added the possibility to setOptionConfiguration to set default options to f without having to use a short-cut macro.
I use the trick exposed by Alexey Popkov in What is in your Mathematica tool bag?
Example:
Needs["PlotLegends`"];
setOptionConfiguration[ListPlot, "myConfig", {LegendPosition -> {0.8, -0.8}},SetAsDefault -> True]
ListPlot[{#, Sin[#]} & /# Range[0, 2 Pi, 0.1], PlotLegend -> {"sine"}]
Here is the implementation
Options[setOptionConfiguration] = {FilterQ -> False, SetAsDefault -> False};
setOptionConfiguration[f_, tag_, {opts___?OptionQ}, OptionsPattern[]] :=
Module[{protectedFunction},
optionConfiguration[f][tag] =
If[OptionValue#FilterQ, FilterRules[{opts},
Options[f]]
,
{opts}
];
If[OptionValue#SetAsDefault,
If[(protectedFunction = MemberQ[Attributes[f], Protected]),
Unprotect[f];
];
DownValues[f] =
Union[
{
(*I want this new rule to be the first in the DownValues of f*)
HoldPattern[f[args___]] :>
Block[{$inF = True},
withOptionConfiguration[f[args], tag]
] /; ! TrueQ[$inF]
}
,
DownValues[f]
];
If[protectedFunction,
Protect[f];
];
];
];

Nice formatting of numbers inside Messages

When printing string with StyleBox by default we get nicely formatted numbers inside string:
StyleBox["some text 1000000"] // DisplayForm
I mean that the numbers look as if would have additional little spaces: "1 000 000".
But in Messages all numbers are displayed without formatting:
f::NoMoreMemory =
"There are less than `1` bytes of free physical memory (`2` bytes \
is free). $Failed is returned.";
Message[f::NoMoreMemory, 1000000, 98000000]
Is there a way to get numbers inside Messages to be formatted?
I'd use Style to apply the AutoNumberFormatting option:
You can use it to target specific messages:
f::NoMoreMemory =
"There are less than `1` bytes of free physical memory (`2` bytes is free). $Failed is returned.";
Message[f::NoMoreMemory,
Style[1000000, AutoNumberFormatting -> True],
Style[98000000, AutoNumberFormatting -> True]]
or you can use it with $MessagePrePrint to apply it to all the messages:
$MessagePrePrint = Style[#, AutoNumberFormatting -> True] &;
Message[f::NoMoreMemory, 1000000, 98000000]
I think you want $MessagePrePrint
$MessagePrePrint =
NumberForm[#, DigitBlock -> 3, NumberSeparator -> " "] &;
Or, incorporating Sjoerd's suggestion:
With[
{opts =
AbsoluteOptions[EvaluationNotebook[],
{DigitBlock, NumberSeparator}]},
$MessagePrePrint = NumberForm[#, Sequence ## opts] &];
Adapting Brett Champion's method, I believe this allows for copy & paste as you requested:
$MessagePrePrint = StyleForm[#, AutoNumberFormatting -> True] &;

Resources