Repository: pyqtgraph/pyqtgraph

Issue:
Allow Parameter to have no default value.
Currently the default is always set to None, while it's clear from the rest of the code that a missing 'default' key in Parameter.opts is meant to represent the case of a missing default.

Edit: To be more clear, the default is set to None if neither 'value' nor 'default' are explicitly set.



Discussion:
When no default is set, the behavior is:
- Parameter.defaultValue raises a KeyError
- Parameter.valueIsDefault returns None, instead of True or False
- The button is hidden

Does everyone else agree with these choices?

## [Current coverage](https://codecov.io/gh/pyqtgraph/pyqtgraph/pull/331?src=pr) is **33.78%**

> Merging [#331](https://codecov.io/gh/pyqtgraph/pyqtgraph/pull/331?src=pr) into [develop](https://codecov.io/gh/pyqtgraph/pyqtgraph/branch/develop?src=pr) will increase coverage by **1.13%**

``` diff
@@            develop       #331   diff @@
==========================================
  Files           198        198          
  Lines         27088      27089     +1   
  Methods           0          0          
  Messages          0          0          
  Branches       4512       4513     +1   
==========================================
+ Hits           8844       9153   +309   
+ Misses        17481      17140   -341   
- Partials        763        796    +33   
```

> Powered by [Codecov](https://codecov.io?src=pr). Last updated by [670d63c...bd4c459](https://codecov.io/gh/pyqtgraph/pyqtgraph/compare/670d63cdf443d667eece3b203083692588f41693...bd4c459e08a6f6d69f962fffa32e4875d6707067)


Resolving patch:
diff --git a/pyqtgraph/parametertree/Parameter.py b/pyqtgraph/parametertree/Parameter.py
--- a/pyqtgraph/parametertree/Parameter.py
+++ b/pyqtgraph/parametertree/Parameter.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 from ..Qt import QtGui, QtCore
 import os, weakref, re
 from ..pgcollections import OrderedDict
@@ -188,13 +189,11 @@ def __init__(self, **opts):
         
         self.addChildren(self.opts.pop('children', []))
         
-        self.opts['value'] = None
         if value is not None:
             self.setValue(value)
 
         if 'default' not in self.opts:
             self.opts['default'] = None
-            self.setDefault(self.opts['value'])
     
         ## Connect all state changed signals to the general sigStateChanged
         self.sigValueChanged.connect(lambda param, data: self.emitStateChanged('value', data))
@@ -420,7 +419,7 @@ def setToDefault(self):
 
     def hasDefault(self):
         """Returns True if this parameter has a default value."""
-        return 'default' in self.opts
+        return self.opts['default'] is not None
         
     def valueIsDefault(self):
         """Returns True if this parameter's value is equal to the default value."""


Test patch:
diff --git a/pyqtgraph/parametertree/tests/test_Parameter.py b/pyqtgraph/parametertree/tests/test_Parameter.py
new file mode 100644
--- /dev/null
+++ b/pyqtgraph/parametertree/tests/test_Parameter.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+import pytest
+from pyqtgraph.parametertree import Parameter
+
+
+def test_parameter_hasdefault():
+    opts = {'name': 'param', 'type': int, 'value': 1}
+
+    # default unspecified
+    p = Parameter(**opts)
+    assert not p.hasDefault()
+
+    p.setDefault(1)
+    assert p.hasDefault()
+    assert p.defaultValue() == 1
+
+    # default specified
+    p = Parameter(default=0, **opts)
+    assert p.hasDefault()
+    assert p.defaultValue() == 0
+
+
+@pytest.mark.parametrize('passdefault', [True, False])
+def test_parameter_hasdefault_none(passdefault):
+    # test that Parameter essentially ignores defualt=None, same as not passing
+    # a default at all
+    opts = {'name': 'param', 'type': int, 'value': 0}
+    if passdefault:
+        opts['default'] = None
+
+    p = Parameter(**opts)
+    assert not p.hasDefault()
+    assert p.defaultValue() is None
+
+    p.setDefault(None)
+    assert not p.hasDefault()