We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
examples/ScatterPlotWidget.py Color Map throws exception
<!-- In the following, please describe your issue in detail! -->
<!-- If some of the sections do not apply, just remove them. -->

### Short description
<!-- This should summarize the issue. -->
examples/ScatterPlotWidget.py which makes use of Parameter Trees throws exception since #1919 (4bf1866c2a28b237ca8ca06ac668686b92ccf967 from bisection)

### Code to reproduce
<!-- Please provide a minimal working example that reproduces the issue in the code block below.
     Ideally, this should be a full example someone else could run without additional setup. -->
run ```examples/ScatterPlotWidget.py```
select ```Color Map``` -> ```Add Mapping``` -> ```x_pos```
an exception will be thrown

### Expected behavior
<!-- What should happen? -->
no exception thrown

### Real behavior
<!-- What happens? -->
following exception thrown

```
Traceback (most recent call last):
  File "d:\github\pyqtgraph\pyqtgraph\widgets\ScatterPlotWidget.py", line 168, in updatePlot
    colors = np.array([fn.mkBrush(*x) for x in self.colorMap.map(data)])
  File "d:\github\pyqtgraph\pyqtgraph\widgets\ColorMapWidget.py", line 150, in map
    colors2 = item.map(data)
  File "d:\github\pyqtgraph\pyqtgraph\widgets\ColorMapWidget.py", line 216, in map
    colors = cmap.map(scaled, mode='float')
AttributeError: 'NoneType' object has no attribute 'map'
```

### Tested environment(s)

 * PyQtGraph version: 0.12.3 <!-- output of pyqtgraph.__version__ -->
 * Qt Python binding: PySide6 6.2.0 <!-- output of pyqtgraph.Qt.VERSION_INFO -->
 * Python version: 3.8.10
 * NumPy version: 1.21.2 <!-- output of numpy.__version__ -->
 * Operating system: Windows 10
 * Installation method: pip install -e . <!-- e.g. pip, conda, system packages, ... -->

