SellFree_MultiTrans/main2.py

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())