streamlit-image-board 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- streamlit_image_board-0.1.0/LICENSE +21 -0
- streamlit_image_board-0.1.0/MANIFEST.in +2 -0
- streamlit_image_board-0.1.0/PKG-INFO +362 -0
- streamlit_image_board-0.1.0/README.md +337 -0
- streamlit_image_board-0.1.0/pyproject.toml +51 -0
- streamlit_image_board-0.1.0/setup.cfg +4 -0
- streamlit_image_board-0.1.0/streamlit_image_board/__init__.py +758 -0
- streamlit_image_board-0.1.0/streamlit_image_board/frontend/build/index.js +6350 -0
- streamlit_image_board-0.1.0/streamlit_image_board/json_config.py +118 -0
- streamlit_image_board-0.1.0/streamlit_image_board/pyproject.toml +8 -0
- streamlit_image_board-0.1.0/streamlit_image_board/selection.py +367 -0
- streamlit_image_board-0.1.0/streamlit_image_board/sizes.py +136 -0
- streamlit_image_board-0.1.0/streamlit_image_board.egg-info/PKG-INFO +362 -0
- streamlit_image_board-0.1.0/streamlit_image_board.egg-info/SOURCES.txt +18 -0
- streamlit_image_board-0.1.0/streamlit_image_board.egg-info/dependency_links.txt +1 -0
- streamlit_image_board-0.1.0/streamlit_image_board.egg-info/requires.txt +15 -0
- streamlit_image_board-0.1.0/streamlit_image_board.egg-info/top_level.txt +1 -0
- streamlit_image_board-0.1.0/tests/test_json_config.py +53 -0
- streamlit_image_board-0.1.0/tests/test_selection.py +82 -0
- streamlit_image_board-0.1.0/tests/test_sizes.py +54 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Streamlit Image Board Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: streamlit-image-board
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Snappy React selectable image board for Streamlit (Components v2)
|
|
5
|
+
Author-email: George Siogkas <gsiogkas@cvrlab.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/gsiogkas/streamlit-image-board
|
|
8
|
+
Project-URL: Repository, https://github.com/gsiogkas/streamlit-image-board
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: streamlit>=1.51
|
|
13
|
+
Requires-Dist: pillow
|
|
14
|
+
Provides-Extra: aggrid
|
|
15
|
+
Requires-Dist: pandas; extra == "aggrid"
|
|
16
|
+
Requires-Dist: streamlit-aggrid>=0.3.4; extra == "aggrid"
|
|
17
|
+
Provides-Extra: aggrid-v2
|
|
18
|
+
Requires-Dist: pandas; extra == "aggrid-v2"
|
|
19
|
+
Requires-Dist: streamlit-aggrid-v2>=0.3.4; extra == "aggrid-v2"
|
|
20
|
+
Provides-Extra: devel
|
|
21
|
+
Requires-Dist: wheel; extra == "devel"
|
|
22
|
+
Requires-Dist: pytest; extra == "devel"
|
|
23
|
+
Requires-Dist: pandas; extra == "devel"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# streamlit-image-board
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
Snappy React **selectable image board** for Streamlit, built on **Components v2**
|
|
31
|
+
(same stack as [`streamlit-drawable-konva`](https://github.com/gsiogkas/streamlit-drawable-konva)).
|
|
32
|
+
|
|
33
|
+
Named **image-board** (not gallery) to avoid clashing with existing
|
|
34
|
+
`image-gallery` packages. Checkboxes, image-frame selection, hover text, styled
|
|
35
|
+
cards, and toolbar actions update **inside the widget** — no full Streamlit
|
|
36
|
+
rerun for every toggle.
|
|
37
|
+
|
|
38
|
+
## Features
|
|
39
|
+
|
|
40
|
+
- CSS grid of image cards (configurable columns / gap)
|
|
41
|
+
- Configurable **image click**: `select` | `open` | `none` (per item or board-wide)
|
|
42
|
+
- Optional **checkboxes** kept in sync with the same selection
|
|
43
|
+
- **Select all / Deselect all** + master **All** control
|
|
44
|
+
- **Hover text** overlay on thumbnails
|
|
45
|
+
- Stylable **label / caption / text chips / buttons**
|
|
46
|
+
- Per-card (and board-default) **background, text color, font**
|
|
47
|
+
- **JSON** items / full board config (file path, string, or dict)
|
|
48
|
+
- Toolbar **selection counter**: file count + total size
|
|
49
|
+
- Optional **paging** (`page_size`) — overflow goes to the next page; checklist spans all pages
|
|
50
|
+
- **Image-only cards** via `show_card_info=False` (hide labels / captions / chips / card buttons)
|
|
51
|
+
- Configurable **button aesthetics** (`button_style`, per-button `variant` / `size` / `style`)
|
|
52
|
+
- Shared checklist contract for **AgGrid** (classic + v2)
|
|
53
|
+
- Returns `selected_ids: list[str]`, `selected_csv`, and size stats
|
|
54
|
+
|
|
55
|
+
## Install
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install streamlit-image-board
|
|
59
|
+
# or editable:
|
|
60
|
+
pip install -e .
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Frontend rebuild (developers):
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
cd streamlit_image_board/frontend
|
|
67
|
+
npm install
|
|
68
|
+
npm run build
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Demo:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
streamlit run app.py
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Bundled demo photos live in [`examples/images/`](examples/images/) (public domain;
|
|
78
|
+
see [`ATTRIBUTION.md`](examples/images/ATTRIBUTION.md)). Refresh with
|
|
79
|
+
`uv run python scripts/fetch_demo_images.py`.
|
|
80
|
+
|
|
81
|
+
## Quick start
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from streamlit_image_board import st_image_board
|
|
85
|
+
|
|
86
|
+
result = st_image_board(
|
|
87
|
+
[
|
|
88
|
+
{
|
|
89
|
+
"id": "a",
|
|
90
|
+
"src": "https://…",
|
|
91
|
+
"label": "Alpha",
|
|
92
|
+
"hover_text": "Alpha details on hover",
|
|
93
|
+
"card_style": {
|
|
94
|
+
"background": "#0f172a",
|
|
95
|
+
"color": "#e2e8f0",
|
|
96
|
+
"fontFamily": "Georgia, serif",
|
|
97
|
+
},
|
|
98
|
+
"texts": [
|
|
99
|
+
{"text": "NEW", "variant": "badge"},
|
|
100
|
+
{"text": "12 MB", "variant": "muted"},
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "b",
|
|
105
|
+
"image": pil_image,
|
|
106
|
+
"label": "Beta",
|
|
107
|
+
"caption": "from PIL",
|
|
108
|
+
"background": "#fff7ed",
|
|
109
|
+
"color": "#9a3412",
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
columns=4,
|
|
113
|
+
selectable=True,
|
|
114
|
+
image_click="select", # or "open" / "none"
|
|
115
|
+
show_select_all=True,
|
|
116
|
+
selection_color="#0f766e",
|
|
117
|
+
card_style={"background": "#ffffff", "color": "#0f172a"}, # board default
|
|
118
|
+
buttons=[
|
|
119
|
+
{"id": "approve", "label": "Approve", "variant": "primary"},
|
|
120
|
+
{"id": "delete", "label": "Delete", "variant": "danger"},
|
|
121
|
+
],
|
|
122
|
+
key="board",
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
ids = result.selected_ids # ["a", "b"]
|
|
126
|
+
csv = result.selected_csv # "a,b"
|
|
127
|
+
n = result.selected_count # 2
|
|
128
|
+
size = result.selected_size # e.g. "240.0 KB" when sizes are known
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Button aesthetics
|
|
132
|
+
|
|
133
|
+
Board-wide theme (toolbar, pager, card buttons):
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
result = st_image_board(
|
|
137
|
+
items,
|
|
138
|
+
button_style={
|
|
139
|
+
"background": "#ffffff",
|
|
140
|
+
"color": "#0f172a",
|
|
141
|
+
"borderRadius": "999px",
|
|
142
|
+
"padding": "6px 14px",
|
|
143
|
+
"fontSize": "13px",
|
|
144
|
+
"primaryBackground": "#0f766e",
|
|
145
|
+
"primaryColor": "#ffffff",
|
|
146
|
+
"dangerBackground": "#fee2e2",
|
|
147
|
+
"dangerColor": "#b91c1c",
|
|
148
|
+
},
|
|
149
|
+
buttons=[
|
|
150
|
+
{"id": "ok", "label": "Approve", "variant": "primary", "size": "lg"},
|
|
151
|
+
{"id": "no", "label": "Skip", "variant": "ghost", "size": "sm"},
|
|
152
|
+
{
|
|
153
|
+
"id": "del",
|
|
154
|
+
"label": "Delete",
|
|
155
|
+
"variant": "danger",
|
|
156
|
+
"style": {"fontWeight": "700"},
|
|
157
|
+
},
|
|
158
|
+
],
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
| Knob | Values |
|
|
163
|
+
| --- | --- |
|
|
164
|
+
| `variant` | `default` · `primary` · `danger` · `ghost` |
|
|
165
|
+
| `size` | `sm` · `md` (default) · `lg` |
|
|
166
|
+
| `style` | per-button sanitized CSS override |
|
|
167
|
+
| `button_style` | board theme → CSS variables for all `.sig-btn` |
|
|
168
|
+
|
|
169
|
+
### Image-only cards
|
|
170
|
+
|
|
171
|
+
Hide everything under the thumbnail (label, caption, chips, per-card buttons):
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
result = st_image_board(items, show_card_info=False)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Checkboxes, hover overlay, selection frame, and toolbar actions still work.
|
|
178
|
+
|
|
179
|
+
### Paging
|
|
180
|
+
|
|
181
|
+
Set `page_size` to a positive integer to paginate instead of scrolling.
|
|
182
|
+
Selection, select-all, and the size counter stay **global** across pages.
|
|
183
|
+
|
|
184
|
+
```python
|
|
185
|
+
result = st_image_board(
|
|
186
|
+
items,
|
|
187
|
+
columns=4,
|
|
188
|
+
page_size=8,
|
|
189
|
+
pager_position="top", # or "bottom"
|
|
190
|
+
)
|
|
191
|
+
# page_size=0 (default) → classic scroll via max_height
|
|
192
|
+
# Pager controls: First / Prev / Next / Last
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Selection size counter
|
|
196
|
+
|
|
197
|
+
Pass `size` / `size_bytes` on items (int bytes, or strings like `"1.5 MB"`).
|
|
198
|
+
Pillow images and `data:` URLs get a size inferred automatically.
|
|
199
|
+
|
|
200
|
+
The toolbar shows e.g. `3 selected / 8 · 1.2 MB` (toggle with
|
|
201
|
+
`show_selection_stats=False`). Python side:
|
|
202
|
+
|
|
203
|
+
| Field | Meaning |
|
|
204
|
+
| --- | --- |
|
|
205
|
+
| `selected_count` | Number of selected files |
|
|
206
|
+
| `selected_bytes` | Sum of known sizes (bytes) |
|
|
207
|
+
| `selected_size` | Human string (`""` if none known) |
|
|
208
|
+
| `result.stats_label(total_count=…)` | Same text as the toolbar |
|
|
209
|
+
|
|
210
|
+
```python
|
|
211
|
+
result = st_image_board(
|
|
212
|
+
[
|
|
213
|
+
{"id": "a", "src": "https://…/a.jpg", "size_bytes": 120_000},
|
|
214
|
+
{"id": "b", "src": "https://…/b.jpg", "size": "2.1 MB"},
|
|
215
|
+
],
|
|
216
|
+
)
|
|
217
|
+
st.write(result.selected_count, result.selected_size)
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Image click behavior
|
|
221
|
+
|
|
222
|
+
| `image_click` | Thumbnail click |
|
|
223
|
+
| --- | --- |
|
|
224
|
+
| `"select"` (default) | Toggle selection frame (same checklist as checkboxes) |
|
|
225
|
+
| `"open"` | Emit `clicked_button="open"` / `clicked_item`, and optionally `window.open` |
|
|
226
|
+
| `"none"` | No thumbnail action (checkboxes / buttons still work) |
|
|
227
|
+
|
|
228
|
+
Set board-wide via `image_click=…`, or per item with `"image_click": "open"`.
|
|
229
|
+
Open target: item `open_url` / `href` / `link`, else `src`. Use `open_in_new_tab=False` to skip the browser tab and only emit the Streamlit event.
|
|
230
|
+
|
|
231
|
+
```python
|
|
232
|
+
result = st_image_board(
|
|
233
|
+
[
|
|
234
|
+
{
|
|
235
|
+
"id": "shot",
|
|
236
|
+
"src": "https://…/thumb.jpg",
|
|
237
|
+
"label": "Full size",
|
|
238
|
+
"open_url": "https://…/full.jpg",
|
|
239
|
+
"image_click": "open",
|
|
240
|
+
}
|
|
241
|
+
],
|
|
242
|
+
image_click="open",
|
|
243
|
+
open_in_new_tab=True,
|
|
244
|
+
selectable=True, # checkboxes still select
|
|
245
|
+
show_checkboxes=True,
|
|
246
|
+
)
|
|
247
|
+
if result.clicked_button == "open":
|
|
248
|
+
st.write("Opened", result.clicked_item)
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### JSON input
|
|
252
|
+
|
|
253
|
+
Pass a path, JSON string, or dict — items only, or a full board config:
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from streamlit_image_board import load_board_config, st_image_board
|
|
257
|
+
|
|
258
|
+
# Items array file (see examples/items.json)
|
|
259
|
+
result = st_image_board("examples/items.json", columns=3, image_click="select")
|
|
260
|
+
|
|
261
|
+
# Full config: items + options (kwargs override file fields)
|
|
262
|
+
result = st_image_board(config="examples/board_config.json")
|
|
263
|
+
|
|
264
|
+
# Or load yourself
|
|
265
|
+
cfg = load_board_config("examples/board_config.json")
|
|
266
|
+
result = st_image_board(**cfg, key="from_json")
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Helpers: `load_board_items`, `load_board_config`.
|
|
270
|
+
|
|
271
|
+
### Card chrome (font / background)
|
|
272
|
+
|
|
273
|
+
| Source | Keys |
|
|
274
|
+
| --- | --- |
|
|
275
|
+
| Board default | `card_style={...}` on `st_image_board` |
|
|
276
|
+
| Per item | `card_style` / `style`, or shortcuts `background`, `color`, `font_family` |
|
|
277
|
+
| Label / caption | `label_style`, `caption_style` |
|
|
278
|
+
|
|
279
|
+
Allowed style keys are sanitized (color, background, fontFamily, padding, …).
|
|
280
|
+
|
|
281
|
+
### Selection UX
|
|
282
|
+
|
|
283
|
+
- Click the **image** → depends on `image_click` (`select` / `open` / `none`)
|
|
284
|
+
- Optional **checkbox** uses the same checklist (independent of open mode)
|
|
285
|
+
- **All** / **Select all** / **Deselect all** in the toolbar
|
|
286
|
+
- Hover shows `hover_text` (falls back to `label`)
|
|
287
|
+
|
|
288
|
+
### `BoardResult`
|
|
289
|
+
|
|
290
|
+
| Field | Meaning |
|
|
291
|
+
| --- | --- |
|
|
292
|
+
| `selected_ids` | `list[str]` — checkbox **and** image clicks |
|
|
293
|
+
| `selected_csv` | Same ids as `"id1,id2"` |
|
|
294
|
+
| `selected_count` | Number of selected files |
|
|
295
|
+
| `selected_bytes` | Sum of known `size_bytes` |
|
|
296
|
+
| `selected_size` | Human-readable total (or `""`) |
|
|
297
|
+
| `clicked_button` | Last toolbar/card button id |
|
|
298
|
+
| `clicked_item` | Item id for card button / activation |
|
|
299
|
+
| `action_nonce` | Increments on each action so repeats are detectable |
|
|
300
|
+
|
|
301
|
+
Helpers: `result.as_list()`, `result.as_set()`, `result.stats_label()`,
|
|
302
|
+
`result.to_aggrid_preselected(df)`, `format_bytes`.
|
|
303
|
+
|
|
304
|
+
`st_image_gallery` / `GalleryResult` remain as thin aliases.
|
|
305
|
+
|
|
306
|
+
## AgGrid checklist bridge (classic + v2)
|
|
307
|
+
|
|
308
|
+
Shared contract: **`list[str]` of stable ids** (plus optional CSV). Both the
|
|
309
|
+
image board and AgGrid read/write that checklist.
|
|
310
|
+
|
|
311
|
+
```python
|
|
312
|
+
from st_aggrid import AgGrid, GridOptionsBuilder, GridUpdateMode
|
|
313
|
+
from streamlit_image_board import (
|
|
314
|
+
configure_aggrid_selection,
|
|
315
|
+
st_image_board,
|
|
316
|
+
sync_checklist,
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
board = st_image_board(items, selected_ids=st.session_state.get("shared_selection", []))
|
|
320
|
+
|
|
321
|
+
gb = GridOptionsBuilder.from_dataframe(df) # df must include an "id" column
|
|
322
|
+
configure_aggrid_selection(gb, st.session_state.get("shared_selection", []), data=df)
|
|
323
|
+
grid = AgGrid(df, gridOptions=gb.build(), update_mode=GridUpdateMode.SELECTION_CHANGED)
|
|
324
|
+
|
|
325
|
+
sel = sync_checklist(
|
|
326
|
+
session_state=st.session_state,
|
|
327
|
+
key="shared_selection",
|
|
328
|
+
board=board,
|
|
329
|
+
aggrid=grid,
|
|
330
|
+
id_column="id",
|
|
331
|
+
)
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
| Helper | Role |
|
|
335
|
+
| --- | --- |
|
|
336
|
+
| `normalize_selected_ids` | Coerce any payload → `list[str]` |
|
|
337
|
+
| `selected_ids_from_aggrid` | Read AgGrid `selected_rows` |
|
|
338
|
+
| `aggrid_preselected_rows` | Map ids → `pre_selected_rows` indices |
|
|
339
|
+
| `configure_aggrid_selection` | Wire `GridOptionsBuilder.configure_selection` |
|
|
340
|
+
| `sync_checklist` | Bidirectional session sync board ↔ AgGrid |
|
|
341
|
+
| `filter_rows_by_ids` | Slice a DataFrame / row list by the checklist |
|
|
342
|
+
|
|
343
|
+
Optional extras: `pip install 'streamlit-image-board[aggrid]'` or `[aggrid-v2]`.
|
|
344
|
+
|
|
345
|
+
## Publishing
|
|
346
|
+
|
|
347
|
+
Step-by-step GitHub / PyPI / Streamlit Cloud / gallery:
|
|
348
|
+
**[PUBLISHING.md](PUBLISHING.md)**.
|
|
349
|
+
|
|
350
|
+
Refresh the usage GIF:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
uv run python scripts/make_usage_gif.py
|
|
354
|
+
# or record from the live demo — see PUBLISHING.md
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
## Requirements
|
|
358
|
+
|
|
359
|
+
- Python ≥ 3.10
|
|
360
|
+
- Streamlit ≥ 1.51 (Components v2)
|
|
361
|
+
- Pillow
|
|
362
|
+
- Node ≥ 18 (frontend build only)
|