### Additional context


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyqtgraph/widgets/ColorMapWidget.py`
Content:
```
1 from collections import OrderedDict
2 
3 import numpy as np
4 
5 from .. import functions as fn
6 from .. import parametertree as ptree
7 from ..Qt import QtCore
8 
9 __all__ = ['ColorMapWidget']
10 
11 class ColorMapWidget(ptree.ParameterTree):
12     """
13     This class provides a widget allowing the user to customize color mapping
14     for multi-column data. Given a list of field names, the user may specify
15     multiple criteria for assigning colors to each record in a numpy record array.
16     Multiple criteria are evaluated and combined into a single color for each
17     record by user-defined compositing methods.
18     
19     For simpler color mapping using a single gradient editor, see 
20     :class:`GradientWidget <pyqtgraph.GradientWidget>`
21     """
22     sigColorMapChanged = QtCore.Signal(object)
23     
24     def __init__(self, parent=None):
25         ptree.ParameterTree.__init__(self, parent=parent, showHeader=False)
26         
27         self.params = ColorMapParameter()
28         self.setParameters(self.params)
29         self.params.sigTreeStateChanged.connect(self.mapChanged)
30         
31         ## wrap a couple methods 
32         self.setFields = self.params.setFields
33         self.map = self.params.map
34 
35     def mapChanged(self):
36         self.sigColorMapChanged.emit(self)
37 
38     def widgetGroupInterface(self):
39         return (self.sigColorMapChanged, self.saveState, self.restoreState)
40 
41     def saveState(self):
42         return self.params.saveState()
43 
44     def restoreState(self, state):
45         self.params.restoreState(state)
46         
47     def addColorMap(self, name):
48         """Add a new color mapping and return the created parameter.
49         """
50         return self.params.addNew(name)
51 
52 
53 class ColorMapParameter(ptree.types.GroupParameter):
54     sigColorMapChanged = QtCore.Signal(object)
55     
56     def __init__(self):
57         self.fields = {}
58         ptree.types.GroupParameter.__init__(self, name='Color Map', addText='Add Mapping..', addList=[])
59         self.sigTreeStateChanged.connect(self.mapChanged)
60         
61     def mapChanged(self):
62         self.sigColorMapChanged.emit(self)
63         
64     def addNew(self, name):
65         fieldSpec = self.fields[name]
66         
67         mode = fieldSpec.get('mode', 'range')        
68         if mode == 'range':
69             item = RangeColorMapItem(name, self.fields[name])
70         elif mode == 'enum':
71             item = EnumColorMapItem(name, self.fields[name])
72 
73         defaults = fieldSpec.get('defaults', {})
74         for k, v in defaults.items():
75             if k == 'colormap':
76                 if mode == 'range':
77                     item.setValue(v)
78                 elif mode == 'enum':
79                     children = item.param('Values').children()
80                     for i, child in enumerate(children):
81                         try:
82                             child.setValue(v[i])
83                         except IndexError:
84                             continue
85             else:
86                 item[k] = v
87 
88         self.addChild(item)
89         return item
90         
91     def fieldNames(self):
92         return list(self.fields.keys())
93     
94     def setFields(self, fields):
95         """
96         Set the list of fields to be used by the mapper. 
97         
98         The format of *fields* is::
99         
100             [ (fieldName, {options}), ... ]
101         
102         ============== ============================================================
103         Field Options:
104         mode           Either 'range' or 'enum' (default is range). For 'range', 
105                        The user may specify a gradient of colors to be applied 
106                        linearly across a specific range of values. For 'enum', 
107                        the user specifies a single color for each unique value
108                        (see *values* option).
109         units          String indicating the units of the data for this field.
110         values         List of unique values for which the user may assign a 
111                        color when mode=='enum'. Optionally may specify a dict 
112                        instead {value: name}.
113         defaults       Dict of default values to apply to color map items when
114                        they are created. Valid keys are 'colormap' to provide
115                        a default color map, or otherwise they a string or tuple
116                        indicating the parameter to be set, such as 'Operation' or
117                        ('Channels..', 'Red').
118         ============== ============================================================
119         """
120         self.fields = OrderedDict(fields)
121         #self.fields = fields
122         #self.fields.sort()
123         names = self.fieldNames()
124         self.setAddList(names)
125         
126     def map(self, data, mode='byte'):
127         """
128         Return an array of colors corresponding to *data*. 
129         
130         ==============  =================================================================
131         **Arguments:**
132         data            A numpy record array where the fields in data.dtype match those
133                         defined by a prior call to setFields().
134         mode            Either 'byte' or 'float'. For 'byte', the method returns an array
135                         of dtype ubyte with values scaled 0-255. For 'float', colors are
136                         returned as 0.0-1.0 float values.
137         ==============  =================================================================
138         """
139         if isinstance(data, dict):
140             data = np.array([tuple(data.values())], dtype=[(k, float) for k in data.keys()])
141 
142         colors = np.zeros((len(data),4))
143         for item in self.children():
144             if not item['Enabled']:
145                 continue
146             chans = item.param('Channels..')
147             mask = np.empty((len(data), 4), dtype=bool)
148             for i,f in enumerate(['Red', 'Green', 'Blue', 'Alpha']):
149                 mask[:,i] = chans[f]
150             
151             colors2 = item.map(data)
152             
153             op = item['Operation']
154             if op == 'Add':
155                 colors[mask] = colors[mask] + colors2[mask]
156             elif op == 'Multiply':
157                 colors[mask] *= colors2[mask]
158             elif op == 'Overlay':
159                 a = colors2[:,3:4]
160                 c3 = colors * (1-a) + colors2 * a
161                 c3[:,3:4] = colors[:,3:4] + (1-colors[:,3:4]) * a
162                 colors = c3
163             elif op == 'Set':
164                 colors[mask] = colors2[mask]            
165                 
166         colors = fn.clip_array(colors, 0., 1.)
167         if mode == 'byte':
168             colors = (colors * 255).astype(np.ubyte)
169         
170         return colors
171             
172     def saveState(self):
173         items = OrderedDict()
174         for item in self:
175             itemState = item.saveState(filter='user')
176             itemState['field'] = item.fieldName
177             items[item.name()] = itemState
178         state = {'fields': self.fields, 'items': items}
179         return state
180 
181     def restoreState(self, state):
182         if 'fields' in state:
183             self.setFields(state['fields'])
184         for name, itemState in state['items'].items():
185             item = self.addNew(itemState['field'])
186             item.restoreState(itemState)
187         
188     
189 class RangeColorMapItem(ptree.types.SimpleParameter):
190     mapType = 'range'
191     
192     def __init__(self, name, opts):
193         self.fieldName = name
194         units = opts.get('units', '')
195         ptree.types.SimpleParameter.__init__(self, 
196             name=name, autoIncrementName=True, type='colormap', removable=True, renamable=True, 
197             children=[
198                 #dict(name="Field", type='list', value=name, limits=fields),
199                 dict(name='Min', type='float', value=0.0, suffix=units, siPrefix=True),
200                 dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),
201                 dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),
202                 dict(name='Channels..', type='group', expanded=False, children=[
203                     dict(name='Red', type='bool', value=True),
204                     dict(name='Green', type='bool', value=True),
205                     dict(name='Blue', type='bool', value=True),
206                     dict(name='Alpha', type='bool', value=True),
207                     ]),
208                 dict(name='Enabled', type='bool', value=True),
209                 dict(name='NaN', type='color'),
210             ])
211 
212     def map(self, data):
213         data = data[self.fieldName]
214         
215         scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
216         cmap = self.value()
217         colors = cmap.map(scaled, mode='float')
218         
219         mask = np.invert(np.isfinite(data))
220         nanColor = self['NaN']
221         nanColor = nanColor.getRgbF()
222         colors[mask] = nanColor
223         
224         return colors        
225 
226 class EnumColorMapItem(ptree.types.GroupParameter):
227     mapType = 'enum'
228     
229     def __init__(self, name, opts):
230         self.fieldName = name
231         vals = opts.get('values', [])
232         if isinstance(vals, list):
233             vals = OrderedDict([(v,str(v)) for v in vals])        
234         childs = []
235         for val,vname in vals.items():
236             ch = ptree.Parameter.create(name=vname, type='color')
237             ch.maskValue = val
238             childs.append(ch)
239         
240         ptree.types.GroupParameter.__init__(self, 
241             name=name, autoIncrementName=True, removable=True, renamable=True, 
242             children=[
243                 dict(name='Values', type='group', children=childs),
244                 dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),
245                 dict(name='Channels..', type='group', expanded=False, children=[
246                     dict(name='Red', type='bool', value=True),
247                     dict(name='Green', type='bool', value=True),
248                     dict(name='Blue', type='bool', value=True),
249                     dict(name='Alpha', type='bool', value=True),
250                     ]),
251                 dict(name='Enabled', type='bool', value=True),
252                 dict(name='Default', type='color'),
253             ])
254     
255     def map(self, data):
256         data = data[self.fieldName]
257         colors = np.empty((len(data), 4))
258         default = np.array(self['Default'].getRgbF())
259         colors[:] = default
260         
261         for v in self.param('Values'):
262             mask = data == v.maskValue
263             c = np.array(v.value().getRgbF())
264             colors[mask] = c
265         #scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)
266         #cmap = self.value()
267         #colors = cmap.map(scaled, mode='float')
268         
269         #mask = np.isnan(data) | np.isinf(data)
270         #nanColor = self['NaN']
271         #nanColor = nanColor.getRgbF()
272         #colors[mask] = nanColor
273         
274         return colors
275
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyqtgraph/widgets/ColorMapWidget.py b/pyqtgraph/widgets/ColorMapWidget.py
--- a/pyqtgraph/widgets/ColorMapWidget.py
+++ b/pyqtgraph/widgets/ColorMapWidget.py
@@ -186,13 +186,13 @@
             item.restoreState(itemState)
         
     
