You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何整合PySimpleGUI图像查看器与自定义USB摇杆输入?

问题描述

我正尝试制作一款街机风格的用户引导故事书终端,采用PySimpleGUI和自定义USB摇杆作为输入方式。已完成带按钮提示的PySimpleGUI图像加载器和摇杆输入处理程序,两者均可正常运行,但不知如何将二者整合,想了解当前思路是否正确,或有无其他实现方式。

已实现代码

PySimpleGUI图像加载器

import io
import os
import PySimpleGUI as sg
from PIL import Image

image = Image.open("/path/to/file.jpg")
image.thumbnail((200, 200))
bio = io.BytesIO()
image.save(bio, format="PNG")

layout = [[sg.Image(key="-IMAGE-")],
          [sg.Button("Green"),
           sg.Button("Blue"),]
          ]
window = sg.Window("Image Viewer", layout, finalize=True)
window["-IMAGE-"].update(data=bio.getvalue())

while True:

    event, values = window.read()
    if event == "Exit" or event == sg.WIN_CLOSED:
        break
    if event == "Green":
        load_image_str = "g"
    if event == "Blue":
        load_image_str = "b"

    file_path = ("/path/to/folder" + load_image_str + ".jpg")

    if os.path.exists(file_path) and file_path.endswith('.jpg'):
        image = Image.open("/path/to/file")
        image.thumbnail((400, 400))
        bio = io.BytesIO()
        image.save(bio, format="PNG")
        window["-IMAGE-"].update(data=bio.getvalue())

window.close()

摇杆输入处理程序

from evdev import InputDevice, ecodes

#Button codes
greenBtn = 288
blueBtn = 289

device = InputDevice('/dev/input/eventXX')

def main():
    for event in device.read_loop():
        if event.type == ecodes.EV_KEY:
            if event.value == 1:
                if event.code == greenBtn:
                    print("green")
                if event.code == blueBtn:
                    print("blue")


if __name__ == "__main__":
    x =main()
整合方案与思路验证

你的初始思路完全正确:拆分实现GUI界面和硬件输入,再进行整合。核心问题是摇杆的read_loop()是阻塞操作,会卡住GUI主线程,下面提供两种可行的整合方法:

方法1:线程+队列实现跨线程通信

将摇杆输入放到后台线程,通过队列传递事件给GUI主线程,避免阻塞GUI响应。

import io
import os
import PySimpleGUI as sg
from PIL import Image
from evdev import InputDevice, ecodes
import threading
import queue

# 摇杆按钮定义
greenBtn = 288
blueBtn = 289
device = InputDevice('/dev/input/eventXX')
# 线程间通信队列
input_queue = queue.Queue()

def joystick_input(queue):
    """后台线程处理摇杆输入"""
    for event in device.read_loop():
        if event.type == ecodes.EV_KEY and event.value == 1:
            if event.code == greenBtn:
                queue.put("Green")
            elif event.code == blueBtn:
                queue.put("Blue")

def load_target_image(btn_type):
    """复用的图片加载逻辑"""
    load_image_str = "g" if btn_type == "Green" else "b"
    file_path = f"/path/to/folder/{load_image_str}.jpg"
    if os.path.exists(file_path) and file_path.endswith('.jpg'):
        image = Image.open(file_path)
        image.thumbnail((400, 400))
        bio = io.BytesIO()
        image.save(bio, format="PNG")
        window["-IMAGE-"].update(data=bio.getvalue())

# 初始化GUI
image = Image.open("/path/to/file.jpg")
image.thumbnail((200, 200))
bio = io.BytesIO()
image.save(bio, format="PNG")

layout = [[sg.Image(key="-IMAGE-")],
          [sg.Button("Green"), sg.Button("Blue")]]
window = sg.Window("Image Viewer", layout, finalize=True)
window["-IMAGE-"].update(data=bio.getvalue())

# 启动摇杆输入后台线程
threading.Thread(target=joystick_input, args=(input_queue,), daemon=True).start()

while True:
    # 非阻塞读取GUI事件,设置timeout定期检查队列
    event, values = window.read(timeout=100)
    if event == sg.WIN_CLOSED:
        break
    
    # 处理GUI按钮事件
    if event in ("Green", "Blue"):
        load_target_image(event)
    
    # 处理摇杆输入事件
    try:
        joystick_event = input_queue.get_nowait()
        load_target_image(joystick_event)
    except queue.Empty:
        pass

window.close()

方法2:GUI循环内轮询摇杆输入

利用evdev的read_one()非阻塞方法,在GUI循环的间隙定期检查摇杆输入,无需额外线程。

import io
import os
import PySimpleGUI as sg
from PIL import Image
from evdev import InputDevice, ecodes

# 摇杆按钮定义
greenBtn = 288
blueBtn = 289
device = InputDevice('/dev/input/eventXX')

def load_target_image(btn_type):
    """复用的图片加载逻辑"""
    load_image_str = "g" if btn_type == "Green" else "b"
    file_path = f"/path/to/folder/{load_image_str}.jpg"
    if os.path.exists(file_path) and file_path.endswith('.jpg'):
        image = Image.open(file_path)
        image.thumbnail((400, 400))
        bio = io.BytesIO()
        image.save(bio, format="PNG")
        window["-IMAGE-"].update(data=bio.getvalue())

# 初始化GUI
image = Image.open("/path/to/file.jpg")
image.thumbnail((200, 200))
bio = io.BytesIO()
image.save(bio, format="PNG")

layout = [[sg.Image(key="-IMAGE-")],
          [sg.Button("Green"), sg.Button("Blue")]]
window = sg.Window("Image Viewer", layout, finalize=True)
window["-IMAGE-"].update(data=bio.getvalue())

while True:
    # 设置timeout定期检查摇杆输入
    event, values = window.read(timeout=50)
    if event == sg.WIN_CLOSED:
        break
    
    # 处理GUI按钮事件
    if event in ("Green", "Blue"):
        load_target_image(event)
    
    # 轮询摇杆输入
    joystick_event = device.read_one()
    if joystick_event:
        if joystick_event.type == ecodes.EV_KEY and joystick_event.value == 1:
            if joystick_event.code == greenBtn:
                load_target_image("Green")
            elif joystick_event.code == blueBtn:
                load_target_image("Blue")

window.close()

思路正确性说明

你拆分实现GUI和硬件输入的思路是合理的,这类硬件交互+GUI的场景,核心就是解决阻塞性输入的问题。上面两种方法分别从线程异步和轮询非阻塞两个角度解决了这个问题,都能实现摇杆与GUI的无缝整合。

内容的提问来源于stack exchange,提问作者gareth_t

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 12:53:27