Как управлять графическим интерфейсом Python через HTTP API - PullRequest
0 голосов
/ 30 марта 2019

Мне нужна программа на Python, которая реализует HTTP API (например, с помощью Flask), на котором она может получать сообщения для отображения различных окон на экране (например, с помощью 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 или tkinter. Это всего лишь инструменты, которые казались хорошими для простой веб-инфраструктуры для API и простого способа создания окон графического интерфейса.

1 Ответ

1 голос
/ 30 марта 2019

Вам действительно понадобятся отдельные темы.

Вот подход, который сработал для меня.Он включает запуск приложения Flask в отдельном потоке, а затем использование чего-то вроде threading.Event для связи с основным потоком графического интерфейса пользователя или 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() на общем элементе управления.См. здесь код для этого.

...