Unable to pass button event to Flask SocketIO to external python program - python

I have need to separate my python Flask web app and running code communicating via socketio. I'm able to get messages up from the external python program to the web, but I'm unable to get events from the web detected by the python program. In effect, I'd like when a user presses a button on a web page, for the external python code to print to terminal a hello world message. In the console for this html, I'm definitely seeing "Button pressed".
<html>
<head>
<title>Listener</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
var socket = io.connect();
$('#mybutton').on('click', function() {
socket.emit('my event', 'yodle');
console.log('Button pressed');
});
});
</script>
<button id="mybutton">Push Me!</button>
</body>
</html>
This is my basic Flask webserver code, which is working:
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'froggy'
app.debug = True
socketio = SocketIO(app, message_queue='redis://')
#app.route("/")
def index():
return render_template("index.html")
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0')
This is my separate running program, which is not printing anything to terminal when the button is pressed:
from flask_socketio import SocketIO
socketio = SocketIO(message_queue='redis://', host='0.0.0.0')
def my_function_handler(data):
print("Hello World")
if __name__ == '__main__':
while True:
socketio.on_event('my event', my_function_handler)
Can anyone point me to where I'm going wrong? Many thanks in advance!

You are trying to do something that is not supported. External processes can only emit, they are not recipients. If you need to emit and receive in the external process, then I recommend that you move the Socket.IO server entirely to this process.

Apparently this behavior is not supported in socketIO according to Miguel's answer, so I'm posting my particular solution to the problem if anyone is interested. I spent a lot of time Googling, Stack Overflowing, and generally falling down rabbit holes. What I ended up doing was that as I was using redis already, I just used it to pass messages between processes. There are a ton of general downsides to this approach, but it fits my needs nicely, so here's what I did. For index.html:
<html>
<head>
<title>Listener</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/1.4.8/socket.io.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
</head>
<body>
<script type="text/javascript">
$(document).ready(function() {
var socket = io.connect();
$('#mybutton').on('click', function() {
socket.emit('button event');
});
});
</script>
<button id="mybutton">Push Me!</button>
</body>
</html>
Server code:
from flask import Flask, render_template
from flask_socketio import SocketIO
import redis
app = Flask(__name__)
app.config['SECRET_KEY'] = 'froggy'
app.debug = True
socketio = SocketIO(app, message_queue='redis://')
r = redis.Redis("localhost")
r.set('button', 'not pressed')
#app.route("/")
def index():
return render_template("index.html")
#socketio.on('button event')
def handleMessage():
r.set('button', 'pressed')
if __name__ == '__main__':
socketio.run(app, host='0.0.0.0')
Separate running process:
import redis
r = redis.Redis("localhost")
if __name__ == '__main__':
while True:
if r.get('button') == 'pressed':
print("Button pressed!")
r.set('button', 'not pressed')

Related

Problem loading a web app using flask, python, HTML and PostgreSQL; not able to connect python and html scripts plus Internal Server Error message

Recently, I have started working on a new project : a web app which will take a name as an input from a user and as result outputs the database rows related to the user input. The database is created using PostgreSQL and in order to complete the task I am using Python as a programming language, followed by Flask (I am new to it) and HTML. I have created 2 source codes, 1 in Python as below :
import os
import psycopg2 as pg
import pandas as pd
import flask
app = flask.Flask(__name__)
#app.route('/')
def home():
return "<a href='/search'>Input a query</a>"
#app.route('/search')
def search():
term = flask.request.args.get('query')
db = pg.connect(
host="***",
database="***",
user ="***",
password="***")
db_cursor = db.cursor()
q = ('SELECT * FROM table1')
possibilities = [i for [i] in db_cursor.execute(q) if term.lower() in i.lower()]
return flask.jsonify({'html':'<p>No results found</p>' if not possibilities else '<ul>\n{}</ul>'.format('\n'.join('<li>{}</li>'.format(i) for i in possibilities))})
if __name__ == '__main__':
app.run()
and HTML code :
<html>
<head>
<script src = "https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
</head>
<body>
<input type='text' name ='query' id='query'>
<button type='button' id='search'>Search</button>
<div id='results'></div>
</body>
<script>
$(document).ready(function(){
$('#search').click(function(){
var text = $('#query').val();
$.ajax({
url: "/search",
type: "get",
data: {query: text},
success: function(response) {
$("#results").html(response.html);
},
error: function(xhr) {
//Do Something to handle error
}
});
});
});
</script>
</html>
For these scripts I read the discussion here.
These scripts are giving me troubles and I have two main questions :
First : How are these two source codes connected to each other? whenever I run the python script or the html, they look completly disconnected and are not functioning.Moreover, when I run the Python script it gives me this error message on the webpage :
Internal Server Error
The server encountered an internal error and was unable to complete your request. Either the server is overloaded or there is an error in the application.
and this message on terminal :
Serving Flask app 'userInferface' (lazy loading)
Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
Debug mode: off
Running on....
Can someone please help me by showing how can these 2 scripts connect and why am I getting such errors. Thank you.
You need to use render_template to connect Flask and your HTML code. For example:
from flask import render_template
#app.route("/", methods=['GET'])
def index():
return render_template('index.html')

