I have a Python 2.7 script that starts a Kivy GUI app and runs a Flask server in a parallel process. Flask listens and fetches incoming JSON http requests and passes them to Kivy. Then Kivy does the parsing.
While parsing I want to open a popup in the Kivy main screen and wait for the user response, each time when a condition is met. Basically I would like to pause and resume the parsing with a popup. See picture below.
So far I managed only to open another Kivy window instead of a popup in the main screen. No matter in which class I put the parsing method always I end up with a window. I do not know if it is a matter of communication between two processes or wrong popup calling or both. I have checked out few resources (sync, pymotw, inter-process, popup triggering) but I am still lost. I would really appreciate some help, thank you.
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
from kivy.properties import StringProperty
kivy.require('1.10.0')
# common modules
import sys
import signal
from multiprocessing import Process, Queue
# 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 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...")
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 LoadBag(Resource):
@auth.login_required
def post(self):
print "********** class Load(Resource): *****************"
data = request.json
print data
parsed_data = self.Parse_LoadBag_Request(data)
#print parsed_data
#lw = Actions()
#data_json = lw.Parse_Load_Request(data)
res = {'data': data_json}
return res, 201
def Parse_LoadBag_Request(self, request): # parse the JSON data
res={}
list_of_tasks = []
data = request
print "********** Parse_LoadBag_Request(self, request) **********"
data = ast.literal_eval(json.dumps(data)) # Removing uni-code chars
# get the value of the key "position", write it to csv and log
p = data.get('position')
print "position", p
# get the value of the key "weight", write it to csv and log
w = data.get('weight')
print "weight", w
# print all elements of the list "tasks"
print "number of tasks", (len(data["tasks"])) # print the number of tasks
popup = MessageBox10(self)
popup.open()
####################### resource calls #######################
api.add_resource(LoadBag, '/loadbag')
################### kivy 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 Touch(App):
MainScreenTitle = "MainScreen title"
MainScreenLabel = "MainScreen label"
MessageBox10Title = "MessageBox10 title"
MessageBox10Label = "MessageBox10 label"
MessageBox20Title = "MessageBox20 title"
MessageBox20Label = "MessageBox20 label"
MessageBox30Title = "MessageBox30 title"
MessageBox30Label = "MessageBox30 label"
MessageButtonConfirm = "CONFERMA"
MessageButtonCancel = "CANCELLA"
def do(self):
print "do something"
def cancel(self):
print "load cancelled by user"
def exit(self):
print "exiting..."
p1.terminate()
exit(1)
def enter(self):
# open the init file and write the parameters
print "do something"
#popup = MessageBox10(self)
#popup.open()
def popup10(self):
popup = MessageBox10(self)
popup.open()
def build(self):
sm = Builder.load_string("""
ScreenManager
MainScreen:
size_hint: 1, .7
auto_dismiss: False
title: app.MainScreenTitle
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.MainScreenLabel
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.MessageButtonConfirm # CONFIRM
on_press:
app.enter()
Button:
text: app.MessageButtonCancel # CANCEL
on_press:
app.exit()
<MessageBox20>:
size_hint: 1, .7
auto_dismiss: False
title: app.MessageBox20Title
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.MessageBox20Label
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.MessageButtonConfirm # "CONFIRM"
on_press:
app.do()
root.dismiss()
Button:
text: app.MessageButtonCancel # "CANCEL"
on_press:
app.cancel()
root.dismiss()
<MessageBox10>:
size_hint: 1, .7
auto_dismiss: False
title: app.MessageBox10Title
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.MessageBox10Label
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.MessageButtonConfirm # "CONFIRM"
on_press:
app.do()
root.dismiss()
Button:
text: app.MessageButtonCancel # "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)
global p1
p1 = Process(target=start_Flask) # assign Flask to a process
p1.start() # run Flask as process
Touch().run()) # run the Kivy UI
UPDATE I have moved the parsing method from the Flask process to the Kivy process. This way I have skipped the use of semaphores and an extra inter-process communication queue. The updated script is below. The parsing now works but the popup blocking the background running script does not. I have checked these two questions: modal view and multithreading kivy but I can not make a popup pause the running script. Any ideas?
CODE
#!/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
from kivy.properties import StringProperty
kivy.require('1.10.0')
# common modules
import sys
import os
import time
import signal
from multiprocessing import Process, Manager, Pool
from multiprocessing import Queue
# Flask modules
from flask import Flask
from flask import request
from flask_restful import reqparse, abort, Api, Resource
# wsgi (Web Server Gateway Interface) modules
import eventlet
from eventlet import wsgi
# json modules
import json, ast
# async server setup
app = Flask(__name__)
api = Api(app)
# global variables
data_json = None
def start_Flask(q):
print("Starting Flask...")
q.put([42, None, 'hello'])
wsgi.server(eventlet.listen(('', 5000)), app) # deploy as an eventlet WSGI server
def signal_handler(signal, frame):
print " CTRL + C detected, exiting ... "
exit(0)
# api resource classes ###############################################
class LoadBag(Resource):
@auth.login_required
def post(self):
print("# class Load(Resource) => putting request in queue ")
data = request.json
package = ("loadbag", data) # create tuple
q.put(package) # put tuple into queue
# setup the Api resource routing here ##################################
api.add_resource(Load, '/load')
# kivy gui classes ######################################################
# main screen
class MainScreen(Screen):
def __init__(self, **kwargs):
self.name="MAIN SCREEN"
super(Screen, self).__init__(**kwargs)
# popup
class MessageBox(Popup):
def __init__(self, obj, **kwargs):
super(MessageBox, self).__init__(**kwargs)
self.obj = obj
class Touch(App):
MainScreenTitle = "MainScreen title"
MainScreenLabel = "MainScreen label"
MessageBoxTitle = "MessageBoxTitle"
MessageBoxLabel = "MessageBoxLabel"
MessageButtonConfirm = "OK"
MessageButtonCancel = "CANCEL"
MessageButtonEnter = "GO"
MessageButtonExit = "EXIT"
def do(self):
print "do something"
def cancel(self):
print "load cancelled by user"
def exit(self):
print "exiting..."
p1.terminate()
exit(1)
def enter(self):
print "queue element"
try:
if q.empty() == False:
package = q.get() # retrieve package from queue
print(package)
data = package[1] # assign second tuple element to data
#print(item)
if package[0] == "load":
self.Parsing_Request(data) # begin parsing the data
except:
self.MainScreenLabel = "queue is empty"
print "end of queue"
def popup(self, text):
message = text
#set the messages of the popup
self.MessageButtonConfirm = "ok"
self.MessageButtonCancel = "exit"
self.MessageBoxTitle = "some title"
self.MessageBoxLabel = message
popup = MessageBox(self) #call popup
popup.open()
def Parsing_Request(self, data): # parse the JSON data
data = ast.literal_eval(json.dumps(data)) # Removing uni-code chars
print("number of tasks", (len(data["tasks"]))) # print the number of tasks
for i in range(len(data["tasks"])):
ad = data["tasks"][i].get("actionDescription")
print("begin task n.", i, ad)
list_of_tasks.append(data["tasks"][i]["actionDescription"])
title = "Parsing request"
msg = str(i)
self.popup(msg)
def build(self):
sm = Builder.load_string("""
ScreenManager
MainScreen:
size_hint: 1, .7
auto_dismiss: False
title: app.MainScreenTitle
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.MainScreenLabel
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.MessageButtonEnter # start app
on_press:
app.enter()
Button:
text: app.MessageButtonExit # exit app
on_press:
app.exit()
<MessageBox>:
size_hint: 1, .7
auto_dismiss: False
title: app.MessageBoxTitle
title_align: "center"
BoxLayout:
orientation: "vertical"
Label:
text: app.MessageBoxLabel
BoxLayout:
orientation: "horizontal"
spacing: 10
size_hint: 1, .5
Button:
text: app.MessageButtonConfirm # "CONFIRM"
on_press:
app.do()
root.dismiss()
Button:
text: app.MessageButtonCancel # "CANCEL"
on_press:
app.cancel()
root.dismiss()
""")
return sm
# main #################################################################
if __name__ == '__main__':
#CTRL+C signal handler
signal.signal(signal.SIGINT, signal_handler)
signal.signal(signal.SIGTERM, signal_handler)
q = Queue()
global p1
p1 = Process(target=start_Flask, args=(q,)) # assign Flask to a process
p1.daemon = True
p1.start() #launch Flask as separate process
Touch().run() # run Kivy app