I have the following setup: Kivy 1.11.1, Flask 1.1.1 and Python 2.7.13. I want to fetch a JSON request but I am stuck at Flask app does not fetch requests while the Kivy GUI is running. Even when run in separate threads they block each other.
The main api script eg_api.py:
#!/usr/bin/python2.7 python2.7
# -*- coding: utf-8 -*-
# kivy modules first, if not Kivy may cause problems
import kivy
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.label import Label
from kivy.uix.popup import Popup
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
kivy.require('1.10.0')
# common modules
import re
import sys
import time
import signal
import threading
from datetime import datetime
# Flask & similar modules
from flask import Flask
from flask import request
from flask_restful import reqparse, abort, Api, Resource
from flask_httpauth import HTTPBasicAuth
import json, ast
import urlparse
import eventlet
from eventlet import wsgi
# imports from other files
from eg_wrapper import Actions
# async server setup
eventlet.monkey_patch()
app = Flask(__name__)
api = Api(app)
auth = HTTPBasicAuth()
# user access setup
USER_DATA = {
"admin": "SuperSecretPwd"
}
# global variable
data_json = None
@auth.verify_password
def verify(username, password):
if not (username and password):
return False
return USER_DATA.get(username) == password
def start_Flask():
print("Starting Flask...")
#app.run(port=5000, debug=False) #specify separate port to run Flask app
wsgi.server(eventlet.listen(('', 5000)), app) # deploy as an eventlet WSGI server
def signal_handler(signal, frame):
print " CTRL + C detected, exiting ... "
exit(0)
####################### flask classes #########################
class Load(Resource):
@auth.login_required
def post(self):
print "********** class Load(Resource): *****************"
data = request.json
print data
lw = Actions()
data_json = lw.Parse_Load_Request(data)
res = {'data': data_json}
return res, 201
####################### resource calls #######################
api.add_resource(Load, '/load')
########################## kivy screen classes ###############################
class MainScreen(Screen):
pass
########################## kivy popup classes ###############################
# main screen
class MainScreen(Screen):
def __init__(self, **kwargs):
self.name="MAIN SCREEN"
super(Screen, self).__init__(**kwargs)
# popup
class MessageBox10(Popup):
def __init__(self, obj, **kwargs):
super(MessageBox10, self).__init__(**kwargs)
self.obj = obj
# popup
class MessageBox20(Popup):
def __init__(self, obj, **kwargs):
super(MessageBox20, self).__init__(**kwargs)
self.obj = obj
class Kivy(App):
w_MessageBox10_1 = "Message_10_1"
w_MessageBox10_2 = "Message_10_2"
w_MessageBox10_3 = "Message_10_3"
w_MessageBox10_4 = "Message_10_4"
w_MessageBox20_1 = "Message_20_1"
w_MessageBox20_2 = "Message_20_2"
w_MessageBox30_1 = "Message_30_1"
w_MessageBox30_2 = "CONFIRM"
w_MessageBox30_3 = "CANCEL"
def do(self):
print "do something"
def cancel(self):
print "load cancelled by user"
def exit(self):
print "exiting..."
exit(1)
def enter(self):
# open the init file and write the parameters
print "********** def enter(self): popup = MessageBox20(self) *************************"
popup = MessageBox20(self)
popup.open()
def build(self):
sm = Builder.load_string("""
ScreenManager
MainScreen:
size_hint: 1, .7
auto_dismiss: False
title: app.w_MessageBox10_1
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.w_MessageBox10_2
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.w_MessageBox30_2 # CONFIRM
on_press:
app.enter()
Button:
text: app.w_MessageBox30_3 # CANCEL
on_press:
app.exit()
<MessageBox20>:
size_hint: 1, .7
auto_dismiss: False
title: app.w_MessageBox20_1
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.w_MessageBox20_2
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.w_MessageBox30_2 # "CONFIRM"
on_press:
app.do()
root.dismiss()
Button:
text: app.w_MessageBox30_3 # "CANCEL"
on_press:
app.cancel()
root.dismiss()
""")
return sm
if __name__ == '__main__':
#CTRL+C signal handler
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
# start the first thread
t1 = threading.Thread(target=start_Flask)
t1.start()
# start the second thread
t2 = threading.Thread(target=Kivy().run)
t2.start()
# join the two threads
t1.join()
t2.join()
The wrapping script eg_wrapper.py:
#!/usr/bin/python2.7 python2.7
#!/bin/sh -i
# -*- coding: utf-8 -*-
"""
This program executes actions called by eg_api.py.
"""
import os
import signal
import re
import json, ast
import sys
import subprocess
import multiprocessing
from subprocess import PIPE, STDOUT
from datetime import datetime
# to solve the send_file issue
import eventlet
eventlet.monkey_patch()
class Actions:
def Parse_Load_Request(self, request): # parse the JSON data
timestamp = datetime.now()
code = "200"
id_code = "0000"
data = request
task = ""
res={}
list_of_tasks = []
print "********** def Parse_Load_Request(self, r): print data without uni-code chars *****************************"
data = ast.literal_eval(json.dumps(data)) # Removing uni-code chars
print "********** def Parse_Load_Request(self, r): print keys for data *****************************"
print(data.keys()) # print all the keys in the dictionary first
res["code"] = code
res["timestamp"] = str(timestamp)
res["id"] = id_code
res["data"] = str(list_of_tasks)
res["task"] = task
return res
def signal_handler(signal, frame):
exit(1)
def main():
# CTRL+C signal handler
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
#the sys.argv parameter is a list of arguments from the command line
ex = Actions()
sys.exit(app.exec_())
if __name__ == "__main__":
# define the stuff
global mainProcess, mainProcessPid
# execute main
main()
The procedure
I run the eg_api.py and Flask and Kivy do start as threads. They get up and run, eventlet is listening and the Kivy window with the first popup gets up. Then I send the request.
The output
(py2) parovelb@Latitude-E6510:~/Desktop/Python2$ python eg_api.py
[INFO ] [Logger ] Record log in /home/parovelb/.kivy/logs/kivy_19-12-23_34.txt
[INFO ] [Kivy ] v1.10.1
[INFO ] [Python ] v2.7.15rc1 (default, Nov 12 2018, 14:31:15)
[GCC 7.3.0]
[INFO ] [Factory ] 194 symbols loaded
[INFO ] [Image ] Providers: img_tex, img_dds, img_sdl2, img_pil, img_gif (img_ffpyplayer ignored)
[INFO ] [Text ] Provider: sdl2
Starting Flask...
(15549) wsgi starting up on http://0.0.0.0:5000
[INFO ] [Window ] Provider: sdl2(['window_egl_rpi'] ignored)
[INFO ] [GL ] Using the "OpenGL" graphics system
[INFO ] [GL ] Backend used <gl>
[INFO ] [GL ] OpenGL version <3.3 (Compatibility Profile) Mesa 19.0.8>
[INFO ] [GL ] OpenGL vendor <nouveau>
[INFO ] [GL ] OpenGL renderer <NVA8>
[INFO ] [GL ] OpenGL parsed version: 3, 3
[INFO ] [GL ] Shading version <3.30>
[INFO ] [GL ] Texture max size <8192>
[INFO ] [GL ] Texture max units <16>
[INFO ] [Window ] auto add sdl2 input provider
[INFO ] [Window ] virtual keyboard not allowed, single mode, not docked
[INFO ] [Base ] Start application main loop
[INFO ] [GL ] NPOT texture support is available
********** def enter(self): popup = MessageBox20(self) *************************
do something
exiting...
[INFO ] [Base ] Leaving application in progress...
(15549) accepted ('192.168.3.88', 53418)
********** class Load(Resource): *****************
{u'position': u'b1', u'tasks': [{u'code': u'24258146312', u'actionDescription': u'Preleva 20ml di glucosio dalla sacca', u'barcode': u'000001', u'results': [u'returnCode'], u'actions': [{u'returnCode': u'200', u'type': u'CONFIRM', u'label': u'Conferma'}, {u'returnCode': u'400', u'type': u'ABORT', u'label': u'Annulla'}], u'itemName': u'Glucosio 100ml'}, {u'code': u'24258146313', u'actionDescription': u'Leggi il barcode e il codice RFID', u'itemName': u'Glucosio 100ml', u'barcode': u'000001', u'results': [u'returnCode', u'barcode', u'rfid'], u'actions': [{u'returnCode': u'200', u'type': u'CONFIRM', u'label': u'Conferma'}, {u'returnCode': u'400', u'type': u'ABORT', u'label': u'Annulla'}], u'type': u'BARCODE_CHECK'}, {u'code': u'24258146314', u'itemName': u'Glucosio 100ml', u'actionDescription': u'Posiziona la sacca nello slot 1', u'actions': [{u'returnCode': u'200', u'type': u'CONFIRM', u'label': u'Conferma'}, {u'returnCode': u'400', u'type': u'ABORT', u'label': u'Annulla'}], u'results': [u'returnCode']}], u'weight': u'133.15'}
********** def Parse_Load_Request(self, r): *****************************
********** def Parse_Load_Request(self, r): print data without uni-code chars *****************************
********** def Parse_Load_Request(self, r): print keys for data *****************************
['position', 'tasks', 'weight']
192.168.3.88 - - [23/Dec/2019 11:08:44] "POST /load HTTP/1.1" 201 247 0.004312
The problem
Flask does not fetch the request when I send it. When I press the button of the first popup, Flask still does not fetch the request. When I press the button on the second popup, Flask still does not fetch the request. Kivy blocks Flask no matter the order how I start the threads. I checked this thread but I does not help. What am I doing wrong?