diff --git a/IPython/html/static/notebook/js/widgets/widget_bool.js b/IPython/html/static/notebook/js/widgets/widget_bool.js
index 4bac72d..5601bfc 100644
--- a/IPython/html/static/notebook/js/widgets/widget_bool.js
+++ b/IPython/html/static/notebook/js/widgets/widget_bool.js
@@ -33,13 +33,11 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.$checkbox = $('<input />')
                 .attr('type', 'checkbox')
                 .click(function(el) {
-                    that.user_invoked_update = true;
             
                     // Calling model.set will trigger all of the other views of the 
                     // model to update.
-                    that.model.set('value', that.$checkbox.prop('checked'));
+                    that.model.set('value', that.$checkbox.prop('checked'), {updated_view: this});
                     that.touch();
-                    that.user_invoked_update = false;
                 })
                 .appendTo(this.$el);
 
@@ -47,12 +45,12 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update(); // Set defaults.
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            if (!this.user_invoked_update) {
+            if (options === undefined || options.updated_view != this) {
                 this.$checkbox.prop('checked', this.model.get('value'));
 
                 var disabled = this.model.get('disabled');
@@ -89,12 +87,12 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update(); // Set defaults.
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            if (!this.user_invoked_update) {
+            if (options === undefined || options.updated_view != this) {
                 if (this.model.get('value')) {
                     this.$button.addClass('active');
                 } else {
@@ -118,13 +116,11 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
         
         // Handles and validates user input.
         handleClick: function(e) { 
-            this.user_invoked_update = true;
-            
+
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', ! $(e.target).hasClass('active'));
+            this.model.set('value', ! $(e.target).hasClass('active'), {updated_view: this});
             this.touch();
-            this.user_invoked_update = false;
         },
     });
 
diff --git a/IPython/html/static/notebook/js/widgets/widget_float_range.js b/IPython/html/static/notebook/js/widgets/widget_float_range.js
index c24fa31..10110eb 100644
--- a/IPython/html/static/notebook/js/widgets/widget_float_range.js
+++ b/IPython/html/static/notebook/js/widgets/widget_float_range.js
@@ -44,66 +44,68 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-
+            
+            if (options === undefined || options.updated_view != this) {
             // Slider related keys.
-            var _keys = ['step', 'max', 'min', 'disabled'];
-            for (var index in _keys) {
-                var key = _keys[index];
-                if (this.model.get(key) !== undefined) {
-                    this.$slider.slider("option", key, this.model.get(key));
+                var _keys = ['step', 'max', 'min', 'disabled'];
+                for (var index in _keys) {
+                    var key = _keys[index];
+                    if (this.model.get(key) !== undefined) {
+                        this.$slider.slider("option", key, this.model.get(key));
+                    }
                 }
-            }
 
-            // WORKAROUND FOR JQUERY SLIDER BUG.
-            // The horizontal position of the slider handle
-            // depends on the value of the slider at the time
-            // of orientation change.  Before applying the new
-            // workaround, we set the value to the minimum to
-            // make sure that the horizontal placement of the
-            // handle in the vertical slider is always 
-            // consistent.
-            var orientation = this.model.get('orientation');
-            var value = this.model.get('min');
-            this.$slider.slider('option', 'value', value);
-            this.$slider.slider('option', 'orientation', orientation);
-            value = this.model.get('value');
-            this.$slider.slider('option', 'value', value);
+                // WORKAROUND FOR JQUERY SLIDER BUG.
+                // The horizontal position of the slider handle
+                // depends on the value of the slider at the time
+                // of orientation change.  Before applying the new
+                // workaround, we set the value to the minimum to
+                // make sure that the horizontal placement of the
+                // handle in the vertical slider is always 
+                // consistent.
+                var orientation = this.model.get('orientation');
+                var value = this.model.get('min');
+                this.$slider.slider('option', 'value', value);
+                this.$slider.slider('option', 'orientation', orientation);
+                value = this.model.get('value');
+                this.$slider.slider('option', 'value', value);
 
-            // Use the right CSS classes for vertical & horizontal sliders
-            if (orientation=='vertical') {
-                this.$slider_container
-                    .removeClass('widget-hslider')
-                    .addClass('widget-vslider');
-                this.$el
-                    .removeClass('widget-hbox-single')
-                    .addClass('widget-vbox-single');
-                this.$label
-                    .removeClass('widget-hlabel')
-                    .addClass('widget-vlabel');
+                // Use the right CSS classes for vertical & horizontal sliders
+                if (orientation=='vertical') {
+                    this.$slider_container
+                        .removeClass('widget-hslider')
+                        .addClass('widget-vslider');
+                    this.$el
+                        .removeClass('widget-hbox-single')
+                        .addClass('widget-vbox-single');
+                    this.$label
+                        .removeClass('widget-hlabel')
+                        .addClass('widget-vlabel');
 
-            } else {
-                this.$slider_container
-                    .removeClass('widget-vslider')
-                    .addClass('widget-hslider');
-                this.$el
-                    .removeClass('widget-vbox-single')
-                    .addClass('widget-hbox-single');
-                this.$label
-                    .removeClass('widget-vlabel')
-                    .addClass('widget-hlabel');
-            }
+                } else {
+                    this.$slider_container
+                        .removeClass('widget-vslider')
+                        .addClass('widget-hslider');
+                    this.$el
+                        .removeClass('widget-vbox-single')
+                        .addClass('widget-hbox-single');
+                    this.$label
+                        .removeClass('widget-vlabel')
+                        .addClass('widget-hlabel');
+                }
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -114,7 +116,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', ui.value); 
+            this.model.set('value', ui.value, {updated_view: this}); 
             this.touch();
         },
     });
@@ -141,29 +143,31 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update(); // Set defaults.
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
 
-            var value = this.model.get('value');
-            if (!this.changing && parseFloat(this.$textbox.val()) != value) {
-                this.$textbox.val(value);
-            }
-            
-            if (this.model.get('disabled')) {
-                this.$textbox.attr('disabled','disabled');
-            } else {
-                this.$textbox.removeAttr('disabled');
-            }
+            if (options === undefined || options.updated_view != this) {
+                var value = this.model.get('value');
+                if (parseFloat(this.$textbox.val()) != value) {
+                    this.$textbox.val(value);
+                }
+                
+                if (this.model.get('disabled')) {
+                    this.$textbox.attr('disabled','disabled');
+                } else {
+                    this.$textbox.removeAttr('disabled');
+                }
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -196,13 +200,11 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
 
                 // Apply the value if it has changed.
                 if (numericalValue != this.model.get('value')) {
-                    this.changing = true;
             
                     // Calling model.set will trigger all of the other views of the 
                     // model to update.
-                    this.model.set('value', numericalValue);
+                    this.model.set('value', numericalValue, {updated_view: this});
                     this.touch();
-                    this.changing = false;
                 }
             }
         },
diff --git a/IPython/html/static/notebook/js/widgets/widget_int_range.js b/IPython/html/static/notebook/js/widgets/widget_int_range.js
index e207ee8..b53552c 100644
--- a/IPython/html/static/notebook/js/widgets/widget_int_range.js
+++ b/IPython/html/static/notebook/js/widgets/widget_int_range.js
@@ -44,66 +44,67 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-
-            // Slider related keys.
-            var _keys = ['step', 'max', 'min', 'disabled'];
-            for (var index in _keys) {
-                var key = _keys[index];
-                if (this.model.get(key) !== undefined) {
-                    this.$slider.slider("option", key, this.model.get(key));
+            if (options === undefined || options.updated_view != this) {
+                // Slider related keys.
+                var _keys = ['step', 'max', 'min', 'disabled'];
+                for (var index in _keys) {
+                    var key = _keys[index];
+                    if (this.model.get(key) !== undefined) {
+                        this.$slider.slider("option", key, this.model.get(key));
+                    }
                 }
-            }
 
-            // WORKAROUND FOR JQUERY SLIDER BUG.
-            // The horizontal position of the slider handle
-            // depends on the value of the slider at the time
-            // of orientation change.  Before applying the new
-            // workaround, we set the value to the minimum to
-            // make sure that the horizontal placement of the
-            // handle in the vertical slider is always 
-            // consistent.
-            var orientation = this.model.get('orientation');
-            var value = this.model.get('min');
-            this.$slider.slider('option', 'value', value);
-            this.$slider.slider('option', 'orientation', orientation);
-            value = this.model.get('value');
-            this.$slider.slider('option', 'value', value);
+                // WORKAROUND FOR JQUERY SLIDER BUG.
+                // The horizontal position of the slider handle
+                // depends on the value of the slider at the time
+                // of orientation change.  Before applying the new
+                // workaround, we set the value to the minimum to
+                // make sure that the horizontal placement of the
+                // handle in the vertical slider is always 
+                // consistent.
+                var orientation = this.model.get('orientation');
+                var value = this.model.get('min');
+                this.$slider.slider('option', 'value', value);
+                this.$slider.slider('option', 'orientation', orientation);
+                value = this.model.get('value');
+                this.$slider.slider('option', 'value', value);
 
-            // Use the right CSS classes for vertical & horizontal sliders
-            if (orientation=='vertical') {
-                this.$slider_container
-                    .removeClass('widget-hslider')
-                    .addClass('widget-vslider');
-                this.$el
-                    .removeClass('widget-hbox-single')
-                    .addClass('widget-vbox-single');
-                this.$label
-                    .removeClass('widget-hlabel')
-                    .addClass('widget-vlabel');
+                // Use the right CSS classes for vertical & horizontal sliders
+                if (orientation=='vertical') {
+                    this.$slider_container
+                        .removeClass('widget-hslider')
+                        .addClass('widget-vslider');
+                    this.$el
+                        .removeClass('widget-hbox-single')
+                        .addClass('widget-vbox-single');
+                    this.$label
+                        .removeClass('widget-hlabel')
+                        .addClass('widget-vlabel');
 
-            } else {
-                this.$slider_container
-                    .removeClass('widget-vslider')
-                    .addClass('widget-hslider');
-                this.$el
-                    .removeClass('widget-vbox-single')
-                    .addClass('widget-hbox-single');
-                this.$label
-                    .removeClass('widget-vlabel')
-                    .addClass('widget-hlabel');
-            }
+                } else {
+                    this.$slider_container
+                        .removeClass('widget-vslider')
+                        .addClass('widget-hslider');
+                    this.$el
+                        .removeClass('widget-vbox-single')
+                        .addClass('widget-hbox-single');
+                    this.$label
+                        .removeClass('widget-vlabel')
+                        .addClass('widget-hlabel');
+                }
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -114,7 +115,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', ~~ui.value); // Double bit-wise not to truncate decimel
+            this.model.set('value', ~~ui.value, {updated_view: this}); // Double bit-wise not to truncate decimel
             this.touch();
         },
     });
