In my project i need to use maybe less common architecture: KivyMD + SocketIO as server and JS client (Flask).
Bellow is a code for minimal reproducible example (two apps): Server App (KivyMD) and Client App:
Structure of MRE:
- main.py
- flask_client.py
- templates/index.html.j2
KivyApp:
This App is based on these two sources: How to run kivy and flask apps together? and https://python-socketio.readthedocs.io/en/latest/intro.html#server-examples (part AioHttp)
# Filename: main.py
# Start Kivy app with Socket IO server: python main.py
from kivymd.app import MDApp
from kivymd.uix.screen import MDScreen
from kivy.lang import Builder
from aiohttp import web
import socketio
# common modules
import signal
from multiprocessing import Process
sio = socketio.AsyncServer(cors_allowed_origins='*')
webapp = web.Application()
sio.attach(webapp)
KV = '''
MainScreen:
MDRaisedButton:
pos_hint: {"center_x": .5, "center_y": .5}
font_size: "18sp"
text: "Push data"
on_release:
root.push_data()
'''
async def index(request):
"""Serve the client-side application."""
with open('index.html') as f:
return web.Response(text=f.read(), content_type='text/html')
@sio.event
def connect(sid, environ):
print('SocketIo Connect connect ', sid)
@sio.on('my_message')
def message(sid, data):
print("Server received message!", data)
@sio.event
def disconnect(sid):
print('disconnect ', sid)
webapp.router.add_static('/', '')
webapp.router.add_get('/', index)
def start_websocket_server():
print("Starting WebSocket server...")
web.run_app(webapp)
def signal_handler(signal, frame):
# for fetching CTRL+C and relatives
print("CTRL + C detected, exiting ... ")
exit(1)
class MainScreen(MDScreen):
def __init__(self, **kwargs):
super(MainScreen, self).__init__(**kwargs)
# ## Unknown part - how to emit data for socketio client? ###
def push_data(self):
print('>> EMIT DATA')
sio.emit('emitted_data', 'test data')
# ## Unknown part - how to emit data for socketio client? ###
class SocketIOApp(MDApp):
def exit(self):
print("Exiting...")
# terminate Flask by pressing on cancel
ws_server_process.terminate()
exit(1)
def build(self):
return Builder.load_string(KV)
if __name__ == '__main__':
# #CTRL+C signal handler
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
global ws_server_process
ws_server_process = Process(target=start_websocket_server)
# run WS as process
ws_server_process.start()
SocketIOApp().run()
The 2nd App: Minimal Flask Client
# Filename: flask_client.py
# Start Flask client: export FLASK_DEBUG=1 && export FLASK_APP=flask_client.py && flask run
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return render_template("index.html.j2")
if __name__ == "__main__":
app.run()
Second file (must be in folder "templates")
Filename: index.html.j2
<!doctype html>
<head>
<!-- Core JS files -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.0.4/socket.io.js" integrity="sha512-aMGMvNYu8Ue4G+fHa359jcPb1u+ytAF+P2SCb+PxrjCdO3n3ZTxJ30zuH39rimUggmTwmh2u7wvQsDTHESnmfQ==" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
<div class="starter-template">
<h1>Bootstrap starter template</h1>
<form id="emit" method="POST" action='#'>
<button type="button" id="socketio-click" class="btn btn-primary">
Send data to server
</button>
</form>
</div>
</div>
<script>
$(document).ready(function() {
console.log('JS works');
var socket = io('http://127.0.0.1:8080');
socket.on('connect', function() {
socket.emit('my_message', {data: 'I\'m connected!'});
});
socket.on('disconnect', function() {
socket.emit('my_message', {data: 'I\'m disconnected!'});
});
// Show recieved data in console
socket.on('emitted_data', function(msg, cb) {
console.log('Recieved data:', msg);
if (cb)
cb();
});
document.getElementById('socketio-click').onclick = function(event) {
console.log('my_message');
socket.emit('my_message', {data: 'Data from WebClient'});
return false;
};
})
</script>
</body>
Apps requirements:
pip install kivymd flask python-socketio aiohttp
In this example it works: When i click on the button in client Flask app, data are emitted and server print recieved data.
But I don't know how to implement socketio for a reverse way: I need to click on button Push Data (KivyMD app) and i need to emit data and show them on client app (console).
When i click on "Push data" button in my current implementation is shown error:
>> EMIT DATA
main.py:75: RuntimeWarning: coroutine 'AsyncServer.emit' was never awaited
sio.emit('emitted_data', 'test data')
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Please, can you help me with this implementation and calling emit data on Kivy App?
Thank you.