diff --git a/CHANGELOG.md b/CHANGELOG.md index d4484b8c1adb8..f1929c19b0833 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ No changes to highlight. - Increase timeout for sending analytics data by [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3647](https://github.com/gradio-app/gradio/pull/3647) - Fix bug where http token was not accessed over websocket connections by [@freddyaboulton](https://github.com/freddyaboulton) in [PR 3735](https://github.com/gradio-app/gradio/pull/3735) +- Add ability to specify `rows`, `columns` and `object-fit` in `style()` for `gr.Gallery()` component by [@dawoodkhan82](https://github.com/dawoodkhan82) in [PR 3586](https://github.com/gradio-app/gradio/pull/3586) - Fix bug where recording an audio file through the microphone resulted in a corrupted file name by [@abidlabs](https://github.com/abidlabs) in [PR 3770](https://github.com/gradio-app/gradio/pull/3770) ## Documentation Changes: diff --git a/demo/fake_gan/run.ipynb b/demo/fake_gan/run.ipynb index e6f38b681fa4d..c053bb0eece51 100644 --- a/demo/fake_gan/run.ipynb +++ b/demo/fake_gan/run.ipynb @@ -1 +1 @@ -{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: fake_gan\n", "### This is a fake GAN that shows how to create a text-to-image interface for image generation. Check out the Stable Diffusion demo for more: https://hf.co/spaces/stabilityai/stable-diffusion/\n", " "]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('files')\n", "!wget -q -O files/cheetah1.jpg https://github.com/gradio-app/gradio/raw/main/demo/fake_gan/files/cheetah1.jpg"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["# This demo needs to be run from the repo folder.\n", "# python demo/fake_gan/run.py\n", "import random\n", "\n", "import gradio as gr\n", "\n", "\n", "def fake_gan():\n", " images = [\n", " (random.choice(\n", " [\n", " \"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80\",\n", " \"https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=386&q=80\",\n", " \"https://images.unsplash.com/photo-1542909168-82c3e7fdca5c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8aHVtYW4lMjBmYWNlfGVufDB8fDB8fA%3D%3D&w=1000&q=80\",\n", " \"https://images.unsplash.com/photo-1546456073-92b9f0a8d413?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80\",\n", " \"https://images.unsplash.com/photo-1601412436009-d964bd02edbc?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=464&q=80\",\n", " ]\n", " ), f\"label {i}\" if i != 0 else \"label\" * 50)\n", " for i in range(3)\n", " ]\n", " return images\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Column(variant=\"panel\"):\n", " with gr.Row(variant=\"compact\"):\n", " text = gr.Textbox(\n", " label=\"Enter your prompt\",\n", " show_label=False,\n", " max_lines=1,\n", " placeholder=\"Enter your prompt\",\n", " ).style(\n", " container=False,\n", " )\n", " btn = gr.Button(\"Generate image\").style(full_width=False)\n", "\n", " gallery = gr.Gallery(\n", " label=\"Generated images\", show_label=False, elem_id=\"gallery\"\n", " ).style(grid=[2], height=\"auto\")\n", "\n", " btn.click(fake_gan, None, gallery)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file +{"cells": [{"cell_type": "markdown", "id": 302934307671667531413257853548643485645, "metadata": {}, "source": ["# Gradio Demo: fake_gan\n", "### This is a fake GAN that shows how to create a text-to-image interface for image generation. Check out the Stable Diffusion demo for more: https://hf.co/spaces/stabilityai/stable-diffusion/\n", " "]}, {"cell_type": "code", "execution_count": null, "id": 272996653310673477252411125948039410165, "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": 288918539441861185822528903084949547379, "metadata": {}, "outputs": [], "source": ["# Downloading files from the demo repo\n", "import os\n", "os.mkdir('files')\n", "!wget -q -O files/cheetah1.jpg https://github.com/gradio-app/gradio/raw/main/demo/fake_gan/files/cheetah1.jpg"]}, {"cell_type": "code", "execution_count": null, "id": 44380577570523278879349135829904343037, "metadata": {}, "outputs": [], "source": ["# This demo needs to be run from the repo folder.\n", "# python demo/fake_gan/run.py\n", "import random\n", "\n", "import gradio as gr\n", "\n", "\n", "def fake_gan():\n", " images = [\n", " (random.choice(\n", " [\n", " \"https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80\",\n", " \"https://images.unsplash.com/photo-1554151228-14d9def656e4?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=386&q=80\",\n", " \"https://images.unsplash.com/photo-1542909168-82c3e7fdca5c?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxzZWFyY2h8MXx8aHVtYW4lMjBmYWNlfGVufDB8fDB8fA%3D%3D&w=1000&q=80\",\n", " \"https://images.unsplash.com/photo-1546456073-92b9f0a8d413?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=387&q=80\",\n", " \"https://images.unsplash.com/photo-1601412436009-d964bd02edbc?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=464&q=80\",\n", " ]\n", " ), f\"label {i}\" if i != 0 else \"label\" * 50)\n", " for i in range(3)\n", " ]\n", " return images\n", "\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Column(variant=\"panel\"):\n", " with gr.Row(variant=\"compact\"):\n", " text = gr.Textbox(\n", " label=\"Enter your prompt\",\n", " show_label=False,\n", " max_lines=1,\n", " placeholder=\"Enter your prompt\",\n", " ).style(\n", " container=False,\n", " )\n", " btn = gr.Button(\"Generate image\").style(full_width=False)\n", "\n", " gallery = gr.Gallery(\n", " label=\"Generated images\", show_label=False, elem_id=\"gallery\"\n", " ).style(columns=[2], rows=[2], object_fit=\"contain\", height=\"auto\")\n", "\n", " btn.click(fake_gan, None, gallery)\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()\n"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/fake_gan/run.py b/demo/fake_gan/run.py index b944ed5ed29f2..6757a78462c12 100644 --- a/demo/fake_gan/run.py +++ b/demo/fake_gan/run.py @@ -36,7 +36,7 @@ def fake_gan(): gallery = gr.Gallery( label="Generated images", show_label=False, elem_id="gallery" - ).style(grid=[2], height="auto") + ).style(columns=[2], rows=[2], object_fit="contain", height="auto") btn.click(fake_gan, None, gallery) diff --git a/gradio/components.py b/gradio/components.py index 0bbc5353cb52c..2e099862bd74a 100644 --- a/gradio/components.py +++ b/gradio/components.py @@ -4060,25 +4060,39 @@ def style( self, *, grid: int | Tuple | None = None, + columns: int | Tuple | None = None, + rows: int | Tuple | None = None, height: str | None = None, container: bool | None = None, preview: bool | None = None, + object_fit: str | None = None, **kwargs, ): """ This method can be used to change the appearance of the gallery component. Parameters: - grid: Represents the number of images that should be shown in one row, for each of the six standard screen sizes (<576px, <768px, <992px, <1200px, <1400px, >1400px). if fewer that 6 are given then the last will be used for all subsequent breakpoints + grid: ('grid' has been renamed to 'columns') Represents the number of images that should be shown in one row, for each of the six standard screen sizes (<576px, <768px, <992px, <1200px, <1400px, >1400px). if fewer that 6 are given then the last will be used for all subsequent breakpoints columns: Represents the number of columns in the image grid, for each of the six standard screen sizes (<576px, <768px, <992px, <1200px, <1400px, >1400px). if fewer that 6 are given then the last will be used for all subsequent breakpoints + rows: Represents the number of rows in the image grid, for each of the six standard screen sizes (<576px, <768px, <992px, <1200px, <1400px, >1400px). if fewer that 6 are given then the last will be used for all subsequent breakpoints height: Height of the gallery. container: If True, will place gallery in a container - providing some extra padding around the border. preview: If True, will display the Gallery in preview mode, which shows all of the images as thumbnails and allows the user to click on them to view them in full size. + object_fit: CSS object-fit property for the thumbnail images in the gallery. Can be "contain", "cover", "fill", "none", or "scale-down". """ if grid is not None: - self._style["grid"] = grid + warnings.warn( + "The 'grid' parameter will be deprecated. Please use 'columns' instead.", + ) + self._style["grid_cols"] = grid + if columns is not None: + self._style["grid_cols"] = columns + if rows is not None: + self._style["grid_rows"] = rows if height is not None: self._style["height"] = height if preview is not None: self._style["preview"] = preview + if object_fit is not None: + self._style["object_fit"] = object_fit Component.style(self, container=container, **kwargs) return self diff --git a/js/gallery/src/Gallery.svelte b/js/gallery/src/Gallery.svelte index b759818af7b92..feaa53fea08dd 100644 --- a/js/gallery/src/Gallery.svelte +++ b/js/gallery/src/Gallery.svelte @@ -16,7 +16,11 @@ export let root: string = ""; export let root_url: null | string = null; export let value: Array<string> | Array<FileData> | null = null; - export let style: Styles = { grid: [2], height: "auto" }; + export let style: Styles = { + grid_cols: [2], + object_fit: "cover", + height: "auto" + }; const dispatch = createEventDispatcher<{ select: SelectData; @@ -123,8 +127,7 @@ $: can_zoom = window_height >= height; function add_height_to_styles(style: Styles): string { - styles = get_styles(style, ["grid"]).styles; - + styles = get_styles(style, ["grid_cols", "grid_rows", "object_fit"]).styles; return styles + ` height: ${style.height}`; } @@ -312,7 +315,7 @@ width: var(--size-full); height: var(--size-full); overflow: hidden; - object-fit: cover; + object-fit: var(--object-fit); } .grid-wrap { @@ -323,6 +326,7 @@ .grid-container { display: grid; + grid-template-rows: var(--grid-rows); grid-template-columns: var(--grid-cols); gap: var(--spacing-lg); } @@ -356,7 +360,7 @@ width: var(--size-full); height: var(--size-full); overflow: hidden; - object-fit: cover; + object-fit: var(--object-fit); } .thumbnail-lg:hover .caption-label { diff --git a/js/utils/src/styles.ts b/js/utils/src/styles.ts index e339b24af3bab..def220bd66dd9 100644 --- a/js/utils/src/styles.ts +++ b/js/utils/src/styles.ts @@ -1,6 +1,7 @@ export interface Styles { container?: boolean; - grid?: number | Array<number>; + grid_cols?: number | Array<number>; + grid_rows?: number | Array<number>; height?: "auto" | string | number; width?: "auto" | string | number; full_width?: boolean; @@ -12,6 +13,7 @@ export interface Styles { gap?: boolean; size?: "sm" | "lg"; preview?: boolean; + object_fit?: "contain" | "cover" | "fill" | "none" | "scale-down"; show_copy_button?: boolean; } @@ -63,15 +65,28 @@ const style_handlers: StyleHandlers = { ? "" : `border-width: 0; box-shadow: none; overflow: visible; background: transparent;`; }, - grid(grid) { - let grid_map = ["", "sm-", "md-", "lg-", "xl-", "2xl-"]; - let _grid = Array.isArray(grid) ? grid : [grid]; + grid_cols(grid_cols) { + let grid_cols_map = ["", "sm-", "md-", "lg-", "xl-", "2xl-"]; + let _grid_cols = Array.isArray(grid_cols) ? grid_cols : [grid_cols]; return [0, 0, 0, 0, 0, 0] .map( (_, i) => - `--${grid_map[i]}grid-cols: var(--grid-${ - _grid?.[i] || _grid?.[_grid?.length - 1] + `--${grid_cols_map[i]}grid-cols: var(--grid-${ + _grid_cols?.[i] || _grid_cols?.[_grid_cols?.length - 1] + });` + ) + .join(" "); + }, + grid_rows(grid_rows) { + let grid_rows_map = ["", "sm-", "md-", "lg-", "xl-", "2xl-"]; + let _grid_rows = Array.isArray(grid_rows) ? grid_rows : [grid_rows]; + + return [0, 0, 0, 0, 0, 0] + .map( + (_, i) => + `--${grid_rows_map[i]}grid-rows: var(--grid-${ + _grid_rows?.[i] || _grid_rows?.[_grid_rows?.length - 1] });` ) .join(" "); @@ -92,6 +107,9 @@ const style_handlers: StyleHandlers = { }, item_container(visible) { return visible ? "" : "border-width:0;"; + }, + object_fit(object_fit) { + return `--object-fit: ${object_fit};`; } } as const;