@@ -140,29 +141,30 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update(); // Set defaults.
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
+            if (options === undefined || options.updated_view != this) {
+                var value = this.model.get('value');
+                if (parseInt(this.$textbox.val()) != value) {
+                    this.$textbox.val(value);
+                }
+                
+                if (this.model.get('disabled')) {
+                    this.$textbox.attr('disabled','disabled');
+                } else {
+                    this.$textbox.removeAttr('disabled');
+                }
 
-            var value = this.model.get('value');
-            if (!this.changing && parseInt(this.$textbox.val()) != value) {
-                this.$textbox.val(value);
-            }
-            
-            if (this.model.get('disabled')) {
-                this.$textbox.attr('disabled','disabled');
-            } else {
-                this.$textbox.removeAttr('disabled');
-            }
-
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -195,13 +197,11 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
                 
                 // Apply the value if it has changed.
                 if (numericalValue != this.model.get('value')) {
-                    this.changing = true;
             
                     // Calling model.set will trigger all of the other views of the 
                     // model to update.
-                    this.model.set('value', numericalValue);
+                    this.model.set('value', numericalValue, {updated_view: this});
                     this.touch();
-                    this.changing = false;
                 }
             }
         },
diff --git a/IPython/html/static/notebook/js/widgets/widget_multicontainer.js b/IPython/html/static/notebook/js/widgets/widget_multicontainer.js
index 50d2675..1e2870c 100644
--- a/IPython/html/static/notebook/js/widgets/widget_multicontainer.js
+++ b/IPython/html/static/notebook/js/widgets/widget_multicontainer.js
@@ -44,38 +44,39 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
         },
         
 
