254 lines
10 KiB
Python
254 lines
10 KiB
Python
import pyautogui
|
|
import ctypes
|
|
import time
|
|
import win32gui, win32con, win32process
|
|
from pyvda import VirtualDesktop, get_virtual_desktops
|
|
import subprocess
|
|
import asyncio
|
|
|
|
class WhaleTranslator:
|
|
def __init__(self, app, logger, secret_mode=True, vd_mode=False):
|
|
self.app = app
|
|
self.logger = logger
|
|
self.vd_mode = vd_mode
|
|
self.newtab = "about:newtab"
|
|
self.whale_pid = None
|
|
isSecret = secret_mode
|
|
|
|
if isSecret:
|
|
self.whale_window_name = "새 시크릿 탭 - Whale"
|
|
else:
|
|
self.whale_window_name = "새 탭 - Whale"
|
|
|
|
self.whale_hwnd = None
|
|
|
|
async def start_whale_browser(self):
|
|
"""비동기 브라우저 시작 및 가상 데스크탑 처리"""
|
|
if self.vd_mode:
|
|
self.ensure_virtual_desktop_2_exists() # 가상 데스크탑 2 생성
|
|
|
|
# Whale 브라우저 실행
|
|
whale_path = r"C:\\Program Files\\Naver\\Naver Whale\\Application\\whale.exe"
|
|
process = subprocess.Popen([whale_path, '--incognito'])
|
|
self.whale_pid = process.pid
|
|
self.logger.debug(f"Whale 브라우저 실행, PID: {self.whale_pid}")
|
|
|
|
await asyncio.sleep(2) # 브라우저가 실행될 때까지 대기
|
|
|
|
hwnd = win32gui.FindWindow(None, self.whale_window_name)
|
|
if hwnd:
|
|
win32gui.ShowWindow(hwnd, win32con.SW_NORMAL)
|
|
win32gui.SetWindowPos(hwnd, None, 0, 0, 1920, 1080, win32con.SWP_NOZORDER)
|
|
else:
|
|
self.logger.debug("Whale 창을 찾을 수 없습니다.")
|
|
|
|
if self.vd_mode:
|
|
self.return_to_virtual_desktop_1() # 가상 데스크탑 1로 복귀
|
|
|
|
def find_window_by_pid(self, pid):
|
|
"""프로세스 ID를 기반으로 창 핸들을 찾는 메서드"""
|
|
def enum_windows_callback(hwnd, pid_list):
|
|
tid, found_pid = win32process.GetWindowThreadProcessId(hwnd)
|
|
if found_pid == pid:
|
|
pid_list.append(hwnd)
|
|
|
|
hwnd_list = []
|
|
win32gui.EnumWindows(enum_windows_callback, hwnd_list)
|
|
return hwnd_list[0] if hwnd_list else None
|
|
|
|
def find_whale_window(self):
|
|
"""프로세스 ID를 기반으로 웨일 창 핸들을 찾는 메서드"""
|
|
if not self.whale_hwnd:
|
|
self.whale_hwnd = self.find_window_by_pid(self.whale_pid)
|
|
return self.whale_hwnd
|
|
|
|
# def find_whale_window(self):
|
|
# """웨일 창 핸들을 찾는 메서드"""
|
|
# if not self.whale_hwnd:
|
|
# self.whale_hwnd = self.find_window_by_title(self.whale_window_name)
|
|
# return self.whale_hwnd
|
|
|
|
# def find_window_by_title(self, window_name):
|
|
# def enum_windows_callback(hwnd, result):
|
|
# if win32gui.IsWindowVisible(hwnd) and window_name in win32gui.GetWindowText(hwnd):
|
|
# result.append(hwnd)
|
|
# result = []
|
|
# win32gui.EnumWindows(enum_windows_callback, result)
|
|
# return result[0] if result else None
|
|
|
|
def ensure_virtual_desktop_2_exists(self):
|
|
"""가상 데스크톱 2가 존재하는지 확인하고, 없으면 생성"""
|
|
try:
|
|
# 현재 활성화된 가상 데스크톱 수 확인
|
|
desktops = get_virtual_desktops()
|
|
number_of_desktops = len(desktops)
|
|
|
|
# 가상 데스크톱 2가 존재하지 않으면 생성
|
|
if number_of_desktops < 2:
|
|
pyautogui.hotkey('win', 'ctrl', 'd') # 새 가상데스크탑 생성
|
|
self.logger.debug("가상 데스크톱 2가 생성되었습니다.")
|
|
time.sleep(1)
|
|
else:
|
|
self.switch_to_virtual_desktop_2()
|
|
self.close_whale_window_if_exists()
|
|
self.logger.debug("가상 데스크톱 2가 이미 존재합니다.")
|
|
|
|
except Exception as e:
|
|
self.logger.debug(f"가상 데스크톱 확인/생성 중 오류 발생: {e}", exc_info=True)
|
|
|
|
def switch_to_whale(self):
|
|
"""웨일로 포커스 전환"""
|
|
|
|
if self.whale_hwnd:
|
|
win32gui.ShowWindow(self.whale_hwnd, win32con.SW_RESTORE)
|
|
win32gui.SetForegroundWindow(self.whale_hwnd)
|
|
self.logger.debug('크롬 창으로 포커스 이동.')
|
|
else:
|
|
self.logger.debug('크롬 창을 찾을 수 없습니다.')
|
|
|
|
def switch_to_virtual_desktop_2(self):
|
|
"""가상 데스크톱 2로 전환"""
|
|
try:
|
|
VirtualDesktop(2).go()
|
|
self.logger.debug("가상 데스크톱 2로 전환되었습니다.")
|
|
time.sleep(0.3)
|
|
except Exception as e:
|
|
self.logger.debug(f"가상 데스크톱 전환 중 오류 발생: {e}", exc_info=True)
|
|
|
|
def return_to_virtual_desktop_1(self):
|
|
"""가상 데스크톱 1로 복귀"""
|
|
try:
|
|
VirtualDesktop(1).go()
|
|
self.logger.debug("가상 데스크톱 1로 전환되었습니다.")
|
|
time.sleep(0.3)
|
|
except Exception as e:
|
|
self.logger.debug(f"가상 데스크톱 전환 중 오류 발생: {e}", exc_info=True)
|
|
|
|
async def translate_image(self, url):
|
|
if self.vd_mode:
|
|
await self.switch_to_virtual_desktop_2()
|
|
|
|
if self.find_whale_window():
|
|
win32gui.ShowWindow(self.whale_hwnd, win32con.SW_RESTORE) # 웨일 창 활성화
|
|
win32gui.SetForegroundWindow(self.whale_hwnd)
|
|
|
|
pyautogui.moveTo(960,580) # 마우스 센터로 이동
|
|
|
|
pyautogui.hotkey('ctrl', 'l') # 웨일 브라우저의 주소창으로 이동
|
|
self.enter_url(url)
|
|
await asyncio.sleep(1) # 페이지 로딩 대기
|
|
|
|
# pyautogui.rightClick()
|
|
# await asyncio.sleep(0.2) # 페이지 로딩 대기
|
|
# pyautogui.press('c') # 원본 이미지 클립보드에 복사
|
|
# await asyncio.sleep(1) # 페이지 로딩 대기
|
|
|
|
pyautogui.rightClick()
|
|
await asyncio.sleep(0.2) # 페이지 로딩 대기
|
|
pyautogui.press('r') # 번역 클릭
|
|
await asyncio.sleep(7) # 페이지 로딩 대기
|
|
|
|
pyautogui.rightClick()
|
|
await asyncio.sleep(0.2) # 페이지 로딩 대기
|
|
pyautogui.press('c') # 번역된 이미지 클립보드에 복사
|
|
pyautogui.hotkey('ctrl', 'l') # 새 탭으로 이동
|
|
pyautogui.typewrite(self.newtab) # URL을 입력
|
|
self.enter_url(self.newtab)
|
|
self.logger.debug(f'번역 완료: {url}')
|
|
|
|
if self.vd_mode:
|
|
await self.return_to_virtual_desktop_1()
|
|
else:
|
|
self.logger.debug('웨일 창을 찾을 수 없습니다.')
|
|
|
|
def switch_language(self):
|
|
# Shift 키 누름
|
|
pyautogui.keyDown('shift')
|
|
|
|
# 0.1초 대기 후 Alt 키 누름
|
|
time.sleep(1)
|
|
pyautogui.keyDown('alt')
|
|
|
|
# 0.5초 대기
|
|
time.sleep(1)
|
|
|
|
# Alt 키 해제
|
|
pyautogui.keyUp('alt')
|
|
|
|
# Shift 키 해제
|
|
pyautogui.keyUp('shift')
|
|
|
|
|
|
def set_input_language(self, lang='EN'):
|
|
# 윈도우에서 입력 언어를 변경하는 코드
|
|
# lang='EN' 또는 lang='KR'을 설정 가능
|
|
user32 = ctypes.WinDLL('user32', use_last_error=True)
|
|
if lang == 'EN':
|
|
# 영어로 변경
|
|
user32.LoadKeyboardLayoutW('00000409', 1)
|
|
elif lang == 'KR':
|
|
# 한글로 변경
|
|
user32.LoadKeyboardLayoutW('00000412', 1)
|
|
|
|
def enter_url(self, url):
|
|
|
|
# 주소창에 URL 입력하기 전에 영어로 변경
|
|
# self.set_input_language('EN')
|
|
|
|
# await asyncio.sleep(0.5) # 입력 모드 전환 후 잠시 대기
|
|
|
|
# 주소창으로 이동 후 URL 입력
|
|
pyautogui.hotkey('ctrl', 'l') # 주소창으로 이동
|
|
time.sleep(0.5)
|
|
pyautogui.typewrite(url) # URL 입력
|
|
pyautogui.press('enter',) # Enter 키 입력
|
|
time.sleep(1) # 페이지 로딩 대기
|
|
|
|
|
|
def close_whale_window_if_exists(self):
|
|
"""웨일 브라우저 창을 프로세스 ID(pid)로 찾아 종료"""
|
|
try:
|
|
if not self.whale_pid:
|
|
self.logger.debug("웨일 프로세스 ID가 설정되지 않았습니다.")
|
|
return
|
|
|
|
def enum_windows_callback(hwnd, result):
|
|
_, found_pid = win32process.GetWindowThreadProcessId(hwnd)
|
|
if found_pid == self.whale_pid and win32gui.IsWindowVisible(hwnd):
|
|
result.append(hwnd)
|
|
|
|
result = []
|
|
win32gui.EnumWindows(enum_windows_callback, result)
|
|
|
|
if result:
|
|
hwnd = result[0]
|
|
self.logger.debug(f"웨일 창을 찾았습니다. 핸들: {hwnd}. 종료 중...")
|
|
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0) # 창을 종료하는 메시지 전송
|
|
time.sleep(1)
|
|
self.logger.debug(f"웨일 창이 성공적으로 종료되었습니다.")
|
|
else:
|
|
self.logger.debug("웨일 창을 찾을 수 없습니다.")
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"웨일 창을 종료하는 중 오류 발생: {e}", exc_info=True)
|
|
|
|
def close_all_virtual_desktops(self):
|
|
"""모든 가상 데스크톱을 종료"""
|
|
try:
|
|
desktops = get_virtual_desktops()
|
|
number_of_desktops = len(desktops)
|
|
|
|
# 가상 데스크톱이 1개 이상일 때만 종료
|
|
while number_of_desktops > 1:
|
|
self.close_whale_window_if_exists() # 웨일 브라우저 창이 있으면 종료
|
|
pyautogui.hotkey('win', 'ctrl', 'f4') # 현재 가상 데스크톱 닫기
|
|
time.sleep(1) # 각 데스크톱 닫기 사이에 짧은 대기시간 추가
|
|
desktops = get_virtual_desktops()
|
|
number_of_desktops = len(desktops)
|
|
self.logger.debug(f"남은 가상 데스크톱 수: {number_of_desktops}")
|
|
|
|
self.logger.debug("모든 가상 데스크톱이 종료되었습니다.")
|
|
|
|
except Exception as e:
|
|
self.logger.debug(f"가상 데스크톱 종료 중 오류 발생: {e}", exc_info=True)
|