This commit is contained in:
Envy_PC 2024-10-29 13:21:19 +09:00
parent 0d4315431d
commit 1de79c6fe0
6 changed files with 29772 additions and 19806 deletions

File diff suppressed because one or more lines are too long

View File

@ -32,18 +32,23 @@ image_selector_template = 'div#productMainContentContainerId li:nth-child({index
price_selector_template = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[3]/div[1]/div[2]/button/span/sup'
# 옵션 상자
option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc'
excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn'
delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")'
; option_box_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc'
option_box_selector = '//*[@id="productMainContentContainerId"]/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[{index}]/div/div[1]/div/div[2]/div'
; excluded_option_marker = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dfauwV.bXsMpn'
excluded_option_marker = '.bXsMpn.sc-dfauwV'
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) span:has-text("삭제")'
; delete_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) div.sc-igZIGL.kQDmyq'
delete_button_selector = '.kQDmyq.sc-igZIGL'
confirm_delete_button_selector = '.ant-modal.css-1li46mu.ant-modal-confirm.ant-modal-confirm-confirm button:has-text("삭제")'
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb'
add_button_selector = 'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div'
add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-dRGYJT.hmQUGb'
; add_button_selector = 'div.hmQUGb.sc-dRGYJT'
; add_button_selector = 'div#productMainContentContainerId div.lesrZh.sc-bYHUQc:nth-child({index}) > .sc-krITIZ.ckztYT'
; file_upload_button_selector = '.ant-modal-content button:has-text("클릭 or 드레그로 파일 업로드")'
file_upload_button_selector = 'div.ant-modal-body > span > div > span > div'
file_upload_button_selector = 'span.ant-upload-btn input[type="file"]'
; confirm_upload_button_selector = '.ant-modal-content button:has-text("이미지 삽입")'
confirm_upload_button_selector = 'div.ant-modal-footer > button[type=\"button\"].ant-btn.css-1li46mu.ant-btn-primary'
confirm_upload_button_selector = 'button.ant-btn-primary span:text('이미지 삽입')'
low_order_button_locator = 'button:has-text("가격 낮은 순")'
AtoZ_button_locator = 'button:has-text("A-Z")'

19
gui.py
View File

@ -1,4 +1,4 @@
from PySide6.QtWidgets import QInputDialog, QWidget, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
from PySide6.QtWidgets import QInputDialog, QWidget, QSpinBox, QPushButton, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy
from PySide6.QtCore import Qt, QRect, QSettings, QTimer
from toggleSwitch import ToggleSwitch
from browser_control import BrowserController
@ -94,6 +94,7 @@ class TranslationApp(QWidget):
'watermark': False, # 워터마크 토글 추가
'watermark_text': "WaterMark", # 워터마크 텍스트 저장
'opacity_percent': 25, # 워터마크 투명도
'max_option_count': 20, # 최대 선택가능한 옵션 수
}
# 이전에 저장된 설정 불러오기
@ -332,6 +333,15 @@ class TranslationApp(QWidget):
# 확인 버튼 클릭 시 watermark_text 업데이트
self.watermark_confirm_button.clicked.connect(self.update_watermark_text)
# 최대 옵션수
self.max_option_count_label = QLabel("최대옵션수", self)
self.max_option_count_input = QSpinBox(self)
self.max_option_count_input.setMinimum(0) # 최소값 0
self.max_option_count_input.setMaximum(100) # 최대값 100
self.max_option_count_input.setValue(20) # 기본값 0
self.max_option_count_input.setToolTip("0으로 설정시 최대") # 툴팁 설정
self.max_option_count_input.valueChanged.connect(self.update_max_option_count) # 값 변경 시 update_max_option_count 메서드 호출
# 워터마크 관련 요소들을 하나의 QHBoxLayout에 추가 (비율 2:3:1)
watermark_layout = QHBoxLayout()
watermark_layout.addWidget(self.watermark_text_label, 2)
@ -340,6 +350,8 @@ class TranslationApp(QWidget):
# 필요한 레이아웃에 추가 (toggle_layout에 추가)
self.toggle_layout.addLayout(watermark_layout, 7, 0, 1, 4)
self.toggle_layout.addWidget(self.max_option_count_label, 8, 0)
self.toggle_layout.addWidget(self.max_option_count_input, 8, 1)
# 초기에는 워터마크 입력창과 버튼 숨김
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.watermark_confirm_button, None)])
@ -591,6 +603,11 @@ class TranslationApp(QWidget):
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
self.logger.debug(f"Updated watermark text: {self.toggle_states['watermark_text']}")
def update_max_option_count(self):
"""QSpinBox에 입력된 값을 toggle_states['max_option_count']에 저장"""
self.toggle_states['max_option_count'] = self.max_option_count_input.value() # 정수 값 가져오기
self.logger.debug(f"최대 선택 가능 옵션 수 업데이트: {self.toggle_states['max_option_count']}")
def on_admin_toggle_clicked(self, is_checked):
"""관리자 토글 상태에 따라 관리자와 직원 필드를 표시/숨김"""
if is_checked:

126
option.py
View File

@ -207,7 +207,7 @@ class OptionHandler:
return self.option_info
# 3. 가격 낮은 순 정렬 클릭
await self.low_order_click()
# await self.low_order_click()
try:
self.logger.info(f"옵션 정보 수집")
@ -620,8 +620,44 @@ class OptionHandler:
except Exception as e:
self.logger.error(f"옵션 필터링 및 조정 중 오류 발생: {e}", exc_info=True)
async def adjust_options(self, filtered_option_names, max_option_count):
"""
필터링된 옵션에 맞게 체크박스 상태를 조정하는 메서드.
:param filtered_option_names: 필터링된 옵션 리스트
:param max_option_count: 최대 선택 가능한 옵션 (0이면 제한 없음)
"""
try:
selected_count = 0 # 현재까지 선택된 옵션 수
for i, name in enumerate(self.option_info['original_names'].values()):
# 최대 옵션 수에 도달하면 더 이상 선택하지 않음
if max_option_count > 0 and selected_count >= max_option_count:
break
checkbox_selector = self.checkbox_selector_template.format(index=i+1)
checkbox_element = await self.page.query_selector(checkbox_selector)
# 디버깅 로그: 현재 옵션 이름과 필터링된 옵션 이름 확인
self.logger.debug(f"옵션 이름: {name}, 필터링된 옵션에 포함 여부: {name in filtered_option_names}")
if checkbox_element:
# 필터링된 옵션에 포함되고, 선택 가능한 수량 내라면 선택
if name in filtered_option_names and (max_option_count == 0 or selected_count < max_option_count):
await checkbox_element.click()
self.logger.debug(f"옵션 '{name}' 체크함")
self.option_info['checked_states'][name] = True
selected_count += 1
# 필터링된 옵션에 포함되지 않으면 선택 해제
else:
await checkbox_element.click()
self.logger.debug(f"옵션 '{name}' 체크 해제함")
self.option_info['checked_states'][name] = False
self.logger.debug(f"옵션 체크 상태 조정 완료. 선택된 옵션 수: {selected_count}/{max_option_count if max_option_count > 0 else '무제한'}")
except Exception as e:
self.logger.error(f"옵션 체크 상태 조정 중 오류 발생: {e}", exc_info=True)
async def adjust_options_without_max_count(self, filtered_option_names, max_option_count):
"""
필터링된 옵션에 맞게 체크박스 상태를 조정하는 메서드.
:param filtered_options: 필터링된 옵션 리스트
@ -680,34 +716,38 @@ class OptionHandler:
try:
# 모든 옵션 상자 요소 가져오기
option_boxes = await self.page.query_selector_all(self.option_box_selector)
# option_boxes = await self.page.query_selector_all(self.option_box_selector)
option_boxes = await self.page.query_selector_all("div#productMainContentContainerId li > div > div:nth-child(1) > div > div:nth-child(2) > div")
# option_image_element = await self.page.locator("xpath=//*[@id='productMainContentContainerId']/div[1]/div[2]/div/div/div[2]/div/div[1]/div/div/div[2]/div/div/div[5]/div[1]/div/div/ul/li[1]/div/div[1]/div/div[2]/div/img").element_handle()
total_options = len(option_boxes)
self.logger.debug(f"{total_options}개의 옵션 이미지 번역을 시작합니다.")
# 각 옵션 상자를 인덱스로 순회
for index in range(1, total_options + 1):
# 실제 옵션 이미지가 존재하는 항목에만 인덱스를 적용하기 위해 별도 카운터 사용
translated_index = 1
# 각 옵션 상자를 순회
for index, option_box in enumerate(option_boxes, start=1):
# 선택자에서 인덱스를 반영해 동적 선택자를 생성
excluded_marker_selector = self.excluded_option_marker.format(index=index)
delete_button_selector = self.delete_button_selector.format(index=index)
add_button_selector = self.add_button_selector.format(index=index)
# 제외된 옵션 확인
excluded_marker = await self.page.query_selector(excluded_marker_selector)
if excluded_marker:
self.logger.debug(f"{index}번째 옵션은 제외된 옵션입니다. 번역을 생략합니다.")
continue
# 옵션 이미지 URL 가져오기
option_image = await option_boxes[index - 1].query_selector("img")
if not option_image:
# 옵션 이미지가 존재하는지 확인
option_image = await option_box.query_selector("img")
if option_image is None:
self.logger.debug(f"{index}번째 옵션에 이미지가 없습니다. 다음 옵션으로 이동합니다.")
continue
option_image_url = await option_image.get_attribute("src")
self.logger.debug(f"{index}번째 옵션 이미지 URL: {option_image_url}")
# 이미지가 SVG 형식일 경우 번역을 건너뜀
if option_image_url.endswith(".svg"):
self.logger.debug(f"{index}번째 옵션은 SVG 이미지입니다. 번역을 생략합니다.")
continue
# 이미지 번역 및 업로드
translated_image_path = os.path.join(temp_dir, f"translated_option_{index}.png") # 이미지 저장 경로 설정
translated_image_path = os.path.join(temp_dir, f"translated_option_{translated_index}.png") # 이미지 저장 경로 설정
try:
# 이미지 번역
@ -715,10 +755,14 @@ class OptionHandler:
is_success_translated = self.whale_translator.translate_image(option_image_url)
self.clipboardImageManager.process_clipboard(option_image_url, is_success_translated, toggle_states, translated_image_path)
self.logger.debug(f"{index}번째 옵션의 번역 이미지 저장 완료: {translated_image_path}")
self.browser_controller.switch_to_chrome() # 크롬으로 포커스 이동
if is_success_translated and os.path.exists(translated_image_path):
# 삭제 버튼 클릭
delete_button = await self.page.query_selector(delete_button_selector)
# delete_button = await self.page.query_selector(delete_button_selector)
delete_button = await option_box.query_selector(".sc-igZIGL.kQDmyq")
self.logger.debug(f"{index}번째 옵션의 이미지 삭제 버튼 가져오기")
if delete_button:
@ -729,23 +773,35 @@ class OptionHandler:
if confirm_delete_button:
await confirm_delete_button.click()
self.logger.debug(f"{index}번째 옵션의 기존 이미지가 삭제되었습니다.")
# '+ 버튼' 클릭 후 파일 업로드
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기")
add_button = await self.page.query_selector(add_button_selector)
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 : {add_button}")
if add_button:
await add_button.click()
self.logger.debug(f"{index}번째 옵션의 이미지 추가 버튼 클릭")
file_upload_button = await self.page.wait_for_selector(self.file_upload_button_selector)
self.logger.debug(f"{index}번째 옵션의 파일업로드 버튼 가져오기")
await file_upload_button.set_input_files(translated_image_path)
self.logger.debug(f"{index}번째 옵션의 파일 업로드")
# '+ 버튼' 클릭 후 파일 업로드
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 가져오기")
# add_button = await self.page.query_selector(add_button_selector)
add_button = option_box
# add_button = await option_box.query_selector('.//div[contains(@class, "sc-krITIZ") and contains(@class, "ckztYT")]')
# add_button = await option_box.query_selector(f'div#productMainContentContainerId li:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div')
# add_button = await option_box.query_selector(f'div:nth-child({index}) > div > div:nth-child(1) > div > div:nth-child(2) > div > div')
if add_button:
await add_button.click()
self.logger.debug(f"{index}번째 옵션의 이미지추가 버튼 클릭")
# 파일 업로드 영역의 input 요소 직접 선택 (수정된 부분)
file_input = await self.page.query_selector(self.file_upload_button_selector) # Ant Design의 클래스 사용
if file_input:
# Playwright의 set_input_files를 사용하여 파일 업로드 처리
await file_input.set_input_files(translated_image_path)
self.logger.debug(f"{index}번째 옵션의 파일 업로드 완료")
# '이미지 삽입' 버튼 클릭
confirm_upload_button = await self.page.wait_for_selector(self.confirm_upload_button_selector)
await confirm_upload_button.click()
self.logger.debug(f"{index}번째 옵션에 이미지가 업로드되었습니다.")
else:
self.logger.error(f"{index}번째 옵션의 파일 입력 요소를 찾을 수 없습니다.")
# '이미지 삽입' 버튼 클릭
confirm_upload_button = await self.page.wait_for_selector(self.confirm_upload_button_selector)
await confirm_upload_button.click()
self.logger.debug(f"{index}번째 옵션에 이미지가 업로드되었습니다.")
finally:
# 파일 사용 후 0.5초 대기하여 접근 완료 보장
time.sleep(0.5)
@ -753,6 +809,8 @@ class OptionHandler:
if not debug_flag and os.path.exists(translated_image_path):
os.remove(translated_image_path)
self.logger.debug(f"{index}번째 옵션의 임시 번역 이미지 파일 삭제 완료: {translated_image_path}")
# 실제 번역이 완료된 경우에만 인덱스 증가
translated_index += 1
except Exception as e:
self.logger.error(f"옵션 이미지 업데이트 중 오류 발생: {e}", exc_info=True)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 KiB