from PyQt5.QtWidgets import QWidget, QPushButton, QVBoxLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy from PyQt5.QtCore import Qt, QRect, QSettings, QTimer from toggleSwitch import ToggleSwitch from browser_control import BrowserController from whale_translator import WhaleTranslator from clipboardImageManager import ClipboardImageManager from vertexAI import VertexAITranslator from option import OptionHandler from logger_module import QTextEditLogger # 추가 import logging class TranslationApp(QWidget): def __init__(self, logger=None): super().__init__() self.initUI() self.logger = logger key_path = 'leensoo1nt.json' self.settings = QSettings("WhenRideMycar", "TranslationApp") # QSettings 초기화 self.browser_controller = BrowserController(self, self.logger) self.whale_translator = WhaleTranslator(self, self.logger, secret_mode=True,vd_mode=True) # 디버그 모드 켜기 self.vertexAI = VertexAITranslator(self.logger, key_path) self.optionHandler = None self.clipboardImageManager = ClipboardImageManager(self, logger, self.browser_controller, debug=False) 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_stage_index = 0 # 현재 진행 중인 단계 인덱스 # 이전에 저장된 설정 불러오기 self.load_settings() # 로거 초기화 self.add_text_edit_logger() # 프로그래스바 초기화 self.update_total_progress(0,0) def add_text_edit_logger(self): """QTextEdit에 로그를 출력하기 위한 핸들러 추가""" text_edit_logger = QTextEditLogger() text_edit_logger.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) # text_edit_logger.appendHtml.connect(self.log.appendHtml) text_edit_logger.appendHtml.connect(self.log.append) # appendHtml 대신 append로 수정 text_edit_logger.scrollToBottom.connect(lambda: self.log.verticalScrollBar().setValue(self.log.verticalScrollBar().maximum())) self.logger.addHandler(text_edit_logger) self.logger.debug('로그기록이 설정되었습니다.') def start_stage(self, stage_index): """지정한 단계에 깜빡임 효과 적용""" if 0 <= stage_index < len(self.stage_labels): self.timer = QTimer(self) self.blink_status = True self.timer.timeout.connect(lambda: self.blink_stage(stage_index)) self.timer.start(500) # 0.5초 간격으로 깜빡임 def blink_stage(self, stage_index): """지정한 단계의 색상을 주기적으로 변경하여 깜빡임 효과를 적용""" label = self.stage_labels[stage_index] if self.blink_status: label.setStyleSheet("background-color: yellow; padding: 5px;") else: label.setStyleSheet("background-color: lightgray; padding: 5px;") self.blink_status = not self.blink_status def stop_blinking_effect(self): """깜빡임 효과 중지""" self.timer.stop() def complete_stage(self, stage_index): """단계 완료 시 깜빡임을 중지하고 완료 상태로 변경""" if 0 <= stage_index < len(self.stage_labels): self.stop_blinking_effect() label = self.stage_labels[stage_index] label.setStyleSheet("background-color: green; padding: 5px;") self.current_stage_index += 1 # 다음 단계로 이동하여 깜빡임 시작 if self.current_stage_index < len(self.stages): self.start_stage(self.current_stage_index) def initUI(self): self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setGeometry(QRect(1740, 500, 180, 400)) self.setWindowTitle('이미지 번역 도구') # 로그 self.log = QTextEdit(self) self.log.setReadOnly(True) # 전체 프로그레스바 self.total_progress_bar = QProgressBar(self) self.total_progress_bar.setValue(0) self.total_progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) # 스테이지 타임라인 self.stageTimeline_layout = QHBoxLayout() # self.stages = ["상품명", "옵션", "가격", "썸네일", "상페"] self.stages = ["옵션", "상페"] self.stage_labels = [] for stage in self.stages: self.stage_layout = QHBoxLayout() label = QLabel(stage) label.setStyleSheet("background-color: lightgray; padding: 5px;") self.stage_labels.append(label) self.stage_layout.addWidget(label) self.stageTimeline_layout.addLayout(self.stage_layout) # 디테일 프로그레스바 self.detail_progress_bar = QProgressBar(self) self.detail_progress_bar.setValue(0) self.detail_progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed) self.detail_progress_bar.setVisible(False) # 관리자 토글 self.admin_toggle = ToggleSwitch(self) self.admin_toggle.clicked.connect(self.on_toggle_clicked) # 관리자 ID 및 PW self.admin_id_label = QLabel("관리자 ID:", self) self.admin_id_input = QLineEdit(self) # 관리자 PW self.admin_pw_label = QLabel("관리자 PW:", self) self.admin_pw_input = QLineEdit(self) self.admin_pw_input.setEchoMode(QLineEdit.Password) # 직원 ID 및 PW self.user_id_label = QLabel("직원 ID:", self) self.user_id_input = QLineEdit(self) self.user_pw_label = QLabel("직원 PW:", self) self.user_pw_input = QLineEdit(self) self.user_pw_input.setEchoMode(QLineEdit.Password) # 크롬 실행 버튼 및 번역 버튼 self.start_chrome_button = QPushButton('크롬 실행', self) self.translate_button = QPushButton('번역 시작', self) self.pause_button = QPushButton('일시정지', self) self.exit_button = QPushButton('종료', self) # 버튼 크기를 1.5배로 설정 button_height = int(self.start_chrome_button.sizeHint().height() * 1.5) self.start_chrome_button.setFixedHeight(button_height) self.translate_button.setFixedHeight(button_height) self.pause_button.setFixedHeight(button_height) self.exit_button.setFixedHeight(button_height) # 메인 레이아웃 설정 self.main_layout = QVBoxLayout() # 관리자 토글 버튼 및 로그인 관련 필드 추가 self.toggle_layout = QHBoxLayout() self.toggle_layout.addWidget(QLabel("관리자 여부:", self)) self.toggle_layout.addWidget(self.admin_toggle) self.main_layout.addLayout(self.toggle_layout,1) # 관리자 ID self.main_layout.addWidget(self.admin_id_label) self.main_layout.addWidget(self.admin_id_input) # 관리자 PW self.admin_layout = QVBoxLayout() self.admin_layout.addWidget(self.admin_pw_label) self.admin_layout.addWidget(self.admin_pw_input) # 직원 ID/PW self.user_layout = QVBoxLayout() self.user_layout.addWidget(self.user_id_label) self.user_layout.addWidget(self.user_id_input) self.user_layout.addWidget(self.user_pw_label) self.user_layout.addWidget(self.user_pw_input) # 관리자와 직원 레이아웃을 메인 레이아웃에 추가 self.main_layout.addLayout(self.admin_layout,3) self.main_layout.addLayout(self.user_layout,3) # 크롬 및 번역 관련 버튼 self.button_layout = QHBoxLayout() self.button_layout.addWidget(self.start_chrome_button) self.button_layout.addWidget(self.translate_button) self.button_layout.addWidget(self.pause_button) self.button_layout.addWidget(self.exit_button) # 로그 및 프로그레스바 레이아웃 self.log_layout = QVBoxLayout() self.log_layout.addWidget(self.log) self.log_layout.addWidget(self.total_progress_bar) self.log_layout.addLayout(self.stageTimeline_layout) self.log_layout.addWidget(self.detail_progress_bar) # 메인 레이아웃에 버튼 레이아웃과 로그 레이아웃 추가 self.main_layout.addLayout(self.button_layout,2) self.main_layout.addLayout(self.log_layout,5) self.setLayout(self.main_layout) # 기본 상태 설정 self.on_toggle_clicked(False) # 버튼 이벤트 연결 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) def on_toggle_clicked(self, is_checked): """관리자 토글 상태에 따라 관리자와 직원 필드를 표시/숨김""" if is_checked: # 관리자 모드: 직원 레이아웃을 숨기고, 관리자 PW를 표시 self.set_layout_visibility(self.admin_layout, True) self.set_layout_visibility(self.user_layout, False) else: # 직원 모드: 관리자 PW를 숨기고, 직원 레이아웃을 표시 self.set_layout_visibility(self.admin_layout, False) self.set_layout_visibility(self.user_layout, True) def set_layout_visibility(self, changelayout, visible): """레이아웃에 포함된 모든 위젯의 가시성을 설정""" for i in range(changelayout.count()): widget = changelayout.itemAt(i).widget() if widget: widget.setVisible(visible) def start_browser(self): """크롬 브라우저 실행 후 로그인""" self.logger.debug('크롬 브라우저를 실행합니다...') 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() 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() self.browser_controller.login(admin_id, user_id, admin_pw, user_pw, is_admin=False) # 로그인 정보 저장 self.save_settings() # 옵션 핸들러 새로 초기화 self.optionHandler = OptionHandler(self.browser_controller.page, self.logger, self.vertexAI) def save_settings(self): """QSettings에 사용자 정보 저장""" self.settings.setValue("admin/id", self.admin_id_input.text()) self.settings.setValue("admin/pw", self.admin_pw_input.text()) self.settings.setValue("user/id", self.user_id_input.text()) self.settings.setValue("user/pw", self.user_pw_input.text()) self.settings.setValue("admin/toggle", self.admin_toggle.isChecked()) def load_settings(self): """QSettings에서 사용자 정보 불러오기""" self.admin_id_input.setText(self.settings.value("admin/id", "")) self.admin_pw_input.setText(self.settings.value("admin/pw", "")) self.user_id_input.setText(self.settings.value("user/id", "")) self.user_pw_input.setText(self.settings.value("user/pw", "")) admin_toggle_state = self.settings.value("admin/toggle", "false") == "true" self.admin_toggle.setChecked(admin_toggle_state) self.on_toggle_clicked(admin_toggle_state) def update_total_progress(self, current_value, total_value): if current_value == 0: self.total_progress_bar.setValue(0) self.total_progress_bar.setFormat("상품 수정 대기") # current_value가 0일 때 표시될 텍스트 else: # 프로그레스바의 값과 텍스트를 설정 percentage = int((current_value / total_value) * 100) self.total_progress_bar.setValue(percentage) self.total_progress_bar.setFormat(f"상품 {current_value}/{total_value}개 완료 [{percentage}%]") def update_detail_progress(self, current_value, total_value): if current_value == 0: self.detail_progress_bar.setValue(0) self.detail_progress_bar.setFormat("수정 대기") # current_value가 0일 때 표시될 텍스트 else: # 프로그레스바의 값과 텍스트를 설정 percentage = int((current_value / total_value) * 100) self.detail_progress_bar.setValue(percentage) self.detail_progress_bar.setFormat(f"{current_value}/{total_value}개 완료 [{percentage}%]") def start_translation(self): self.logger.debug('번역 작업을 시작합니다...') self.running = True # 번역 작업이 시작됨 try: # 1. "신규 상품 등록" 페이지로 이동 self.logger.debug('신규 상품 등록 페이지로 이동 중...') self.browser_controller.go_to_new_product_page() # 2. 총 상품 수 수집 self.browser_controller.scroll_page_to_bottom() total_products = self.browser_controller.get_total_product_count() if total_products == 0: self.logger.debug('수집할 상품이 없습니다. 작업을 종료합니다.') return self.total_progress_bar.setMaximum(total_products) self.total_progress_bar.setValue(0) completed_count = 0 self.update_total_progress(completed_count, total_products) page_number = 1 while self.running or completed_count < total_products: # 4. 현재 페이지의 모든 "세부사항 수정 및 업로드" 버튼 찾기 self.logger.debug(f'현재 페이지: {page_number}') if not page_number == 1: self.browser_controller.scroll_with_wheel("up") product_buttons = self.browser_controller.get_product_edit_buttons() if not product_buttons: self.logger.debug('수정할 상품이 없습니다. 번역 작업을 종료합니다.') break # 5. 각 상품에 대해 번역 작업 수행 for index, button in enumerate(product_buttons, start=1): if not self.running or completed_count >= total_products: self.logger.debug('번역 작업이 중단되었습니다.') break self.logger.debug(f'{index}/{len(product_buttons)}: 세부사항 수정 작업 중...') # 상품명 수집 및 수집 오류 처리 product_name = self.browser_controller.get_product_name(index) if product_name == "수집 오류 발생": self.logger.debug('상품 수집 오류, 다음 상품으로 넘어갑니다.') continue # 상품 수정 다이얼로그 열기 self.browser_controller.open_product_edit_dialog(button) # 옵션 수정 self.start_stage(0) self.edit_option() self.complete_stage(0) # 상세페이지 수정 self.start_stage(1) self.detail_trans() self.complete_stage(1) # 수정 후 저장 self.logger.debug('상품 세부사항 저장 중...') self.browser_controller.save_and_ecs_product_edit() completed_count += 1 self.update_total_progress(completed_count, total_products) self.logger.debug(f'{completed_count}/[{total_products}]개 상품 수정 완료.') # 모든 상품이 완료되었는지 체크 if completed_count >= total_products: self.logger.debug('모든 상품이 완료되었습니다.') break # 6. 다음 페이지로 이동 (있으면) if not self.browser_controller.go_to_next_page(): self.logger.debug('더 이상 페이지가 없습니다. 작업을 종료합니다.') break page_number += 1 if self.running: self.logger.debug('모든 상품 번역 및 저장 완료.') self.running = False # 작업 종료 후 상태를 False로 전환 except Exception as e: self.logger.debug(f"번역 작업 중 오류 발생: {e}", exc_info=True) self.running = False def pause_translation(self): self.logger.debug('번역 작업을 중단합니다...') self.running = False # 번역 작업 중단 def close(self): self.logger.debug('프로그램을 종료합니다...') self.save_settings() self.browser_controller.close_browser() # 브라우저 종료 self.whale_translator.close_all_virtual_desktops() super().close() def detail_trans(self): # 상세페이지 탭 클릭 self.browser_controller.click_detail_tab() self.detail_progress_bar.setValue(0) self.detail_progress_bar.setVisible(True) # 이미지 URL 추출 image_urls = self.browser_controller.extract_image_urls() total_images = len(image_urls) self.logger.debug(f"현재 상품의 총 이미지 수 : {total_images}개") self.detail_image_count += total_images # 이미지 번역 작업 진행 for i, url in enumerate(image_urls): current_image_count = i +1 if not self.running: self.logger.debug('번역 작업이 중단되었습니다.') break self.whale_translator.translate_image(url) self.browser_controller.paste_image_in_chrome(self.clipboardImageManager, url) self.update_detail_progress(i,total_images) current_image_count += 1 # 수정 후 저장 self.logger.debug('상품 세부사항 저장 중...') self.browser_controller.save_product_edit() self.detail_progress_bar.setVisible(False) self.detail_progress_bar.setValue(0) def edit_option(self): # 상세페이지 탭 클릭 self.browser_controller.click_option_tab() self.detail_progress_bar.setVisible(True) # 옵션 최대선택갯수 max_option_count = 10 self.optionHandler.process_options(max_option_count) # 수정 후 저장 self.optionHandler.save_option() self.detail_progress_bar.setVisible(False)