diff --git a/examples/Flowchart.py b/examples/Flowchart.py
index 09ea1f930354d7637c879ce0b89f700d51981537..86c2564b9218cb4ba44e08527462d54d2d91c59b 100644
--- a/examples/Flowchart.py
+++ b/examples/Flowchart.py
@@ -58,11 +58,15 @@ fc.setInput(dataIn=data)
 
 ## populate the flowchart with a basic set of processing nodes. 
 ## (usually we let the user do this)
+plotList = {'Top Plot': pw1, 'Bottom Plot': pw2}
+
 pw1Node = fc.createNode('PlotWidget', pos=(0, -150))
+pw1Node.setPlotList(plotList)
 pw1Node.setPlot(pw1)
 
 pw2Node = fc.createNode('PlotWidget', pos=(150, -150))
 pw2Node.setPlot(pw2)
+pw2Node.setPlotList(plotList)
 
 fNode = fc.createNode('GaussianFilter', pos=(0, 0))
 fNode.ctrls['sigma'].setValue(5)
diff --git a/pyqtgraph/flowchart/library/Display.py b/pyqtgraph/flowchart/library/Display.py
index 2c352fb2a03633c98f6fb9d28085616533c53253..642e64913f5ea54f75f8fa29e6e22d2cdc42a41f 100644
--- a/pyqtgraph/flowchart/library/Display.py
+++ b/pyqtgraph/flowchart/library/Display.py
@@ -4,7 +4,7 @@ import weakref
 from ...Qt import QtCore, QtGui
 from ...graphicsItems.ScatterPlotItem import ScatterPlotItem
 from ...graphicsItems.PlotCurveItem import PlotCurveItem
-from ... import PlotDataItem
+from ... import PlotDataItem, ComboBox
 
 from .common import *
 import numpy as np
@@ -16,7 +16,9 @@ class PlotWidgetNode(Node):
     
     def __init__(self, name):
         Node.__init__(self, name, terminals={'In': {'io': 'in', 'multi': True}})
-        self.plot = None
+        self.plot = None  # currently selected plot 
+        self.plots = {}   # list of available plots user may select from
+        self.ui = None 
         self.items = {}
         
     def disconnected(self, localTerm, remoteTerm):
@@ -26,16 +28,27 @@ class PlotWidgetNode(Node):
         
     def setPlot(self, plot):
         #print "======set plot"
+        if plot == self.plot:
+            return
+        
+        # clear data from previous plot
+        if self.plot is not None:
+            for vid in list(self.items.keys()):
+                self.plot.removeItem(self.items[vid])
+                del self.items[vid]
+
         self.plot = plot
+        self.updateUi()
+        self.update()
         self.sigPlotChanged.emit(self)
         
     def getPlot(self):
         return self.plot
         
     def process(self, In, display=True):
-        if display:
-            #self.plot.clearPlots()
+        if display and self.plot is not None:
             items = set()
+            # Add all new input items to selected plot
             for name, vals in In.items():
                 if vals is None:
                     continue
@@ -45,14 +58,13 @@ class PlotWidgetNode(Node):
                 for val in vals:
                     vid = id(val)
                     if vid in self.items and self.items[vid].scene() is self.plot.scene():
+                        # Item is already added to the correct scene
+                        #   possible bug: what if two plots occupy the same scene? (should
+                        #   rarely be a problem because items are removed from a plot before
+                        #   switching).
                         items.add(vid)
                     else:
-                        #if isinstance(val, PlotCurveItem):
-                            #self.plot.addItem(val)
-                            #item = val
-                        #if isinstance(val, ScatterPlotItem):
-                            #self.plot.addItem(val)
-                            #item = val
+                        # Add the item to the plot, or generate a new item if needed.
                         if isinstance(val, QtGui.QGraphicsItem):
                             self.plot.addItem(val)
                             item = val
@@ -60,22 +72,48 @@ class PlotWidgetNode(Node):
                             item = self.plot.plot(val)
                         self.items[vid] = item
                         items.add(vid)
