【问题标题】:Running Flask API without CMD在没有 CMD 的情况下运行 Flask API
【发布时间】:2021-07-27 07:04:49
【问题描述】:

我在一家公司工作,该公司将他们的系统部署在本地计算机和服务器上。

给我的任务是创建一个 python 应用程序,它与 plc 通信并使用Reactjs/Electron.js 显示收集的数据或将数据保存在MS SQL Server 上。

我的想法是使用 tkinter 创建 gui,并通过创建一个类似于 XAMPP<filename>.spec 文件和 pyinstaller 将其转换为应用程序,我可以在其中重新启动并启动 api。

如果我使用tkinter,我应该从哪里开始?当 gui 打开时,我设法启动了后端,但我不知道停止或重新启动后端。我对threads 也没有太多想法,但我认为它会帮助我创造我想要的东西。

我也在寻找是否可以使用XAMPP 作为主机来运行 api,但没有运气。

如果你们有更好的方法在没有 cmd 的情况下在 localhost 上运行 api 真的很有帮助

【问题讨论】:

  • 我不认为在另一个 thread 上运行服务器就足够了,我建议如果可能的话使用另一个进程,因为服务器非常消耗资源(至少我这么认为)所以让它运行在与 tkinter 相同的线程中可能会减慢速度(如果烧瓶是可腌制的,这是可行的,编辑:实际上可能不需要),我可以想象的是运行 subprocess.Popen 进程,它将启动服务器然后流式传输输出到 tkinter 中的文本小部件,尚未考虑停止,但这也不应该太难(将尝试写一个答案)
  • 我会努力学习subprocess。谢谢你的回答

标签: python flask tkinter


【解决方案1】:

好的,所以这花了一段时间,但我想出了如何不使用subprocess(主要是因为还有另一个关于不使用python的问题,所以唯一的方法是将flask应用程序转换为.exe然后使用subprocess,但我发现了如何只使用python来做到这一点),主要问题(简单修复,但在使用subprocess.Popen时也必须解决)是flask重新启动服务器启动另一个进程所以你必须使用use_reloader=False.run() 方法中。 cmets中的解释:

app.py

# import what's necessary
from flask import Flask, render_template_string, url_for
from flask import request


app = Flask(__name__)


# sample route
@app.route('/')
def home():
    return render_template_string('<a href="{{ url_for("about") }}">To About Page</a>'
                                  '<h1>Home Page</h1>')


# sample route
@app.route('/about')
def about():
    return render_template_string('<a href="{{ url_for("home") }}">To Home Page</a>'
                                  '<h1>About Page</h1>')


# important route that will do the stopping part since that was a requirement in the question
# link to this in the answer at the bottom
@app.route('/kill_server', methods=['GET'])
def kill_server():
    func = request.environ.get('werkzeug.server.shutdown')
    if func is None:
        raise RuntimeError('Not running with the Werkzeug Server. Could not shut down server.')
    func()
    return 'Server shutting down...'


# to prevent this from running when importing
if __name__ == '__main__':
    app.run(debug=True)

run.py

# import all that is necessary
from tkinter import Tk, Text, Button, Frame
from for_so_dir.app import app as server
from threading import Thread
from queue import Queue, Empty
import requests
import logging.handlers


# simple dictionary to avoid using `global`, just a preference of mine
info = {'server_is_running': False}


# the function that starts server (multiprocessing is not possible with flask as far as I know)
# basically checks if the server is not running already and if it is not then starts a thread
# with the server (which is the same as `app` in the app.py) and sets that server is running
def start_server():
    if info['server_is_running']:
        return
    Thread(target=server.run, kwargs={'debug': True, 'use_reloader': False}, daemon=True).start()
    info['server_is_running'] = True


# function from stopping server, again in the answer at the bottom, but basically
# this sends a request to the server and that request executes a function
# that stops the server
def stop_server():
    if not info['server_is_running']:
        return
    requests.get('http://127.0.0.1:5000/kill_server')


# function for showing the logs in the Text widget, it simply tries to get data from
# the queue (if there is nothing it simply loops again) and then inserts that data into
# the text widget
def update_text_log():
    try:
        data = queue.get(block=False)
    except Empty:
        pass
    else:
        log.config(state='normal')
        log.insert('end', data.msg + '\n')
        log.config(state='disabled')
    finally:
        root.after(100, update_text_log)


# this `if statement` is not that necessary in the current code but it might as well
# stay here
if __name__ == '__main__':
    # initialise the Queue
    queue = Queue()
    # now the main part, to get the info from flask you need to use `logging`
    # since that is what it uses for all the messages, and use the `QueueHandler`
    # to put the messages in the queue and then update them using the above
    # function
    logging.basicConfig(handlers=(logging.handlers.QueueHandler(queue), ))
    
    # simple `tkinter` stuff
    root = Tk()
    # this part can be removed or made toggleable but it allows to easier see how
    # this works in action
    root.attributes('-topmost', True)

    log = Text(root, state='disabled')
    log.pack(expand=True, fill='both')

    update_text_log()

    button_frame = Frame(root)
    button_frame.pack(fill='x')
    Button(button_frame, text='Start Server', command=start_server).pack(expand=True, fill='x', side='left')
    Button(button_frame, text='Stop Server', command=stop_server).pack(expand=True, fill='x', side='right')

    root.mainloop()

来源:

唯一的问题是关于在控制台中显示启动消息,有一种方法可以通过添加import os 然后将os.environ['WERKZEUG_RUN_MAIN'] = 'true' 添加到app.py 文件中来删除它,但是有一个小问题然后停止服务器使用该按钮将执行此操作:Process finished with exit code 15(至少在 Windows 上)因此您必须找到解决此问题的方法,因为我还不能这样做

【讨论】:

  • 我刚刚注意到你还问了我在回答中提到的另一个问题,如果你仍然需要带有子进程的选项(需要安装 python)我可以尝试写一个也可以,否则您可以将应用程序转换为 exe,然后使用子进程运行它
猜你喜欢
  • 2021-12-28
  • 1970-01-01
  • 2019-09-13
  • 2013-09-09
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 2018-12-21
  • 2020-08-23
相关资源
最近更新 更多