-class RangeColorMapItem(ptree.types.SimpleParameter):
+class RangeColorMapItem(ptree.types.ColorMapParameter):
     mapType = 'range'
     
     def __init__(self, name, opts):
         self.fieldName = name
         units = opts.get('units', '')
-        ptree.types.SimpleParameter.__init__(self, 
+        ptree.types.ColorMapParameter.__init__(self,
             name=name, autoIncrementName=True, type='colormap', removable=True, renamable=True, 
             children=[
                 #dict(name="Field", type='list', value=name, limits=fields),

{"golden_diff": "diff --git a/pyqtgraph/widgets/ColorMapWidget.py b/pyqtgraph/widgets/ColorMapWidget.py\n--- a/pyqtgraph/widgets/ColorMapWidget.py\n+++ b/pyqtgraph/widgets/ColorMapWidget.py\n@@ -186,13 +186,13 @@\n             item.restoreState(itemState)\n         \n     \n-class RangeColorMapItem(ptree.types.SimpleParameter):\n+class RangeColorMapItem(ptree.types.ColorMapParameter):\n     mapType = 'range'\n     \n     def __init__(self, name, opts):\n         self.fieldName = name\n         units = opts.get('units', '')\n-        ptree.types.SimpleParameter.__init__(self, \n+        ptree.types.ColorMapParameter.__init__(self,\n             name=name, autoIncrementName=True, type='colormap', removable=True, renamable=True, \n             children=[\n                 #dict(name=\"Field\", type='list', value=name, limits=fields),\n", "issue": "examples/ScatterPlotWidget.py Color Map throws exception\n<!-- In the following, please describe your issue in detail! -->\r\n<!-- If some of the sections do not apply, just remove them. -->\r\n\r\n### Short description\r\n<!-- This should summarize the issue. -->\r\nexamples/ScatterPlotWidget.py which makes use of Parameter Trees throws exception since #1919 (4bf1866c2a28b237ca8ca06ac668686b92ccf967 from bisection)\r\n\r\n### Code to reproduce\r\n<!-- Please provide a minimal working example that reproduces the issue in the code block below.\r\n     Ideally, this should be a full example someone else could run without additional setup. -->\r\nrun ```examples/ScatterPlotWidget.py```\r\nselect ```Color Map``` -> ```Add Mapping``` -> ```x_pos```\r\nan exception will be thrown\r\n\r\n### Expected behavior\r\n<!-- What should happen? -->\r\nno exception thrown\r\n\r\n### Real behavior\r\n<!-- What happens? -->\r\nfollowing exception thrown\r\n\r\n```\r\nTraceback (most recent call last):\r\n  File \"d:\\github\\pyqtgraph\\pyqtgraph\\widgets\\ScatterPlotWidget.py\", line 168, in updatePlot\r\n    colors = np.array([fn.mkBrush(*x) for x in self.colorMap.map(data)])\r\n  File \"d:\\github\\pyqtgraph\\pyqtgraph\\widgets\\ColorMapWidget.py\", line 150, in map\r\n    colors2 = item.map(data)\r\n  File \"d:\\github\\pyqtgraph\\pyqtgraph\\widgets\\ColorMapWidget.py\", line 216, in map\r\n    colors = cmap.map(scaled, mode='float')\r\nAttributeError: 'NoneType' object has no attribute 'map'\r\n```\r\n\r\n### Tested environment(s)\r\n\r\n * PyQtGraph version: 0.12.3 <!-- output of pyqtgraph.__version__ -->\r\n * Qt Python binding: PySide6 6.2.0 <!-- output of pyqtgraph.Qt.VERSION_INFO -->\r\n * Python version: 3.8.10\r\n * NumPy version: 1.21.2 <!-- output of numpy.__version__ -->\r\n * Operating system: Windows 10\r\n * Installation method: pip install -e . <!-- e.g. pip, conda, system packages, ... -->\r\n\r\n### Additional context\r\n\n", "before_files": [{"content": "from collections import OrderedDict\n\nimport numpy as np\n\nfrom .. import functions as fn\nfrom .. import parametertree as ptree\nfrom ..Qt import QtCore\n\n__all__ = ['ColorMapWidget']\n\nclass ColorMapWidget(ptree.ParameterTree):\n    \"\"\"\n    This class provides a widget allowing the user to customize color mapping\n    for multi-column data. Given a list of field names, the user may specify\n    multiple criteria for assigning colors to each record in a numpy record array.\n    Multiple criteria are evaluated and combined into a single color for each\n    record by user-defined compositing methods.\n    \n    For simpler color mapping using a single gradient editor, see \n    :class:`GradientWidget <pyqtgraph.GradientWidget>`\n    \"\"\"\n    sigColorMapChanged = QtCore.Signal(object)\n    \n    def __init__(self, parent=None):\n        ptree.ParameterTree.__init__(self, parent=parent, showHeader=False)\n        \n        self.params = ColorMapParameter()\n        self.setParameters(self.params)\n        self.params.sigTreeStateChanged.connect(self.mapChanged)\n        \n        ## wrap a couple methods \n        self.setFields = self.params.setFields\n        self.map = self.params.map\n\n    def mapChanged(self):\n        self.sigColorMapChanged.emit(self)\n\n    def widgetGroupInterface(self):\n        return (self.sigColorMapChanged, self.saveState, self.restoreState)\n\n    def saveState(self):\n        return self.params.saveState()\n\n    def restoreState(self, state):\n        self.params.restoreState(state)\n        \n    def addColorMap(self, name):\n        \"\"\"Add a new color mapping and return the created parameter.\n        \"\"\"\n        return self.params.addNew(name)\n\n\nclass ColorMapParameter(ptree.types.GroupParameter):\n    sigColorMapChanged = QtCore.Signal(object)\n    \n    def __init__(self):\n        self.fields = {}\n        ptree.types.GroupParameter.__init__(self, name='Color Map', addText='Add Mapping..', addList=[])\n        self.sigTreeStateChanged.connect(self.mapChanged)\n        \n    def mapChanged(self):\n        self.sigColorMapChanged.emit(self)\n        \n    def addNew(self, name):\n        fieldSpec = self.fields[name]\n        \n        mode = fieldSpec.get('mode', 'range')        \n        if mode == 'range':\n            item = RangeColorMapItem(name, self.fields[name])\n        elif mode == 'enum':\n            item = EnumColorMapItem(name, self.fields[name])\n\n        defaults = fieldSpec.get('defaults', {})\n        for k, v in defaults.items():\n            if k == 'colormap':\n                if mode == 'range':\n                    item.setValue(v)\n                elif mode == 'enum':\n                    children = item.param('Values').children()\n                    for i, child in enumerate(children):\n                        try:\n                            child.setValue(v[i])\n                        except IndexError:\n                            continue\n            else:\n                item[k] = v\n\n        self.addChild(item)\n        return item\n        \n    def fieldNames(self):\n        return list(self.fields.keys())\n    \n    def setFields(self, fields):\n        \"\"\"\n        Set the list of fields to be used by the mapper. \n        \n        The format of *fields* is::\n        \n            [ (fieldName, {options}), ... ]\n        \n        ============== ============================================================\n        Field Options:\n        mode           Either 'range' or 'enum' (default is range). For 'range', \n                       The user may specify a gradient of colors to be applied \n                       linearly across a specific range of values. For 'enum', \n                       the user specifies a single color for each unique value\n                       (see *values* option).\n        units          String indicating the units of the data for this field.\n        values         List of unique values for which the user may assign a \n                       color when mode=='enum'. Optionally may specify a dict \n                       instead {value: name}.\n        defaults       Dict of default values to apply to color map items when\n                       they are created. Valid keys are 'colormap' to provide\n                       a default color map, or otherwise they a string or tuple\n                       indicating the parameter to be set, such as 'Operation' or\n                       ('Channels..', 'Red').\n        ============== ============================================================\n        \"\"\"\n        self.fields = OrderedDict(fields)\n        #self.fields = fields\n        #self.fields.sort()\n        names = self.fieldNames()\n        self.setAddList(names)\n        \n    def map(self, data, mode='byte'):\n        \"\"\"\n        Return an array of colors corresponding to *data*. \n        \n        ==============  =================================================================\n        **Arguments:**\n        data            A numpy record array where the fields in data.dtype match those\n                        defined by a prior call to setFields().\n        mode            Either 'byte' or 'float'. For 'byte', the method returns an array\n                        of dtype ubyte with values scaled 0-255. For 'float', colors are\n                        returned as 0.0-1.0 float values.\n        ==============  =================================================================\n        \"\"\"\n        if isinstance(data, dict):\n            data = np.array([tuple(data.values())], dtype=[(k, float) for k in data.keys()])\n\n        colors = np.zeros((len(data),4))\n        for item in self.children():\n            if not item['Enabled']:\n                continue\n            chans = item.param('Channels..')\n            mask = np.empty((len(data), 4), dtype=bool)\n            for i,f in enumerate(['Red', 'Green', 'Blue', 'Alpha']):\n                mask[:,i] = chans[f]\n            \n            colors2 = item.map(data)\n            \n            op = item['Operation']\n            if op == 'Add':\n                colors[mask] = colors[mask] + colors2[mask]\n            elif op == 'Multiply':\n                colors[mask] *= colors2[mask]\n            elif op == 'Overlay':\n                a = colors2[:,3:4]\n                c3 = colors * (1-a) + colors2 * a\n                c3[:,3:4] = colors[:,3:4] + (1-colors[:,3:4]) * a\n                colors = c3\n            elif op == 'Set':\n                colors[mask] = colors2[mask]            \n                \n        colors = fn.clip_array(colors, 0., 1.)\n        if mode == 'byte':\n            colors = (colors * 255).astype(np.ubyte)\n        \n        return colors\n            \n    def saveState(self):\n        items = OrderedDict()\n        for item in self:\n            itemState = item.saveState(filter='user')\n            itemState['field'] = item.fieldName\n            items[item.name()] = itemState\n        state = {'fields': self.fields, 'items': items}\n        return state\n\n    def restoreState(self, state):\n        if 'fields' in state:\n            self.setFields(state['fields'])\n        for name, itemState in state['items'].items():\n            item = self.addNew(itemState['field'])\n            item.restoreState(itemState)\n        \n    \nclass RangeColorMapItem(ptree.types.SimpleParameter):\n    mapType = 'range'\n    \n    def __init__(self, name, opts):\n        self.fieldName = name\n        units = opts.get('units', '')\n        ptree.types.SimpleParameter.__init__(self, \n            name=name, autoIncrementName=True, type='colormap', removable=True, renamable=True, \n            children=[\n                #dict(name=\"Field\", type='list', value=name, limits=fields),\n                dict(name='Min', type='float', value=0.0, suffix=units, siPrefix=True),\n                dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),\n                dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),\n                dict(name='Channels..', type='group', expanded=False, children=[\n                    dict(name='Red', type='bool', value=True),\n                    dict(name='Green', type='bool', value=True),\n                    dict(name='Blue', type='bool', value=True),\n                    dict(name='Alpha', type='bool', value=True),\n                    ]),\n                dict(name='Enabled', type='bool', value=True),\n                dict(name='NaN', type='color'),\n            ])\n\n    def map(self, data):\n        data = data[self.fieldName]\n        \n        scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1)\n        cmap = self.value()\n        colors = cmap.map(scaled, mode='float')\n        \n        mask = np.invert(np.isfinite(data))\n        nanColor = self['NaN']\n        nanColor = nanColor.getRgbF()\n        colors[mask] = nanColor\n        \n        return colors        \n\nclass EnumColorMapItem(ptree.types.GroupParameter):\n    mapType = 'enum'\n    \n    def __init__(self, name, opts):\n        self.fieldName = name\n        vals = opts.get('values', [])\n        if isinstance(vals, list):\n            vals = OrderedDict([(v,str(v)) for v in vals])        \n        childs = []\n        for val,vname in vals.items():\n            ch = ptree.Parameter.create(name=vname, type='color')\n            ch.maskValue = val\n            childs.append(ch)\n        \n        ptree.types.GroupParameter.__init__(self, \n            name=name, autoIncrementName=True, removable=True, renamable=True, \n            children=[\n                dict(name='Values', type='group', children=childs),\n                dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),\n                dict(name='Channels..', type='group', expanded=False, children=[\n                    dict(name='Red', type='bool', value=True),\n                    dict(name='Green', type='bool', value=True),\n                    dict(name='Blue', type='bool', value=True),\n                    dict(name='Alpha', type='bool', value=True),\n                    ]),\n                dict(name='Enabled', type='bool', value=True),\n                dict(name='Default', type='color'),\n            ])\n    \n    def map(self, data):\n        data = data[self.fieldName]\n        colors = np.empty((len(data), 4))\n        default = np.array(self['Default'].getRgbF())\n        colors[:] = default\n        \n        for v in self.param('Values'):\n            mask = data == v.maskValue\n            c = np.array(v.value().getRgbF())\n            colors[mask] = c\n        #scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)\n        #cmap = self.value()\n        #colors = cmap.map(scaled, mode='float')\n        \n        #mask = np.isnan(data) | np.isinf(data)\n        #nanColor = self['NaN']\n        #nanColor = nanColor.getRgbF()\n        #colors[mask] = nanColor\n        \n        return colors\n", "path": "pyqtgraph/widgets/ColorMapWidget.py"}], "after_files": [{"content": "from collections import OrderedDict\n\nimport numpy as np\n\nfrom .. import functions as fn\nfrom .. import parametertree as ptree\nfrom ..Qt import QtCore\n\n__all__ = ['ColorMapWidget']\n\nclass ColorMapWidget(ptree.ParameterTree):\n    \"\"\"\n    This class provides a widget allowing the user to customize color mapping\n    for multi-column data. Given a list of field names, the user may specify\n    multiple criteria for assigning colors to each record in a numpy record array.\n    Multiple criteria are evaluated and combined into a single color for each\n    record by user-defined compositing methods.\n    \n    For simpler color mapping using a single gradient editor, see \n    :class:`GradientWidget <pyqtgraph.GradientWidget>`\n    \"\"\"\n    sigColorMapChanged = QtCore.Signal(object)\n    \n    def __init__(self, parent=None):\n        ptree.ParameterTree.__init__(self, parent=parent, showHeader=False)\n        \n        self.params = ColorMapParameter()\n        self.setParameters(self.params)\n        self.params.sigTreeStateChanged.connect(self.mapChanged)\n        \n        ## wrap a couple methods \n        self.setFields = self.params.setFields\n        self.map = self.params.map\n\n    def mapChanged(self):\n        self.sigColorMapChanged.emit(self)\n\n    def widgetGroupInterface(self):\n        return (self.sigColorMapChanged, self.saveState, self.restoreState)\n\n    def saveState(self):\n        return self.params.saveState()\n\n    def restoreState(self, state):\n        self.params.restoreState(state)\n        \n    def addColorMap(self, name):\n        \"\"\"Add a new color mapping and return the created parameter.\n        \"\"\"\n        return self.params.addNew(name)\n\n\nclass ColorMapParameter(ptree.types.GroupParameter):\n    sigColorMapChanged = QtCore.Signal(object)\n    \n    def __init__(self):\n        self.fields = {}\n        ptree.types.GroupParameter.__init__(self, name='Color Map', addText='Add Mapping..', addList=[])\n        self.sigTreeStateChanged.connect(self.mapChanged)\n        \n    def mapChanged(self):\n        self.sigColorMapChanged.emit(self)\n        \n    def addNew(self, name):\n        fieldSpec = self.fields[name]\n        \n        mode = fieldSpec.get('mode', 'range')        \n        if mode == 'range':\n            item = RangeColorMapItem(name, self.fields[name])\n        elif mode == 'enum':\n            item = EnumColorMapItem(name, self.fields[name])\n\n        defaults = fieldSpec.get('defaults', {})\n        for k, v in defaults.items():\n            if k == 'colormap':\n                if mode == 'range':\n                    item.setValue(v)\n                elif mode == 'enum':\n                    children = item.param('Values').children()\n                    for i, child in enumerate(children):\n                        try:\n                            child.setValue(v[i])\n                        except IndexError:\n                            continue\n            else:\n                item[k] = v\n\n        self.addChild(item)\n        return item\n        \n    def fieldNames(self):\n        return list(self.fields.keys())\n    \n    def setFields(self, fields):\n        \"\"\"\n        Set the list of fields to be used by the mapper. \n        \n        The format of *fields* is::\n        \n            [ (fieldName, {options}), ... ]\n        \n        ============== ============================================================\n        Field Options:\n        mode           Either 'range' or 'enum' (default is range). For 'range', \n                       The user may specify a gradient of colors to be applied \n                       linearly across a specific range of values. For 'enum', \n                       the user specifies a single color for each unique value\n                       (see *values* option).\n        units          String indicating the units of the data for this field.\n        values         List of unique values for which the user may assign a \n                       color when mode=='enum'. Optionally may specify a dict \n                       instead {value: name}.\n        defaults       Dict of default values to apply to color map items when\n                       they are created. Valid keys are 'colormap' to provide\n                       a default color map, or otherwise they a string or tuple\n                       indicating the parameter to be set, such as 'Operation' or\n                       ('Channels..', 'Red').\n        ============== ============================================================\n        \"\"\"\n        self.fields = OrderedDict(fields)\n        #self.fields = fields\n        #self.fields.sort()\n        names = self.fieldNames()\n        self.setAddList(names)\n        \n    def map(self, data, mode='byte'):\n        \"\"\"\n        Return an array of colors corresponding to *data*. \n        \n        ==============  =================================================================\n        **Arguments:**\n        data            A numpy record array where the fields in data.dtype match those\n                        defined by a prior call to setFields().\n        mode            Either 'byte' or 'float'. For 'byte', the method returns an array\n                        of dtype ubyte with values scaled 0-255. For 'float', colors are\n                        returned as 0.0-1.0 float values.\n        ==============  =================================================================\n        \"\"\"\n        if isinstance(data, dict):\n            data = np.array([tuple(data.values())], dtype=[(k, float) for k in data.keys()])\n\n        colors = np.zeros((len(data),4))\n        for item in self.children():\n            if not item['Enabled']:\n                continue\n            chans = item.param('Channels..')\n            mask = np.empty((len(data), 4), dtype=bool)\n            for i,f in enumerate(['Red', 'Green', 'Blue', 'Alpha']):\n                mask[:,i] = chans[f]\n            \n            colors2 = item.map(data)\n            \n            op = item['Operation']\n            if op == 'Add':\n                colors[mask] = colors[mask] + colors2[mask]\n            elif op == 'Multiply':\n                colors[mask] *= colors2[mask]\n            elif op == 'Overlay':\n                a = colors2[:,3:4]\n                c3 = colors * (1-a) + colors2 * a\n                c3[:,3:4] = colors[:,3:4] + (1-colors[:,3:4]) * a\n                colors = c3\n            elif op == 'Set':\n                colors[mask] = colors2[mask]            \n                \n        colors = fn.clip_array(colors, 0., 1.)\n        if mode == 'byte':\n            colors = (colors * 255).astype(np.ubyte)\n        \n        return colors\n            \n    def saveState(self):\n        items = OrderedDict()\n        for item in self:\n            itemState = item.saveState(filter='user')\n            itemState['field'] = item.fieldName\n            items[item.name()] = itemState\n        state = {'fields': self.fields, 'items': items}\n        return state\n\n    def restoreState(self, state):\n        if 'fields' in state:\n            self.setFields(state['fields'])\n        for name, itemState in state['items'].items():\n            item = self.addNew(itemState['field'])\n            item.restoreState(itemState)\n        \n    \nclass RangeColorMapItem(ptree.types.ColorMapParameter):\n    mapType = 'range'\n    \n    def __init__(self, name, opts):\n        self.fieldName = name\n        units = opts.get('units', '')\n        ptree.types.ColorMapParameter.__init__(self,\n            name=name, autoIncrementName=True, type='colormap', removable=True, renamable=True, \n            children=[\n                #dict(name=\"Field\", type='list', value=name, limits=fields),\n                dict(name='Min', type='float', value=0.0, suffix=units, siPrefix=True),\n                dict(name='Max', type='float', value=1.0, suffix=units, siPrefix=True),\n                dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),\n                dict(name='Channels..', type='group', expanded=False, children=[\n                    dict(name='Red', type='bool', value=True),\n                    dict(name='Green', type='bool', value=True),\n                    dict(name='Blue', type='bool', value=True),\n                    dict(name='Alpha', type='bool', value=True),\n                    ]),\n                dict(name='Enabled', type='bool', value=True),\n                dict(name='NaN', type='color'),\n            ])\n\n    def map(self, data):\n        data = data[self.fieldName]\n        \n        scaled = fn.clip_array((data-self['Min']) / (self['Max']-self['Min']), 0, 1)\n        cmap = self.value()\n        colors = cmap.map(scaled, mode='float')\n        \n        mask = np.invert(np.isfinite(data))\n        nanColor = self['NaN']\n        nanColor = nanColor.getRgbF()\n        colors[mask] = nanColor\n        \n        return colors        \n\nclass EnumColorMapItem(ptree.types.GroupParameter):\n    mapType = 'enum'\n    \n    def __init__(self, name, opts):\n        self.fieldName = name\n        vals = opts.get('values', [])\n        if isinstance(vals, list):\n            vals = OrderedDict([(v,str(v)) for v in vals])        \n        childs = []\n        for val,vname in vals.items():\n            ch = ptree.Parameter.create(name=vname, type='color')\n            ch.maskValue = val\n            childs.append(ch)\n        \n        ptree.types.GroupParameter.__init__(self, \n            name=name, autoIncrementName=True, removable=True, renamable=True, \n            children=[\n                dict(name='Values', type='group', children=childs),\n                dict(name='Operation', type='list', value='Overlay', limits=['Overlay', 'Add', 'Multiply', 'Set']),\n                dict(name='Channels..', type='group', expanded=False, children=[\n                    dict(name='Red', type='bool', value=True),\n                    dict(name='Green', type='bool', value=True),\n                    dict(name='Blue', type='bool', value=True),\n                    dict(name='Alpha', type='bool', value=True),\n                    ]),\n                dict(name='Enabled', type='bool', value=True),\n                dict(name='Default', type='color'),\n            ])\n    \n    def map(self, data):\n        data = data[self.fieldName]\n        colors = np.empty((len(data), 4))\n        default = np.array(self['Default'].getRgbF())\n        colors[:] = default\n        \n        for v in self.param('Values'):\n            mask = data == v.maskValue\n            c = np.array(v.value().getRgbF())\n            colors[mask] = c\n        #scaled = np.clip((data-self['Min']) / (self['Max']-self['Min']), 0, 1)\n        #cmap = self.value()\n        #colors = cmap.map(scaled, mode='float')\n        \n        #mask = np.isnan(data) | np.isinf(data)\n        #nanColor = self['NaN']\n        #nanColor = nanColor.getRgbF()\n        #colors[mask] = nanColor\n        \n        return colors\n", "path": "pyqtgraph/widgets/ColorMapWidget.py"}]}