Repository: holoviz/panel

Issue:
Gridspec, dynamically updating results in overlap
```
import panel as pn
pn.extension()
gs = pn.GridSpec()
gs[0:2, 0] = pn.widgets.MultiSelect(options=[0, 1, 2, 3], name='Initial')
gs
```
Initialize above, then run the bottom code.

```
 gs[0:1, 0] = pn.widgets.MultiSelect(options=[5, 6, 7], name='Second')
gs[1:2, 0] = pn.widgets.MultiSelect(options=[9, 10, 11], name='Third')
```
Leads to:
![image](https://user-images.githubusercontent.com/15331990/60993146-bf85f480-a302-11e9-9904-32406025df28.png)

Work around:
```
gs = pn.GridSpec(mode='override')
gs[0:2, 0] = pn.widgets.MultiSelect(options=[0, 1, 2, 3], name='Initial')
gs
gs[0:2, 0] = None
gs[0:1, 0] = pn.widgets.MultiSelect(options=[5, 6, 7], name='Second')
gs[1:2, 0] = pn.widgets.MultiSelect(options=[9, 10, 11], name='Third')
```



Discussion:

Resolving patch:
diff --git a/panel/layout.py b/panel/layout.py
--- a/panel/layout.py
+++ b/panel/layout.py
@@ -719,8 +719,7 @@ class GridSpec(Panel):
 
     mode = param.ObjectSelector(
         default='warn', objects=['warn', 'error', 'override'], doc="""
-        Whether to warn, error or simply override on overlapping
-        assignment.""")
+        Whether to warn, error or simply override on overlapping assignment.""")
 
     width = param.Integer(default=600)
 
@@ -825,12 +824,12 @@ def _object_grid(self):
     @property
     def nrows(self):
         max_yidx = [y1 for (_, _, y1, _) in self.objects if y1 is not None]
-        return max(max_yidx) if max_yidx else 0
+        return max(max_yidx) if max_yidx else (1 if len(self.objects) else 0)
 
     @property
     def ncols(self):
         max_xidx = [x1 for (_, _, _, x1) in self.objects if x1 is not None]
-        return max(max_xidx) if max_xidx else 0
+        return max(max_xidx) if max_xidx else (1 if len(self.objects) else 0)
 
     @property
     def grid(self):
@@ -862,7 +861,7 @@ def __iter__(self):
         for obj in self.objects.values():
             yield obj
 
-    def __delitem__(self, index, trigger=True):
+    def __delitem__(self, index):
         if isinstance(index, tuple):
             yidx, xidx = index
         else:
@@ -873,11 +872,9 @@ def __delitem__(self, index, trigger=True):
             deleted = OrderedDict([list(o)[0] for o in subgrid.flatten()])
         else:
             deleted = [list(subgrid)[0][0]]
-        if deleted:
-            for key in deleted:
-                del self.objects[key]
-            if trigger:
-                self.param.trigger('objects')
+        for key in deleted:
+            del self.objects[key]
+        self.param.trigger('objects')
 
     def __getitem__(self, index):
         if isinstance(index, tuple):
@@ -937,7 +934,7 @@ def __setitem__(self, index, obj):
 
         key = (y0, x0, y1, x1)
         overlap = key in self.objects
-        clone = self.clone(mode='override')
+        clone = self.clone(objects=OrderedDict(self.objects), mode='override')
         if not overlap:
             clone.objects[key] = Pane(obj)
             grid = clone.grid
@@ -950,7 +947,10 @@ def __setitem__(self, index, obj):
             overlapping = ''
             objects = []
             for (yidx, xidx) in zip(*np.where(overlap_grid)):
-                old_obj = self[yidx, xidx]
+                try:
+                    old_obj = self[yidx, xidx]
+                except:
+                    continue
                 if old_obj not in objects:
                     objects.append(old_obj)
                     overlapping += '    (%d, %d): %s\n\n' % (yidx, xidx, old_obj)
@@ -963,7 +963,14 @@ def __setitem__(self, index, obj):
                 raise IndexError(overlap_text)
             elif self.mode == 'warn':
                 self.param.warning(overlap_text)
-            self.__delitem__(index, False)
+
+            subgrid = self._object_grid[index]
+            if isinstance(subgrid, set):
+                objects = [list(subgrid)[0][0]] if subgrid else []
+            else:
+                objects = [list(o)[0][0] for o in subgrid.flatten()]
+            for dkey in objects:
+                del self.objects[dkey]
         self.objects[key] = Pane(obj)
         self.param.trigger('objects')
 


Test patch: