We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
DataFrame widget raises exception if two columns have the same names
Panel: 0.7

#### Description of expected behavior and the observed behavior

Normally I don't have the same column names for my dataframes. But I was trying to reproduce this dashboard https://getbootstrap.com/docs/4.3/examples/dashboard/ and they have the same column names in multiple columns.

I can see that the panel.widgets.DataFrame does not support this. I would have expected it to support this situation.

#### Complete, minimal, self-contained example code that reproduces the issue

```python
import panel as pn
import pandas as pd

table_data = pd.DataFrame([(1001, "Lorem"),], columns=["Header", "Header"])
pn.widgets.DataFrame(table_data).servable()
```
#### Stack traceback and/or browser JavaScript console output

```bash
$ python -m panel serve 'scripts\issue2.py' --dev
2019-11-29 09:00:56,834 Starting Bokeh server version 1.4.0 (running on Tornado 6.0.3)
2019-11-29 09:00:56,836 User authentication hooks NOT provided (default user enabled)
2019-11-29 09:00:56,840 Bokeh app running at: http://localhost:5006/issue2
2019-11-29 09:00:56,840 Starting Bokeh server with process id: 10992
2019-11-29 09:00:58,603 Error running application handler <bokeh.application.handlers.script.ScriptHandler object at 0x00000028FF59DD48>: 'DataFrame' object has no attribute 'dtype'
File "generic.py", line 5179, in __getattr__:
return object.__getattribute__(self, name) Traceback (most recent call last):
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\bokeh\application\handlers\code_runner.py", line 179, in run
    exec(self._code, module.__dict__)
  File "C:\repos\private\awesome-panel\scripts\issue2.py", line 5, in <module>
    pn.widgets.DataFrame(table_data).servable()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 525, in servable
    self.server_doc(title=title)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 498, in server_doc
    model = self.get_root(doc)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\viewable.py", line 434, in get_root
    root = self._get_model(doc, comm=comm)
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\widgets\tables.py", line 112, in _get_model
    model = DataTable(**self._get_properties())
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\widgets\tables.py", line 99, in _get_properties
    props['columns'] = self._get_columns()
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\panel\widgets\tables.py", line 61, in _get_columns
    kind = data.dtype.kind
  File "C:\repos\private\awesome-panel\.venv\lib\site-packages\pandas\core\generic.py", line 5179, in __getattr__
    return object.__getattribute__(self, name)
AttributeError: 'DataFrame' object has no attribute 'dtype'

2019-11-29 09:00:58,990 WebSocket connection opened
2019-11-29 09:00:58,991 ServerConnection created
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `panel/widgets/tables.py`
Content:
```
1 from __future__ import absolute_import, division, unicode_literals
2 
3 import numpy as np
4 import param
5 
6 from bokeh.models import ColumnDataSource
7 from bokeh.models.widgets import (
8     DataTable, TableColumn, NumberEditor, NumberFormatter,
9     DateFormatter, DateEditor, StringFormatter, StringEditor, IntEditor
10 )
11 
12 from ..viewable import Layoutable
13 from ..util import isdatetime
14 from .base import Widget
15 
16 
17 class DataFrame(Widget):
18 
19     editors = param.Dict(default={}, doc="""
20         Bokeh CellEditor to use for a particular column
21         (overrides the default chosen based on the type).""")
22 
23     formatters = param.Dict(default={}, doc="""
24         Bokeh CellFormatter to use for a particular column
25         (overrides the default chosen based on the type).""")
26 
27     fit_columns = param.Boolean(default=True, doc="""
28         Whether columns should expand to the available width. This
29         results in no horizontal scrollbar showing up, but data can
30         get unreadable if there is no enough space available.""")
31 
32     selection = param.List(default=[], doc="""
33         The currently selected rows of the table.""")
34 
35     row_height = param.Integer(default=25, doc="""
36         The height of each table row.""")
37 
38     widths = param.Dict(default={}, doc="""
39         A mapping from column name to column width.""")
40 
41     value = param.Parameter(default=None)
42 
43     _rename = {'editors': None, 'formatters': None, 'widths': None,
44                'disabled': None}
45 
46     _manual_params = ['value', 'editors', 'formatters', 'selection', 'widths']
47 
48     def __init__(self, value=None, **params):
49         super(DataFrame, self).__init__(value=value, **params)
50         self._renamed_cols = {}
51 
52     def _get_columns(self):
53         if self.value is None:
54             return []
55 
56         index = [self.value.index.name or 'index']
57         col_names = index + list(self.value.columns)
58         columns = []
59         for col in col_names:
60             if col in self.value.columns:
61                 data = self.value[col]
62             else:
63                 data = self.value.index
64             kind = data.dtype.kind
65             if kind == 'i':
66                 formatter = NumberFormatter()
67                 editor = IntEditor()
68             elif kind == 'f':
69                 formatter = NumberFormatter(format='0,0.0[00000]')
70                 editor = NumberEditor()
71             elif isdatetime(data) or kind == 'M':
72                 formatter = DateFormatter(format='%Y-%m-%d %H:%M:%S')
73                 editor = DateEditor()
74             else:
75                 formatter = StringFormatter()
76                 editor = StringEditor()
77 
78             if col in self.editors:
79                 editor = self.editors[col]
80             if col in self.formatters:
81                 formatter = self.formatters[col]
82             if str(col) != col:
83                 self._renamed_cols[str(col)] = col
84             width = self.widths.get(str(col))
85             column = TableColumn(field=str(col), title=str(col),
86                                  editor=editor, formatter=formatter,
87                                  width=width)
88             columns.append(column)
89         return columns
90 
91     def _get_properties(self):
92         props = {p : getattr(self, p) for p in list(Layoutable.param)
93                  if getattr(self, p) is not None}
94         if self.value is None:
95             data = {}
96         else:
97             data = {k if isinstance(k, str) else str(k): v
98                     for k, v in ColumnDataSource.from_df(self.value).items()}
99         if props.get('height', None) is None:
100             length = max([len(v) for v in data.values()]) if data else 0
101             props['height'] = length * self.row_height + 30
102         props['source'] = ColumnDataSource(data=data)
103         props['columns'] = self._get_columns()
104         props['index_position'] = None
105         props['fit_columns'] = self.fit_columns
106         props['row_height'] = self.row_height
107         props['editable'] = not self.disabled
108         return props
109 
110     def _process_param_change(self, msg):
111         if 'disabled' in msg:
112             msg['editable'] = not msg.pop('disabled')
113         return super(DataFrame, self)._process_param_change(msg)
114     
115     def _get_model(self, doc, root=None, parent=None, comm=None):
116         model = DataTable(**self._get_properties())
117         if root is None:
118             root = model
119         self._link_props(model.source, ['data', ('patching', 'data')], doc, root, comm)
120         self._link_props(model.source.selected, ['indices'], doc, root, comm)
121         self._models[root.ref['id']] = (model, parent)
122         return model
123 
124     def _manual_update(self, events, model, doc, root, parent, comm):
125         for event in events:
126             if event.name == 'value':
127                 cds = model.source
128                 data = {k if isinstance(k, str) else str(k): v
129                         for k, v in ColumnDataSource.from_df(self.value).items()}
130                 cds.data = data
131                 model.columns = self._get_columns()
132             elif event.name == 'selection':
133                 model.source.selected.indices = self.selection
134             else:
135                 for col in model.columns:
136                     if col.name in self.editors:
137                         col.editor = self.editors[col.name]
138                     if col.name in self.formatters:
139                         col.formatter = self.formatters[col.name]
140                     if col.name in self.widths:
141                         col.width = self.widths[col.name]
142 
143     def _process_events(self, events):
144         if 'data' in events:
145             data = events.pop('data')
146             updated = False
147             for k, v in data.items():
148                 if k == 'index':
149                     continue
150                 k = self._renamed_cols.get(k, k)
151                 if isinstance(v, dict):
152                     v = [v for _, v in sorted(v.items(), key=lambda it: int(it[0]))]
153                 try:
154                     isequal = (self.value[k].values == np.asarray(v)).all()
155                 except Exception:
156                     isequal = False
157                 if not isequal:
158                     self.value[k] = v
159                     updated = True
160             if updated:
161                 self.param.trigger('value')
162         if 'indices' in events:
163             self.selection = events.pop('indices')
164         super(DataFrame, self)._process_events(events)
165 
166     @property
167     def selected_dataframe(self):
168         """
169         Returns a DataFrame of the currently selected rows.
170         """
171         if not self.selection:
172             return self.value
173         return self.value.iloc[self.selection]
174
```
--- 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/panel/widgets/tables.py b/panel/widgets/tables.py
--- a/panel/widgets/tables.py
+++ b/panel/widgets/tables.py
@@ -47,8 +47,18 @@
 
     def __init__(self, value=None, **params):
         super(DataFrame, self).__init__(value=value, **params)
+        self.param.watch(self._validate, 'value')
+        self._validate(None)
         self._renamed_cols = {}
 
+    def _validate(self, event):
+        if self.value is None:
+            return
+        cols = self.value.columns
+        if len(cols) != len(cols.drop_duplicates()):
+            raise ValueError('Cannot display a pandas.DataFrame with '
+                             'duplicate column names.')
+
     def _get_columns(self):
         if self.value is None:
             return []
@@ -122,6 +132,7 @@
         return model
 
     def _manual_update(self, events, model, doc, root, parent, comm):
+        self._validate(None)
         for event in events:
             if event.name == 'value':
                 cds = model.source

{"golden_diff": "diff --git a/panel/widgets/tables.py b/panel/widgets/tables.py\n--- a/panel/widgets/tables.py\n+++ b/panel/widgets/tables.py\n@@ -47,8 +47,18 @@\n \n     def __init__(self, value=None, **params):\n         super(DataFrame, self).__init__(value=value, **params)\n+        self.param.watch(self._validate, 'value')\n+        self._validate(None)\n         self._renamed_cols = {}\n \n+    def _validate(self, event):\n+        if self.value is None:\n+            return\n+        cols = self.value.columns\n+        if len(cols) != len(cols.drop_duplicates()):\n+            raise ValueError('Cannot display a pandas.DataFrame with '\n+                             'duplicate column names.')\n+\n     def _get_columns(self):\n         if self.value is None:\n             return []\n@@ -122,6 +132,7 @@\n         return model\n \n     def _manual_update(self, events, model, doc, root, parent, comm):\n+        self._validate(None)\n         for event in events:\n             if event.name == 'value':\n                 cds = model.source\n", "issue": "DataFrame widget raises exception if two columns have the same names\nPanel: 0.7\r\n\r\n#### Description of expected behavior and the observed behavior\r\n\r\nNormally I don't have the same column names for my dataframes. But I was trying to reproduce this dashboard https://getbootstrap.com/docs/4.3/examples/dashboard/ and they have the same column names in multiple columns.\r\n\r\nI can see that the panel.widgets.DataFrame does not support this. I would have expected it to support this situation.\r\n\r\n#### Complete, minimal, self-contained example code that reproduces the issue\r\n\r\n```python\r\nimport panel as pn\r\nimport pandas as pd\r\n\r\ntable_data = pd.DataFrame([(1001, \"Lorem\"),], columns=[\"Header\", \"Header\"])\r\npn.widgets.DataFrame(table_data).servable()\r\n```\r\n#### Stack traceback and/or browser JavaScript console output\r\n\r\n```bash\r\n$ python -m panel serve 'scripts\\issue2.py' --dev\r\n2019-11-29 09:00:56,834 Starting Bokeh server version 1.4.0 (running on Tornado 6.0.3)\r\n2019-11-29 09:00:56,836 User authentication hooks NOT provided (default user enabled)\r\n2019-11-29 09:00:56,840 Bokeh app running at: http://localhost:5006/issue2\r\n2019-11-29 09:00:56,840 Starting Bokeh server with process id: 10992\r\n2019-11-29 09:00:58,603 Error running application handler <bokeh.application.handlers.script.ScriptHandler object at 0x00000028FF59DD48>: 'DataFrame' object has no attribute 'dtype'\r\nFile \"generic.py\", line 5179, in __getattr__:\r\nreturn object.__getattribute__(self, name) Traceback (most recent call last):\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\bokeh\\application\\handlers\\code_runner.py\", line 179, in run\r\n    exec(self._code, module.__dict__)\r\n  File \"C:\\repos\\private\\awesome-panel\\scripts\\issue2.py\", line 5, in <module>\r\n    pn.widgets.DataFrame(table_data).servable()\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\viewable.py\", line 525, in servable\r\n    self.server_doc(title=title)\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\viewable.py\", line 498, in server_doc\r\n    model = self.get_root(doc)\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\viewable.py\", line 434, in get_root\r\n    root = self._get_model(doc, comm=comm)\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\widgets\\tables.py\", line 112, in _get_model\r\n    model = DataTable(**self._get_properties())\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\widgets\\tables.py\", line 99, in _get_properties\r\n    props['columns'] = self._get_columns()\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\panel\\widgets\\tables.py\", line 61, in _get_columns\r\n    kind = data.dtype.kind\r\n  File \"C:\\repos\\private\\awesome-panel\\.venv\\lib\\site-packages\\pandas\\core\\generic.py\", line 5179, in __getattr__\r\n    return object.__getattribute__(self, name)\r\nAttributeError: 'DataFrame' object has no attribute 'dtype'\r\n\r\n2019-11-29 09:00:58,990 WebSocket connection opened\r\n2019-11-29 09:00:58,991 ServerConnection created\r\n```\n", "before_files": [{"content": "from __future__ import absolute_import, division, unicode_literals\n\nimport numpy as np\nimport param\n\nfrom bokeh.models import ColumnDataSource\nfrom bokeh.models.widgets import (\n    DataTable, TableColumn, NumberEditor, NumberFormatter,\n    DateFormatter, DateEditor, StringFormatter, StringEditor, IntEditor\n)\n\nfrom ..viewable import Layoutable\nfrom ..util import isdatetime\nfrom .base import Widget\n\n\nclass DataFrame(Widget):\n\n    editors = param.Dict(default={}, doc=\"\"\"\n        Bokeh CellEditor to use for a particular column\n        (overrides the default chosen based on the type).\"\"\")\n\n    formatters = param.Dict(default={}, doc=\"\"\"\n        Bokeh CellFormatter to use for a particular column\n        (overrides the default chosen based on the type).\"\"\")\n\n    fit_columns = param.Boolean(default=True, doc=\"\"\"\n        Whether columns should expand to the available width. This\n        results in no horizontal scrollbar showing up, but data can\n        get unreadable if there is no enough space available.\"\"\")\n\n    selection = param.List(default=[], doc=\"\"\"\n        The currently selected rows of the table.\"\"\")\n\n    row_height = param.Integer(default=25, doc=\"\"\"\n        The height of each table row.\"\"\")\n\n    widths = param.Dict(default={}, doc=\"\"\"\n        A mapping from column name to column width.\"\"\")\n\n    value = param.Parameter(default=None)\n\n    _rename = {'editors': None, 'formatters': None, 'widths': None,\n               'disabled': None}\n\n    _manual_params = ['value', 'editors', 'formatters', 'selection', 'widths']\n\n    def __init__(self, value=None, **params):\n        super(DataFrame, self).__init__(value=value, **params)\n        self._renamed_cols = {}\n\n    def _get_columns(self):\n        if self.value is None:\n            return []\n\n        index = [self.value.index.name or 'index']\n        col_names = index + list(self.value.columns)\n        columns = []\n        for col in col_names:\n            if col in self.value.columns:\n                data = self.value[col]\n            else:\n                data = self.value.index\n            kind = data.dtype.kind\n            if kind == 'i':\n                formatter = NumberFormatter()\n                editor = IntEditor()\n            elif kind == 'f':\n                formatter = NumberFormatter(format='0,0.0[00000]')\n                editor = NumberEditor()\n            elif isdatetime(data) or kind == 'M':\n                formatter = DateFormatter(format='%Y-%m-%d %H:%M:%S')\n                editor = DateEditor()\n            else:\n                formatter = StringFormatter()\n                editor = StringEditor()\n\n            if col in self.editors:\n                editor = self.editors[col]\n            if col in self.formatters:\n                formatter = self.formatters[col]\n            if str(col) != col:\n                self._renamed_cols[str(col)] = col\n            width = self.widths.get(str(col))\n            column = TableColumn(field=str(col), title=str(col),\n                                 editor=editor, formatter=formatter,\n                                 width=width)\n            columns.append(column)\n        return columns\n\n    def _get_properties(self):\n        props = {p : getattr(self, p) for p in list(Layoutable.param)\n                 if getattr(self, p) is not None}\n        if self.value is None:\n            data = {}\n        else:\n            data = {k if isinstance(k, str) else str(k): v\n                    for k, v in ColumnDataSource.from_df(self.value).items()}\n        if props.get('height', None) is None:\n            length = max([len(v) for v in data.values()]) if data else 0\n            props['height'] = length * self.row_height + 30\n        props['source'] = ColumnDataSource(data=data)\n        props['columns'] = self._get_columns()\n        props['index_position'] = None\n        props['fit_columns'] = self.fit_columns\n        props['row_height'] = self.row_height\n        props['editable'] = not self.disabled\n        return props\n\n    def _process_param_change(self, msg):\n        if 'disabled' in msg:\n            msg['editable'] = not msg.pop('disabled')\n        return super(DataFrame, self)._process_param_change(msg)\n    \n    def _get_model(self, doc, root=None, parent=None, comm=None):\n        model = DataTable(**self._get_properties())\n        if root is None:\n            root = model\n        self._link_props(model.source, ['data', ('patching', 'data')], doc, root, comm)\n        self._link_props(model.source.selected, ['indices'], doc, root, comm)\n        self._models[root.ref['id']] = (model, parent)\n        return model\n\n    def _manual_update(self, events, model, doc, root, parent, comm):\n        for event in events:\n            if event.name == 'value':\n                cds = model.source\n                data = {k if isinstance(k, str) else str(k): v\n                        for k, v in ColumnDataSource.from_df(self.value).items()}\n                cds.data = data\n                model.columns = self._get_columns()\n            elif event.name == 'selection':\n                model.source.selected.indices = self.selection\n            else:\n                for col in model.columns:\n                    if col.name in self.editors:\n                        col.editor = self.editors[col.name]\n                    if col.name in self.formatters:\n                        col.formatter = self.formatters[col.name]\n                    if col.name in self.widths:\n                        col.width = self.widths[col.name]\n\n    def _process_events(self, events):\n        if 'data' in events:\n            data = events.pop('data')\n            updated = False\n            for k, v in data.items():\n                if k == 'index':\n                    continue\n                k = self._renamed_cols.get(k, k)\n                if isinstance(v, dict):\n                    v = [v for _, v in sorted(v.items(), key=lambda it: int(it[0]))]\n                try:\n                    isequal = (self.value[k].values == np.asarray(v)).all()\n                except Exception:\n                    isequal = False\n                if not isequal:\n                    self.value[k] = v\n                    updated = True\n            if updated:\n                self.param.trigger('value')\n        if 'indices' in events:\n            self.selection = events.pop('indices')\n        super(DataFrame, self)._process_events(events)\n\n    @property\n    def selected_dataframe(self):\n        \"\"\"\n        Returns a DataFrame of the currently selected rows.\n        \"\"\"\n        if not self.selection:\n            return self.value\n        return self.value.iloc[self.selection]\n", "path": "panel/widgets/tables.py"}], "after_files": [{"content": "from __future__ import absolute_import, division, unicode_literals\n\nimport numpy as np\nimport param\n\nfrom bokeh.models import ColumnDataSource\nfrom bokeh.models.widgets import (\n    DataTable, TableColumn, NumberEditor, NumberFormatter,\n    DateFormatter, DateEditor, StringFormatter, StringEditor, IntEditor\n)\n\nfrom ..viewable import Layoutable\nfrom ..util import isdatetime\nfrom .base import Widget\n\n\nclass DataFrame(Widget):\n\n    editors = param.Dict(default={}, doc=\"\"\"\n        Bokeh CellEditor to use for a particular column\n        (overrides the default chosen based on the type).\"\"\")\n\n    formatters = param.Dict(default={}, doc=\"\"\"\n        Bokeh CellFormatter to use for a particular column\n        (overrides the default chosen based on the type).\"\"\")\n\n    fit_columns = param.Boolean(default=True, doc=\"\"\"\n        Whether columns should expand to the available width. This\n        results in no horizontal scrollbar showing up, but data can\n        get unreadable if there is no enough space available.\"\"\")\n\n    selection = param.List(default=[], doc=\"\"\"\n        The currently selected rows of the table.\"\"\")\n\n    row_height = param.Integer(default=25, doc=\"\"\"\n        The height of each table row.\"\"\")\n\n    widths = param.Dict(default={}, doc=\"\"\"\n        A mapping from column name to column width.\"\"\")\n\n    value = param.Parameter(default=None)\n\n    _rename = {'editors': None, 'formatters': None, 'widths': None,\n               'disabled': None}\n\n    _manual_params = ['value', 'editors', 'formatters', 'selection', 'widths']\n\n    def __init__(self, value=None, **params):\n        super(DataFrame, self).__init__(value=value, **params)\n        self.param.watch(self._validate, 'value')\n        self._validate(None)\n        self._renamed_cols = {}\n\n    def _validate(self, event):\n        if self.value is None:\n            return\n        cols = self.value.columns\n        if len(cols) != len(cols.drop_duplicates()):\n            raise ValueError('Cannot display a pandas.DataFrame with '\n                             'duplicate column names.')\n\n    def _get_columns(self):\n        if self.value is None:\n            return []\n\n        index = [self.value.index.name or 'index']\n        col_names = index + list(self.value.columns)\n        columns = []\n        for col in col_names:\n            if col in self.value.columns:\n                data = self.value[col]\n            else:\n                data = self.value.index\n            kind = data.dtype.kind\n            if kind == 'i':\n                formatter = NumberFormatter()\n                editor = IntEditor()\n            elif kind == 'f':\n                formatter = NumberFormatter(format='0,0.0[00000]')\n                editor = NumberEditor()\n            elif isdatetime(data) or kind == 'M':\n                formatter = DateFormatter(format='%Y-%m-%d %H:%M:%S')\n                editor = DateEditor()\n            else:\n                formatter = StringFormatter()\n                editor = StringEditor()\n\n            if col in self.editors:\n                editor = self.editors[col]\n            if col in self.formatters:\n                formatter = self.formatters[col]\n            if str(col) != col:\n                self._renamed_cols[str(col)] = col\n            width = self.widths.get(str(col))\n            column = TableColumn(field=str(col), title=str(col),\n                                 editor=editor, formatter=formatter,\n                                 width=width)\n            columns.append(column)\n        return columns\n\n    def _get_properties(self):\n        props = {p : getattr(self, p) for p in list(Layoutable.param)\n                 if getattr(self, p) is not None}\n        if self.value is None:\n            data = {}\n        else:\n            data = {k if isinstance(k, str) else str(k): v\n                    for k, v in ColumnDataSource.from_df(self.value).items()}\n        if props.get('height', None) is None:\n            length = max([len(v) for v in data.values()]) if data else 0\n            props['height'] = length * self.row_height + 30\n        props['source'] = ColumnDataSource(data=data)\n        props['columns'] = self._get_columns()\n        props['index_position'] = None\n        props['fit_columns'] = self.fit_columns\n        props['row_height'] = self.row_height\n        props['editable'] = not self.disabled\n        return props\n\n    def _process_param_change(self, msg):\n        if 'disabled' in msg:\n            msg['editable'] = not msg.pop('disabled')\n        return super(DataFrame, self)._process_param_change(msg)\n    \n    def _get_model(self, doc, root=None, parent=None, comm=None):\n        model = DataTable(**self._get_properties())\n        if root is None:\n            root = model\n        self._link_props(model.source, ['data', ('patching', 'data')], doc, root, comm)\n        self._link_props(model.source.selected, ['indices'], doc, root, comm)\n        self._models[root.ref['id']] = (model, parent)\n        return model\n\n    def _manual_update(self, events, model, doc, root, parent, comm):\n        self._validate(None)\n        for event in events:\n            if event.name == 'value':\n                cds = model.source\n                data = {k if isinstance(k, str) else str(k): v\n                        for k, v in ColumnDataSource.from_df(self.value).items()}\n                cds.data = data\n                model.columns = self._get_columns()\n            elif event.name == 'selection':\n                model.source.selected.indices = self.selection\n            else:\n                for col in model.columns:\n                    if col.name in self.editors:\n                        col.editor = self.editors[col.name]\n                    if col.name in self.formatters:\n                        col.formatter = self.formatters[col.name]\n                    if col.name in self.widths:\n                        col.width = self.widths[col.name]\n\n    def _process_events(self, events):\n        if 'data' in events:\n            data = events.pop('data')\n            updated = False\n            for k, v in data.items():\n                if k == 'index':\n                    continue\n                k = self._renamed_cols.get(k, k)\n                if isinstance(v, dict):\n                    v = [v for _, v in sorted(v.items(), key=lambda it: int(it[0]))]\n                try:\n                    isequal = (self.value[k].values == np.asarray(v)).all()\n                except Exception:\n                    isequal = False\n                if not isequal:\n                    self.value[k] = v\n                    updated = True\n            if updated:\n                self.param.trigger('value')\n        if 'indices' in events:\n            self.selection = events.pop('indices')\n        super(DataFrame, self)._process_events(events)\n\n    @property\n    def selected_dataframe(self):\n        \"\"\"\n        Returns a DataFrame of the currently selected rows.\n        \"\"\"\n        if not self.selection:\n            return self.value\n        return self.value.iloc[self.selection]\n", "path": "panel/widgets/tables.py"}]}