+                        
+            # Any left-over items that did not appear in the input must be removed
             for vid in list(self.items.keys()):
                 if vid not in items:
-                    #print "remove", self.items[vid]
                     self.plot.removeItem(self.items[vid])
                     del self.items[vid]
             
     def processBypassed(self, args):
+        if self.plot is None:
+            return
         for item in list(self.items.values()):
             self.plot.removeItem(item)
         self.items = {}
         
-    #def setInput(self, **args):
-        #for k in args:
-            #self.plot.plot(args[k])
+    def ctrlWidget(self):
+        if self.ui is None:
+            self.ui = ComboBox()
+            self.ui.currentIndexChanged.connect(self.plotSelected)
+            self.updateUi()
+        return self.ui
     
-
+    def plotSelected(self, index):
+        self.setPlot(self.ui.value())
+    
+    def setPlotList(self, plots):
+        """
+        Specify the set of plots (PlotWidget or PlotItem) that the user may
+        select from.
+        
+        *plots* must be a dictionary of {name: plot} pairs.
+        """
+        self.plots = plots
+        self.updateUi()
+    
+    def updateUi(self):
+        # sets list and automatically preserves previous selection
+        self.ui.setItems(self.plots)
+        try:
+            self.ui.setValue(self.plot)
+        except ValueError:
+            pass
+        
 
 class CanvasNode(Node):
     """Connection to a Canvas widget."""
diff --git a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
index baff1aa91e14aecde1f9e916e4858438261d1f87..575a1599cafc1443992d45488c8abc138ba6c75a 100644
--- a/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
+++ b/pyqtgraph/graphicsItems/PlotItem/PlotItem.py
@@ -95,7 +95,6 @@ class PlotItem(GraphicsWidget):
     
     
     lastFileDir = None
-    managers = {}
     
     def __init__(self, parent=None, name=None, labels=None, title=None, viewBox=None, axisItems=None, enableMenu=True, **kargs):
         """
@@ -369,28 +368,6 @@ class PlotItem(GraphicsWidget):
         self.scene().removeItem(self.vb)
         self.vb = None
         
-        ## causes invalid index errors:
-        #for i in range(self.layout.count()):
-            #self.layout.removeAt(i)
-            
-        #for p in self.proxies:
-            #try:
-                #p.setWidget(None)
-            #except RuntimeError:
-                #break
-            #self.scene().removeItem(p)
-        #self.proxies = []
-        
-        #self.menuAction.releaseWidget(self.menuAction.defaultWidget())
-        #self.menuAction.setParent(None)
-        #self.menuAction = None
-        
-        #if self.manager is not None:
-            #self.manager.sigWidgetListChanged.disconnect(self.updatePlotList)
-            #self.manager.removeWidget(self.name)
-        #else:
-            #print "no manager"
-
     def registerPlot(self, name):   ## for backward compatibility
         self.vb.register(name)
         
diff --git a/pyqtgraph/widgets/ComboBox.py b/pyqtgraph/widgets/ComboBox.py
index 72ac384fb194d741fc79a0f53f1b3f022e4489fb..f9983c9782029df7bdd492e530b0a7738a3b27cb 100644
--- a/pyqtgraph/widgets/ComboBox.py
+++ b/pyqtgraph/widgets/ComboBox.py
@@ -1,41 +1,212 @@
 from ..Qt import QtGui, QtCore
 from ..SignalProxy import SignalProxy
-
+from ..pgcollections import OrderedDict
+from ..python2_3 import asUnicode
 
 class ComboBox(QtGui.QComboBox):
     """Extends QComboBox to add extra functionality.