Flask-SocketIO not receiving events on server

I'm currently learning Flask and have recently found out about Flask-SocketIO. I've learned, that the module is based on events so that the client side can communicate with the server side, so I tried doing that. But for some reason, the events I send from the client side, don't make it to the server. I've tried fixing it for a few hours but I don't understand what is wrong with my code. Thanks for helping me!
main.py
from flask import Flask, render_template
from flask_socketio import SocketIO
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route("/", methods=["GET", "POST"])
def home():
return render_template("index.html")
#socketio.on('my event')
def handle_my_custom_event(json):
print('received json: ' + str(json))
if __name__ == "__main__":
socketio.run(app, debug=True)
/templates/index.html
<!DOCTYPE html>
<html lang="en">
<h1> Chat room</h1>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js" integrity="sha512-q/dWJ3kcmjBLU4Qc47E4A9kTB4m3wuTY7vkFJDTZKjTs8jhyGQnaUrxa0Ytd0ssMZhbNua9hE+E7Qv1j+DyZwA==" crossorigin="anonymous"></script>
<script type="text/javascript" charset="utf-8">
var socket = io();
socket.on('connect', function() {
socket.emit('my event', {data: 'I\'m connected!'}); #Creating event when connected
});
</script>
</html>
Any time things don't go the way you think they should go, you have to look for clues left in the logs. In this case that means looking at the output of the Flask process, and the browser console.
The Flask process does not show any errors, but it does show that there was no Socket.IO connection attempted. So really this isn't a problem about events not being received, but connections don't being made.
The browser console shows this:
Uncaught SyntaxError: Private field '#Creating' must be declared in an enclosing class
You see the problem? You are using # to start a comment. That should have been a // in JavaScript.

How to refresh front page after back-end task completed

I'm design a web application with python flask, the processing flow is like:
User choose a specified a URL, and ask server site to do some time-consuming task;
The time-consuming task is then running in a new thread;
After #2 is done, server will update some info regarding the URL, I need to refresh the page in #1
I don't know how to notify the "completion" status to front-end so as to refresh the page.
Anyone can help me out?
Thanks
Using flask-socketio would probably work for what you're asking. It establishes a connection between the front-end and back-end that lets the backend emit a message and the front-end registers a callback that fires when it receives the event.
$ pip install flask-socketio
app.py
import json
import time
from flask import Flask, render_template
from flask_socketio import SocketIO, emit
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)
#app.route('/')
def hello(name=None):
return render_template('index.html')
#socketio.on('long-running-event')
def handle_my_custom_event(input_json):
time.sleep(5)
emit('processing-finished', json.dumps({'data': 'finished processing!'}))
if __name__ == '__main__':
socketio.run(app)
templates/index.html
<html>
<body>
<button id="long-running-operation">Process data</button>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
var socket = io.connect('http://' + document.domain + ':' + location.port);
let button = document.getElementById('long-running-operation');
button.addEventListener('click', () => {
socket.emit('long-running-event', {data: 'process data'});
});
socket.on('processing-finished', function (data) {
alert(JSON.parse(data).data)
});
</script>
</body>
</html>
Then run the app with python app.py. Visit the app in the browser at http://127.0.0.1:5000/ and click the button. The button will send the 'long-running-event' request to flask and flask processes it by sleeping for 5 seconds to simulate a long running process. Then flask emits the 'processing-finished message and javascript fires the callback registered to listen for that event by alerting the message that flask sent.

