diff --git a/demo/filter_records.py b/demo/filter_records.py
index a947952fc1..a2e4ed2fdc 100644
--- a/demo/filter_records.py
+++ b/demo/filter_records.py
@@ -1,8 +1,6 @@
# Demo: (Dataframe, Dropdown) -> (Dataframe)
import gradio as gr
-import numpy as np
-import random
def filter_records(records, gender):
return records[records['gender'] == gender]
diff --git a/frontend/src/interfaces/output/dataframe.jsx b/frontend/src/interfaces/output/dataframe.jsx
index fd367c3ebb..82ef0d7072 100644
--- a/frontend/src/interfaces/output/dataframe.jsx
+++ b/frontend/src/interfaces/output/dataframe.jsx
@@ -1,45 +1,129 @@
import React from 'react';
import ComponentExample from '../component_example';
-import jspreadsheet from "jspreadsheet-ce";
-import "../../../node_modules/jspreadsheet-ce/dist/jspreadsheet.css"
import classNames from 'classnames';
+import { array_compare } from "../../utils";
class DataframeOutput extends React.Component {
constructor(props) {
super(props);
- this.wrapper = React.createRef();
+ this.state = this.constructor.get_default_state()
}
- componentDidMount = function() {
- this.el = jspreadsheet(this.wrapper.current, {minDimensions: [1, 1]});
+ static get_default_state() {
+ return {
+ "page": 0,
+ "sort_by": null,
+ "sort_descending": false
+ }
}
- resetData(new_data) {
- let [new_rows, new_cols] = [new_data.length, new_data[0].length];
- let current_data = this.el.getData();
- let [cur_rows, cur_cols] = [current_data.length, current_data[0].length];
- if (cur_rows < new_rows) {
- this.el.insertRow(new_rows - cur_rows);
- } else if (cur_rows > new_rows) {
- this.el.deleteRow(0, cur_rows - new_rows);
+ static getDerivedStateFromProps(nextProps, prevState) {
+ if (prevState.data === undefined ||
+ !array_compare(nextProps.value.data, prevState.data)) {
+ let new_state = DataframeOutput.get_default_state();
+ new_state["data"] = nextProps.value.data;
+ return new_state;
}
- if (cur_cols < new_cols) {
- this.el.insertColumn(new_cols - cur_cols);
- } else if (cur_cols > new_cols) {
- this.el.deleteColumn(0, cur_cols - new_cols);
+ return null;
+ }
+ set_page(page) {
+ this.setState({ "page": page });
+ }
+ sort_table(col_index) {
+ if (this.state.sort_by === col_index) {
+ this.setState({ "sort_descending": !this.state.sort_descending, "page": 0 });
+ } else {
+ this.setState({ "sort_by": col_index, "sort_descending": false, "page": 0 });
}
- this.el.setData(new_data);
}
render() {
- if (this.props.value && this.props.value.headers && this.el) {
- for (let [i, header] of this.props.value.headers.entries())
- this.el.setHeader(i, header);
+ if (this.props.value.data.length === 0) {
+ return null;
}
- if (this.props.value && this.el) {
- this.resetData(this.props.value.data)
+ let headers = this.props.headers || this.props.value.headers;
+ let row_count = this.props.value.data.length;
+ let col_count = this.props.value.data[0].length;
+ let selected_data = this.props.value.data.slice();
+ if (this.state.sort_by !== null) {
+ selected_data.sort((function (index) {
+ return function (a, b) {
+ return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
+ };
+ })(this.state.sort_by));
+ if (this.state.sort_descending) {
+ selected_data.reverse();
+ }
}
- return (
-
-
-
)
+ let visible_pages = null;
+ if (this.props.max_rows !== null && row_count > this.props.max_rows) {
+ if (this.props.overflow_row_behaviour === "paginate") {
+ selected_data = selected_data.slice(
+ this.state.page * this.props.max_rows,
+ (this.state.page + 1) * this.props.max_rows
+ );
+ let page_count = Math.ceil(row_count / this.props.max_rows);
+ visible_pages = [];
+ [0, this.state.page, page_count - 1].forEach(anchor => {
+ for (let i = anchor - 2; i <= anchor + 2; i++) {
+ if (i >= 0 && i < page_count && !visible_pages.includes(i)) {
+ if (visible_pages.length > 0 && i - visible_pages[visible_pages.length - 1] > 1) {
+ visible_pages.push(null);
+ }
+ visible_pages.push(i);
+ }
+ }
+ })
+ } else {
+ selected_data = selected_data.slice(
+ 0,
+ Math.ceil(this.props.max_rows / 2)
+ ).concat(
+ [Array(col_count).fill("...")],
+ selected_data.slice(
+ row_count - Math.floor(this.props.max_rows / 2)
+ ));
+ }
+ }
+ if (this.props.max_cols !== null && col_count > this.props.max_cols) {
+ let [hidden_col_start, hidden_col_end] = [
+ Math.ceil(this.props.max_cols / 2),
+ col_count - Math.floor(this.props.max_cols / 2) - 1
+ ];
+ headers = headers.slice(0, hidden_col_start) + ["..."] + headers.slice(hidden_col_end);
+ selected_data = selected_data.map(row =>
+ row.slice(0, hidden_col_start) + ["..."] + row.slice(hidden_col_end)
+ );
+ }
+ return
+
+ {headers ?
+
+ {headers.map((header, i) =>
+
+ {header}
+ {this.state.sort_by === i ? (
+ this.state.sort_descending ? "⇧" : "⇩")
+ : false}
+ | )}
+
+ : false}
+
+ {selected_data.map(row => {
+ return {row.map(cell => {cell} | )}
+ })}
+
+
+ {visible_pages !== null ?
+
+ Pages: {visible_pages.map(page => page === null ?
+
...
:
+
+ )}
+
+ : false}
+
}
}
@@ -71,4 +155,4 @@ class DataframeOutputExample extends ComponentExample {
}
}
-export {DataframeOutput, DataframeOutputExample};
+export { DataframeOutput, DataframeOutputExample };
diff --git a/frontend/src/themes/defaults.scss b/frontend/src/themes/defaults.scss
index c9c93d9b2f..dffd287d51 100644
--- a/frontend/src/themes/defaults.scss
+++ b/frontend/src/themes/defaults.scss
@@ -472,6 +472,31 @@
@apply text-2xl p-2;
}
}
+ .output_dataframe {
+ table {
+ thead {
+ @apply font-bold border-gray-200 border-b-2;
+ }
+ th {
+ @apply transition cursor-pointer;
+ }
+ th:hover {
+ @apply bg-gray-200;
+ }
+ td, th {
+ @apply px-4;
+ }
+ }
+ .pages {
+ @apply flex gap-1 items-center;
+ }
+ .page {
+ @apply px-2 py-1 bg-gray-200;
+ }
+ .page.selected {
+ @apply bg-gray-300;
+ }
+ }
.output_carousel {
@apply flex flex-col gap-2;
.carousel_control {
diff --git a/frontend/src/utils.jsx b/frontend/src/utils.jsx
index f60933d218..1d8c52c34b 100644
--- a/frontend/src/utils.jsx
+++ b/frontend/src/utils.jsx
@@ -88,4 +88,22 @@ export function saveAs(uri, filename) {
} else {
window.open(uri);
}
- }
\ No newline at end of file
+}
+
+export function array_compare(a1, a2) {
+ if (a1.length != a2.length) {
+ return false;
+ }
+ for (var i in a1) {
+ // Don't forget to check for arrays in our arrays.
+ if (a1[i] instanceof Array && a2[i] instanceof Array) {
+ if (!array_compare(a1[i], a2[i])) {
+ return false;
+ }
+ }
+ else if (a1[i] != a2[i]) {
+ return false;
+ }
+ }
+ return true;
+}
diff --git a/gradio.egg-info/requires.txt b/gradio.egg-info/requires.txt
index 8e565209f9..b6135b3e41 100644
--- a/gradio.egg-info/requires.txt
+++ b/gradio.egg-info/requires.txt
@@ -1,15 +1,15 @@
-numpy
-scipy
-matplotlib
-pandas
-pillow
+Flask-Cors>=3.0.8
+Flask-Login
+Flask>=1.1.1
+analytics-python
ffmpy
+flask-cachebuster
markdown2
+matplotlib
+numpy
+pandas
+paramiko
+pillow
pycryptodome
requests
-paramiko
-analytics-python
-Flask>=1.1.1
-Flask-Cors>=3.0.8
-flask-cachebuster
-Flask-Login
+scipy
diff --git a/gradio/outputs.py b/gradio/outputs.py
index 74a9298622..1ac2379102 100644
--- a/gradio/outputs.py
+++ b/gradio/outputs.py
@@ -441,21 +441,30 @@ class Dataframe(OutputComponent):
Output type: Union[pandas.DataFrame, numpy.array, List[Union[str, float]], List[List[Union[str, float]]]]
"""
- def __init__(self, headers=None, type="auto", label=None):
+ def __init__(self, headers=None, max_rows=20, max_cols=None, overflow_row_behaviour="paginate", type="auto", label=None):
'''
Parameters:
- headers (List[str]): Header names to dataframe.
+ headers (List[str]): Header names to dataframe. Only applicable if type is "numpy" or "array".
+ max_rows (int): Maximum number of rows to display at once. Set to None for infinite.
+ max_cols (int): Maximum number of columns to display at once. Set to None for infinite.
+ overflow_row_behaviour (str): If set to "paginate", will create pages for overflow rows. If set to "show_ends", will show initial and final rows and truncate middle rows.
type (str): Type of value to be passed to component. "pandas" for pandas dataframe, "numpy" for numpy array, or "array" for Python array, "auto" detects return type.
label (str): component name in interface.
'''
- self.type = type
self.headers = headers
+ self.max_rows = max_rows
+ self.max_cols = max_cols
+ self.overflow_row_behaviour = overflow_row_behaviour
+ self.type = type
super().__init__(label)
def get_template_context(self):
return {
"headers": self.headers,
+ "max_rows": self.max_rows,
+ "max_cols": self.max_cols,
+ "overflow_row_behaviour": self.overflow_row_behaviour,
**super().get_template_context()
}