-          - updateList() - updates the items in the comboBox while blocking signals, remembers and resets to the previous values if it's still in the list
+
+    * Handles dict mappings -- user selects a text key, and the ComboBox indicates
+      the selected value.
+    * Requires item strings to be unique
+    * Remembers selected value if list is cleared and subsequently repopulated
+    * setItems() replaces the items in the ComboBox and blocks signals if the
+      value ultimately does not change.
     """
     
     
     def __init__(self, parent=None, items=None, default=None):
         QtGui.QComboBox.__init__(self, parent)
+        self.currentIndexChanged.connect(self.indexChanged)
+        self._ignoreIndexChange = False
         
-        #self.value = default
+        self._chosenText = None
+        self._items = OrderedDict()
         
         if items is not None:
-            self.addItems(items)
+            self.setItems(items)
             if default is not None:
                 self.setValue(default)
     
     def setValue(self, value):
-        ind = self.findText(value)
+        """Set the selected item to the first one having the given value."""
+        text = None
+        for k,v in self._items.items():
+            if v == value:
+                text = k
+                break
+        if text is None:
+            raise ValueError(value)
+            
+        self.setText(text)
+
+    def setText(self, text):
+        """Set the selected item to the first one having the given text."""
+        ind = self.findText(text)
         if ind == -1:
-            return
+            raise ValueError(text)
         #self.value = value
-        self.setCurrentIndex(ind)    
+        self.setCurrentIndex(ind)
+       
+    def value(self):
+        """
+        If items were given as a list of strings, then return the currently 
+        selected text. If items were given as a dict, then return the value
+        corresponding to the currently selected key. If the combo list is empty,
+        return None.
+        """
+        if self.count() == 0:
+            return None
+        text = asUnicode(self.currentText())
+        return self._items[text]
+    
+    def ignoreIndexChange(func):
+        # Decorator that prevents updates to self._chosenText
+        def fn(self, *args, **kwds):
+            prev = self._ignoreIndexChange
+            self._ignoreIndexChange = True
+            try:
+                ret = func(self, *args, **kwds)
+            finally:
+                self._ignoreIndexChange = prev
+            return ret
+        return fn
+    
+    def blockIfUnchanged(func):
+        # decorator that blocks signal emission during complex operations
+        # and emits currentIndexChanged only if the value has actually
+        # changed at the end.
+        def fn(self, *args, **kwds):
+            prevVal = self.value()
+            blocked = self.signalsBlocked()
+            self.blockSignals(True)
+            try:
+                ret = func(self, *args, **kwds)
+            finally:
+                self.blockSignals(blocked)
+                
+            # only emit if the value has changed
+            if self.value() != prevVal:
+                self.currentIndexChanged.emit(self.currentIndex())
+                
+            return ret
+        return fn
+    
+    @ignoreIndexChange
+    @blockIfUnchanged
+    def setItems(self, items):
+        """
+        *items* may be a list or a dict. 
+        If a dict is given, then the keys are used to populate the combo box
+        and the values will be used for both value() and setValue().
+        """
+        prevVal = self.value()
         
-    def updateList(self, items):
-        prevVal = str(self.currentText())
+        self.blockSignals(True)
         try:
-            self.blockSignals(True)
             self.clear()
             self.addItems(items)
-            self.setValue(prevVal)
-            
         finally:
             self.blockSignals(False)
             
-        if str(self.currentText()) != prevVal:
+        # only emit if we were not able to re-set the original value
+        if self.value() != prevVal:
             self.currentIndexChanged.emit(self.currentIndex())
-        
\ No newline at end of file
+        
+    def items(self):
+        return self.items.copy()
+        
+    def updateList(self, items):
+        # for backward compatibility
+        return self.setItems(items)
+
+    def indexChanged(self, index):
+        # current index has changed; need to remember new 'chosen text'
+        if self._ignoreIndexChange:
+            return
+        self._chosenText = asUnicode(self.currentText())
+        
+    def setCurrentIndex(self, index):
+        QtGui.QComboBox.setCurrentIndex(self, index)
+        
+    def itemsChanged(self):
+        # try to set the value to the last one selected, if it is available.
+        if self._chosenText is not None:
+            try:
+                self.setText(self._chosenText)
+            except ValueError:
+                pass
+
+    @ignoreIndexChange
+    def insertItem(self, *args):
+        raise NotImplementedError()
+        #QtGui.QComboBox.insertItem(self, *args)
+        #self.itemsChanged()
+        
+    @ignoreIndexChange
+    def insertItems(self, *args):
+        raise NotImplementedError()
+        #QtGui.QComboBox.insertItems(self, *args)
+        #self.itemsChanged()
+    
+    @ignoreIndexChange
+    def addItem(self, *args, **kwds):
+        # Need to handle two different function signatures for QComboBox.addItem
+        try:
+            if isinstance(args[0], basestring):
+                text = args[0]
+                if len(args) == 2:
+                    value = args[1]
+                else:
+                    value = kwds.get('value', text)
+            else:
+                text = args[1]
+                if len(args) == 3:
+                    value = args[2]
+                else:
+                    value = kwds.get('value', text)
+        
+        except IndexError:
+            raise TypeError("First or second argument of addItem must be a string.")
+            
+        if text in self._items:
+            raise Exception('ComboBox already has item named "%s".' % text)
+        
+        self._items[text] = value
+        QtGui.QComboBox.addItem(self, *args)
+        self.itemsChanged()
+        
+    def setItemValue(self, name, value):
+        if name not in self._items:
+            self.addItem(name, value)
+        else:
+            self._items[name] = value
+        
+    @ignoreIndexChange
+    @blockIfUnchanged
+    def addItems(self, items):
+        if isinstance(items, list):
+            texts = items
+            items = dict([(x, x) for x in items])
+        elif isinstance(items, dict):
+            texts = list(items.keys())
+        else:
+            raise TypeError("items argument must be list or dict (got %s)." % type(items))
+        
+        for t in texts:
+            if t in self._items:
+                raise Exception('ComboBox already has item named "%s".' % t)
+                
+        
+        for k,v in items.items():
+            self._items[k] = v
+        QtGui.QComboBox.addItems(self, list(texts))
+        
+        self.itemsChanged()
+        
+    @ignoreIndexChange
+    def clear(self):
+        self._items = OrderedDict()
+        QtGui.QComboBox.clear(self)
+        self.itemsChanged()
+        
diff --git a/pyqtgraph/widgets/tests/test_combobox.py b/pyqtgraph/widgets/tests/test_combobox.py
new file mode 100644
index 0000000000000000000000000000000000000000..f511331c10c90de81f9fce691e5d6f4b17de9c21
--- /dev/null
+++ b/pyqtgraph/widgets/tests/test_combobox.py
@@ -0,0 +1,44 @@
+import pyqtgraph as pg
+pg.mkQApp()
+
+def test_combobox():
+    cb = pg.ComboBox()
+    items = {'a': 1, 'b': 2, 'c': 3}
+    cb.setItems(items)
+    cb.setValue(2)
+    assert str(cb.currentText()) == 'b'
+    assert cb.value() == 2
+    
+    # Clear item list; value should be None
+    cb.clear()
+    assert cb.value() == None
+    
+    # Reset item list; value should be set automatically
+    cb.setItems(items)
+    assert cb.value() == 2
+    
+    # Clear item list; repopulate with same names and new values
+    items = {'a': 4, 'b': 5, 'c': 6}
+    cb.clear()
+    cb.setItems(items)
+    assert cb.value() == 5
+    
+    # Set list instead of dict
+    cb.setItems(list(items.keys()))
+    assert str(cb.currentText()) == 'b'
+    
+    cb.setValue('c')
+    assert cb.value() == str(cb.currentText())
+    assert cb.value() == 'c'
+    
+    cb.setItemValue('c', 7)
+    assert cb.value() == 7
+    
+    
+if __name__ == '__main__':
+    cb = pg.ComboBox()
+    cb.show()
+    cb.setItems({'': None, 'a': 1, 'b': 2, 'c': 3})
+    def fn(ind):
+        print("New value: %s" % cb.value())
+    cb.currentIndexChanged.connect(fn)
\ No newline at end of file