Repository: holoviz/panel

Issue:
Interact should accept tuple of min, max, and value as input
At the moment interact only supports passing value OR (min, max). It would be very helpful to be able to pass all three. For context - this came up in the context of making widgets from intake user parameters which have default values in addition to min, and max. 


Discussion:
Definitely.  PR?
The problem is that a tuple of three currently means (min, max, step) so either the value could be the 4th item, or the meaning of a 3 tuple would change.


It would need to be the 4th item, then.
For clarity - it would work just as well for my use case if there were a way to set function defaults from a dict. 

This works as expected:
```python
@interact(year=(2017, 2018), month=(1, 12), day=(1, 31))
def day(year=2018, month=1, day=1):
    return cat.anaconda_package_data_by_day(**locals())
```
But I need to set the interact kwargs with 

```python
ranges = {}
defaults = {}
for up in cat.anaconda_package_data_by_day._user_parameters:
    ranges[up.name] = (up.min, up.max)
    defaults[up.name] = up.default
print(ranges, defaults)
```

```
({'year': (2017, 2018), 'month': (1, 12), 'day': (1, 31)},
 {'year': 2018, 'month': 12, 'day': 1})
```

The issue is that those default values are in a dict and I don't know how to extract them into the function definition.

Resolving patch:
diff --git a/panel/interact.py b/panel/interact.py
--- a/panel/interact.py
+++ b/panel/interact.py
@@ -251,7 +251,7 @@ def widgets_from_abbreviations(self, seq):
     def applies(cls, object):
         return isinstance(object, types.FunctionType)
 
-    @classmethod    
+    @classmethod
     def widget_from_abbrev(cls, abbrev, name, default=empty):
         """Build a ValueWidget instance given an abbreviation or Widget."""
         if isinstance(abbrev, Widget):
@@ -324,6 +324,23 @@ def widget_from_tuple(o, name, default=empty):
             else:
                 cls = FloatSlider
             return cls(value=value, start=min, end=max, step=step, name=name)
+        elif _matches(o, (Real, Real, Real, Real)):
+            step = o[2]
+            if step <= 0:
+                raise ValueError("step must be >= 0, not %r" % step)
+            min, max, value = _get_min_max_value(o[0], o[1], value=o[3], step=step)
+            if all(isinstance(_, Integral) for _ in o):
+                cls = IntSlider
+            else:
+                cls = FloatSlider
+            return cls(value=value, start=min, end=max, step=step, name=name)
+        elif len(o) == 4:
+            min, max, value = _get_min_max_value(o[0], o[1], value=o[3])
+            if all(isinstance(_, Integral) for _ in [o[0], o[1], o[3]]):
+                cls = IntSlider
+            else:
+                cls = FloatSlider
+            return cls(value=value, start=min, end=max, name=name)
 
     @staticmethod
     def widget_from_iterable(o, name):


Test patch:
diff --git a/panel/tests/test_interact.py b/panel/tests/test_interact.py
--- a/panel/tests/test_interact.py
+++ b/panel/tests/test_interact.py
@@ -103,6 +103,41 @@ def test(a=3.1):
     assert widget.start == 0
     assert widget.end == 4
 
+def test_tuple_range_interact_with_step_and_value():
+    def test(a):
+        return a
+
+    interact_pane = interactive(test, a=(0, 4, 1, 0))
+    widget = interact_pane._widgets['a']
+    assert isinstance(widget, widgets.IntSlider)
+    assert widget.value == 0
+    assert widget.start == 0
+    assert widget.step == 1
+    assert widget.end == 4
+
+def test_tuple_float_range_interact_with_step_and_value():
+    def test(a):
+        return a
+
+    interact_pane = interactive(test, a=(0, 4, .1, 0))
+    widget = interact_pane._widgets['a']
+    assert isinstance(widget, widgets.FloatSlider)
+    assert widget.value == 0
+    assert widget.start == 0
+    assert widget.step == .1
+    assert widget.end == 4
+
+def test_tuple_range_interact_with_no_step_and_value():
+    def test(a):
+        return a
+
+    interact_pane = interactive(test, a=(0, 4, None, 0))
+    widget = interact_pane._widgets['a']
+    assert isinstance(widget, widgets.IntSlider)
+    assert widget.value == 0
+    assert widget.start == 0
+    assert widget.end == 4
+
 def test_numeric_list_interact():
     def test(a):
         return a