/* * Copyright 2017 TWO SIGMA OPEN SOURCE, LLC * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.twosigma.beaker.widgets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.twosigma.beaker.widgets.bools.Checkbox; import com.twosigma.beaker.widgets.floats.FloatSlider; import com.twosigma.beaker.widgets.integers.IntSlider; import com.twosigma.beaker.widgets.selections.Dropdown; import com.twosigma.beaker.widgets.strings.Text; public class InteractiveBase { private static final Logger logger = LoggerFactory.getLogger(InteractiveBase.class); /** * Build a ValueWidget instance given an abbreviation or Widget. * Similar but not equivalent of {@code ipywidgets/widgets/interaction.py#widgets_from_abbreviations} * * @param input * @return */ protected static List<ValueWidget<?>> widgetsFromAbbreviations(Object ...input){ List<ValueWidget<?>> ret = new ArrayList<>(); for (Object param : input) { ValueWidget<?> widget = getWidgetFromAbbrev(param); if(widget == null){ String text = ""; if(input!= null && input[0] != null){ text = input.getClass().getSimpleName(); }else{ text = "null"; } throw new RuntimeException(text + " cannot be transformed to a widget"); }else if(!(widget instanceof ValueWidget)){ throw new RuntimeException(input.getClass().getSimpleName() + " is not a ValueWidget"); } ret.add(widget); } logger.info("total " + ret.size() + " widgets was created"); return ret; } /** * Build a ValueWidget instance given an abbreviation or Widget. * Similar but not equivalent of {@code ipywidgets/widgets/interaction.py#widget_from_abbrev} * * @param input * @return */ protected static ValueWidget<?> getWidgetFromAbbrev(Object ...input){ ValueWidget<?> ret = null; if(input != null && input.length > 0){ ret = widgetFromTuple(input); if(ret == null){ ret = widgetFromSingleValue(input[0]); } if(ret == null){ ret = widgetFromIterable(input[0]); } } return ret; } /** * Make widgets from a tuple abbreviation. * Equivalent of {@code ipywidgets/widgets/interaction.py#widget_from_tuple} * * @param input * @return */ protected static ValueWidget<?> widgetFromTuple(Object ...input){ ValueWidget<?> ret = null; if(input != null && input.length > 0){ boolean isFloat = isFloat(input[0]); boolean isInt = isInt(input[0]); if(input.length > 2){ if(isFloat){ Double[] minMaxValue = getDoubleArray(getMinMaxValue((Double)input[0], (Double)input[1], null)); FloatSlider witget = new FloatSlider(); witget.setMin(minMaxValue[0]); witget.setMax(minMaxValue[1]); witget.setValue(minMaxValue[2]); ret = witget; }else if(isInt){ Integer[] minMaxValue = getIntArray(getMinMaxValue((Integer)input[0], (Integer)input[1], null)); IntSlider witget = new IntSlider(); witget.setMin(minMaxValue[0]); witget.setMax(minMaxValue[1]); witget.setValue(minMaxValue[2]); ret = witget; } }else if(input.length > 3){ if(isFloat){ Double step = (Double)input[2]; if((Double)input[2] <= 0){ throw new RuntimeException("step must be >= 0, not " + step); } Double[] minMaxValue = getDoubleArray(getMinMaxValue((Double)input[0], (Double)input[1], null)); FloatSlider witget = new FloatSlider(); witget.setMin(minMaxValue[0]); witget.setMax(minMaxValue[1]); witget.setValue(minMaxValue[2]); witget.setStep(step); ret = witget; }else if(isInt){ Integer step = (Integer)input[2]; if((Integer)input[2] <= 0){ throw new RuntimeException("step must be >= 0, not " + step); } Integer[] minMaxValue = getIntArray(getMinMaxValue((Integer)input[0], (Integer)input[1], null)); IntSlider witget = new IntSlider(); witget.setMin(minMaxValue[0]); witget.setMax(minMaxValue[1]); witget.setValue(minMaxValue[2]); witget.setStep(step); ret = witget; } } } return ret; } /** * Make widgets from single values, which can be used as parameter defaults. * Equivalent of {@code ipywidgets/widgets/interaction.py#widget_from_tuple} * * @param o * @return */ protected static ValueWidget<?> widgetFromSingleValue(Object o){ ValueWidget<?> ret = null; if (o instanceof String){ Text witget = new Text(); witget.setValue((String)o); ret = witget; }else if (o instanceof Boolean){ Checkbox witget = new Checkbox(); witget.setValue((Boolean)o); ret = witget; }else if (isInt(o)){ Integer value = (Integer)o; Integer[] result = getIntArray(getMinMaxValue(null, null, value)); IntSlider witget = new IntSlider(); witget.setMin(result[0]); witget.setMax(result[1]); witget.setValue(value); ret = witget; }else if (isFloat(o)){ Double value = (Double)o; Double[] result = getDoubleArray(getMinMaxValue(null, null, value)); FloatSlider witget = new FloatSlider(); witget.setMin(result[0]); witget.setMax(result[1]); witget.setValue(value); ret = witget; } return ret; } /** * Make widgets from an iterable. This should not be done for a string or tuple. * Equivalent of {@code ipywidgets/widgets/interaction.py#widget_from_iterable} * * @param o * @return */ @SuppressWarnings("unchecked") protected static Dropdown widgetFromIterable(Object o){ Dropdown ret = null; if (o instanceof Collection<?>){ Collection<Object> value = (Collection<Object>)o; ret = new Dropdown(); ret.setOptions(value); }if (o instanceof Object[]){ Object[] value = (Object[])o; ret = new Dropdown(); ret.setOptions(value); }else if (o instanceof Map<?,?>){ Map<Object,Object> value = (Map<Object,Object>)o; ret = new Dropdown(); ret.setOptions(value.values()); }else{ List<Object> value = new ArrayList<>(1); value.add(o); ret = new Dropdown(); ret.setOptions(value); } return ret; } /** * Return min, max, value given input values with possible None. * Equivalent of {@code ipywidgets/widgets/interaction.py#get_min_max_value} * * @param min * @param max * @param value * @return array: min max value */ protected static Number[] getMinMaxValue(Number min, Number max, Number value){ Number[] ret = new Number[3]; if(value == null){ if( min == null || max == null){ throw new RuntimeException("unable to infer range, value from: (" + min + ", " + max + ", " + value + ")"); } if(isAllInt(min,max)){ int diff = (int)max - (int)min; ret[2] = (Integer) (int)min + (diff / 2); }else{ double diff = (double)max - (double)min; ret[2] = (Double) (double)min + (diff / 2); } ret[0] = min; ret[1] = max; }else{ Number[] vrange = new Number[2]; double dValue = (value instanceof Integer) ? ((Integer) value).doubleValue() : (double) value; boolean isInt = isAllInt(min, max, value); // This gives (0, 1) of the correct type if(dValue == 0){ if(isInt){ vrange[0] = (int)dValue; vrange[1] = (int)dValue + 1; }else{ vrange[0] = dValue; vrange[1] = dValue +1; } }else if(dValue > 0){ if(isInt){ vrange[0] = (int)dValue*-1; vrange[1] = (int)dValue*3; }else{ vrange[0] = dValue*-1; vrange[1] = dValue*3; } }else { if(isInt){ vrange[0] = (int)(dValue*3); vrange[1] = (int)(dValue*-1); }else{ vrange[0] = dValue*3; vrange[1] = dValue*-1; } } if(min == null){ ret[0] = vrange[0]; } if(max == null){ ret[1] = vrange[1]; } ret[2] = value; } return ret; } /** * No equivalent in python. Help method. * * @param input * @return */ protected static Integer[] getIntArray(Number[] input){ return Arrays.copyOf(input, input.length, Integer[].class); } /** * No equivalent in python. Help method. * * @param input * @return */ protected static Double[] getDoubleArray(Number[] input){ return Arrays.copyOf(input, input.length, Double[].class); } /** * No equivalent in python. Help method. * * @param o * @return */ protected static boolean isAllInt(Number ... o){ boolean ret = true; for (Number number : o) { if(number != null){ ret = isInt(number); if(!ret){ break; } } } return ret; } /** * No equivalent in python. Help method. * * @param o * @return {@code (o instanceof Integer || o instanceof Short || o instanceof Byte)} */ protected static boolean isInt(Object o){ return (o instanceof Integer || o instanceof Short || o instanceof Byte); } /** * No equivalent in python. Help method. * * @param o * @return {@code (o instanceof Double || o instanceof Float)} */ protected static boolean isFloat(Object o){ return (o instanceof Double || o instanceof Float); } }