Send drawing to server - WIP
This commit is contained in:
commit
6c908a572f
4 changed files with 231 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
venv/
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
tornado
|
||||
coloredlogs
|
105
server.py
Normal file
105
server.py
Normal file
|
@ -0,0 +1,105 @@
|
|||
import tornado.ioloop
|
||||
import tornado.web
|
||||
import tornado.websocket
|
||||
import logging
|
||||
import coloredlogs
|
||||
import argparse
|
||||
import json
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
logger = logging.getLogger("drawing")
|
||||
|
||||
argParser = argparse.ArgumentParser(description='Start up the server to have non-Mechanical Turks draw their sketches.')
|
||||
argParser.add_argument(
|
||||
'--port',
|
||||
'-p',
|
||||
default=8888,
|
||||
help='The port for the server to listen'
|
||||
)
|
||||
argParser.add_argument(
|
||||
'--verbose',
|
||||
'-v',
|
||||
action="store_true",
|
||||
)
|
||||
|
||||
class StaticFileWithHeaderHandler(tornado.web.StaticFileHandler):
|
||||
def set_extra_headers(self, path):
|
||||
"""For subclass to add extra headers to the response"""
|
||||
if path[-5:] == '.html':
|
||||
self.set_header("Access-Control-Allow-Origin", "*")
|
||||
|
||||
class WebSocketHandler(tornado.websocket.WebSocketHandler):
|
||||
CORS_ORIGINS = ['localhost', '.mturk.com']
|
||||
connections = set()
|
||||
|
||||
def check_origin(self, origin):
|
||||
parsed_origin = urlparse(origin)
|
||||
# parsed_origin.netloc.lower() gives localhost:3333
|
||||
valid = any([parsed_origin.hostname.endswith(origin) for origin in self.CORS_ORIGINS])
|
||||
return valid
|
||||
|
||||
# the client connected
|
||||
def open(self, p = None):
|
||||
self.__class__.connections.add(self)
|
||||
logger.info("New client connected")
|
||||
self.write_message("hello!")
|
||||
|
||||
# the client sent the message
|
||||
def on_message(self, message):
|
||||
logger.debug(f"recieve: {message}")
|
||||
try:
|
||||
msg = json.loads(message)
|
||||
if msg['action'] == 'move':
|
||||
pass
|
||||
elif msg['action'] == 'up':
|
||||
logger.info(f'up: {msg}')
|
||||
elif msg['action'] == 'submit':
|
||||
logger.info(f'up: {msg}')
|
||||
self.write_message(json.dumps('submitted'))
|
||||
elif msg['action'] == 'down':
|
||||
# not used, implicit in move?
|
||||
pass
|
||||
else:
|
||||
# self.send({'alert': 'Unknown request: {}'.format(message)})
|
||||
logger.warn('Unknown request: {}'.format(message))
|
||||
|
||||
except Exception as e:
|
||||
# self.send({'alert': 'Invalid request: {}'.format(e)})
|
||||
logger.exception(e)
|
||||
|
||||
# client disconnected
|
||||
def on_close(self):
|
||||
self.__class__.rmConnection(self)
|
||||
logger.info("Client disconnected")
|
||||
|
||||
@classmethod
|
||||
def rmConnection(cls, client):
|
||||
if client not in cls.connections:
|
||||
return
|
||||
cls.connections.remove(client)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
args = argParser.parse_args()
|
||||
print(logger.level)
|
||||
coloredlogs.install(
|
||||
level=logging.DEBUG if args.verbose else logging.INFO,
|
||||
)
|
||||
|
||||
logger.addHandler(
|
||||
logging.handlers.RotatingFileHandler(
|
||||
'mt_server.log',
|
||||
maxBytes=1024*512,
|
||||
backupCount=5
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
application = tornado.web.Application([
|
||||
(r"/ws(.*)", WebSocketHandler),
|
||||
(r"/(.*)", StaticFileWithHeaderHandler,
|
||||
{"path": 'www', "default_filename": 'index.html'}),
|
||||
], debug=True)
|
||||
application.listen(args.port)
|
||||
tornado.ioloop.IOLoop.current().start()
|
123
www/index.html
Normal file
123
www/index.html
Normal file
|
@ -0,0 +1,123 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MT Request: draw over the image</title>
|
||||
<style media="screen">
|
||||
svg{
|
||||
height:600px;
|
||||
width:600px;
|
||||
border:solid 1px;
|
||||
}
|
||||
|
||||
path {
|
||||
fill: none;
|
||||
stroke: red;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id='interface'>
|
||||
<img src="" id='sample'>
|
||||
<svg id="canvas">
|
||||
<path d="" id="stroke">
|
||||
</svg>
|
||||
<button id='submit'>Submit</button>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
let url = window.location.origin.replace('http', 'ws') +'/ws';
|
||||
let svgEl = document.getElementById("canvas");
|
||||
let strokeEl = document.getElementById('stroke');
|
||||
let submitEl = document.getElementById('submit');
|
||||
let strokes = [];
|
||||
let isDrawing = false;
|
||||
let draw = function(e) {
|
||||
let pos = svgEl.getBoundingClientRect()
|
||||
let x = e.x - pos['left'];
|
||||
let y = e.y - pos['top'];
|
||||
strokes.push([x, y, 0]);
|
||||
|
||||
let d = strokes2D(strokes);
|
||||
console.log(d);
|
||||
strokeEl.setAttribute('d', d);
|
||||
|
||||
socket.send(JSON.stringify({
|
||||
'action': 'move',
|
||||
'direction': [x, y]
|
||||
}));
|
||||
};
|
||||
let penup = function(e) {
|
||||
if(!isDrawing) {
|
||||
return;
|
||||
}
|
||||
isDrawing = false;
|
||||
|
||||
document.body.removeEventListener('mousemove', draw);
|
||||
|
||||
if(strokes.length > 0){
|
||||
// mark point as last of stroke
|
||||
strokes[strokes.length - 1][2] = 1;
|
||||
}
|
||||
socket.send(JSON.stringify({
|
||||
'action': 'up',
|
||||
}));
|
||||
|
||||
};
|
||||
let strokes2D = function(strokes) {
|
||||
// strokes to a d attribute for a path
|
||||
let d = "";
|
||||
let last_stroke = undefined;
|
||||
let cmd = "";
|
||||
for (let stroke of strokes) {
|
||||
if(!last_stroke) {
|
||||
d += `M${stroke[0]},${stroke[1]} `;
|
||||
cmd = 'M';
|
||||
} else {
|
||||
if (last_stroke[2] == 1) {
|
||||
d += " m";
|
||||
cmd = 'm';
|
||||
} else if (cmd != 'l') {
|
||||
d+=' l ';
|
||||
cmd = 'l';
|
||||
}
|
||||
let rel_stroke = [stroke[0] - last_stroke[0], stroke[1] - last_stroke[1]];
|
||||
d += `${rel_stroke[0]},${rel_stroke[1]} `;
|
||||
}
|
||||
last_stroke = stroke;
|
||||
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
let socket = new WebSocket(url);
|
||||
|
||||
socket.addEventListener('message', function(e){
|
||||
console.log('receive', e.data);
|
||||
if(e.data == 'submitted') {
|
||||
// TODO close the interface
|
||||
}
|
||||
});
|
||||
|
||||
document.body.addEventListener('mouseup', penup);
|
||||
svgEl.addEventListener('mousedown', function(e){
|
||||
isDrawing = true;
|
||||
|
||||
// start drawing
|
||||
document.body.addEventListener('mousemove', draw);
|
||||
|
||||
});
|
||||
submitEl.addEventListener('click', function(e){
|
||||
if(!strokes.length){
|
||||
alert('please draw before submitting');
|
||||
return;
|
||||
}
|
||||
|
||||
socket.send(JSON.stringify({
|
||||
'action': 'submit'
|
||||
}));
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in a new issue