-        update: function() {
+        update: function(options) {
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-
-            // Set tab titles
-            var titles = this.model.get('_titles');
-            for (var page_index in titles) {
-
-                var accordian = this.containers[page_index];
-                if (accordian !== undefined) {
-                    accordian
-                        .find('.accordion-heading')
-                        .find('.accordion-toggle')
-                        .html(titles[page_index]);
+            
+            if (options === undefined || options.updated_view != this) {
+                // Set tab titles
+                var titles = this.model.get('_titles');
+                for (var page_index in titles) {
+
+                    var accordian = this.containers[page_index];
+                    if (accordian !== undefined) {
+                        accordian
+                            .find('.accordion-heading')
+                            .find('.accordion-toggle')
+                            .html(titles[page_index]);
+                    }
                 }
-            }
 
-            // Set selected page
-            var selected_index = this.model.get("selected_index");
-            if (0 <= selected_index && selected_index < this.containers.length) {
-                for (var index in this.containers) {
-                    if (index==selected_index) {
-                        this.containers[index].find('.accordion-body').collapse('show');    
-                    } else {
-                        this.containers[index].find('.accordion-body').collapse('hide');    
+                // Set selected page
+                var selected_index = this.model.get("selected_index");
+                if (0 <= selected_index && selected_index < this.containers.length) {
+                    for (var index in this.containers) {
+                        if (index==selected_index) {
+                            this.containers[index].find('.accordion-body').collapse('show');    
+                        } else {
+                            this.containers[index].find('.accordion-body').collapse('hide');    
+                        }
+                        
                     }
-                    
                 }
             }
-            
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
 
@@ -99,7 +100,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
                     // Calling model.set will trigger all of the other views of the 
                     // model to update.
-                    that.model.set("selected_index", index);
+                    that.model.set("selected_index", index, {updated_view: this});
                     that.touch();
                  })
                 .html('Page ' + index)
@@ -160,26 +161,26 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             }, this)
         },
 
-        update: function() {
+        update: function(options) {
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            
-            // Set tab titles
-            var titles = this.model.get('_titles');
-            for (var page_index in titles) {
-                var tab_text = this.containers[page_index];
-                if (tab_text !== undefined) {
-                    tab_text.html(titles[page_index]);
+            if (options === undefined || options.updated_view != this) {
+                // Set tab titles
+                var titles = this.model.get('_titles');
+                for (var page_index in titles) {
+                    var tab_text = this.containers[page_index];
+                    if (tab_text !== undefined) {
+                        tab_text.html(titles[page_index]);
+                    }
                 }
-            }
 
-            var selected_index = this.model.get('selected_index');
-            if (0 <= selected_index && selected_index < this.containers.length) {
-                this.select_page(selected_index);
+                var selected_index = this.model.get('selected_index');
+                if (0 <= selected_index && selected_index < this.containers.length) {
+                    this.select_page(selected_index);
+                }
             }
-
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
 
@@ -200,7 +201,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
                     // Calling model.set will trigger all of the other views of the 
                     // model to update.
-                    that.model.set("selected_index", index);
+                    that.model.set("selected_index", index, {updated_view: this});
                     that.touch();
                     that.select_page(index);
                 });
diff --git a/IPython/html/static/notebook/js/widgets/widget_selection.js b/IPython/html/static/notebook/js/widgets/widget_selection.js
index 5e8f78e..128ec56 100644
--- a/IPython/html/static/notebook/js/widgets/widget_selection.js
+++ b/IPython/html/static/notebook/js/widgets/widget_selection.js
@@ -55,49 +55,51 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
 
-            var selected_item_text = this.model.get('value');
-            selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
-            selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
-            if (selected_item_text.length === 0) {
-                this.$droplabel.html('&nbsp;');
-            } else {
-                this.$droplabel.html(selected_item_text);    
-            }
-            
-            var items = this.model.get('values');
-            this.$droplist.html('');
-            for (var index in items) {
-                var that = this;
-                var item_button = $('<a href="#"/>')
-                    .html(items[index])
-                    .on('click', $.proxy(this.handle_click, this));
-                this.$droplist.append($('<li />').append(item_button));
-            }
-            
-            if (this.model.get('disabled')) {
-                this.$buttongroup.attr('disabled','disabled');
-                this.$droplabel.attr('disabled','disabled');
-                this.$dropbutton.attr('disabled','disabled');
-                this.$droplist.attr('disabled','disabled');
-            } else {
-                this.$buttongroup.removeAttr('disabled');
-                this.$droplabel.removeAttr('disabled');
-                this.$dropbutton.removeAttr('disabled');
-                this.$droplist.removeAttr('disabled');
-            }
+            if (options === undefined || options.updated_view != this) {
+                var selected_item_text = this.model.get('value');
+                selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
+                selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
+                if (selected_item_text.length === 0) {
+                    this.$droplabel.html('&nbsp;');
+                } else {
+                    this.$droplabel.html(selected_item_text);    
+                }
+                
+                var items = this.model.get('values');
+                this.$droplist.html('');
+                for (var index in items) {
+                    var that = this;
+                    var item_button = $('<a href="#"/>')
+                        .html(items[index])
+                        .on('click', $.proxy(this.handle_click, this));
+                    this.$droplist.append($('<li />').append(item_button));
+                }
+                
+                if (this.model.get('disabled')) {
+                    this.$buttongroup.attr('disabled','disabled');
+                    this.$droplabel.attr('disabled','disabled');
+                    this.$dropbutton.attr('disabled','disabled');
+                    this.$droplist.attr('disabled','disabled');
+                } else {
+                    this.$buttongroup.removeAttr('disabled');
+                    this.$droplabel.removeAttr('disabled');
+                    this.$dropbutton.removeAttr('disabled');
+                    this.$droplist.removeAttr('disabled');
+                }
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -107,7 +109,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', $(e.target).html(), this);
+            this.model.set('value', $(e.target).html(), {updated_view: this});
             this.touch();
         },
         
@@ -134,62 +136,63 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            
-            // Add missing items to the DOM.
-            var items = this.model.get('values');
-            var disabled = this.model.get('disabled');
-            for (var index in items) {
-                var item_query = ' :input[value="' + items[index] + '"]';
-                if (this.$el.find(item_query).length === 0) {
-                    var $label = $('<label />')
-                        .addClass('radio')
-                        .html(items[index])
-                        .appendTo(this.$container);
-                    
-                    $('<input />')
-                        .attr('type', 'radio')
-                        .addClass(this.model)
-                        .val(items[index])
-                        .prependTo($label)
-                        .on('click', $.proxy(this.handle_click, this));
-                }
-                
-                var $item_element = this.$container.find(item_query);
-                if (this.model.get('value') == items[index]) {
-                    $item_element.prop('checked', true);
-                } else {
-                    $item_element.prop('checked', false);
-                }
-                $item_element.prop('disabled', disabled);
-            }
-            
-            // Remove items that no longer exist.
-            this.$container.find('input').each(function(i, obj) {
-                var value = $(obj).val();
-                var found = false;
+            if (options === undefined || options.updated_view != this) {
+                // Add missing items to the DOM.
+                var items = this.model.get('values');
+                var disabled = this.model.get('disabled');
                 for (var index in items) {
-                    if (items[index] == value) {
-                        found = true;
-                        break;
+                    var item_query = ' :input[value="' + items[index] + '"]';
+                    if (this.$el.find(item_query).length === 0) {
+                        var $label = $('<label />')
+                            .addClass('radio')
+                            .html(items[index])
+                            .appendTo(this.$container);
+                        
+                        $('<input />')
+                            .attr('type', 'radio')
+                            .addClass(this.model)
+                            .val(items[index])
+                            .prependTo($label)
+                            .on('click', $.proxy(this.handle_click, this));
+                    }
+                    
+                    var $item_element = this.$container.find(item_query);
+                    if (this.model.get('value') == items[index]) {
+                        $item_element.prop('checked', true);
+                    } else {
+                        $item_element.prop('checked', false);
                     }
+                    $item_element.prop('disabled', disabled);
                 }
                 
-                if (!found) {
-                    $(obj).parent().remove();
-                }
-            });
+                // Remove items that no longer exist.
+                this.$container.find('input').each(function(i, obj) {
+                    var value = $(obj).val();
+                    var found = false;
+                    for (var index in items) {
+                        if (items[index] == value) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    
+                    if (!found) {
+                        $(obj).parent().remove();
+                    }
+                });
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -199,7 +202,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', $(e.target).val(), this);
+            this.model.set('value', $(e.target).val(), {updated_view: this});
             this.touch();
         },
     });
@@ -226,57 +229,58 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            
-            // Add missing items to the DOM.
-            var items = this.model.get('values');
-            var disabled = this.model.get('disabled');
-            for (var index in items) {
-                var item_query = ' :contains("' + items[index] + '")';
-                if (this.$buttongroup.find(item_query).length === 0) {
-                    $('<button />')
-                        .attr('type', 'button')
-                        .addClass('btn')
-                        .html(items[index])
-                        .appendTo(this.$buttongroup)
-                        .on('click', $.proxy(this.handle_click, this));
-                }
-                
-                var $item_element = this.$buttongroup.find(item_query);
-                if (this.model.get('value') == items[index]) {
-                    $item_element.addClass('active');
-                } else {
-                    $item_element.removeClass('active');
-                }
-                $item_element.prop('disabled', disabled);
-            }
-            
-            // Remove items that no longer exist.
-            this.$buttongroup.find('button').each(function(i, obj) {
-                var value = $(obj).html();
-                var found = false;
+            if (options === undefined || options.updated_view != this) {
+                // Add missing items to the DOM.
+                var items = this.model.get('values');
+                var disabled = this.model.get('disabled');
                 for (var index in items) {
-                    if (items[index] == value) {
-                        found = true;
-                        break;
+                    var item_query = ' :contains("' + items[index] + '")';
+                    if (this.$buttongroup.find(item_query).length === 0) {
+                        $('<button />')
+                            .attr('type', 'button')
+                            .addClass('btn')
+                            .html(items[index])
+                            .appendTo(this.$buttongroup)
+                            .on('click', $.proxy(this.handle_click, this));
+                    }
+                    
+                    var $item_element = this.$buttongroup.find(item_query);
+                    if (this.model.get('value') == items[index]) {
+                        $item_element.addClass('active');
+                    } else {
+                        $item_element.removeClass('active');
                     }
+                    $item_element.prop('disabled', disabled);
                 }
                 
-                if (!found) {
-                    $(obj).remove();
-                }
-            });
+                // Remove items that no longer exist.
+                this.$buttongroup.find('button').each(function(i, obj) {
+                    var value = $(obj).html();
+                    var found = false;
+                    for (var index in items) {
+                        if (items[index] == value) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    
+                    if (!found) {
+                        $(obj).remove();
+                    }
+                });
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -286,7 +290,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', $(e.target).html(), this);
+            this.model.set('value', $(e.target).html(), {updated_view: this});
             this.touch();
         },
         
@@ -313,54 +317,55 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update();
         },
         
-        update : function(){
+        update : function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            
-            // Add missing items to the DOM.
-            var items = this.model.get('values');
-            for (var index in items) {
-                var item_query = ' :contains("' + items[index] + '")';
-                if (this.$listbox.find(item_query).length === 0) {
-                    $('<option />')
-                        .html(items[index])
-                        .attr('value', items[index])
-                        .appendTo(this.$listbox)
-                        .on('click', $.proxy(this.handle_click, this));
-                }
-            }
-
-            // Select the correct element
-            this.$listbox.val(this.model.get('value'));
-            
-            // Disable listbox if needed
-            var disabled = this.model.get('disabled');
-            this.$listbox.prop('disabled', disabled);
-
-            // Remove items that no longer exist.
-            this.$listbox.find('option').each(function(i, obj) {
-                var value = $(obj).html();
-                var found = false;
+            if (options === undefined || options.updated_view != this) {
+                // Add missing items to the DOM.
+                var items = this.model.get('values');
                 for (var index in items) {
-                    if (items[index] == value) {
-                        found = true;
-                        break;
+                    var item_query = ' :contains("' + items[index] + '")';
+                    if (this.$listbox.find(item_query).length === 0) {
+                        $('<option />')
+                            .html(items[index])
+                            .attr('value', items[index])
+                            .appendTo(this.$listbox)
+                            .on('click', $.proxy(this.handle_click, this));
                     }
                 }
+
+                // Select the correct element
+                this.$listbox.val(this.model.get('value'));
                 
-                if (!found) {
-                    $(obj).remove();
-                }
-            });
+                // Disable listbox if needed
+                var disabled = this.model.get('disabled');
+                this.$listbox.prop('disabled', disabled);
+
+                // Remove items that no longer exist.
+                this.$listbox.find('option').each(function(i, obj) {
+                    var value = $(obj).html();
+                    var found = false;
+                    for (var index in items) {
+                        if (items[index] == value) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    
+                    if (!found) {
+                        $(obj).remove();
+                    }
+                });
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -370,7 +375,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', $(e.target).html(), this);
+            this.model.set('value', $(e.target).html(), {updated_view: this});
             this.touch();
         },
         
diff --git a/IPython/html/static/notebook/js/widgets/widget_string.js b/IPython/html/static/notebook/js/widgets/widget_string.js
index a98c42b..3d15e69 100644
--- a/IPython/html/static/notebook/js/widgets/widget_string.js
+++ b/IPython/html/static/notebook/js/widgets/widget_string.js
@@ -95,24 +95,24 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
         },
 
         
-        update: function(){
+        update: function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            if (!this.user_invoked_update) {
+            if (options === undefined || options.updated_view != this) {
                 this.$textbox.val(this.model.get('value'));
-            }
 
-            var disabled = this.model.get('disabled');
-            this.$textbox.prop('disabled', disabled);
+                var disabled = this.model.get('disabled');
+                this.$textbox.prop('disabled', disabled);
 
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -123,13 +123,11 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
         
         // Handles and validates user input.
         handleChanging: function(e) { 
-            this.user_invoked_update = true;
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', e.target.value);
+            this.model.set('value', e.target.value, {updated_view: this});
             this.touch();
-            this.user_invoked_update = false;
         },
     });
 
@@ -154,24 +152,26 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             this.update(); // Set defaults.
         },
         
-        update: function(){
+        update: function(options){
             // Update the contents of this view
             //
             // Called when the model is changed.  The model may have been 
             // changed by another view or by a state update from the back-end.
-            if (this.$textbox.val() != this.model.get('value')) {
-                this.$textbox.val(this.model.get('value'));
-            }
-
-            var disabled = this.model.get('disabled');
-            this.$textbox.prop('disabled', disabled);
-
-            var description = this.model.get('description');
-            if (description.length === 0) {
-                this.$label.hide();
-            } else {
-                this.$label.html(description);
-                this.$label.show();
+            if (options === undefined || options.updated_view != this) {
+                if (this.$textbox.val() != this.model.get('value')) {
+                    this.$textbox.val(this.model.get('value'));
+                }
+
+                var disabled = this.model.get('disabled');
+                this.$textbox.prop('disabled', disabled);
+
+                var description = this.model.get('description');
+                if (description.length === 0) {
+                    this.$label.hide();
+                } else {
+                    this.$label.html(description);
+                    this.$label.show();
+                }
             }
             return IPython.DOMWidgetView.prototype.update.call(this);
         },
@@ -186,7 +186,7 @@ define(["notebook/js/widgets/widget"], function(widget_manager){
             
             // Calling model.set will trigger all of the other views of the 
             // model to update.
-            this.model.set('value', e.target.value);
+            this.model.set('value', e.target.value, {updated_view: this});
             this.touch();
         },