251 lines
9.9 KiB
Python
251 lines
9.9 KiB
Python
import sys
|
|
import logging
|
|
import keyboard
|
|
import pyperclip
|
|
from PySide6.QtCore import QTimer, QEvent, Qt, QSettings
|
|
from PySide6.QtWidgets import (
|
|
QApplication, QMainWindow, QVBoxLayout, QHBoxLayout, QCheckBox, QPushButton,
|
|
QMessageBox, QWidget, QLineEdit
|
|
)
|
|
from translatepy.translators.bing import BingTranslate
|
|
from translatepy.translators.deepl import DeeplTranslate
|
|
from translatepy.translators.google import GoogleTranslate
|
|
from translatepy.translators.libre import LibreTranslate
|
|
from translatepy.translators.mymemory import MyMemoryTranslate
|
|
from translatepy.translators.reverso import ReversoTranslate
|
|
from translatepy.translators.yandex import YandexTranslate
|
|
from translatepy.translators.microsoft import MicrosoftTranslate
|
|
|
|
# 로깅 설정
|
|
logging.basicConfig(
|
|
level=logging.DEBUG,
|
|
format="%(asctime)s - %(levelname)s - %(message)s",
|
|
handlers=[
|
|
logging.FileHandler("translator_app.log"),
|
|
logging.StreamHandler()
|
|
]
|
|
)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MultiTranslatorApp(QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("다중 번역기 설정")
|
|
self.setGeometry(100, 100, 500, 600)
|
|
|
|
# QSettings 초기화
|
|
self.settings = QSettings("MultiTranslatorApp", "TranslatorSettings")
|
|
|
|
# 번역 요청 카운터
|
|
self.request_count = {
|
|
"Bing": 0,
|
|
"DeepL": 0,
|
|
"Google": 0,
|
|
"LibreTranslate": 0,
|
|
"MyMemory": 0,
|
|
"Reverso": 0,
|
|
"Yandex": 0,
|
|
"Microsoft": 0
|
|
}
|
|
|
|
# GUI 레이아웃
|
|
self.central_widget = QWidget()
|
|
self.setCentralWidget(self.central_widget)
|
|
self.layout = QVBoxLayout(self.central_widget)
|
|
|
|
# 번역기 체크박스 및 API 키 필드 생성
|
|
self.translator_settings = {
|
|
"Bing": {"checkbox": QCheckBox("Bing Translate"), "api_key": QLineEdit()},
|
|
"DeepL": {"checkbox": QCheckBox("DeepL Translate"), "api_key": None},
|
|
"Google": {"checkbox": QCheckBox("Google Translate"), "api_key": None},
|
|
"LibreTranslate": {"checkbox": QCheckBox("LibreTranslate"), "api_key": None},
|
|
"MyMemory": {"checkbox": QCheckBox("MyMemory Translate"), "api_key": None},
|
|
"Reverso": {"checkbox": QCheckBox("Reverso Translate"), "api_key": None},
|
|
"Yandex": {"checkbox": QCheckBox("Yandex Translate"), "api_key": QLineEdit()},
|
|
"Microsoft": {"checkbox": QCheckBox("Microsoft Translate"), "api_key": QLineEdit()}
|
|
}
|
|
|
|
# 번역기 옵션 UI 구성
|
|
for name, widgets in self.translator_settings.items():
|
|
checkbox = widgets["checkbox"]
|
|
api_key_field = widgets["api_key"]
|
|
|
|
# 수평 레이아웃으로 배치
|
|
row_layout = QHBoxLayout()
|
|
row_layout.addWidget(checkbox)
|
|
|
|
if api_key_field:
|
|
api_key_field.setPlaceholderText(f"{name} API 키 입력")
|
|
api_key_field.setEnabled(False) # 처음에는 비활성화
|
|
row_layout.addWidget(api_key_field)
|
|
|
|
# 체크박스와 API 키 활성화 상태를 연결
|
|
checkbox.stateChanged.connect(self.create_checkbox_handler(api_key_field))
|
|
|
|
self.layout.addLayout(row_layout)
|
|
|
|
# 자동 번역 토글
|
|
self.auto_translate_toggle = QCheckBox("클립보드 변경 시 자동 번역")
|
|
self.layout.addWidget(self.auto_translate_toggle)
|
|
self.auto_translate_toggle.stateChanged.connect(self.toggle_auto_translate)
|
|
|
|
# 적용 버튼
|
|
self.apply_button = QPushButton("설정 적용")
|
|
self.apply_button.clicked.connect(self.apply_settings)
|
|
self.layout.addWidget(self.apply_button)
|
|
|
|
# 번역기 초기화
|
|
self.selected_services = []
|
|
|
|
# 클립보드 감지 관련
|
|
self.auto_translate_enabled = False
|
|
self.last_clipboard_content = ""
|
|
self.clipboard_timer = QTimer(self)
|
|
self.clipboard_timer.timeout.connect(self.monitor_clipboard)
|
|
|
|
# 단축키 등록
|
|
keyboard.add_hotkey("ctrl+shift+t", self.schedule_translation)
|
|
|
|
# ESC 키로 창 최소화
|
|
self.installEventFilter(self)
|
|
|
|
# 이전 설정 복원
|
|
self.load_settings()
|
|
|
|
def create_checkbox_handler(self, api_key_field):
|
|
"""체크박스 상태 변경에 따라 API 키 입력 필드 활성화/비활성화."""
|
|
def handler(state):
|
|
api_key_field.setEnabled(state == Qt.Checked)
|
|
logger.debug(f"API 키 필드 {'활성화' if state == Qt.Checked else '비활성화'}됨")
|
|
return handler
|
|
|
|
def load_settings(self):
|
|
"""QSettings에서 체크박스 상태 및 API 키를 불러옵니다."""
|
|
logger.debug("QSettings에서 상태 복원 중...")
|
|
for name, widgets in self.translator_settings.items():
|
|
checkbox = widgets["checkbox"]
|
|
api_key_field = widgets["api_key"]
|
|
|
|
# 체크박스 상태 복원
|
|
state = self.settings.value(f"{name}_checked", False, type=bool)
|
|
checkbox.setChecked(state)
|
|
logger.debug(f"{name} 체크박스 상태: {state}")
|
|
|
|
# API 키 복원
|
|
if api_key_field:
|
|
api_key = self.settings.value(f"{name}_api_key", "", type=str)
|
|
api_key_field.setText(api_key)
|
|
api_key_field.setEnabled(state) # 체크박스 상태에 따라 활성화
|
|
logger.debug(f"{name} API 키: {api_key}")
|
|
|
|
def save_settings(self):
|
|
"""QSettings에 체크박스 상태 및 API 키를 저장합니다."""
|
|
logger.debug("QSettings에 상태 저장 중...")
|
|
for name, widgets in self.translator_settings.items():
|
|
checkbox = widgets["checkbox"]
|
|
api_key_field = widgets["api_key"]
|
|
|
|
# 체크박스 상태 저장
|
|
self.settings.setValue(f"{name}_checked", checkbox.isChecked())
|
|
logger.debug(f"{name} 체크박스 상태 저장: {checkbox.isChecked()}")
|
|
|
|
# API 키 저장
|
|
if api_key_field:
|
|
self.settings.setValue(f"{name}_api_key", api_key_field.text())
|
|
logger.debug(f"{name} API 키 저장: {api_key_field.text()}")
|
|
|
|
def apply_settings(self):
|
|
"""선택된 번역기를 업데이트하고 설정을 저장합니다."""
|
|
self.selected_services = []
|
|
for name, widgets in self.translator_settings.items():
|
|
checkbox = widgets["checkbox"]
|
|
api_key_field = widgets["api_key"]
|
|
|
|
if checkbox.isChecked():
|
|
logger.debug(f"{name} 번역기 활성화 및 추가 중...")
|
|
try:
|
|
if name == "Bing":
|
|
self.selected_services.append(BingTranslate())
|
|
elif name == "DeepL":
|
|
self.selected_services.append(DeeplTranslate())
|
|
elif name == "Google":
|
|
self.selected_services.append(GoogleTranslate())
|
|
elif name == "LibreTranslate":
|
|
self.selected_services.append(LibreTranslate())
|
|
elif name == "MyMemory":
|
|
self.selected_services.append(MyMemoryTranslate())
|
|
elif name == "Reverso":
|
|
self.selected_services.append(ReversoTranslate())
|
|
elif name == "Yandex":
|
|
self.selected_services.append(YandexTranslate(api_key=api_key_field.text()))
|
|
elif name == "Microsoft":
|
|
self.selected_services.append(MicrosoftTranslate(api_key=api_key_field.text()))
|
|
except Exception as e:
|
|
logger.error(f"{name} 번역기 추가 실패: {e}")
|
|
|
|
# 설정 저장
|
|
self.save_settings()
|
|
|
|
# 창 최소화
|
|
self.showMinimized()
|
|
|
|
def toggle_auto_translate(self):
|
|
if self.auto_translate_toggle.isChecked():
|
|
self.auto_translate_enabled = True
|
|
self.last_clipboard_content = pyperclip.paste()
|
|
self.clipboard_timer.start(500) # 0.5초마다 클립보드 감지
|
|
else:
|
|
self.auto_translate_enabled = False
|
|
self.clipboard_timer.stop()
|
|
|
|
def monitor_clipboard(self):
|
|
if not self.auto_translate_enabled:
|
|
return
|
|
clipboard_text = pyperclip.paste()
|
|
if clipboard_text != self.last_clipboard_content:
|
|
self.last_clipboard_content = clipboard_text
|
|
self.schedule_translation()
|
|
|
|
def schedule_translation(self):
|
|
"""메인 스레드에서 번역 실행 예약."""
|
|
QTimer.singleShot(0, self.run_translation)
|
|
|
|
def run_translation(self):
|
|
clipboard_text = pyperclip.paste()
|
|
if not clipboard_text.strip():
|
|
self.show_popup("클립보드에 텍스트가 없습니다.")
|
|
return
|
|
|
|
results = []
|
|
for service in self.selected_services:
|
|
try:
|
|
result = service.translate(clipboard_text, "Korean")
|
|
service_name = service.__class__.__name__.replace("Translate", "")
|
|
if service_name in self.request_count:
|
|
self.request_count[service_name] += 1
|
|
results.append(f"[{service_name}]\n{result.result}\n\n")
|
|
except Exception as e:
|
|
results.append(f"[{service_name}] 번역 실패: {str(e)}")
|
|
|
|
self.show_popup("\n\n".join(results))
|
|
|
|
def show_popup(self, message):
|
|
popup = QMessageBox(self)
|
|
popup.setWindowTitle("번역 결과")
|
|
popup.setText(message)
|
|
popup.addButton("닫기(ESC)", QMessageBox.AcceptRole)
|
|
popup.show()
|
|
|
|
def eventFilter(self, source, event):
|
|
if event.type() == QEvent.KeyPress and event.key() == Qt.Key_Escape:
|
|
self.close()
|
|
return super().eventFilter(source, event)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
app = QApplication(sys.argv)
|
|
window = MultiTranslatorApp()
|
|
window.show()
|
|
sys.exit(app.exec())
|