5

I have a flask app serving as a server and I have a kivy app serving as a front end to a server. How can I run flask and then kivy app so they work together at the same time?

Flask app:

from flask import Flask

app = Flask(__name__)


@app.route('/')
def hello():
    return 'Hello'

if __name__ == "__main__":
    app.run()

Kivy app:

from kivy.app import App
from kivy.builder import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
kivy.require('1.10.0')

Builder.load_file('kivy.kv')

sm = ScreenManager()

class MainScreen(Screen)
    pass

class OtherScreen(Screen)
    pass

sm.add_widget(MainScreen(name='main'))
sm.add_widget(OtherScreen(name='other'))

class MyApp(App):
    def build(self):
        return sm


MyApp().run()

Update: If someone is having problems implementing a webserver with apache, try docker, simpler and faster solution, in my opinion!

Ademir Gotov
  • 179
  • 1
  • 14

2 Answers2

6

I wanted Flask to run continuously. I tried the proposed solution to run them together as threads as suggested by @amanb. I found out Flask is blocking Kivy and vice versa no matter the timing or how arranged are the threads. The reason being the interpreter's GIL. Therefore I tried with processes and it seems it does the work.

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.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
kivy.require('1.10.0')


# common modules
import sys
import signal
from multiprocessing import Process


# Flask & similar modules
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource
import eventlet
from eventlet import wsgi


# async server setup
app = Flask(__name__)
api = Api(app)


def start_Flask():
    print("Starting server...")
    # start an eventlet WSGI server on port 5000
    wsgi.server(eventlet.listen(('', 5000)), app)     


def signal_handler(signal, frame):
    # for fetching CTRL+C and relatives
    print " CTRL + C detected, exiting ... "
    exit(1)


# Kivy screen class
class MainScreen(Screen):
    def __init__(self, **kwargs):
        self.name="MAIN SCREEN"
        super(Screen, self).__init__(**kwargs)


# Kivy app class
class Kivy(App):
    w_MessageBox10_1 = "MAIN SCREEN"
    w_MessageBox10_2 = "One golden glance of what should be"
    w_MessageBox30_2 = "CHORUS"
    w_MessageBox30_3 = "EXIT"


    # exit button action   
    def exit(self):
        print "exiting... one shaft of light will show the way..."
        p1.terminate()  # terminate Flask by pressing on cancel
        exit(1)


    # do magic button action
    def do_magic(self):
        # your code goes here or maybe not
        print "***** it's a kind of magic *************************"


    # Kivy UI builder file
    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  # DO MAGIC
                    on_press:
                        app.do_magic()
                Button:
                    text: app.w_MessageBox30_3  # EXIT
                    on_press:
                        app.exit()


        """)

        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
    Kivy().run()                        # run Kivy UI

UPDATE To run Flask on demand by pressing a button in Kivy I use the script below.

#!/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.floatlayout import FloatLayout
from kivy.uix.screenmanager import ScreenManager, Screen
kivy.require('1.10.0')

# common modules
import sys
import os
import time
import signal
from multiprocessing import Process

# Flask modules
from flask import Flask

# wsgi (Web Server Gateway Interface) modules
import eventlet
from eventlet import wsgi

# async server setup
app = Flask(__name__)

def signal_handler(signal, frame):
    print " CTRL + C detected, exiting ... "
    exit(0)


# kivy gui classes ######################################################     
class MainScreen(Screen):
    def __init__(self, **kwargs):
        self.name="MAIN SCREEN"
        super(Screen, self).__init__(**kwargs)

class MainApp(App):
    MainScreenTitle = "MainScreen title"
    MainScreenLabel = "MainScreen label"
    MessageButtonEnter = "START"
    MessageButtonExit = "EXIT"

    def start_Flask(self):
        print("Starting Flask...")
        wsgi.server(eventlet.listen(('', 5000)), app)     # deploy as an eventlet WSGI server

    def stop(self):
        print "terminating Flask and exiting..."
        global p1
        p1.terminate()
        exit(1)

    def start(self):
        print "starting Flask as process..."
        global p1
        p1 = Process(target=self.start_Flask) # assign Flask to a process
        p1.daemon = True
        p1.start()  #launch Flask as separate process

    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.start()
                Button:
                    text: app.MessageButtonExit  # exit app
                    on_press:
                        app.stop()

        """)

        return sm


# main ################################################
if __name__ == '__main__':

    #CTRL+C signal handler
    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    MainApp().run()   # run Kivy app
parovelb
  • 353
  • 4
  • 19
  • This looks interesting, is it possible to start the `kivy` app first and then have a button to start `flask` and have another button to `stop` it? – windowws Jan 19 '20 at 13:42
  • 1
    @windowws, yes it is possible. To start Flask as a process with a button from Kivy assign a method to on_press in the builder eg. `on_press: app.start_process_Flask()`. The same to terminate. – parovelb Jan 19 '20 at 19:14
  • how do we host different applications inside this. For example, the hello world looks like below ` from flask import Flask app = Flask(__name__) @app.route("/", methods=['GET']) def hello(): return "Hello World!" if __name__ == "__main__": app.run()` How do we host this – windowws Jan 20 '20 at 16:26
  • @windowws you have to be more specific on "inside this" and on what do you want your script to do – parovelb Jan 21 '20 at 11:10
  • sorry, basically, i want to stop and start `flask` from `kivy` – windowws Jan 21 '20 at 12:30
  • 1
    @windowws, I updated my answer and added the script forstart/stop Flask with a button – parovelb Jan 21 '20 at 16:22
4

The development server in Flask is part of Werkzeug. It should only be used for development purposes as it cannot accommodate high loads as in production deployments. I suggest that you set-up an Apache server with mod_wsgi to run both apps together. This will also provide isolation & parallelism at the same time and is ideal for development,testing and production deployments.

A solution with threading works but with a caveat: The Werkzeug server can run in a separate thread but the app reloader expects to run in the main thread. This means that your Flask app will not reload when you make any changes to the app. Take a look at this answer.

The below code uses two separate threads to run each app. A 'Hello World' window appears for the Kivy app and at the same time it is possible to display a 'Hello World' message in the browser when the Flask app is run on http://localhost:5000/.

import threading
import kivy
from kivy.app import App
from flask import Flask
import os
from kivy.uix.label import Label

kivy.require('1.10.0')
new_environ = os.environ.copy()

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello World'
def start_app():
    print("Starting Flask app...")
    app.run(port=5000, debug=False)     #specify separate port to run Flask app

class MyApp(App):

    def build(self):
        return Label(text='Hello world')

if __name__ == '__main__':
    if os.environ.get("WERKZEUG_RUN_MAIN") != 'true':
        threading.Thread(target=start_app).start()
    MyApp().run()
amanb
  • 5,276
  • 3
  • 19
  • 38