【问题标题】:How to control a Python GUI via HTTP API如何通过 HTTP API 控制 Python GUI
【发布时间】:2019-03-30 14:30:12
【问题描述】:

我想要一个实现 HTTP API(例如使用 Flask)的 Python 程序,它可以在该程序上接收消息以在屏幕上显示各种窗口(例如使用 tkinter)。

构建这样一个程序的好方法是什么?我相信我需要两个单独的线程:一个用于绘制 tkinter 窗口,一个用于侦听 HTTP 请求。

说,我想向例如发送一个 http 请求。 /show_window,然后在屏幕上显示并保持一个窗口,直到向 /hide_window 发送请求,然后关闭窗口。

我可以通过 tkinter 很好地绘制窗口。但是如果我把它放在 Flask 路由中,它当然会卡在 window.mainloop() 上。

import tkinter as tk
from flask import Flask
app = Flask(__name__)

@app.route("/show")
def show():
    root = tk.Tk()
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    root.attributes('-alpha', 0.0) #For icon
    root.iconify()
    window = tk.Toplevel(root)
    window.geometry("%sx%s" % (screen_width, screen_height))
    window.configure(background='black', cursor='none')
    window.overrideredirect(1)
    window.attributes('-topmost', 1)
    close = tk.Button(window, text = "Close Window", command = lambda: root.destroy())
    close.pack(fill = tk.BOTH, expand = 0)
    window.mainloop() # app is stuck here until gui window is closed
    return "show!"

@app.route("/hide")
def hide():
    ### Code to destroy or hide the Window here.
    return "hide"

我在想我需要两个线程:一个运行 Flask + 一个启动窗口,然后 Flask 线程需要向窗口线程发送消息以显示、隐藏、创建、销毁窗口,等等 但我不确定该怎么做。

注意,使用 Flask 或 tkinter 绝不是必需的。这只是看起来很适合用于 API 的简单 Web 框架和创建 GUI 窗口的简单方法的工具。

【问题讨论】:

    标签: python user-interface flask tkinter python-multithreading


    【解决方案1】:

    您确实需要单独的线程。

    这是一种对我有用的方法。它涉及在单独的线程中启动 Flask 应用程序,然后使用 threading.Event 之类的东西与前台 GUI 线程通信,或使用 threading.Lock 来控制对共享数据结构的访问。

    在线程中启动 Flask 应用程序很简单,看起来像

    import threading
    import time
    
    from yourapp import app
    
    def webserver(shared_state):
        app.config['SHARED'] = shared_state
        # It isn't safe to use the reloader in a thread
        app.run(host='127.0.0.1', debug=True, use_reloader=False)
    
    def main():
        shared_state = SharedState()
        ui_thread = threading.Thread(target=webserver, args=(shared_state,))
        ui_thread.start()
    
        while shared_state.running():
            time.sleep(0.1)
            if shared_state.button_clicked():
                # do your Tk popup here
        ui_thread.join()
    
    if __name__ == '__main__':
        main()
    

    (这是“自旋锁”方法。请查看threading.Event 了解不同的方法。)

    有趣的是共享状态对象,它使用线程锁来序列化对共享数据的访问(在本例中为点击计数器)

    class SharedState:
        def __init__(self):
            self._lock = threading.Lock()
            self._running = True
            self._click_count = 0
        def record_click(self):
            # this gets called from the Flask thread to record a click
            with self._lock:
                self._click_count += 1
        def clicked(self):
            # this gets called from the GUI thread to 'get' a click
            with self._lock:
                if self._click_count > 0:
                    self._click_count -= 1
                    return True
                return False
        def stop(self):
            # called from either side to stop running
            with self._lock:
                self._running = False
    

    Flask 端(yourapp.py)做了类似的事情

    app = Flask(__name__)
    
    @app.route('/')
    def home():
        if request.method == 'POST':
            app.config['SHARED'].record_click()
        return render_response('index.html')
    

    从 Flask 端停止应用程序比仅在共享控件上调用 .stop() 有点棘手。有关执行此操作的代码,请参阅 here

    【讨论】:

    • 感谢您的建议,我尝试遵循。 (我的尝试在这里:paste.yt/p8122.html)虽然我确实认为 SharedState 可以按预期工作,但似乎我仍然无法从烧瓶中真正关闭窗口。发生的情况是,当 shared_state.visible 为 True 时,它​​似乎确实打开了窗口。但是,如果我再次点击烧瓶页面来切换可见性,似乎没有任何改变。窗口未关闭。我猜主线程卡在“window.mainloop()”上。不知道如何从烧瓶线程中将其踢出?
    猜你喜欢
    • 1970-01-01
    • 2013-07-13
    • 2018-02-07
    • 1970-01-01
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 1970-01-01
    • 2011-02-04
    相关资源
    最近更新 更多