如何通过HTTP API控制Python GUI [英] How to control a Python GUI via HTTP API

查看:54
本文介绍了如何通过HTTP API控制Python GUI的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want a Python program that implements an HTTP API (e.g. using Flask) on which it can receive messages to show various windows on the screen (e.g. using tkinter).

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

What is a good way of structuring such a program? I believe I will need two separate threads: one for drawing the tkinter windows and one for listening for HTTP requests.

例如,我想发送http请求到/show_window,然后显示一个窗口并保留在屏幕上,直到将请求发送到/hide_window,然后关闭该窗口.

say, I want to send an http request to e.g. /show_window, then a window is shown and kept on screen until a request is sent to /hide_window, and the window is then closed.

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

I can draw the window just fine via tkinter. But if I put this in a Flask route, of course it gets stuck on 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线程需要将消息发送到窗口线程,以便显示,隐藏,创建,销毁窗口,等等.但是我不确定如何做到这一点.

I am thinking I need something like two threads: One that runs Flask + one that starts up the window and then the flask thread needs to send messages to the window thread in order to show, hide, create, destroy, windows, etc. But I am not really sure how to do that.

请注意,绝对不需要使用Flask或tkinter.对于那些简单的API Web框架和创建GUI窗口的简单方法来说,这只是工具.

Note, it is in no way a requirement to use Flask or tkinter. This is just the tools that seemed good for a simple web framework for the API and a simple way of creating GUI windows.

推荐答案

您确实需要单独的线程.

You will indeed need separate threads.

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

Here's an approach that's worked for me. It involves starting the Flask app in a separate thread, and then using something like threading.Event to communicate with the foreground GUI thread, or threading.Lock to control access to shared data structures.

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

Starting a Flask app in a thread is straightforward, and looks something like

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

(This is the 'spin lock' approach. Check into threading.Event for a different approach.)

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

The interesting bit is the shared state object, which uses a threading lock to serialize access to shared data (a click counter, in this example)

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

烧瓶侧面(在 yourapp.py 中)的作用类似

The Flask side (in yourapp.py) does something like

app = Flask(__name__)

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

从Flask端停止应用程序要比仅在共享控件上调用 .stop()稍微麻烦一些.请参见此处以获取执行此操作的代码.

Stopping the app from the Flask side is a bit trickier than just calling .stop() on the shared control. See here for the code to do that.

这篇关于如何通过HTTP API控制Python GUI的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