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

Tkinter触摸屏虚拟键盘:解决打开后立即关闭的问题

Tkinter虚拟键盘打开后立即关闭的问题解决方法

我明白你遇到的问题啦——当点击输入框唤起虚拟键盘后,它立刻就消失了,这确实挺头疼的。咱们先说说原因,再一步步解决:

问题根源

当你给输入框绑定FocusIn事件打开键盘时,键盘窗口弹出后,窗口上的按钮会自动获取焦点,这就导致原来的输入框触发了FocusOut事件,直接触发关闭键盘的逻辑,于是键盘刚打开就被关掉了。

解决方案思路

我们需要给关闭键盘的逻辑加个“例外判断”:只有当焦点转移到虚拟键盘以外的控件时,才关闭键盘。同时还要修正代码里的小bug(比如select函数的参数错误)。

修改后的完整代码

import tkinter as tk
import pyautogui

# 全局变量跟踪大写状态和当前聚焦的输入控件
uppercase = False
focused_entry = None
window = None

def select(entry, value):
    global uppercase
    if value == "Space":
        value = ' '
    elif value == 'Enter':
        value = '\n'
    elif value == 'Tab':
        value = '\t'
    
    if value == "Backspace":
        if isinstance(entry, tk.Entry):
            entry.delete(len(entry.get())-1, 'end')
        else:
            # 处理Text控件的退格
            entry.delete('end - 2c', 'end')
    elif value in ('Caps Lock', 'Shift'):
        uppercase = not uppercase  # 切换大小写状态
    else:
        if uppercase:
            value = value.upper()
        entry.insert('end', value)
    
    # 可选:如果需要模拟系统按键,这里用value而不是event
    pyautogui.press(value.lower() if value not in ['Space','Enter','Tab','Backspace'] else value)

def create(root, entry):
    global window
    # 如果已有键盘窗口,先销毁
    if window and window.winfo_exists():
        window.destroy()
    
    alphabets = [
        ['`','1','2','3','4','5','6','7','8','9','0','-','=','Backspace'],
        ['Tab','q','w','e','r','t','y','u','i','o','p','[',']','\\'],
        ['Caps Lock','a','s','d','f','g','h','j','k','l',';',"'",'Enter'],
        ['Shift','z','x','c','v','b','n','m',',','.','/','Shift'],
        ['Space']
    ]
    
    window = tk.Toplevel(root)
    window.configure(background="cornflowerblue")
    window.geometry("+0+483")
    window.wm_attributes("-alpha", 0.7)
    # 给键盘窗口加个标识,方便后续判断
    window.is_keyboard = True
    
    for y, row in enumerate(alphabets):
        x = 0
        for text in row:
            # 设置按钮宽度和跨列
            if text in ('Enter', 'Shift'):
                width = 18
                columnspan = 2
            elif text == 'Space':
                width = 124
                columnspan = 16
            elif text in ('Backspace', '\\', 'Tab', '`', 'Caps Lock'):
                width = 10
                columnspan = 1
            else:
                width = 4
                columnspan = 1
            
            # 创建按钮,确保不获取焦点
            btn = tk.Button(
                window, 
                text=text, 
                width=width, 
                command=lambda value=text: select(entry, value),
                padx=3, 
                pady=3, 
                bd=12, 
                bg="black", 
                fg="white", 
                takefocus=False
            )
            btn.grid(row=y, column=x, columnspan=columnspan)
            x += columnspan

def remember_focus(event):
    global focused_entry
    focused_entry = event.widget
    # 唤起键盘
    create(event.widget.master, focused_entry)

def close_keyboard(event):
    global window
    # 获取当前焦点所在的控件
    current_focus = event.widget.master.focus_get()
    # 如果焦点在键盘窗口内,不关闭键盘
    if current_focus and getattr(current_focus.winfo_toplevel(), 'is_keyboard', False):
        return
    # 否则关闭键盘
    if window and window.winfo_exists():
        window.destroy()
        window = None

if __name__ == '__main__':
    root = tk.Tk()
    root.title('Test Keyboard')
    root.grid_rowconfigure(1, weight=1)
    root.grid_rowconfigure(2, weight=1)
    root.grid_rowconfigure(3, weight=1)
    root.grid_columnconfigure(0, weight=1)
    
    label = tk.Label(root, text='Test Keyboard')
    label.grid(row=0, column=0, columnspan=2)
    
    entry1 = tk.Entry(root)
    entry1.grid(row=1, column=0, sticky='news')
    entry1.bind("<FocusIn>", remember_focus)
    entry1.bind("<FocusOut>", close_keyboard)
    
    entry2 = tk.Entry(root)
    entry2.grid(row=2, column=0, sticky='news')
    entry2.bind("<FocusIn>", remember_focus)
    entry2.bind("<FocusOut>", close_keyboard)
    
    text1 = tk.Text(root)
    text1.grid(row=3, column=0, sticky='news')
    text1.bind("<FocusIn>", remember_focus)
    text1.bind("<FocusOut>", close_keyboard)
    
    root.mainloop()

关键修改点说明

  1. 修正select函数参数:原来的函数多了一个event参数,但调用时并没有传递,导致运行报错,现在已经移除多余参数。
  2. 键盘窗口标识:给键盘窗口添加is_keyboard = True属性,方便在FocusOut回调中判断焦点是否在键盘内。
  3. 智能关闭逻辑:在close_keyboard函数中,检查当前焦点的顶层窗口是否是键盘窗口,如果是就不执行关闭操作,避免误关闭。
  4. 控件布局优化:添加了grid_rowconfigure和grid_columnconfigure,让输入控件能自适应窗口大小,更适合触摸屏场景。

这样修改后,当点击输入框唤起键盘时,即使焦点转移到键盘按钮上,也不会关闭键盘;只有当你点击键盘以外的区域(比如另一个输入框),才会关闭当前键盘并唤起对应输入框的键盘(或者直接关闭)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:52:34