Thread OK

This commit is contained in:
R5600U_PC 2024-11-03 18:35:29 +09:00
parent ffe62c8ac3
commit 858217ffd7
4 changed files with 1093 additions and 237 deletions

File diff suppressed because it is too large Load Diff

300
gui2.py
View File

@ -1,5 +1,5 @@
from PySide6.QtWidgets import QInputDialog, QWidget, QSpinBox, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
from PySide6.QtCore import Qt, QRect, QSettings, QTimer, QThread, Signal, Slot
from PySide6.QtCore import Qt, Slot, QRect, QSettings, QTimer
from toggleSwitch import ToggleSwitch
from browser_control import BrowserController
from whale_translator import WhaleTranslator
@ -16,145 +16,21 @@ import logging
import asyncio, sys
import os, shutil, time
class PlaywrightWorker(QThread):
"""Playwright 작업을 담당하는 QThread"""
finished = Signal() # 작업 완료 시그널
def __init__(self, browser_controller, logger):
super().__init__()
self.browser_controller = browser_controller
self.logger = logger
async def run_playwright_task(self):
"""비동기 Playwright 작업을 실행"""
try:
await self.browser_controller.start_browser()
self.logger.info(f"브라우저 컨트롤러 스레드 완료")
"""크롬 브라우저 실행 후 로그인"""
self.logger.debug('크롬 브라우저를 실행합니다...')
# await self.whale_translator.start_whale_browser()
await self.browser_controller.start_browser()
# 관리자 토글 상태에 따라 로그인
if self.admin_toggle.isChecked():
admin_id = self.admin_id_input.text()
admin_pw = self.admin_pw_input.text()
user_id = self.user_id_input.text()
user_pw = self.user_pw_input.text()
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=True)
else:
admin_id = self.admin_id_input.text()
admin_pw = self.admin_pw_input.text()
user_id = self.user_id_input.text()
user_pw = self.user_pw_input.text()
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=False)
# 로그인 정보 저장
self.save_settings()
# "신규 상품 등록" 페이지로 이동
if self.toggle_states['ed_mode']:
await self.browser_controller.go_to_registered_product_page()
self.logger.info('등록 상품 관리 페이지로 이동 중...')
else:
self.logger.info('신규 상품 등록 페이지로 이동 중...')
await self.browser_controller.go_to_new_product_page()
# 각 핸들러에 초기화된 page 객체 전달.
self.optionHandler.update_page(self.browser_controller.page)
self.optionHandler.update_whale(self.whale_translator)
self.titleHandler.update_page(self.browser_controller.page)
self.priceHandler.update_page(self.browser_controller.page)
self.translate_button.setEnabled(True)
self.pause_button.setEnabled(True)
except Exception as e:
self.logger.error(f"Playwright 오류: {e}")
finally:
self.finished.emit() # 작업 완료 시그널 발생
def run(self):
"""스레드 내에서 비동기 작업 시작"""
asyncio.run(self.run_playwright_task())
async def stop_playwright(self):
"""Playwright 및 Whale 브라우저 종료"""
await self.browser_controller.close_browser()
if self.whale_translator:
self.whale_translator.close_all_virtual_desktops()
class AutoPercentyGUI(QWidget):
def __init__(self, logger=None, app=None):
super().__init__()
self.initUI()
self.app = app
self.logger = logger
self.debug = False
# key_path = 'leensoo1nt.json'
self.settings = QSettings("WhenRideMycar", "TranslationApp") # QSettings 초기화
self.locator_manager = LocatorManager()
# PlaywrightWorker 생성 및 browser_control 관리
self.browser_controller = BrowserController(self, self.logger, self.locator_manager)
self.playwright_worker = PlaywrightWorker(self, self.browser_controller, self.logger)
self.whale_translator = None
self.app = app
self.vertexAI = VertexAITranslator(self.logger)
self.optionHandler = None
# DB 파일 경로 설정
self.base_dir = self.get_base_dir()
self.user_db_path = os.path.join(self.base_dir, "userDB.db")
self.initial_db_path = os.path.join(self.base_dir, "src", "initialDB.db")
# userDB.db 생성 (없으면 initialDB.db 복사)
self.create_user_db_if_not_exists()
# # userDB.db 파일이 없으면 initialDB.db를 복사해서 생성
# if not os.path.exists(self.user_db_path):
# if os.path.exists(self.initial_db_path):
# shutil.copyfile(self.initial_db_path, self.user_db_path)
# print("initialDB.db를 userDB.db로 복사했습니다.")
# else:
# raise FileNotFoundError("initialDB.db 파일이 없습니다. 초기 DB 파일이 존재하는지 확인해주세요.")
# DatabaseManager 초기화
self.db_manager = DatabaseManager(db_url=f"sqlite:///{self.user_db_path}", logger=self.logger)
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.clipboardImageManager, self.logger, self.vertexAI, self.debug)
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
self.running = False
# 변수 설정
self.start_time = 0
self.finish_time = 0
self.total_product_count = 0
self.current_product_count = 0
self.title_count = 0
self.option_count = 0
self.price_count = 0
self.detail_image_count = 0
self.thumb_image_count = 0
self.current_options_info = {}
self.current_stage_index = 0 # 현재 진행 중인 단계 인덱스
self.login_infos={
'admin_id' : None,
'admin_pw' : None,
'user_id' : None,
'user_pw' : None,
'is_admin' : False,
}
# 토글 상태를 저장할 딕셔너리 초기화
self.toggle_states = {
@ -177,6 +53,46 @@ class AutoPercentyGUI(QWidget):
'max_option_count': 20, # 최대 선택가능한 옵션 수
}
self.settings = QSettings("WhenRideMycar", "TranslationApp") # QSettings 초기화
self.locator_manager = LocatorManager()
self.browser_controller = BrowserController(self, self.logger, self.locator_manager, self.login_infos, self.toggle_states)
self.vertexAI = VertexAITranslator(self.logger)
self.optionHandler = None
self.whale_translator = None
# DB 파일 경로 설정
self.base_dir = self.get_base_dir()
self.user_db_path = os.path.join(self.base_dir, "userDB.db")
self.initial_db_path = os.path.join(self.base_dir, "src", "initialDB.db")
# userDB.db 생성 (없으면 initialDB.db 복사)
self.create_user_db_if_not_exists()
# DatabaseManager 초기화
self.db_manager = DatabaseManager(db_url=f"sqlite:///{self.user_db_path}", logger=self.logger)
self.cmb_diag = CMBSettingsDialog(parent=self, logger=self.logger, db_manager=self.db_manager, initial_db_path=self.initial_db_path, user_db_path=self.user_db_path, debug=self.debug)
self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, watermark_font_size=36, debug=self.debug)
self.optionHandler = OptionHandler(self.locator_manager, self.browser_controller, self.whale_translator, self.clipboardImageManager, self.logger, self.vertexAI, self.debug)
self.priceHandler = PriceHandler(self.locator_manager, self.browser_controller, self.logger, self.optionHandler, self.vertexAI, self.cmb_diag, self.debug)
self.titleHandler = TitleHandler(self.locator_manager, self.browser_controller, self.logger)
self.running = False
# 변수 설정
self.start_time = 0
self.finish_time = 0
self.total_product_count = 0
self.current_product_count = 0
self.title_count = 0
self.option_count = 0
self.price_count = 0
self.detail_image_count = 0
self.thumb_image_count = 0
self.current_options_info = {}
self.current_stage_index = 0 # 현재 진행 중인 단계 인덱스
# 이전에 저장된 설정 불러오기
self.load_settings()
@ -186,11 +102,6 @@ class AutoPercentyGUI(QWidget):
# 프로그래스바 초기화
self.update_total_progress(0,0)
async def run_async_tasks(self):
"""비동기 작업을 실행"""
while True:
await asyncio.sleep(0.1) # 비동기적으로 잠시 대기하여 응답성을 유지
def get_base_dir(self):
"""
실행 환경에 따라 base_dir을 설정하는 메서드.
@ -266,7 +177,7 @@ class AutoPercentyGUI(QWidget):
def initUI(self):
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setGeometry(QRect(1240, 900, 280, 600))
self.setGeometry(QRect(500, 600, 380, 700))
self.setWindowTitle('AutoPecenty2')
# 로그
@ -531,21 +442,8 @@ class AutoPercentyGUI(QWidget):
self.log_layout.addLayout(self.stageTimeline_layout)
self.log_layout.addWidget(self.detail_progress_bar)
# # 토글 레이아웃
# self.toggle_layout = QGridLayout()
# self.toggle_layout.addWidget(self.title_toggle,0,0)
# self.toggle_layout.addWidget(self.optionTrnas_toggle,1,0)
# self.toggle_layout.addWidget(self.optionIMGTrans_toggle,2,0)
# self.toggle_layout.addWidget(self.optionAutoSelect_toggle,3,0)
# self.toggle_layout.addWidget(self.price_toggle,0,1)
# self.toggle_layout.addWidget(self.tag_toggle,1,1)
# self.toggle_layout.addWidget(self.thumb_toggle,2,1)
# self.toggle_layout.addWidget(self.detail_Option_toggle,3,1)
# self.toggle_layout.addWidget(self.detail_IMGTrans_toggle,0,2)
# self.toggle_layout.addWidget(self.debug_toggle,1,2)
# 메인 레이아웃에 버튼 레이아웃과 로그 레이아웃 추가
# self.main_layout.addLayout(self.admin_toggle_layout,2)
self.main_layout.addLayout(self.toggle_layout,2)
self.main_layout.addLayout(self.button_layout,2)
self.main_layout.addLayout(self.log_layout,5)
@ -556,22 +454,13 @@ class AutoPercentyGUI(QWidget):
self.on_admin_toggle_clicked(False)
# 버튼 이벤트 연결
# self.start_chrome_button.clicked.connect(self.start_browser)
self.start_chrome_button.clicked.connect(self.on_start_chrome_button_clicked)
# self.translate_button.clicked.connect(self.start_translation)
self.translate_button.clicked.connect(self.on_start_translation_button_clicked)
self.start_chrome_button.clicked.connect(self.start_browser)
self.translate_button.clicked.connect(self.start_translation)
self.pause_button.clicked.connect(self.pause_translation)
# self.exit_button.clicked.connect(self.close)
self.cmb_button.clicked.connect(self.on_cmb_button_clicked)
self.cmb_test_button.clicked.connect(self.on_cmb_test_button_clicked)
async def run_async_tasks(self):
"""비동기 작업을 실행"""
while True:
await asyncio.sleep(0.1) # 비동기적으로 잠시 대기하여 응답성을 유지
def load_toggle_settings(self):
"""QSettings에서 토글 상태 불러오기"""
for key in self.toggle_states.keys():
@ -732,22 +621,12 @@ class AutoPercentyGUI(QWidget):
if widget:
widget.setVisible(visible)
@Slot()
def on_start_chrome_button_clicked(self):
url = self.url_input.text()
if not url:
self.logger.info("URL을 입력하세요.")
return
self.logger.info(f"{url}로 연결 시도 중...")
# 기존 스레드가 실행 중이라면 종료 후 새로 시작
if self.playwright_worker and self.playwright_worker.isRunning():
self.playwright_worker.stop_browser()
# PlaywrightWorker 생성 및 실행
self.playwright_worker = PlaywrightWorker(self.browser_controller, url, self.logger)
self.playwright_worker.start()
async def on_close_button_clicked(self):
"""크롬 실행 버튼 클릭 시 호출"""
self.logger.debug('크롬 실행 버튼 클릭됨')
# 비동기 함수 실행을 위해 asyncio.create_task 사용
task = asyncio.create_task(self.close())
await task # 작업이 완료될 때까지 대기
def on_cmb_test_button_clicked(self, test_cat):
"""크무비 설정 실행 버튼 클릭 시 호출"""
@ -763,39 +642,32 @@ class AutoPercentyGUI(QWidget):
self.logger.debug('크무비 설정 버튼 클릭됨')
self.cmb_diag.show()
async def start_browser(self):
@Slot()
def start_browser(self):
"""크롬 브라우저 실행 후 로그인"""
self.logger.debug('크롬 브라우저를 실행합니다...')
# await self.whale_translator.start_whale_browser()
# self.logger.debug(f'self.browser_controller.page : {self.browser_controller.page}')
optionIMGTrans_status = self.toggle_states['optionIMGTrans']
detail_IMGTrans_status = self.toggle_states['detail_IMGTrans']
vd_mode_status = self.toggle_states['vd_mode']
await self.browser_controller.start_browser()
if optionIMGTrans_status or detail_IMGTrans_status:
self.logger.debug(f"optionIMGTrans_status : {optionIMGTrans_status}, detail_IMGTrans_status : {detail_IMGTrans_status}")
self.whale_translator = WhaleTranslator(self.app, self.logger, secret_mode=True, vd_mode=vd_mode_status) # 모드 켜기
self.whale_translator.start_whale_browser()
# 관리자 토글 상태에 따라 로그인
self.login_infos['admin_id'] = self.admin_id_input.text()
self.login_infos['admin_pw'] = self.admin_pw_input.text()
self.login_infos['user_id'] = self.user_id_input.text()
self.login_infos['user_pw'] = self.user_pw_input.text()
self.login_infos['is_admin'] = self.admin_toggle.isChecked()
self.browser_controller.whale_browser = self.whale_translator # whale_browser 설정
self.browser_controller.start()
if self.admin_toggle.isChecked():
admin_id = self.admin_id_input.text()
admin_pw = self.admin_pw_input.text()
user_id = self.user_id_input.text()
user_pw = self.user_pw_input.text()
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=True)
else:
admin_id = self.admin_id_input.text()
admin_pw = self.admin_pw_input.text()
user_id = self.user_id_input.text()
user_pw = self.user_pw_input.text()
await self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=False)
# 로그인 정보 저장
self.save_settings()
# "신규 상품 등록" 페이지로 이동
if self.toggle_states['ed_mode']:
await self.browser_controller.go_to_registered_product_page()
self.logger.info('등록 상품 관리 페이지로 이동 중...')
else:
self.logger.info('신규 상품 등록 페이지로 이동 중...')
await self.browser_controller.go_to_new_product_page()
# 각 핸들러에 초기화된 page 객체 전달.
self.optionHandler.update_page(self.browser_controller.page)
self.optionHandler.update_whale(self.whale_translator)
@ -852,12 +724,7 @@ class AutoPercentyGUI(QWidget):
self.detail_progress_bar.setValue(percentage)
self.detail_progress_bar.setFormat(f"{current_value}/{total_value}개 완료 [{percentage}%]")
def on_start_translation_button_clicked(self):
"""번역 작업 버튼 클릭 시 호출"""
self.logger.debug('번역 작업 버튼 클릭됨')
# 비동기 함수 실행을 위해 asyncio.create_task 사용
asyncio.create_task(self.start_translation())
@Slot()
async def start_translation(self):
self.logger.debug('번역 작업을 시작합니다...')
self.running = True # 번역 작업이 시작됨
@ -1007,13 +874,11 @@ class AutoPercentyGUI(QWidget):
self.logger.debug('번역 작업을 중단합니다...')
self.running = False # 번역 작업 중단
def close_app(self):
"""프로그램 종료 시 Playwright 스레드 및 리소스를 정리"""
if self.playwright_worker and self.playwright_worker.isRunning():
self.playwright_worker.stop_browser()
self.logger.info("프로그램 종료 중...")
self.save_settings()
self.close() # GUI 닫기
async def close(self):
"""종료 시 모든 자원 반환 및 Playwright 종료"""
await self.playwright_worker.stop_playwright()
self.logger.debug('프로그램을 종료합니다.')
super().close()
async def close(self):
self.logger.debug('프로그램을 종료합니다...')
@ -1023,7 +888,6 @@ class AutoPercentyGUI(QWidget):
self.whale_translator.close_all_virtual_desktops()
super().close()
async def detail_trans(self):
# 상세페이지 탭 클릭
await self.browser_controller.click_detail_tab()

View File

@ -3,7 +3,7 @@ import os
from logging.handlers import RotatingFileHandler
from PyQt5.QtCore import pyqtSignal, QObject
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=10):
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=5):
"""로거 설정을 위한 함수"""
formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(name)s - %(levelname)s - %(message)s')

View File

@ -9,6 +9,7 @@ import KO_EN
import pyperclip # 클립보드 데이터를 확인하기 위한 라이브러리
from PIL import ImageGrab
import re
import psutil
class WhaleTranslator:
def __init__(self, app, logger, secret_mode=True, vd_mode=False, pixel_check_interval=0.1, timeout=10, color_tolerance=20):
@ -490,7 +491,32 @@ class WhaleTranslator:
time.sleep(1) # 페이지 로딩 대기
def close_whale_window_if_exists(self):
"""윈도우 핸들을 사용하여 웨일 창을 닫음"""
try:
if not self.whale_hwnd:
self.logger.debug("웨일 창 핸들이 설정되지 않았습니다.")
return
# 핸들이 유효한지 확인
if win32gui.IsWindow(self.whale_hwnd):
self.logger.debug(f"웨일 창 핸들을 찾았습니다: {self.whale_hwnd}. 종료 중...")
win32gui.PostMessage(self.whale_hwnd, win32con.WM_CLOSE, 0, 0) # 창을 종료하는 메시지 전송
time.sleep(1) # 잠시 대기하여 창 종료 확인
if not win32gui.IsWindow(self.whale_hwnd):
self.logger.debug("웨일 창이 성공적으로 종료되었습니다.")
else:
self.logger.debug("웨일 창 종료에 실패했습니다.")
else:
self.logger.debug("유효하지 않은 웨일 창 핸들입니다.")
except Exception as e:
self.logger.error(f"핸들을 사용한 웨일 창 종료 중 오류 발생: {e}", exc_info=True)
def close_whale_window_if_exists_ori(self):
"""웨일 브라우저 창을 프로세스 ID(pid)로 찾아 종료"""
try:
if not self.whale_pid: