| import sys
|
| import keyboard
|
| from PySide6.QtWidgets import QApplication
|
| from PySide6.QtCore import QThread, Signal, QTimer, QObject
|
|
|
| import state
|
| from api import fetch_candidates
|
| from ui import CandidateWindow
|
|
|
|
|
| class EventBridge(QObject):
|
| """负责将子线程的键盘事件安全转发到主线程"""
|
| request_refresh_sig = Signal()
|
| reset_ui_sig = Signal()
|
|
|
|
|
| class FetchThread(QThread):
|
| result_ready = Signal(list)
|
|
|
| def __init__(self, text):
|
| super().__init__()
|
| self.text = text
|
|
|
| def run(self):
|
|
|
| res = fetch_candidates(self.text)
|
| self.result_ready.emit(res)
|
|
|
| class IMEController:
|
| def __init__(self):
|
| self.app = QApplication(sys.argv)
|
| self.window = CandidateWindow()
|
| self.bridge = EventBridge()
|
| self.worker = None
|
|
|
|
|
| self.debounce_timer = QTimer()
|
| self.debounce_timer.setSingleShot(True)
|
|
|
|
|
| self.bridge.request_refresh_sig.connect(self._handle_refresh_request)
|
| self.bridge.reset_ui_sig.connect(self.reset_ime)
|
| self.debounce_timer.timeout.connect(self._start_fetch)
|
|
|
| def on_key_event(self, event):
|
| """键盘钩子回调(运行在非主线程)"""
|
| if event.event_type == "up":
|
| return True
|
|
|
| name = event.name
|
|
|
|
|
| if len(name) == 1 and name.isalpha():
|
|
|
| state.buffer += name
|
| self.bridge.request_refresh_sig.emit()
|
| return False
|
|
|
|
|
| elif name == "backspace":
|
| if state.buffer:
|
| state.buffer = state.buffer[:-1]
|
| self.bridge.request_refresh_sig.emit()
|
| return False
|
| return True
|
|
|
|
|
| elif name == "space" or (name in "12345" and state.buffer):
|
| if state.candidates:
|
| idx = 0 if name == "space" else int(name) - 1
|
| if idx < len(state.candidates):
|
| keyboard.write(state.candidates[idx])
|
| self.bridge.reset_ui_sig.emit()
|
| return False
|
| return True
|
|
|
|
|
| elif name in ["enter", "esc"]:
|
| if state.buffer:
|
| if name == "enter":
|
| keyboard.write(state.buffer)
|
| self.bridge.reset_ui_sig.emit()
|
| return False
|
| return True
|
|
|
| return True
|
|
|
|
|
| def _handle_refresh_request(self):
|
| """主线程逻辑:控制定时器"""
|
| if not state.buffer:
|
| self.reset_ime()
|
| return
|
|
|
| self.debounce_timer.start(70)
|
|
|
| def _start_fetch(self):
|
| """主线程逻辑:启动异步拉取词条"""
|
| if self.worker and self.worker.isRunning():
|
| self.worker.terminate()
|
| self.worker.wait()
|
|
|
| self.worker = FetchThread(state.buffer)
|
| self.worker.result_ready.connect(self._on_data_received)
|
| self.worker.start()
|
|
|
| def _on_data_received(self, candidates):
|
| """主线程逻辑:更新UI内容"""
|
| state.candidates = candidates
|
| if candidates:
|
| self.window.update_candidates(candidates)
|
| else:
|
| self.window.hide()
|
|
|
| def reset_ime(self):
|
| """主线程逻辑:重置状态并隐藏UI"""
|
| state.buffer = ""
|
| state.candidates = []
|
| self.window.hide()
|
| if self.debounce_timer.isActive():
|
| self.debounce_timer.stop()
|
|
|
| def run(self):
|
|
|
| keyboard.hook(self.on_key_event)
|
|
|
| sys.exit(self.app.exec())
|
|
|
| if __name__ == "__main__":
|
|
|
| ime = IMEController()
|
| ime.run() |