Spaces:
Sleeping
Sleeping
Add smartphone capability.
Browse files- app.py +7 -2
- s_i_c/__init__.py +134 -0
- s_i_c/frontend/index.html +15 -0
- s_i_c/frontend/main.js +107 -0
- s_i_c/frontend/streamlit-component-lib.js +34 -0
- s_i_c/frontend/style.css +17 -0
app.py
CHANGED
@@ -3,7 +3,7 @@ from my_chess.learner.environments import Chess
|
|
3 |
|
4 |
from chessmodels import DCMinMax
|
5 |
import streamlit as st
|
6 |
-
from
|
7 |
|
8 |
import torch
|
9 |
|
@@ -63,7 +63,12 @@ def play(bot_select, t_p):
|
|
63 |
col1, col2 = st.columns([3,1])
|
64 |
with col1:
|
65 |
st.markdown("# {}".format(information(st.session_state.play, t_p)))
|
66 |
-
st.session_state["board"] = streamlit_image_coordinates(
|
|
|
|
|
|
|
|
|
|
|
67 |
with col2:
|
68 |
st.button("Reset", on_click=reset, args=[t_p])
|
69 |
st.markdown("#### You are playing as {}!".format("white" if st.session_state.play.get_human_player() == "player_0" else "black"))
|
|
|
3 |
|
4 |
from chessmodels import DCMinMax
|
5 |
import streamlit as st
|
6 |
+
from s_i_c import streamlit_image_coordinates
|
7 |
|
8 |
import torch
|
9 |
|
|
|
63 |
col1, col2 = st.columns([3,1])
|
64 |
with col1:
|
65 |
st.markdown("# {}".format(information(st.session_state.play, t_p)))
|
66 |
+
st.session_state["board"] = streamlit_image_coordinates(
|
67 |
+
st.session_state.play.render_board(),
|
68 |
+
key="brd",
|
69 |
+
use_column_width=True,
|
70 |
+
click_and_drag=True)
|
71 |
+
print(st.session_state.board)
|
72 |
with col2:
|
73 |
st.button("Reset", on_click=reset, args=[t_p])
|
74 |
st.markdown("#### You are playing as {}!".format("white" if st.session_state.play.get_human_player() == "player_0" else "black"))
|
s_i_c/__init__.py
ADDED
@@ -0,0 +1,134 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Taken from https://github.com/blackary/streamlit-image-coordinates/tree/main/src/streamlit_image_coordinates
|
3 |
+
and modified for use with smart phones (disabling scroll within image).
|
4 |
+
"""
|
5 |
+
|
6 |
+
from __future__ import annotations
|
7 |
+
|
8 |
+
import base64
|
9 |
+
from io import BytesIO
|
10 |
+
from pathlib import Path
|
11 |
+
|
12 |
+
import numpy as np
|
13 |
+
import streamlit as st
|
14 |
+
import streamlit.components.v1 as components
|
15 |
+
from PIL import Image
|
16 |
+
from streamlit.elements.image import UseColumnWith
|
17 |
+
|
18 |
+
# Tell streamlit that there is a component called streamlit_image_coordinates,
|
19 |
+
# and that the code to display that component is in the "frontend" folder
|
20 |
+
frontend_dir = (Path(__file__).parent / "frontend").absolute()
|
21 |
+
_component_func = components.declare_component(
|
22 |
+
"streamlit_image_coordinates", path=str(frontend_dir)
|
23 |
+
)
|
24 |
+
|
25 |
+
|
26 |
+
# Create the python function that will be called
|
27 |
+
def streamlit_image_coordinates(
|
28 |
+
source: str | Path | np.ndarray | object,
|
29 |
+
height: int | None = None,
|
30 |
+
width: int | None = None,
|
31 |
+
key: str | None = None,
|
32 |
+
use_column_width: UseColumnWith | str | None = None,
|
33 |
+
click_and_drag: bool = False,
|
34 |
+
):
|
35 |
+
"""
|
36 |
+
Take an image source and return the coordinates of the image clicked
|
37 |
+
|
38 |
+
Parameters
|
39 |
+
----------
|
40 |
+
source : str | Path | object
|
41 |
+
The image source
|
42 |
+
height : int | None
|
43 |
+
The height of the image. If None, the height will be the original height
|
44 |
+
width : int | None
|
45 |
+
The width of the image. If None, the width will be the original width
|
46 |
+
use_column_width : "auto", "always", "never", or bool
|
47 |
+
If "auto", set the image's width to its natural size,
|
48 |
+
but do not exceed the width of the column.
|
49 |
+
If "always" or True, set the image's width to the column width.
|
50 |
+
If "never" or False, set the image's width to its natural size.
|
51 |
+
Note: if set, `use_column_width` takes precedence over the `width` parameter.
|
52 |
+
click_and_drag: bool
|
53 |
+
If true, the event is not sent until the user releases the mouse. The
|
54 |
+
mouse down event is returned as x1, y1 and the mouse up event is returned
|
55 |
+
as x2, y2. Note that x2 and y2 may be outside the image.
|
56 |
+
"""
|
57 |
+
|
58 |
+
if isinstance(source, (Path, str)):
|
59 |
+
if not str(source).startswith("http"):
|
60 |
+
content = Path(source).read_bytes()
|
61 |
+
src = "data:image/png;base64," + base64.b64encode(content).decode("utf-8")
|
62 |
+
else:
|
63 |
+
src = str(source)
|
64 |
+
elif hasattr(source, "save"):
|
65 |
+
buffered = BytesIO()
|
66 |
+
source.save(buffered, format="PNG") # type: ignore
|
67 |
+
src = "data:image/png;base64,"
|
68 |
+
src += base64.b64encode(buffered.getvalue()).decode("utf-8") # type: ignore
|
69 |
+
elif isinstance(source, np.ndarray):
|
70 |
+
image = Image.fromarray(source)
|
71 |
+
buffered = BytesIO()
|
72 |
+
image.save(buffered, format="PNG") # type: ignore
|
73 |
+
src = "data:image/png;base64,"
|
74 |
+
src += base64.b64encode(buffered.getvalue()).decode("utf-8") # type: ignore
|
75 |
+
else:
|
76 |
+
raise ValueError(
|
77 |
+
"Must pass a string, Path, numpy array or object with a save method"
|
78 |
+
)
|
79 |
+
|
80 |
+
return _component_func(
|
81 |
+
src=src,
|
82 |
+
height=height,
|
83 |
+
width=width,
|
84 |
+
use_column_width=use_column_width,
|
85 |
+
key=key,
|
86 |
+
click_and_drag=click_and_drag,
|
87 |
+
)
|
88 |
+
|
89 |
+
|
90 |
+
def main():
|
91 |
+
st.set_page_config(
|
92 |
+
page_title="Streamlit Image Coordinates",
|
93 |
+
page_icon="🎯",
|
94 |
+
layout="wide",
|
95 |
+
)
|
96 |
+
col1, col2, col3 = st.columns(3)
|
97 |
+
|
98 |
+
with col1:
|
99 |
+
st.write("## Url example")
|
100 |
+
|
101 |
+
with st.echo():
|
102 |
+
value = streamlit_image_coordinates(
|
103 |
+
"https://placekitten.com/200/300",
|
104 |
+
key="url",
|
105 |
+
)
|
106 |
+
|
107 |
+
st.write(value)
|
108 |
+
|
109 |
+
with col2:
|
110 |
+
st.write("## Local image example")
|
111 |
+
|
112 |
+
with st.echo():
|
113 |
+
value = streamlit_image_coordinates(
|
114 |
+
"kitty.jpeg",
|
115 |
+
key="local",
|
116 |
+
)
|
117 |
+
|
118 |
+
st.write(value)
|
119 |
+
|
120 |
+
with col3:
|
121 |
+
st.write("## Custom size example")
|
122 |
+
|
123 |
+
with st.echo():
|
124 |
+
value = streamlit_image_coordinates(
|
125 |
+
"kitty.jpeg",
|
126 |
+
width=250,
|
127 |
+
key="local2",
|
128 |
+
)
|
129 |
+
|
130 |
+
st.write(value)
|
131 |
+
|
132 |
+
|
133 |
+
if __name__ == "__main__":
|
134 |
+
main()
|
s_i_c/frontend/index.html
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8" />
|
5 |
+
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
6 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
7 |
+
<title>streamlit-image-coordinates</title>
|
8 |
+
<script src="./streamlit-component-lib.js"></script>
|
9 |
+
<script src="./main.js"></script>
|
10 |
+
<link rel="stylesheet" href="./style.css" />
|
11 |
+
</head>
|
12 |
+
<body>
|
13 |
+
<img id="image" draggable="false" />
|
14 |
+
</body>
|
15 |
+
</html>
|
s_i_c/frontend/main.js
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
// The `Streamlit` object exists because our html file includes
|
2 |
+
// `streamlit-component-lib.js`.
|
3 |
+
// If you get an error about "Streamlit" not being defined, that
|
4 |
+
// means you're missing that file.
|
5 |
+
|
6 |
+
function sendValue(value) {
|
7 |
+
Streamlit.setComponentValue(value)
|
8 |
+
}
|
9 |
+
|
10 |
+
/**
|
11 |
+
* The component's render function. This will be called immediately after
|
12 |
+
* the component is initially loaded, and then again every time the
|
13 |
+
* component gets new data from Python.
|
14 |
+
*/
|
15 |
+
|
16 |
+
function clickListener(event) {
|
17 |
+
const {offsetX, offsetY} = event;
|
18 |
+
const img = document.getElementById("image");
|
19 |
+
|
20 |
+
sendValue({x: offsetX, y: offsetY, width: img.width, height: img.height});
|
21 |
+
}
|
22 |
+
|
23 |
+
function mouseDownListener(downEvent) {
|
24 |
+
const [x1, y1] = [downEvent.offsetX, downEvent.offsetY];
|
25 |
+
|
26 |
+
window.addEventListener("mouseup", (upEvent) => {
|
27 |
+
const [x2, y2] = [upEvent.clientX, upEvent.clientY];
|
28 |
+
const img = document.getElementById("image");
|
29 |
+
const rect = img.getBoundingClientRect();
|
30 |
+
|
31 |
+
sendValue({x1: x1, y1: y1, x2: x2 - rect.left, y2: y2 - rect.top, width: img.width, height: img.height});
|
32 |
+
}, {once: true})
|
33 |
+
}
|
34 |
+
|
35 |
+
function touchStartListener(startEvent) {
|
36 |
+
startEvent.preventDefault();
|
37 |
+
|
38 |
+
var touch = startEvent.changedTouches[0];
|
39 |
+
|
40 |
+
const img = document.getElementById("image");
|
41 |
+
const rect = img.getBoundingClientRect();
|
42 |
+
|
43 |
+
const [x1, y1] = [touch.clientX - rect.left, touch.clientY - rect.top];
|
44 |
+
|
45 |
+
window.addEventListener("touchend", (upEvent) => {
|
46 |
+
var touch = upEvent.changedTouches[0];
|
47 |
+
const [x2, y2] = [touch.clientX, touch.clientY];
|
48 |
+
const img = document.getElementById("image");
|
49 |
+
const rect = img.getBoundingClientRect();
|
50 |
+
|
51 |
+
sendValue({x1: x1, y1: y1, x2: x2 - rect.left, y2: y2 - rect.top, width: img.width, height: img.height});
|
52 |
+
}, {once: true})
|
53 |
+
}
|
54 |
+
|
55 |
+
function onRender(event) {
|
56 |
+
let {src, height, width, use_column_width, click_and_drag} = event.detail.args;
|
57 |
+
|
58 |
+
const img = document.getElementById("image");
|
59 |
+
|
60 |
+
if (img.src !== src) {
|
61 |
+
img.src = src;
|
62 |
+
}
|
63 |
+
|
64 |
+
function resizeImage() {
|
65 |
+
img.classList.remove("auto", "fullWidth");
|
66 |
+
img.removeAttribute("width");
|
67 |
+
img.removeAttribute("height");
|
68 |
+
|
69 |
+
if (use_column_width === "always" || use_column_width === true) {
|
70 |
+
img.classList.add("fullWidth");
|
71 |
+
} else if (use_column_width === "auto") {
|
72 |
+
img.classList.add("auto");
|
73 |
+
} else {
|
74 |
+
if (!width && !height) {
|
75 |
+
width = img.naturalWidth;
|
76 |
+
height = img.naturalHeight;
|
77 |
+
} else if (!height) {
|
78 |
+
height = width * img.naturalHeight / img.naturalWidth;
|
79 |
+
} else if (!width) {
|
80 |
+
width = height * img.naturalWidth / img.naturalHeight;
|
81 |
+
}
|
82 |
+
|
83 |
+
img.width = width;
|
84 |
+
img.height = height;
|
85 |
+
}
|
86 |
+
|
87 |
+
Streamlit.setFrameHeight(img.height);
|
88 |
+
}
|
89 |
+
|
90 |
+
img.onload = resizeImage;
|
91 |
+
window.addEventListener("resize", resizeImage);
|
92 |
+
|
93 |
+
// When image is clicked, send the coordinates to Python through sendValue
|
94 |
+
if (click_and_drag) {
|
95 |
+
img.onclick = null;
|
96 |
+
img.onmousedown = mouseDownListener;
|
97 |
+
img.ontouchstart = touchStartListener;
|
98 |
+
} else {
|
99 |
+
img.onmousedown = null;
|
100 |
+
img.onclick = clickListener;
|
101 |
+
}
|
102 |
+
}
|
103 |
+
|
104 |
+
// Render the component whenever python send a "render event"
|
105 |
+
Streamlit.events.addEventListener(Streamlit.RENDER_EVENT, onRender)
|
106 |
+
// Tell Streamlit that the component is ready to receive events
|
107 |
+
Streamlit.setComponentReady()
|
s_i_c/frontend/streamlit-component-lib.js
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
// Borrowed minimalistic Streamlit API from Thiago
|
3 |
+
// https://discuss.streamlit.io/t/code-snippet-create-components-without-any-frontend-tooling-no-react-babel-webpack-etc/13064
|
4 |
+
function sendMessageToStreamlitClient(type, data) {
|
5 |
+
console.log(type, data)
|
6 |
+
const outData = Object.assign({
|
7 |
+
isStreamlitMessage: true,
|
8 |
+
type: type,
|
9 |
+
}, data);
|
10 |
+
window.parent.postMessage(outData, "*");
|
11 |
+
}
|
12 |
+
|
13 |
+
const Streamlit = {
|
14 |
+
setComponentReady: function() {
|
15 |
+
sendMessageToStreamlitClient("streamlit:componentReady", {apiVersion: 1});
|
16 |
+
},
|
17 |
+
setFrameHeight: function(height) {
|
18 |
+
sendMessageToStreamlitClient("streamlit:setFrameHeight", {height: height});
|
19 |
+
},
|
20 |
+
setComponentValue: function(value) {
|
21 |
+
sendMessageToStreamlitClient("streamlit:setComponentValue", {value: value});
|
22 |
+
},
|
23 |
+
RENDER_EVENT: "streamlit:render",
|
24 |
+
events: {
|
25 |
+
addEventListener: function(type, callback) {
|
26 |
+
window.addEventListener("message", function(event) {
|
27 |
+
if (event.data.type === type) {
|
28 |
+
event.detail = event.data
|
29 |
+
callback(event);
|
30 |
+
}
|
31 |
+
});
|
32 |
+
}
|
33 |
+
}
|
34 |
+
}
|
s_i_c/frontend/style.css
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.auto {
|
2 |
+
width: auto;
|
3 |
+
height: auto;
|
4 |
+
}
|
5 |
+
|
6 |
+
.fullWidth {
|
7 |
+
width: 100%;
|
8 |
+
height: auto;
|
9 |
+
}
|
10 |
+
|
11 |
+
body {
|
12 |
+
margin: 0;
|
13 |
+
}
|
14 |
+
|
15 |
+
img {
|
16 |
+
user-select: none;
|
17 |
+
}
|