RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1596920
Accepted
winkot
winkot
Asked:2024-10-17 01:39:43 +0000 UTC2024-10-17 01:39:43 +0000 UTC 2024-10-17 01:39:43 +0000 UTC

奇怪的布局切换

  • 772

我做了一个布局切换测试:

import win32api
import pyautogui
lcid = win32api.GetKeyboardLayout()
lang_code = hex(lcid & 0xFFFF)
print(lang_code)
pyautogui.hotkey('shift', 'alt')
lcid = win32api.GetKeyboardLayout()
lang_code = hex(lcid & 0xFFFF)
print(lang_code)

但它的工作方式非常奇怪:布局发生了变化,但 Python 没有注意到。

print(lang_code)开关前后输出相同的值。重新启动时,该值会发生变化,但在切换之前和之后仍然保持不变。

这里可能出了什么问题?

python
  • 2 2 个回答
  • 127 Views

2 个回答

  • Voted
  1. Best Answer
    Amgarak
    2024-10-17T05:07:20Z2024-10-17T05:07:20Z

    看完上面评论的话题后,我去研究enSO,发现了一个有趣的讨论。

    结论:

    操作系统 仅向“顶部活动窗口”发送WM_INPUTLANGCHANGE消息,通知其语言更改。如果应用程序未处于活动状态,则方法调用将始终返回最后一个已知的语言值,该值可能已过时。GetKeyboardLayout

    实际上,针对该问题提出的解决方案在各处都是相同的:

    使用当前活动的窗口进程提取键盘语言。

    最小的例子:

    import time
    import ctypes
    
    def get_keyboard_layout():
        
        user32 = ctypes.WinDLL('user32', use_last_error=True)
        # Дескриптор активного окна
        hwnd = user32.GetForegroundWindow()
        # Идентификатор потока из дескриптора окна
        thread_id = user32.GetWindowThreadProcessId(hwnd, None)
        # Идентификатор раскладки клавиатуры из thread_id
        layout_id = user32.GetKeyboardLayout(thread_id)
        # Язык клавиатуры из идентификатора раскладки клавиатуры
        lang_id = layout_id & 0xFFFF
        return "RU" if lang_id == 0x0419 else "EN"
    
    if __name__ == "__main__":
        while True:
            current_layout = get_keyboard_layout()
            print(f'Текущая раскладка клавиатуры: {current_layout}')
            time.sleep(1)
    

    我借鉴了MessageBox和Language ID的一点想法,并将其全部分组到一个示例中:

    import time
    import ctypes
    import threading
    from tkinter import messagebox, Tk
    
    class KeyboardLayoutWatcher:
        languages = {'0x436' : "Afrikaans - South Africa", '0x041c' : "Albanian - Albania", '0x045e' : "Amharic - Ethiopia", '0x401' : "Arabic - Saudi Arabia",
                     '0x1401' : "Arabic - Algeria", '0x3c01' : "Arabic - Bahrain", '0x0c01' : "Arabic - Egypt", '0x801' : "Arabic - Iraq", '0x2c01' : "Arabic - Jordan",
                     '0x3401' : "Arabic - Kuwait", '0x3001' : "Arabic - Lebanon", '0x1001' : "Arabic - Libya", '0x1801' : "Arabic - Morocco", '0x2001' : "Arabic - Oman",
                     '0x4001' : "Arabic - Qatar", '0x2801' : "Arabic - Syria", '0x1c01' : "Arabic - Tunisia", '0x3801' : "Arabic - U.A.E.", '0x2401' : "Arabic - Yemen",
                     '0x042b' : "Armenian - Armenia", '0x044d' : "Assamese", '0x082c' : "Azeri (Cyrillic)", '0x042c' : "Azeri (Latin)", '0x042d' : "Basque",
                     '0x423' : "Belarusian", '0x445' : "Bengali (India)", '0x845' : "Bengali (Bangladesh)", '0x141A' : "Bosnian (Bosnia/Herzegovina)", '0x402' : "Bulgarian",
                     '0x455' : "Burmese", '0x403' : "Catalan", '0x045c' : "Cherokee - United States", '0x804' : "Chinese - People's Republic of China", 
                     '0x1004' : "Chinese - Singapore", '0x404' : "Chinese - Taiwan", '0x0c04' : "Chinese - Hong Kong SAR", '0x1404' : "Chinese - Macao SAR", '0x041a' : "Croatian",
                     '0x101a' : "Croatian (Bosnia/Herzegovina)", '0x405' : "Czech", '0x406' : "Danish", '0x465' : "Divehi", '0x413' : "Dutch - Netherlands", '0x813' : "Dutch - Belgium",
                     '0x466' : "Edo", '0x409' : "English - United States", '0x809' : "English - United Kingdom", '0x0c09' : "English - Australia", '0x2809' : "English - Belize",
                     '0x1009' : "English - Canada", '0x2409' : "English - Caribbean", '0x3c09' : "English - Hong Kong SAR", '0x4009' : "English - India", '0x3809' : "English - Indonesia",
                     '0x1809' : "English - Ireland", '0x2009' : "English - Jamaica", '0x4409' : "English - Malaysia", '0x1409' : "English - New Zealand", '0x3409' : "English - Philippines",
                     '0x4809' : "English - Singapore", '0x1c09' : "English - South Africa", '0x2c09' : "English - Trinidad", '0x3009' : "English - Zimbabwe", '0x425' : "Estonian",
                     '0x438' : "Faroese", '0x429' : "Farsi", '0x464' : "Filipino", '0x040b' : "Finnish", '0x040c' : "French - France", '0x080c' : "French - Belgium",
                     '0x2c0c' : "French - Cameroon", '0x0c0c' : "French - Canada", '0x240c' : "French - Democratic Rep. of Congo", '0x300c' : "French - Cote d'Ivoire",
                     '0x3c0c' : "French - Haiti", '0x140c' : "French - Luxembourg", '0x340c' : "French - Mali", '0x180c' : "French - Monaco", '0x380c' : "French - Morocco",
                     '0xe40c' : "French - North Africa", '0x200c' : "French - Reunion", '0x280c' : "French - Senegal", '0x100c' : "French - Switzerland", 
                     '0x1c0c' : "French - West Indies", '0x462' : "Frisian - Netherlands", '0x467' : "Fulfulde - Nigeria", '0x042f' : "FYRO Macedonian", '0x083c' : "Gaelic (Ireland)",
                     '0x043c' : "Gaelic (Scotland)", '0x456' : "Galician", '0x437' : "Georgian", '0x407' : "German - Germany", '0x0c07' : "German - Austria", '0x1407' : "German - Liechtenstein",
                     '0x1007' : "German - Luxembourg", '0x807' : "German - Switzerland", '0x408' : "Greek", '0x474' : "Guarani - Paraguay", '0x447' : "Gujarati", '0x468' : "Hausa - Nigeria",
                     '0x475' : "Hawaiian - United States", '0x040d' : "Hebrew", '0x439' : "Hindi", '0x040e' : "Hungarian", '0x469' : "Ibibio - Nigeria", '0x040f' : "Icelandic",
                     '0x470' : "Igbo - Nigeria", '0x421' : "Indonesian", '0x045d' : "Inuktitut", '0x410' : "Italian - Italy", '0x810' : "Italian - Switzerland", '0x411' : "Japanese",
                     '0x044b' : "Kannada", '0x471' : "Kanuri - Nigeria", '0x860' : "Kashmiri", '0x460' : "Kashmiri (Arabic)", '0x043f' : "Kazakh", '0x453' : "Khmer", '0x457' : "Konkani",
                     '0x412' : "Korean", '0x440' : "Kyrgyz (Cyrillic)", '0x454' : "Lao", '0x476' : "Latin", '0x426' : "Latvian", '0x427' : "Lithuanian", '0x043e' : "Malay - Malaysia",
                     '0x083e' : "Malay - Brunei Darussalam", '0x044c' : "Malayalam", '0x043a' : "Maltese", '0x458' : "Manipuri", '0x481' : "Maori - New Zealand", '0x044e' : "Marathi",
                     '0x450' : "Mongolian (Cyrillic)", '0x850' : "Mongolian (Mongolian)", '0x461' : "Nepali", '0x861' : "Nepali - India", '0x414' : "Norwegian (Bokmål)", 
                     '0x814' : "Norwegian (Nynorsk)", '0x448' : "Oriya", '0x472' : "Oromo", '0x479' : "Papiamentu", '0x463' : "Pashto", '0x415' : "Polish", '0x416' : "Portuguese - Brazil",
                     '0x816' : "Portuguese - Portugal", '0x446' : "Punjabi", '0x846' : "Punjabi (Pakistan)", '0x046B' : "Quecha - Bolivia", '0x086B' : "Quecha - Ecuador", 
                     '0x0C6B' : "Quecha - Peru", '0x417' : "Rhaeto-Romanic", '0x418' : "Romanian", '0x818' : "Romanian - Moldava", '0x419' : "Russian", '0x819' : "Russian - Moldava",
                     '0x043b' : "Sami (Lappish)", '0x044f' : "Sanskrit", '0x046c' : "Sepedi", '0x0c1a' : "Serbian (Cyrillic)", '0x081a' : "Serbian (Latin)", '0x459' : "Sindhi - India",
                     '0x859' : "Sindhi - Pakistan", '0x045b' : "Sinhalese - Sri Lanka", '0x041b' : "Slovak", '0x424' : "Slovenian", '0x477' : "Somali", '0x042e' : "Sorbian", 
                     '0x0c0a' : "Spanish - Spain (Modern Sort)", '0x040a' : "Spanish - Spain (Traditional Sort)", '0x2c0a' : "Spanish - Argentina", '0x400a' : "Spanish - Bolivia",
                     '0x340a' : "Spanish - Chile", '0x240a' : "Spanish - Colombia", '0x140a' : "Spanish - Costa Rica", '0x1c0a' : "Spanish - Dominican Republic", 
                     '0x300a' : "Spanish - Ecuador", '0x440a' : "Spanish - El Salvador", '0x100a' : "Spanish - Guatemala", '0x480a' : "Spanish - Honduras", '0xe40a' : "Spanish - Latin America",
                     '0x080a' : "Spanish - Mexico", '0x4c0a' : "Spanish - Nicaragua", '0x180a' : "Spanish - Panama", '0x3c0a' : "Spanish - Paraguay", '0x280a' : "Spanish - Peru",
                     '0x500a' : "Spanish - Puerto Rico", '0x540a' : "Spanish - United States", '0x380a' : "Spanish - Uruguay", '0x200a' : "Spanish - Venezuela", '0x430' : "Sutu",
                     '0x441' : "Swahili", '0x041d' : "Swedish", '0x081d' : "Swedish - Finland", '0x045a' : "Syriac", '0x428' : "Tajik", '0x045f' : "Tamazight (Arabic)", 
                     '0x085f' : "Tamazight (Latin)", '0x449' : "Tamil", '0x444' : "Tatar", '0x044a' : "Telugu", '0x041e' : "Thai", '0x851' : "Tibetan - Bhutan", 
                     '0x451' : "Tibetan - People's Republic of China", '0x873' : "Tigrigna - Eritrea", '0x473' : "Tigrigna - Ethiopia", '0x431' : "Tsonga", '0x432' : "Tswana",
                     '0x041f' : "Turkish", '0x442' : "Turkmen", '0x480' : "Uighur - China", '0x422' : "Ukrainian", '0x420' : "Urdu", '0x820' : "Urdu - India", '0x843' : "Uzbek (Cyrillic)",
                     '0x443' : "Uzbek (Latin)", '0x433' : "Venda", '0x042a' : "Vietnamese", '0x452' : "Welsh", '0x434' : "Xhosa", '0x478' : "Yi", '0x043d' : "Yiddish", '0x046a' : "Yoruba",
                     '0x435' : "Zulu", '0x04ff' : "HID (Human Interface Device)"
                     }
    
        def __init__(self):
            self.current_language = None
            self.root = Tk()  
            self.root.withdraw()  
            self.root.attributes('-topmost', True) # Поверх других окон для наглядности
    
        def get_keyboard_layout(self):
            user32 = ctypes.WinDLL('user32', use_last_error=True)
            hwnd = user32.GetForegroundWindow()
            thread_id = user32.GetWindowThreadProcessId(hwnd, None)
            layout_id  = user32.GetKeyboardLayout(thread_id)
            lang_id = layout_id  & 0xFFFF # lang_id  = layout_id & (2 ** 16 - 1)
            language_id_hex = hex(lang_id) 
    
            if language_id_hex in self.languages.keys():
                return self.languages[language_id_hex]
            else:
                return str(language_id_hex)
    
        def handle_current_language(self):
            new_language = self.get_keyboard_layout()
            if self.current_language != new_language:
                self.current_language = new_language
                messagebox.showinfo("Изменение раскладки", f"Текущая раскладка клавиатуры: {self.current_language}")
    
        def watch_language(self):
            while True:
                self.handle_current_language()
                time.sleep(0.5)
    
        def start(self):
            threading.Thread(target=self.watch_language, daemon=True).start()
            self.root.mainloop()  
    
    if __name__ == "__main__":
        watcher = KeyboardLayoutWatcher()
        watcher.start()
    

    在此输入图像描述

    有点令人失望的是,Windows 没有API来获取当前布局(全局),并且您需要在焦点窗口中导航。但如果你看得更远一点,问题就变得显而易见了。


    import time
    import ctypes
    
    def get_keyboard_layout():
        user32 = ctypes.WinDLL('user32', use_last_error=True)
        layout_id = user32.GetKeyboardLayout(0)
        lang_id = layout_id & 0xFFFF
        return "RU" if lang_id == 0x0419 else "EN"
        
    
    if __name__ == "__main__":
        while True:
            current_layout = get_keyboard_layout()
            print(f'Текущая раскладка клавиатуры: {current_layout}')
            time.sleep(1)
    

    可能会出现问题:为什么这段代码在控制台程序中运行时总是返回相同的布局(开始),即使布局发生了变化?

    回答: 问题是控制台应用程序不允许处理Windows 消息。

    因此,您必须创建自己的窗口并在那里处理 WM_INPUTLANGCHANGE消息,该消息在键盘布局更改时触发 - 根据找到的材料--one--、--two--我们有一个示例:

    import ctypes
    import win32gui
    import win32con
    import win32api
    
    def get_keyboard_layout():
        user32 = ctypes.WinDLL('user32', use_last_error=True)
        layout_id = user32.GetKeyboardLayout(0)
        lang_id = layout_id & 0xFFFF
        return "RU" if lang_id == 0x0419 else "EN"
    
    class MyWindow:
        def __init__(self):
            wc = win32gui.WNDCLASS()
            wc.lpfnWndProc = self.wnd_proc  
            wc.lpszClassName = "MyWindowClass"
            wc.hInstance = win32api.GetModuleHandle(None)
            self.class_atom = win32gui.RegisterClass(wc)
            
            self.hwnd = win32gui.CreateWindow(
                self.class_atom,
                "Current language",
                win32con.WS_OVERLAPPEDWINDOW, 
                100, 100, 200, 100,  
                0, 0, wc.hInstance, None
            )
            
            self.h_textbox = win32gui.CreateWindowEx(
                0,
                "EDIT",
                "",
                win32con.WS_CHILD | win32con.WS_VISIBLE | win32con.WS_BORDER | win32con.ES_MULTILINE,
                10, 10, 150, 50,  
                self.hwnd,
                0,
                wc.hInstance,
                None
            )
    
            self.current_layout = get_keyboard_layout()  
            self.update_textbox()  
    
        def update_textbox(self):
            win32gui.SetWindowText(self.h_textbox, f"Язык ввода: {self.current_layout}")
            print(f"Язык ввода {self.current_layout}")
    
        def wnd_proc(self, hwnd, msg, wparam, lparam):
            if msg == win32con.WM_INPUTLANGCHANGE:
                self.current_layout = get_keyboard_layout()
                self.update_textbox()  
            return win32gui.DefWindowProc(hwnd, msg, wparam, lparam)
    
        def run(self):
            win32gui.ShowWindow(self.hwnd, win32con.SW_SHOW)
            win32gui.UpdateWindow(self.hwnd)
            while True:
                win32gui.PumpWaitingMessages()  
    
    if __name__ == "__main__":
        window = MyWindow()
        window.run()
    

    但即便如此,只有当焦点(或焦点返回)在我们的窗口上时,布局才会改变。


    PS也许我错过了一些东西或者没有正确理解它,请随时在评论中纠正它,我会添加到答案中。

    • 4
  2. Fox Fox
    2024-10-17T08:35:34Z2024-10-17T08:35:34Z

    如何确定当前布局,如果是非英语,则切换到它?这是一个现成的控制台脚本(适用于 Windows;请注意,退出程序后,Windows 将返回其环境中的布局):

    import os
    import ctypes
    import keyboard
    
    print("-" * 50 + "\nПереключение текущей раскладки клавиатуры на английскую:\n" + "-" * 50)
    
    while ctypes.WinDLL("user32", use_last_error=True).GetKeyboardLayout(0) & 0xFFFF != 1033:
     keyboard.send("ctrl+shift") # или keyboard.send("alt+shift")
    
    os.system("pause")
    
    • -1

相关问题

  • 是否可以以某种方式自定义 QTabWidget?

  • telebot.anihelper.ApiException 错误

  • Python。检查一个数字是否是 3 的幂。输出 无

  • 解析多个响应

  • 交换两个数组的元素,以便它们的新内容也反转

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5