Qt在Mac平台下如何将虚拟键码转换为对应键盘Unicode字符
问题原因
你现在的转换逻辑失效是必然的,核心问题有三个:
- 接口适用范围不对:
UCKeyTranslate配合kTISPropertyUnicodeKeyLayoutData只能读取纯键盘布局的映射表,日文Kana、韩文2Set这类带输入法转换逻辑的输入源,根本不会把键位映射存在这个属性里,你拿到的要么是空值要么是底层基础键盘的布局,自然出不来「つ」「ㅋ」这类字符。 - 代码有硬bug:修饰键映射写反了——Qt::MetaModifier对应Mac的Command键(cmdKey),Qt::ControlModifier才对应controlKey,你现在的映射关系是错的;另外
UCKeyTranslate的字符缓冲区只开了1个UniChar长度,遇到需要UTF-16代理对的字符直接内存溢出;还有Copy出来的TISInputSourceRef没释放,会长期内存泄漏。 - 事件时机不对:你在
QEvent::ShortcutOverride事件里做转换,这个事件触发在输入法逻辑执行之前,日文、韩文输入法的键位转换、候选词处理根本还没跑,就算接口对也拿不到最终输出的字符。
另外你自己打日志的时候其实已经拿到正确值了:日文场景下Key=12388就是「つ」的Unicode编码,韩文场景下Key=12619就是「ㅋ」的Unicode编码,根本不需要再拿nativeVirtualKey二次转换。
正确实现方式
优先直接用Qt自带的keyEvent->text()取值,这是Qt对接完系统输入法、所有键盘布局之后返回的最终字符,不管是哪国布局、哪种输入法都能拿到正确结果,完全没必要自己重复实现键码转换逻辑。
如果确实有脱离按键事件、主动转换虚拟键码的需求,分场景处理:
纯键盘布局场景(美式、瑞士德语这类无输入法的布局)
修正原有ConvertKeyCodeToText的bug即可,修正后代码:
std::pair<bool, std::string> ConvertKeyCodeToText(int mac_key_code, Qt::KeyboardModifiers qt_modifiers) { TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource(); CFDataRef layout_data = static_cast<CFDataRef>(TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData)); if (!layout_data) { CFRelease(currentKeyboard); return {false, ""}; } const UCKeyboardLayout *keyboard_layout = (const UCKeyboardLayout *)CFDataGetBytePtr(layout_data); UInt32 mac_modifiers = 0; // 修正之前写反的修饰键映射 if (qt_modifiers & Qt::ShiftModifier) mac_modifiers |= shiftKey; if (qt_modifiers & Qt::ControlModifier) mac_modifiers |= controlKey; if (qt_modifiers & Qt::AltModifier) mac_modifiers |= optionKey; if (qt_modifiers & Qt::MetaModifier) mac_modifiers |= cmdKey; UInt32 modifier_key_state = (mac_modifiers >> 8) & 0xFF; UInt32 dead_key_state = 0; UniCharCount char_count = 0; // 缓冲区开4位长度,兼容UTF-16代理对、多字符输出 UniChar characters[4] = {0}; OSStatus status = UCKeyTranslate( keyboard_layout, static_cast<UInt16>(mac_key_code), kUCKeyActionDown, modifier_key_state, LMGetKbdLast(), kUCKeyTranslateNoDeadKeysBit, &dead_key_state, 4, &char_count, characters ); bool is_dead_key = false; if (status == noErr && char_count == 0 && dead_key_state != 0) { is_dead_key = true; dead_key_state = 0; status = UCKeyTranslate( keyboard_layout, static_cast<UInt16>(mac_key_code), kUCKeyActionDown, modifier_key_state, LMGetKbdLast(), kUCKeyTranslateNoDeadKeysBit, &dead_key_state, 4, &char_count, characters ); } CFRelease(currentKeyboard); if (status == noErr && char_count > 0) { QString res = QString::fromUtf16(characters, char_count); if (!res.isEmpty() && !res.at(0).isPrint()) { return {is_dead_key, ""}; } return {is_dead_key, res.toStdString()}; } return {false, ""}; }
输入法类场景(日文、韩文、中文输入法)
别尝试用UCKeyTranslate做转换,这个系统接口本身就不支持输入法级别的键位映射。正确做法是监听QEvent::KeyPress事件,这个事件触发时输入法逻辑已经执行完毕,直接读keyEvent->text()就能拿到正确的输出字符,不需要做任何额外转换。
其他问题
你写的g_nativeKeycode函数里同样存在修饰键映射写反的问题,而且在输入法激活场景下,反向从Qt::Key查询虚拟键码的逻辑也会失效,这类场景直接用Qt提供的标准接口即可,不要自己手动解析系统的键盘布局结构,后续系统版本更新很容易出现兼容性问题。
内容的提问来源于stack exchange,提问作者Xenius
相关产品推荐
相关产品推荐

