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

Tkinter文本框无输出问题求助

问题描述

我尝试将原本输出到控制台的内容改为写入自定义的Tkinter文本框,这些内容来自process_input函数的match-case分支。但目前遇到问题:控制台和文本框均无任何输出,且未抛出任何错误。以下是我编写的代码:

from tkinter import *
import sys
import time
import multiprocessing


class Window:
    def __init__(self, WIDTH, HEIGHT, WIN) -> None:
        """
        Parameters
        ----------
        self.width : int
            The width of the window created.
        self.height : int
            The height of the window created.
        self.window : tk.TK
            The window object.

        Variables
        ---------
        self.data : dict
            A dictonary containing all information about buttons creates and titles displayed on screen.
        """
        self.width  = WIDTH
        self.height = HEIGHT
        self.window = WIN

        self.data : dict = {
            "Title" : None,
            "btns" : {}
        }

    def constract(self, title : str, background_color : str) -> None:
        """
        Creates the main window adds a title and a background color.

        Parameters
        ----------
        title : str
            A string which will serve as the window's title.
        backgound_color : str
            A string represents a hex code color (e.g. #FFFFFF).
        """
        self.window.title(title)
        self.window.geometry(f'{self.width}x{self.height}')
        self.window.configure(bg=background_color)

    def header(self, text : str, fill : str = "black", font : str = 'Arial 28 bold', background : str ='#E1DCDC') -> None:
        """
        Displays a title on the screen.

        Parametes
        ---------
        text : str
            The text which will be displayed on the screen.
        fill : str
            The color of the text, can be an existing color in tkinter or a custom hex code color (e.g. #FFFFFF).
        font : str
            The font type, the size of the letters and other designs for the text.
        backgound : str
            The color of the box around the text, can be an existing color in tkinter or a custom hex code color (e.g. #FFFFFF).
        """
        T = Label(self.window, text=text, bg=background ,fg=fill, font=font)
        T.pack()
        self.data["Title"] = text

    def logger(self, BG_color : str, state : bool = False, side ='BOTTOM', anchor='se', area : tuple = ()) -> object:
        # anchors - n s e w ne nw se sw center
        # state - TOP BOTTOM LEFT RIGHT
        if not area:
            t = Text(self.window, bg=BG_color,bd=1, width=self.width//50, height=self.height//40)
        else:
            t = Text(self.window, bg=BG_color,bd=1, width=area[0], height=area[1])

        # if not state:
        #     t.config(state=DISABLED)

        match side:
            case 'TOP':
                t.pack(side=TOP, anchor=anchor)
            case 'BOTTOM':
                t.pack(side=BOTTOM, anchor=anchor)
            case 'LEFT':
                t.pack(side=LEFT, anchor=anchor)
            case 'RIGHT':
                t.pack(side=RIGHT, anchor=anchor)
            case _:
                raise(ValueError("Value should be a string and one of those: \"LEFT\", \"RIGHT\" \"TOP\", \"BOTTOM\"."))

        return t

class PrintLogger:
    def __init__(self, textbox):
        self.textbox = textbox

    def write(self, text):
        self.textbox.config(state='normal')
        self.textbox.insert(END, text)
        self.textbox.config(state='disabled')

    def flush(self):
        pass


def Move(direction, q):
    match direction:
        case 'left':
            q.enqueue('left')
        case 'right':
            q.enqueue('right')

def get_input(seconds, q):
    win = Tk()

    WIDTH, HEIGHT = win.winfo_screenwidth(), win.winfo_screenheight()

    main_window = Window(WIDTH, HEIGHT, win)

    main_window.constract("AntiBalloons system controls", "#E1DCDC")
    main_window.header("AntiAir system controls")
    l = main_window.logger(BG_color='#E1DCDC')

    sys.stdout = PrintLogger(l)  # pass the Text widget to PrintLogger

    while True:
        try:
            win.bind('<A>', lambda event: Move('left', q))
            win.bind('<a>', lambda event: Move('left', q))
            win.bind('<D>', lambda event: Move('right', q))
            win.bind('<d>', lambda event: Move('right', q))
        except Exception:
            print('err')
            break
        #main_window.window.after(100, stdout_not_moving)
        main_window.window.mainloop()
        time.sleep(seconds)


def process_input(seconds, q):
    while True:
        if not q.is_empty():
            command = q.dequeue()

            match command:
                case 'left':
                    sys.stdout.write('Moving left')
                case 'right':
                    sys.stdout.write('Moving right')
        else:
            sys.stdout.write('Not moving')

        time.sleep(seconds)

# def stdout_not_moving():
#     print('Not moving')

if __name__ == '__main__':


    multiprocessing.freeze_support()
    manager = multiprocessing.Manager()
    q = manager.Queue()

    p1 = multiprocessing.Process(target=get_input, args=[0.1, q])
    p2 = multiprocessing.Process(target=process_input, args=[0.03015, q])

    p1.start()
    p2.start()
    p1.join()
    p2.join()

问题分析与修复方案

1. 多进程sys.stdout重定向无效

每个进程的sys.stdout是独立的,get_input进程里的重定向对process_input进程完全不起作用,这是输出无响应的核心原因。

  • 修复:新增一个专门传递输出消息的队列output_q,process_input把要输出的内容放入队列,get_input进程负责从队列取消息并写入文本框。

2. get_input函数逻辑错误

mainloop()是阻塞的事件循环,一旦调用就会持续运行,外层的while True循环和time.sleep永远不会执行;同时按键绑定代码放在循环里完全多余,绑定一次即可。

  • 修复:移除外层while True,将按键绑定代码移到mainloop之前,通过Tkinter的after方法定时更新文本框内容。

3. 多进程队列方法错误

多进程队列没有is_empty()、enqueue()、dequeue()方法,正确的方法是empty()、put()、get()。

  • 修复:替换所有错误的队列方法调用。

4. 文本框体验优化

原代码写入的内容没有换行,会堆积在一起;同时需要自动滚动到最新内容。

  • 修复:每次写入消息后添加换行符,调用see(END)滚动到文本框底部。

修复后的完整代码

from tkinter import *
import time
import multiprocessing


class Window:
    def __init__(self, WIDTH, HEIGHT, WIN) -> None:
        self.width  = WIDTH
        self.height = HEIGHT
        self.window = WIN

        self.data : dict = {
            "Title" : None,
            "btns" : {}
        }

    def constract(self, title : str, background_color : str) -> None:
        self.window.title(title)
        self.window.geometry(f'{self.width}x{self.height}')
        self.window.configure(bg=background_color)

    def header(self, text : str, fill : str = "black", font : str = 'Arial 28 bold', background : str ='#E1DCDC') -> None:
        T = Label(self.window, text=text, bg=background ,fg=fill, font=font)
        T.pack()
        self.data["Title"] = text

    def logger(self, BG_color : str, state : bool = False, side ='BOTTOM', anchor='se', area : tuple = ()) -> object:
        if not area:
            t = Text(self.window, bg=BG_color,bd=1, width=self.width//50, height=self.height//40)
        else:
            t = Text(self.window, bg=BG_color,bd=1, width=area[0], height=area[1])

        match side:
            case 'TOP':
                t.pack(side=TOP, anchor=anchor)
            case 'BOTTOM':
                t.pack(side=BOTTOM, anchor=anchor)
            case 'LEFT':
                t.pack(side=LEFT, anchor=anchor)
            case 'RIGHT':
                t.pack(side=RIGHT, anchor=anchor)
            case _:
                raise(ValueError("Value should be a string and one of those: \"LEFT\", \"RIGHT\" \"TOP\", \"BOTTOM\"."))

        return t


def Move(direction, q):
    match direction:
        case 'left':
            q.put('left')
        case 'right':
            q.put('right')

def get_input(output_q, cmd_q):
    win = Tk()

    WIDTH, HEIGHT = win.winfo_screenwidth(), win.winfo_screenheight()

    main_window = Window(WIDTH, HEIGHT, win)
    main_window.constract("AntiBalloons system controls", "#E1DCDC")
    main_window.header("AntiAir system controls")
    log_text = main_window.logger(BG_color='#E1DCDC')

    # 定时从输出队列获取消息并更新文本框
    def update_log():
        while not output_q.empty():
            msg = output_q.get()
            log_text.config(state='normal')
            log_text.insert(END, msg + '\n')
            log_text.config(state='disabled')
            log_text.see(END)  # 自动滚动到最新内容
        win.after(100, update_log)

    # 绑定按键事件
    win.bind('<A>', lambda event: Move('left', cmd_q))
    win.bind('<a>', lambda event: Move('left', cmd_q))
    win.bind('<D>', lambda event: Move('right', cmd_q))
    win.bind('<d>', lambda event: Move('right', cmd_q))

    update_log()
    win.mainloop()


def process_input(seconds, cmd_q, output_q):
    while True:
        if not cmd_q.empty():
            command = cmd_q.get()
            match command:
                case 'left':
                    output_q.put('Moving left')
                case 'right':
                    output_q.put('Moving right')
        else:
            output_q.put('Not moving')
        time.sleep(seconds)

if __name__ == '__main__':
    multiprocessing.freeze_support()
    manager = multiprocessing.Manager()
    cmd_q = manager.Queue()
    output_q = manager.Queue()

    p1 = multiprocessing.Process(target=get_input, args=[output_q, cmd_q])
    p2 = multiprocessing.Process(target=process_input, args=[0.5, cmd_q, output_q])

    p1.start()
    p2.start()
    p1.join()
    p2.join()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:45:04