如何同时运行 kivy 和烧瓶应用程序?

Posted

技术标签:

【中文标题】如何同时运行 kivy 和烧瓶应用程序?【英文标题】:How to run kivy and flask apps together? 【发布时间】:2018-09-27 19:13:23 【问题描述】:

我有一个烧瓶应用程序作为服务器,我有一个 kivy 应用程序作为服务器的前端。如何运行烧瓶然后运行 ​​kivy 应用程序以便它们同时协同工作?

Flask 应用:

from flask import Flask

app = Flask(__name__)


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

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

Kivy 应用:

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()

更新: 如果有人在使用 apache 实现网络服务器时遇到问题,请尝试 docker,我认为这是一种更简单、更快的解决方案!

【问题讨论】:

可以在kivy应用中使用线程运行flask应用。 【参考方案1】:

我希望 Flask 连续运行。我尝试了建议的解决方案,按照@amanb 的建议将它们作为线程一起运行。我发现 Flask 正在阻塞 Kivy,反之亦然,无论线程的时间安排或排列方式如何。原因是解释器的 GIL。因此,我尝试了流程,似乎它确实有效。

代码

#!/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

更新 要通过在 Kivy 中按下按钮来按需运行 Flask,我使用下面的脚本。

#!/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

【讨论】:

这看起来很有趣,是否可以先启动kivy 应用程序,然后有一个按钮启动flask 并有另一个按钮启动stop 呢? @windowws,是的,这是可能的。要使用 Kivy 中的按钮将 Flask 作为进程启动,请在构建器中为 on_press 分配一个方法,例如。 on_press: app.start_process_Flask()。同样终止。 我们如何在其中托管不同的应用程序。例如,hello world 如下所示 ` from flask import Flask app = Flask(name) @app.route("/", methods=['GET']) def hello(): return “你好世界!” if name == "main": app.run()` 我们如何托管这个 @windowws 您必须更具体地了解“内部”以及您希望脚本做什么 @windowws,我更新了我的答案并使用按钮添加了用于启动/停止 Flask 的脚本【参考方案2】:

Flask 中的开发服务器是Werkzeug 的一部分。它只能用于开发目的,因为它不能像生产部署那样适应高负载。我建议您使用mod_wsgi 设置一个Apache 服务器来同时运行这两个应用程序。这还将同时提供隔离和并行性,是开发、测试和生产部署的理想选择。

threading 的解决方案有效,但有一个警告:Werkzeug 服务器可以在单独的线程中运行,但应用程序重新加载器希望在主线程中运行。这意味着当您对应用程序进行任何更改时,您的 Flask 应用程序将不会重新加载。看看这个answer。

以下代码使用两个单独的线程来运行每个应用程序。 Kivy 应用会出现一个“Hello World”窗口,同时当 Flask 应用在http://localhost:5000/ 上运行时,可以在浏览器中显示“Hello World”消息。

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()

【讨论】:

看来还是用apache比较好,谢谢!

以上是关于如何同时运行 kivy 和烧瓶应用程序?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在 gunicorn 上运行的烧瓶应用程序中使用日志轮换时同时在多个文件上写入日志?

使用 Google Cloud Platform 运行 Websocket

如何使用 buildozer 和最新的 kivy 构建 kivy 应用程序?

在 KIVY 中运行程序时如何更改窗口上的图标

在 iOS 设备上运行时,如何允许我的 Kivy 应用程序访问文本文件?

在 Windows 上使用 apache mod_wsgi 运行烧瓶应用程序时的导入冲突