In Flask, can I show one template while a function runs and redirect to another once the function is complete?

Basically I want to show a loading page while a time-consuming process takes place and then redirect to my complicated other page.
While not impossible to achieve, I recommend using javascript for this task.
Here is a small example. First lets write a very simple flask server, with one very slow endpoint.
from flask import Flask, render_template, jsonify
app = Flask(__name__)
#app.route("/")
def hello():
return render_template('redirect.html')
#app.route("/done")
def done():
return "Done!"
#app.route("/slow")
def slow():
import time
time.sleep(5)
return jsonify("oh so slow")
if __name__ == "__main__":
app.run()
Now, we can make a beautiful user experience by invoking the endpoint from javascript instead. Save it as templates/redirect.html as per usual.
<html>
<head>
<script>
function navigate() {
window.location.href = 'done'; // redirect when done!
}
fetch('slow').then(navigate); // load the slow url then navigate
</script>
</head>
<body>
Loading... <!-- Display a fancy loading screen -->
</body>
</html>

Server sent events with Flask and Tornado

I have been playing around with sending server sent events with Flask and Tornado. I took a look at this blog article:
https://s-n.me/blog/2012/10/16/realtime-websites-with-flask/
I decided to try writing my own Flask app to send server sent events as an exercise. Here is the code for my Flask app called sse_server.py:
#! /usr/bin/python
from flask import Flask, request, Response, render_template
from tornado.wsgi import WSGIContainer
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
app = Flask(__name__)
def event_stream():
count = 0
while True:
print 'data: {0}\n\n'.format(count)
yield 'data: {0}\n\n'.format(count)
count += 1
#app.route('/my_event_source')
def sse_request():
return Response(
event_stream(),
mimetype='text/event-stream')
#app.route('/')
def page():
return render_template('index.html')
if __name__ == '__main__':
print "Please open a web browser to http://127.0.0.1:5000."
# Spin up the app
http_server = HTTPServer(WSGIContainer(app))
http_server.listen(5000)
IOLoop.instance().start()
In my templates folder, I have a simple index.html page:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test</title>
<script src="//code.jquery.com/jquery-1.11.0.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script type="text/javascript" src="../static/sse_client.js"></script>
</head>
<body>
<h3>Test</h3>
<ul id="output">
</ul>
</body>
</html>
In my static folder, I have a file called sse_client.js:
var queue = [];
var interval = setInterval(function(){addItem()}, 1000);
function addItem(){
if(queue.length > 0){
var item = queue[0];
queue.shift();
$('#output').append(item);
}
}
$(document).ready(
function() {
var sse = new EventSource('/my_event_source');
console.log('blah');
sse.onmessage = function(event) {
console.log('A message has arrived!');
var list_item = '<li>' + event.data + '</li>';
console.log(list_item);
queue.push(list_item);
};
})
Basically, my app's structure is
sse/
sse_server.py
static/
sse_client.js
templates/
index.html
The app displays the index page, but the data is not getting streamed to it. I have no idea what I am doing wrong. I think I need another set of eyes on this. I'm sure it's something really minor and stupid.
Tornado's WSGIContainer does not support streaming responses from wsgi apps. You can either use Flask with a multi-threaded or greenlet-based wsgi server, or use Tornado's native RequestHandler interface, but not when you're combining Flask and Tornado with WSGIContainer.
Combining Flask and Tornado is usually not a good idea; see https://github.com/mitsuhiko/flask/issues/986
To use the url "../static/sse_client.js" you need your webserver or your Flask app to serve the static JavaScript file. From the Flask docs:
To generate URLs for static files, use the special 'static' endpoint
name:
url_for('static', filename='style.css')
The file has to be stored on the filesystem as static/style.css.
Read more

Categories

Resources