mzimm003 commited on
Commit
a529397
·
1 Parent(s): a4ba045

Add smartphone capability.

Browse files
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 streamlit_image_coordinates import streamlit_image_coordinates
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(st.session_state.play.render_board(), key="brd", click_and_drag=True)
 
 
 
 
 
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
+ }