1623 lines
77 KiB
Python
1623 lines
77 KiB
Python
from PySide6.QtWidgets import QApplication, QInputDialog, QWidget, QMessageBox, QSpinBox, QPushButton, QGroupBox, QFormLayout, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy, QComboBox
|
|
from PySide6.QtCore import Qt, Signal, Slot, QRect, QSettings, QTimer, QThreadPool
|
|
from toggleSwitch import ToggleSwitch
|
|
from browser_control import BrowserController
|
|
# from whale_translator import WhaleTranslator
|
|
# from vertexAI import VertexAITranslator
|
|
from locatorManager_by_SP import LocatorManager
|
|
|
|
from src.cmdDiag.cmb_diag import CMBSettingsDialog
|
|
from src.cmdDiag.cmb_DB_Manager import CMB_Database_Manager
|
|
|
|
|
|
from src.priceSetDiag.priceSettingManager import PriceSettingManager
|
|
|
|
|
|
|
|
from src.inputDiag.detail_Input_Diag import DetailTextEditor
|
|
import logging
|
|
import sys
|
|
import os, shutil, time
|
|
import asyncio
|
|
from datetime import datetime
|
|
|
|
# from src.keyword_manager import KeywordManager
|
|
# from src.kwDBManager_with_sp import KeywordDBManager
|
|
# from src.kiprisAPI import Kipris_API
|
|
|
|
from src.keyword.db_manager import DBManager
|
|
from src.keyword.keyword_manager import KeywordManager
|
|
from src.keyword.Kipris_for_web import MarkInfoScraper
|
|
|
|
from src.discord_manager import DiscordManager
|
|
|
|
# from src.sp_manager import SupabaseManager
|
|
from user_manual_dialog import UserManualDialog
|
|
import configparser
|
|
|
|
class AutoPercentyGUI(QWidget):
|
|
|
|
def __init__(self, logger, user_info, supabase_manager, update_log="", app=None):
|
|
"""
|
|
:param user: 로그인 후 SupabaseManager.login 또는 register 로부터 전달받은 사용자 정보 (dict)
|
|
"""
|
|
super().__init__()
|
|
self.logger = logger
|
|
|
|
self.initial_setting = False
|
|
|
|
|
|
# 사용자 정보 저장 (user_id 등)
|
|
self.user_info = user_info
|
|
self.sp_user_id = user_info.get("id") # 예를 들어, 'users' 테이블의 PK 값
|
|
|
|
|
|
# 디스코드 관리자 초기화
|
|
self.discord_manager = DiscordManager(self.logger)
|
|
self.job_start_time = None
|
|
self.supabase_manager = supabase_manager
|
|
self.initUI()
|
|
|
|
self.supabase_manager.increment_concurrent(self.sp_user_id)
|
|
|
|
self.logger.log(f"로그기록이 설정되었습니다.", level=logging.INFO)
|
|
|
|
# update_log 인자가 있다면 표시
|
|
if update_log:
|
|
QMessageBox.information(self, "업데이트 변경사항", update_log)
|
|
|
|
self.debug = False
|
|
|
|
self.settings = QSettings("WhenRideMycar", "AutoPercenty3") # QSettings 초기화
|
|
self.locator_manager = LocatorManager(self.supabase_manager)
|
|
# self.vertexAI = VertexAITranslator(self.logger)
|
|
|
|
|
|
# DB 파일 경로 설정
|
|
self.base_dir = self.get_base_dir()
|
|
self.base_db_dir = os.path.join(self.base_dir, "user_data")
|
|
self.logger.log(f"base_db_dir 경로: {self.base_db_dir}", level=logging.DEBUG)
|
|
|
|
# 폴더가 존재하는지 확인하고 없으면 생성
|
|
if not os.path.exists(self.base_db_dir):
|
|
os.makedirs(self.base_db_dir)
|
|
self.logger.log(f"DB 폴더 생성됨: {self.base_db_dir}", level=logging.INFO)
|
|
self.initial_setting = True
|
|
else:
|
|
self.logger.log(f"DB 폴더 이미 존재함: {self.base_db_dir}", level=logging.INFO)
|
|
|
|
self.detail_text_db_path = os.path.join(self.base_db_dir, "detail_text.db")
|
|
self.price_db_path = os.path.join(self.base_db_dir, "price_settings.db")
|
|
self.kw_db_path = os.path.join(self.base_db_dir, f"user_data_{self.sp_user_id}.db")
|
|
self.logger.log(f"detail_text_db_path 경로: {self.detail_text_db_path}", level=logging.DEBUG)
|
|
self.logger.log(f"price_db_path 경로: {self.price_db_path}", level=logging.DEBUG)
|
|
self.logger.log(f"kw_db_path 경로: {self.kw_db_path}", level=logging.DEBUG)
|
|
|
|
if self.initial_setting:
|
|
self.logger.log(f"초기 설정 파일 생성 중...", level=logging.INFO)
|
|
|
|
self.detail_text_widget = DetailTextEditor(logger=logger, db_path=self.detail_text_db_path)
|
|
self.price_setting_diag = PriceSettingManager(parent=self, logger=self.logger, user_id=self.sp_user_id, db_path=self.price_db_path, debug=self.debug)
|
|
self.browser_controller = BrowserController(self, self.logger, self.locator_manager, self.price_setting_diag, self.detail_text_widget, self.login_infos, self.toggle_states, user_id=self.sp_user_id, supabase_manager=self.supabase_manager)
|
|
|
|
|
|
# 이전에 저장된 설정 불러오기
|
|
self.load_settings()
|
|
|
|
# 브라우저 시작 완료 및 오류 시그널 연결
|
|
self.browser_controller.browser_started.connect(self.on_browser_started)
|
|
self.browser_controller.browser_error.connect(self.on_browser_error)
|
|
|
|
# 상품수정관련련 시그널 연결
|
|
self.browser_controller.translation_started.connect(self.on_PercentyJob_started)
|
|
self.browser_controller.translation_completed.connect(self.on_PercentyJob_completed)
|
|
self.browser_controller.translation_error.connect(self.on_PercentyJob_error)
|
|
|
|
# 현황 표시 시그널 연결
|
|
self.browser_controller.total_progressbar_signal.connect(self.update_total_progress)
|
|
|
|
|
|
# 상품수정 단계표시 시그널 연결
|
|
self.browser_controller.start_stage_signal.connect(self.start_stage)
|
|
self.browser_controller.complete_stage_signal.connect(self.complete_stage)
|
|
self.browser_controller.update_detail_progress_signal.connect(self.update_detail_progress_value)
|
|
self.browser_controller.set_progress_visible_signal.connect(self.set_progress_visibility)
|
|
self.browser_controller.percentyJob_button_Enable.connect(self.percentyJob_button_Enable)
|
|
|
|
# 브라우저 컨트롤러의 선택된 그룹 이름 시그널 연결
|
|
self.browser_controller.selected_group_name_signal.connect(self.update_selected_group_label)
|
|
|
|
# self.config = self.load_config("config.ini")
|
|
# self.kipris_api_key = self.config.get("Kipris_API", "api_key")
|
|
|
|
# 1kw_db_path = os.path.join("src", "ForbiddenKeyword.db")
|
|
# self.spManager = SupabaseManager().get_client()
|
|
|
|
# KeywordDBManager에 user_id 전달 (로그인한 사용자별 DB 동기화를 위해)
|
|
# self.kw_db_manager = KeywordDBManager(kw_db_path, self.user_id, supabase_manager, self.logger)
|
|
# self.kiprisapi = Kipris_API(logger=self.logger, apikey=self.kipris_api_key)
|
|
# self.keyword_manager = KeywordManager(logger=self.logger, kw_db_manager=self.kw_db_manager, kipris_api=self.kiprisapi, user_info=self.user_info, parent=self)
|
|
|
|
|
|
self.thread_pool = QThreadPool() # 스레드 풀 초기화
|
|
|
|
self.kwdb_manager = DBManager(self.kw_db_path, logger=self.logger, user_id=self.sp_user_id, spManager=self.supabase_manager)
|
|
|
|
self.kiprisapi = MarkInfoScraper(logger=self.logger)
|
|
|
|
self.keyword_manager = KeywordManager(logger=self.logger, db_manager=self.kwdb_manager, sp_manager=self.supabase_manager, kipris_api=self.kiprisapi, user_info=self.user_info, thread_pool=self.thread_pool, parent=self)
|
|
|
|
|
|
# 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.start_stage_signal.connect(self.start_stage)
|
|
# self.complete_stage_signal.connect(self.complete_stage)
|
|
# self.update_detail_progress_signal.connect(self.update_detail_progress_value) # 연결
|
|
# self.set_progress_visible_signal.connect(self.set_progress_visibility)
|
|
|
|
# self.percentyJob_button_Signal.connect(self.percentyJob_button_Enable)
|
|
|
|
|
|
@Slot(str)
|
|
def update_selected_group_label(self, group_name: str):
|
|
"""선택된 그룹 이름을 QLabel에 업데이트"""
|
|
self.selected_group_label.setText(f"선택된 그룹: {group_name}")
|
|
self.logger.log(f"선택된 그룹 이름 업데이트: {group_name}", level=logging.INFO)
|
|
|
|
def append_log(self, message):
|
|
self.log_display.append(message)
|
|
|
|
def load_config(self, file_path: str) -> configparser.ConfigParser:
|
|
"""
|
|
config.ini 파일을 읽어서 ConfigParser 객체로 반환
|
|
"""
|
|
config = configparser.ConfigParser()
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as config_file:
|
|
config.read_file(config_file)
|
|
|
|
|
|
# config.read(file_path)
|
|
self.logger.log(f"Config 파일 '{file_path}' 로드 성공", level=logging.INFO)
|
|
except Exception as e:
|
|
self.logger.log(f"Config 파일 로드 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
return config
|
|
|
|
def get_base_dir(self):
|
|
"""
|
|
실행 환경에 따라 base_dir을 설정하는 메서드.
|
|
cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정.
|
|
"""
|
|
if getattr(sys, 'frozen', False): # 패키징된 경우
|
|
base_dir = os.path.dirname(sys.executable)
|
|
internal_dir = os.path.join(base_dir, 'lib') # lib 디렉토리 포함
|
|
if os.path.exists(internal_dir): # lib 디렉토리가 존재하면 base_dir로 설정
|
|
return internal_dir
|
|
|
|
else: # 일반 Python 실행 환경
|
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
|
return base_dir
|
|
|
|
|
|
# def create_user_db_if_not_exists(self):
|
|
# """
|
|
# userDB.db 파일이 없으면 initialDB.db를 복사해서 생성하는 메서드.
|
|
# """
|
|
# try:
|
|
# if not os.path.exists(self.user_db_path):
|
|
# self.logger.log(f"userDB.db 파일이 존재하지 않아 initialDB.db를 복사합니다.", level=logging.DEBUG)
|
|
# if os.path.exists(self.initial_db_path):
|
|
# shutil.copyfile(self.initial_db_path, self.user_db_path)
|
|
# self.logger.log("initialDB.db를 userDB.db로 복사했습니다.", level=logging.DEBUG)
|
|
# else:
|
|
# raise FileNotFoundError(f"{self.initial_db_path} 파일이 없습니다. 초기 DB 파일이 존재하는지 확인해주세요.")
|
|
# except FileNotFoundError as e:
|
|
# self.logger.log(f"DB 초기화 실패: {e}", level=logging.ERROR, exc_info=True)
|
|
# raise e
|
|
# except Exception as e:
|
|
# self.logger.log(f"DB 파일 복사 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
# raise e
|
|
|
|
# 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 add_text_edit_logger(self):
|
|
# """QPlainTextEdit에 로그를 출력하기 위한 핸들러 추가"""
|
|
# try:
|
|
# # GUI 핸들러 추가
|
|
# self.plain_text_edit_logger = QTextEditLogger(self.log)
|
|
# self.plain_text_edit_logger.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s'))
|
|
# self.logger.addHandler(self.plain_text_edit_logger)
|
|
# # self.inspect_logger_handlers(self.logger)
|
|
|
|
# for handler in self.logger.handlers:
|
|
# if isinstance(handler, QTextEditLogger):
|
|
# print(f"Handler: {handler}, Type: QTextEditLogger")
|
|
|
|
# self.logger.debug('로그기록이 설정되었습니다.')
|
|
# except Exception as e:
|
|
# print(f"Error in add_text_edit_logger: {e}")
|
|
|
|
# @Slot(int)
|
|
# 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()
|
|
|
|
# @Slot(int)
|
|
# 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 reset_stages(self):
|
|
# """
|
|
# 스테이지 진행 상태를 초기화합니다.
|
|
# - 현재 진행 중인 스테이지 인덱스를 0으로 재설정
|
|
# - 모든 스테이지 레이블의 스타일을 기본 상태(대기: lightgray)로 변경
|
|
# - 진행중인 깜빡임 타이머가 있다면 중지합니다.
|
|
# """
|
|
# self.current_stage_index = 0
|
|
# # 깜빡임 타이머가 동작중이면 중지
|
|
# if hasattr(self, "timer") and self.timer.isActive():
|
|
# self.timer.stop()
|
|
|
|
# # 모든 스테이지 레이블을 기본 스타일로 재설정
|
|
# for label in self.stage_labels:
|
|
# label.setStyleSheet("background-color: lightgray; padding: 5px;")
|
|
|
|
@Slot(int)
|
|
def start_stage(self, stage_index):
|
|
"""지정한 단계에 작업중 상태(노란색)를 적용"""
|
|
if 0 <= stage_index < len(self.stage_labels):
|
|
label = self.stage_labels[stage_index]
|
|
label.setStyleSheet("background-color: yellow; padding: 5px;")
|
|
|
|
@Slot(int)
|
|
def complete_stage(self, stage_index):
|
|
"""단계 완료 시 해당 단계의 색상을 초록색으로 변경하고 다음 단계로 진행"""
|
|
if 0 <= stage_index < len(self.stage_labels):
|
|
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.stage_labels):
|
|
self.start_stage(self.current_stage_index)
|
|
|
|
def reset_stages(self):
|
|
"""
|
|
스테이지 진행 상태를 초기화합니다.
|
|
- 현재 진행 중인 스테이지 인덱스를 0으로 재설정
|
|
- 모든 스테이지 레이블의 스타일을 기본 상태(대기: lightgray)로 변경
|
|
"""
|
|
self.logger.log("스테이지 초기화", level=logging.DEBUG)
|
|
self.current_stage_index = 0
|
|
for label in self.stage_labels:
|
|
label.setStyleSheet("background-color: lightgray; padding: 5px;")
|
|
|
|
def init_settings(self):
|
|
|
|
self.login_infos={
|
|
'admin_id' : None,
|
|
'admin_pw' : None,
|
|
'user_id' : None,
|
|
'user_pw' : None,
|
|
'is_admin' : False,
|
|
}
|
|
|
|
# 토글 상태를 저장할 딕셔너리 초기화
|
|
self.toggle_states = {
|
|
'title': False,
|
|
'use_lens': False,
|
|
'use_API': False,
|
|
'clientID': "",
|
|
'clientSecret': "",
|
|
'optionTrnas': False,
|
|
'optionIMGTrans': False,
|
|
'optionAutoSelect': False,
|
|
'price': False,
|
|
'thumb': False,
|
|
'tag': False,
|
|
'detail_Option': False,
|
|
'detail_IMGTrans': False,
|
|
'debug_mode': False,
|
|
'recovery_mode': False,
|
|
'ed_mode': False, # 등록된 상품을 수정할때
|
|
'discord': False, # 디스코드 토글
|
|
'discord_webhook': "", # 디스코드 웹훅 URL
|
|
'watermark': False, # 워터마크 토글 추가
|
|
'watermark_text': "", # 워터마크 텍스트 저장
|
|
'opacity_percent': 25, # 워터마크 투명도
|
|
'max_option_count': 20, # 최대 선택가능한 옵션 수
|
|
'group_index': 1, # 작업그룹 선택
|
|
}
|
|
|
|
def initUI(self):
|
|
self.init_settings()
|
|
|
|
# self.setWindowFlags(Qt.WindowStaysOnTopHint)
|
|
self.setGeometry(QRect(1100, 200, 400, 700))
|
|
self.setWindowTitle('AutoPecenty3')
|
|
|
|
# 로그
|
|
self.log_display = QTextEdit(self)
|
|
self.log_display.setReadOnly(True)
|
|
|
|
# 전체 프로그레스바 생성 및 스타일 적용
|
|
self.total_progress_bar = QProgressBar(self)
|
|
self.total_progress_bar.setFormat("상품 수정 대기")
|
|
self.total_progress_bar.setValue(0)
|
|
self.total_progress_bar.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
|
|
self.total_progress_bar.setTextVisible(True)
|
|
self.total_progress_bar.setStyleSheet("""
|
|
QProgressBar {
|
|
border: none;
|
|
background-color: transparent;
|
|
text-align: center;
|
|
font: 12pt 'Segoe UI';
|
|
color: #4A4A4A;
|
|
height: 25px;
|
|
}
|
|
QProgressBar::chunk {
|
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
|
stop:0 #4A90E2, stop:1 #007AFF);
|
|
border: none;
|
|
}
|
|
""")
|
|
|
|
# 스테이지 타임라인
|
|
self.stageTimeline_layout = QHBoxLayout()
|
|
self.stages = ["상품명", "옵션", "가격", "썸네일", "태그", "상페"]
|
|
self.stage_labels = []
|
|
|
|
for stage in self.stages:
|
|
# self.stage_layout = QHBoxLayout()
|
|
label = QLabel(stage)
|
|
label.setStyleSheet("background-color: lightgray; padding: 3px;")
|
|
self.stage_labels.append(label)
|
|
|
|
# self.stage_layout.addWidget(label)
|
|
# self.stageTimeline_layout.addLayout(self.stage_layout)
|
|
self.stageTimeline_layout.addWidget(label) # 수정: QLabel을 추가할 때 addWidget() 사용
|
|
|
|
# 디테일 프로그레스바
|
|
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.detail_progress_bar.setStyleSheet("""
|
|
QProgressBar {
|
|
border: 1px solid #d0d0d0;
|
|
border-radius: 5px;
|
|
background-color: #f9f9f9;
|
|
text-align: center;
|
|
font: 10pt 'Segoe UI';
|
|
color: #333333;
|
|
height: 20px;
|
|
}
|
|
QProgressBar::chunk {
|
|
background-color: #3daee9;
|
|
border-radius: 5px;
|
|
}
|
|
""")
|
|
|
|
# 동작옵션 토글 및 레이블 설정
|
|
self.toggle_layout = QGridLayout()
|
|
|
|
# 상품명 수정 토글
|
|
self.title_toggle_label = QLabel("상품명 수정", self)
|
|
self.title_toggle = ToggleSwitch(self)
|
|
self.title_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('title', checked))
|
|
self.toggle_layout.addWidget(self.title_toggle_label, 0, 0)
|
|
self.toggle_layout.addWidget(self.title_toggle, 0, 1)
|
|
|
|
# 쇼핑렌즈 사용 토글
|
|
self.use_lens_toggle_label = QLabel("쇼핑렌즈 사용", self)
|
|
self.use_lens_toggle = ToggleSwitch(self)
|
|
self.use_lens_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('use_lens', checked))
|
|
self.toggle_layout.addWidget(self.use_lens_toggle_label, 0, 2)
|
|
self.toggle_layout.addWidget(self.use_lens_toggle, 0, 3)
|
|
|
|
# API 사용 토글
|
|
self.use_API_toggle_label = QLabel("API 사용", self)
|
|
self.use_API_toggle = ToggleSwitch(self)
|
|
# self.use_API_toggle.setEnabled(False)
|
|
self.use_API_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('use_API', checked))
|
|
self.toggle_layout.addWidget(self.use_API_toggle_label, 0, 4)
|
|
self.toggle_layout.addWidget(self.use_API_toggle, 0, 5)
|
|
|
|
# API 관련 필드 초기화
|
|
self.client_id_label = QLabel("Client ID", self)
|
|
self.client_id_input = QLineEdit(self)
|
|
self.client_id_input.setPlaceholderText("Client ID 입력")
|
|
self.client_id_input.returnPressed.connect(self.update_client_id_input)
|
|
|
|
self.client_secret_label = QLabel("Client Secret", self)
|
|
self.client_secret_input = QLineEdit(self)
|
|
self.client_secret_input.setPlaceholderText("Client Secret 입력")
|
|
self.client_secret_input.returnPressed.connect(self.update_client_secret_input)
|
|
|
|
# 초기에는 숨김 상태
|
|
self.client_id_label.setVisible(False)
|
|
self.client_id_input.setVisible(False)
|
|
self.client_secret_label.setVisible(False)
|
|
self.client_secret_input.setVisible(False)
|
|
|
|
# 옵션명 AI번역 토글
|
|
self.optionTrnas_toggle_label = QLabel("옵션명 AI번역", self)
|
|
self.optionTrnas_toggle = ToggleSwitch(self)
|
|
self.optionTrnas_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionTrnas', checked))
|
|
|
|
# 옵션이미지 번역 토글
|
|
self.optionIMGTrans_toggle_label = QLabel("옵션이미지 번역", self)
|
|
self.optionIMGTrans_toggle = ToggleSwitch(self)
|
|
self.optionIMGTrans_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionIMGTrans', checked))
|
|
|
|
# 옵션Auto선택 토글
|
|
self.optionAutoSelect_toggle_label = QLabel("옵션 Auto선택", self)
|
|
self.optionAutoSelect_toggle = ToggleSwitch(self)
|
|
self.optionAutoSelect_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('optionAutoSelect', checked))
|
|
|
|
# 가격 수정 토글
|
|
self.price_toggle_label = QLabel("가격 수정", self)
|
|
self.price_toggle = ToggleSwitch(self)
|
|
self.price_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('price', checked))
|
|
|
|
# 태그 수정 토글
|
|
self.tag_toggle_label = QLabel("태그 수정", self)
|
|
self.tag_toggle = ToggleSwitch(self)
|
|
self.tag_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('tag', checked))
|
|
|
|
# 썸네일 AI수정 토글
|
|
self.thumb_toggle_label = QLabel("썸네일 AI수정", self)
|
|
self.thumb_toggle = ToggleSwitch(self)
|
|
self.thumb_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('thumb', checked))
|
|
|
|
# 상페 옵션명 삽입 토글
|
|
self.detail_Option_toggle_label = QLabel("상페 설명&옵션명 삽입", self)
|
|
self.detail_Option_toggle = ToggleSwitch(self)
|
|
self.detail_Option_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('detail_Option', checked))
|
|
|
|
# 상페 이미지 번역 토글
|
|
self.detail_IMGTrans_toggle_label = QLabel("상 이미지 번역", self)
|
|
self.detail_IMGTrans_toggle = ToggleSwitch(self)
|
|
self.detail_IMGTrans_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('detail_IMGTrans', checked))
|
|
|
|
# 디버그 모드 토글~
|
|
self.debug_toggle_label = QLabel("디버그 모드", self)# 작업 완료 메서드 수정
|
|
self.debug_toggle = ToggleSwitch(self)
|
|
self.debug_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('debug_mode', checked))
|
|
|
|
# 수정등록 모드 토글
|
|
self.ed_mode_toggle_label = QLabel("수정등록 모드", self)
|
|
self.ed_mode_toggle = ToggleSwitch(self)
|
|
self.ed_mode_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('ed_mode', checked))
|
|
|
|
# # VD 모드 토글
|
|
# self.vd_mode_toggle_label = QLabel("VD 모드", self)
|
|
# self.vd_mode_toggle = ToggleSwitch(self)
|
|
# self.vd_mode_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('vd_mode', checked))
|
|
# self.toggle_layout.addWidget(self.vd_mode_toggle_label, 5, 2)
|
|
# self.toggle_layout.addWidget(self.vd_mode_toggle, 5, 3)
|
|
# self.vd_mode_toggle.setVisible(False)
|
|
# self.vd_mode_toggle_label.setVisible(False)
|
|
|
|
# recovery 모드 토글
|
|
self.recovery_mode_toggle_label = QLabel("복구 모드", self)
|
|
self.recovery_mode_toggle = ToggleSwitch(self)
|
|
self.recovery_mode_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('recovery_mode', checked))
|
|
|
|
# 디스코드 알림 토글 및 입력 필드 설정
|
|
self.discord_notify_toggle = ToggleSwitch(self)
|
|
self.discord_notify_toggle_label = QLabel("디스코드 알림", self)
|
|
|
|
# 웹훅 입력 필드
|
|
self.webhook_input = QLineEdit(self)
|
|
self.webhook_input.setPlaceholderText("웹훅 URL을 입력하세요")
|
|
self.webhook_input.textChanged.connect(self.update_webhook_url)
|
|
|
|
# 토글 상태 변경 시 update_discord_settings 호출
|
|
self.discord_notify_toggle.clicked.connect(self.update_discord_settings)
|
|
|
|
# 워터마크 토글 추가
|
|
self.watermark_toggle_label = QLabel("워터마크", self)
|
|
self.watermark_toggle = ToggleSwitch(self)
|
|
self.watermark_toggle.clicked.connect(lambda checked: self.on_toggle_clicked_generic('watermark', checked))
|
|
|
|
# 워터마크 관련 UI 요소 생성
|
|
self.watermark_text_label = QLabel("회사 이름", self)
|
|
self.watermark_text_input = QLineEdit(self)
|
|
self.watermark_text_input.returnPressed.connect(self.update_watermark_text)
|
|
# self.watermark_confirm_button = QPushButton("확인", self)
|
|
|
|
# # 확인 버튼 클릭 시 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 메서드 호출
|
|
|
|
# 워터마크 투명도 설정
|
|
self.opacity_percent_label = QLabel("WM투명도", self)
|
|
self.opacity_percent_input = QSpinBox(self)
|
|
self.opacity_percent_input.setMinimum(0) # 최소값 0
|
|
self.opacity_percent_input.setMaximum(80) # 최대값 100
|
|
self.opacity_percent_input.setValue(25) # 기본값 0
|
|
self.opacity_percent_input.setToolTip("워터마크 투명도 설정: 낮을수록 투명") # 툴팁 설정
|
|
self.opacity_percent_input.valueChanged.connect(self.update_opacity_percent) # 값 변경 시 update_max_option_count 메서드 호출
|
|
|
|
|
|
# 그룹 선택 드롭박스 및 툴팁 추가
|
|
self.group_selector_label = QLabel("그룹 선택:", self)
|
|
self.group_selector = QComboBox(self)
|
|
self.group_selector.setToolTip(
|
|
"직원계정은 3개, 관리자계정은 20개 중 선택할 수 있습니다.\n해당 그룹이 없을 경우 기본으로 1번그룹을 작업합니다."
|
|
)
|
|
self.group_selector.currentIndexChanged.connect(self.on_group_selected)
|
|
|
|
# 그룹 이름 표시 QLabel
|
|
self.selected_group_label = QLabel("선택된 그룹: 없음", self)
|
|
self.selected_group_label.setAlignment(Qt.AlignCenter) # 가운데 정렬
|
|
|
|
# 기본 상태는 직원 (3개 그룹)
|
|
self.update_group_items(is_admin=False)
|
|
|
|
|
|
# 초기 위치에서 배치
|
|
self.update_widget_positions(use_api_row=1)
|
|
|
|
|
|
# 초기에는 워터마크 입력창과 버튼 숨김
|
|
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
|
|
|
|
|
|
# 관리자 토글
|
|
self.admin_toggle = ToggleSwitch(self)
|
|
self.admin_toggle.clicked.connect(self.on_admin_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.PercentyJob_button = QPushButton('상품수정 시작', self)
|
|
self.PercentyJob_button.setEnabled(False)
|
|
self.PercentyJob_button.setStyleSheet("""
|
|
QPushButton:disabled {
|
|
color: gray;
|
|
background-color: lightgray;
|
|
border: 1px solid gray;
|
|
}
|
|
""")
|
|
self.pause_button = QPushButton('일시정지', self)
|
|
self.pause_button.setEnabled(False)
|
|
self.pause_button.setStyleSheet("""
|
|
QPushButton:disabled {
|
|
color: gray;
|
|
background-color: lightgray;
|
|
border: 1px solid gray;
|
|
}
|
|
""")
|
|
self.cmb_button = QPushButton('크무비설정', self)
|
|
# self.cmb_test_button = QPushButton('크무비테스트', self)
|
|
self.forbbidenWord_button = QPushButton('금지어', self)
|
|
self.detail_text_button = QPushButton('상페텍스트', self)
|
|
self.manual_button = QPushButton('매뉴얼', self)
|
|
|
|
# 버튼 크기를 1.5배로 설정
|
|
button_height = int(self.start_chrome_button.sizeHint().height() * 1.5)
|
|
self.start_chrome_button.setFixedHeight(button_height)
|
|
self.PercentyJob_button.setFixedHeight(button_height)
|
|
self.pause_button.setFixedHeight(button_height)
|
|
self.cmb_button.setFixedHeight(button_height)
|
|
# self.cmb_test_button.setFixedHeight(button_height)
|
|
self.forbbidenWord_button.setFixedHeight(button_height)
|
|
self.detail_text_button.setFixedHeight(button_height)
|
|
self.manual_button.setFixedHeight(button_height)
|
|
|
|
# 메인 레이아웃 설정
|
|
self.main_layout = QVBoxLayout()
|
|
|
|
# 관리자 토글 버튼 및 로그인 관련 필드 추가
|
|
self.admin_toggle_layout = QHBoxLayout()
|
|
self.admin_toggle_layout.addWidget(QLabel("관리자 여부:", self))
|
|
self.admin_toggle_layout.addWidget(self.admin_toggle)
|
|
self.main_layout.addLayout(self.admin_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.PercentyJob_button)
|
|
self.button_layout.addWidget(self.pause_button)
|
|
self.button_layout.addWidget(self.cmb_button)
|
|
# self.button_layout.addWidget(self.cmb_test_button)
|
|
self.button_layout.addWidget(self.forbbidenWord_button)
|
|
self.button_layout.addWidget(self.detail_text_button)
|
|
self.button_layout.addWidget(self.manual_button)
|
|
|
|
# 로그 및 프로그레스바 레이아웃
|
|
self.log_layout = QVBoxLayout()
|
|
self.log_layout.addWidget(self.total_progress_bar)
|
|
self.log_layout.addLayout(self.stageTimeline_layout)
|
|
self.log_layout.addWidget(self.detail_progress_bar)
|
|
self.log_layout.addWidget(self.log_display)
|
|
|
|
# 메인 레이아웃에 버튼 레이아웃과 로그 레이아웃 추가
|
|
self.main_layout.addLayout(self.toggle_layout,2)
|
|
self.main_layout.addLayout(self.button_layout,2)
|
|
self.main_layout.addLayout(self.log_layout,5)
|
|
|
|
self.setLayout(self.main_layout)
|
|
|
|
# 기본 상태 설정
|
|
self.on_admin_toggle_clicked(False)
|
|
|
|
# 버튼 이벤트 연결
|
|
self.start_chrome_button.clicked.connect(self.start_browser_thread)
|
|
self.PercentyJob_button.clicked.connect(self.on_start_PercentyJob_clicked)
|
|
self.cmb_button.clicked.connect(self.on_cmb_button_clicked)
|
|
# self.cmb_test_button.clicked.connect(self.on_cmb_test_button_clicked)
|
|
self.forbbidenWord_button.clicked.connect(self.on_forbbidenWord_button_clicked)
|
|
self.detail_text_button.clicked.connect(self.on_detail_text_button_clicked)
|
|
self.manual_button.clicked.connect(self.on_manual_button_clicked)
|
|
|
|
self.logger.set_gui_logger(self.append_log)
|
|
|
|
def update_stage_timeline(self, active_stages: list):
|
|
"""
|
|
active_stages: 사용자가 선택한 작업 항목의 리스트 (예: ["상품명", "옵션"])
|
|
"""
|
|
# 기존 레이아웃의 모든 위젯 제거
|
|
while self.stageTimeline_layout.count():
|
|
item = self.stageTimeline_layout.takeAt(0)
|
|
widget = item.widget()
|
|
if widget is not None:
|
|
widget.deleteLater()
|
|
|
|
# 새로운 스테이지 레이블 리스트 초기화
|
|
self.stage_labels = []
|
|
|
|
# 선택한 작업 항목만 레이블로 추가
|
|
for stage in active_stages:
|
|
label = QLabel(stage)
|
|
label.setStyleSheet("background-color: lightgray; padding: 3px;")
|
|
self.stage_labels.append(label)
|
|
self.stageTimeline_layout.addWidget(label)
|
|
|
|
|
|
def load_toggle_settings(self):
|
|
"""QSettings에서 토글 상태 불러오기"""
|
|
for key, default_value in self.toggle_states.items():
|
|
# 데이터 타입을 각 항목의 기본값에 따라 결정
|
|
if isinstance(default_value, bool):
|
|
self.toggle_states[key] = self.settings.value(f"toggle/{key}", default_value, type=bool)
|
|
|
|
self.update_toggle_ui(key)
|
|
|
|
def save_toggle_settings(self):
|
|
"""QSettings에 토글 상태 저장"""
|
|
for key, value in self.toggle_states.items():
|
|
self.settings.setValue(f"toggle/{key}", value)
|
|
# 상태가 변경되었을 때 UI를 업데이트
|
|
self.update_toggle_ui(key)
|
|
|
|
def update_toggle_ui(self, key):
|
|
"""토글 상태에 따라 UI 업데이트"""
|
|
|
|
toggle_widget = getattr(self, f"{key}_toggle", None)
|
|
|
|
# bool 타입인 경우
|
|
if isinstance(self.toggle_states[key], bool) and toggle_widget:
|
|
toggle_widget.setChecked(self.toggle_states[key])
|
|
|
|
def on_watermark_toggle_clicked(self, is_checked):
|
|
"""워터마크 토글 여부에 따라 회사 이름 입력 필드와 확인 버튼을 표시/숨김"""
|
|
if is_checked:
|
|
self.watermark_text_label.setVisible(True)
|
|
self.watermark_text_input.setVisible(True)
|
|
self.opacity_percent_label.setVisible(True)
|
|
self.opacity_percent_input.setVisible(True)
|
|
|
|
# 워터마크 텍스트 입력 필드의 내용을 딕셔너리에 저장
|
|
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
|
|
|
|
else:
|
|
self.watermark_text_label.setVisible(False)
|
|
self.watermark_text_input.setVisible(False)
|
|
self.opacity_percent_label.setVisible(False)
|
|
self.opacity_percent_input.setVisible(False)
|
|
|
|
def show_message(self, title: str, message: str):
|
|
"""
|
|
공통적으로 메시지 박스를 표시하는 메서드
|
|
:param title: 메시지 박스의 제목
|
|
:param message: 메시지 박스의 내용
|
|
"""
|
|
if not hasattr(self, "_message_box"):
|
|
self._message_box = QMessageBox(self) # 메시지 박스를 한 번만 생성
|
|
|
|
self._message_box.setIcon(QMessageBox.Information)
|
|
self._message_box.setWindowTitle(title)
|
|
self._message_box.setText(message)
|
|
self._message_box.setStandardButtons(QMessageBox.Ok)
|
|
self._message_box.exec_()
|
|
|
|
def update_client_id_input(self):
|
|
"""QLineEdit에 입력된 텍스트를 toggle_states['clientID']에 저장하고 메시지 표시"""
|
|
# clientID 저장
|
|
self.toggle_states['clientID'] = self.client_id_input.text()
|
|
self.logger.log(f"Updated client ID: {self.toggle_states['clientID']}", level=logging.DEBUG)
|
|
|
|
# 메시지 박스를 통해 업데이트 알림
|
|
self.show_message("클라이언트 ID 업데이트", "클라이언트 ID가 업데이트되었습니다.")
|
|
|
|
def update_client_secret_input(self):
|
|
"""QLineEdit에 입력된 텍스트를 toggle_states['clientSecret']에 저장하고 메시지 표시"""
|
|
# clientSecret 저장
|
|
self.toggle_states['clientSecret'] = self.client_secret_input.text()
|
|
self.logger.log(f"Updated client secret: {self.toggle_states['clientSecret']}", level=logging.DEBUG)
|
|
|
|
# 메시지 박스를 통해 업데이트 알림
|
|
self.show_message("클라이언트 Secret 업데이트", "클라이언트 Secret이 업데이트되었습니다.")
|
|
|
|
def update_watermark_text(self):
|
|
"""QLineEdit에 입력된 텍스트를 toggle_states['watermark_text']에 저장"""
|
|
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
|
|
self.logger.log(f"Updated watermark text: {self.toggle_states['watermark_text']}", level=logging.DEBUG)
|
|
|
|
# 메시지 박스를 통해 업데이트 알림 (값 포함)
|
|
self.show_message(
|
|
"워터마크 텍스트 업데이트",
|
|
f"워터마크 텍스트가 업데이트되었습니다: {self.toggle_states['watermark_text']}"
|
|
)
|
|
|
|
def update_max_option_count(self, value):
|
|
"""QSpinBox에 입력된 값을 toggle_states['max_option_count']에 저장"""
|
|
self.toggle_states['max_option_count'] = value # 변경된 정수 값을 바로 저장
|
|
self.logger.log(f"최대 선택 가능 옵션 수 업데이트: {self.toggle_states['max_option_count']}", level=logging.DEBUG)
|
|
|
|
def update_opacity_percent(self, value):
|
|
"""QSpinBox에 입력된 값을 toggle_states['opacity_percent']에 저장"""
|
|
self.toggle_states['opacity_percent'] = value # 변경된 정수 값을 바로 저장
|
|
self.logger.log(f"워터마크 투명도 업데이트: {self.toggle_states['opacity_percent']}", level=logging.DEBUG)
|
|
|
|
def update_clientID(self, value):
|
|
self.toggle_states['clientID'] = value # 변경된 정수 값을 바로 저장
|
|
self.logger.log(f"clientID 업데이트: {self.toggle_states['clientID']}", level=logging.DEBUG)
|
|
|
|
def update_clientSecret(self, value):
|
|
# self.client_id_input.setText(self.toggle_states[value])
|
|
self.toggle_states['clientSecret'] = value # 변경된 정수 값을 바로 저장
|
|
self.logger.log(f"clientSecret 업데이트: {self.toggle_states['clientSecret']}", level=logging.DEBUG)
|
|
|
|
def update_discord_settings(self, checked=None):
|
|
"""
|
|
디스코드 알림 설정 업데이트
|
|
- 토글 상태에 따라 웹훅 입력 필드 표시/숨김
|
|
- 설정값 저장
|
|
"""
|
|
# 토글 상태 확인 (인자가 없을 경우 현재 상태 사용)
|
|
is_enabled = checked if checked is not None else self.discord_notify_toggle.isChecked()
|
|
|
|
# 웹훅 입력 필드 가시성 설정
|
|
self.webhook_input.setVisible(is_enabled)
|
|
|
|
# 설정값 저장 (toggle_states 딕셔너리 사용)
|
|
self.toggle_states['discord'] = is_enabled
|
|
self.toggle_states['discord_webhook'] = self.webhook_input.text()
|
|
|
|
# 로그 출력
|
|
self.logger.log(f"디스코드 알림 설정 변경: {'활성화' if is_enabled else '비활성화'}",
|
|
level=logging.DEBUG)
|
|
|
|
# 토글 텍스트 업데이트
|
|
self.discord_notify_toggle_label.setText(
|
|
"디스코드 알림" + (" ON" if is_enabled else " OFF")
|
|
)
|
|
|
|
def update_webhook_url(self):
|
|
"""웹훅 URL 변경 시 설정 저장"""
|
|
url = self.webhook_input.text().strip()
|
|
self.toggle_states['discord_webhook'] = url
|
|
self.discord_manager.set_webhook_url(url)
|
|
self.logger.log(f"디스코드 웹훅 URL 업데이트됨", level=logging.DEBUG)
|
|
|
|
def update_watermark_visibility(self):
|
|
"""이미지 번역 토글 중 하나라도 켜져 있으면 워터마크 토글을 보이게 하고, visible이 되면 상태에 따라 레이아웃도 제어"""
|
|
if self.toggle_states['optionIMGTrans'] or self.toggle_states['detail_IMGTrans'] or self.toggle_states['thumb']:
|
|
# 이미지 번역 토글이 하나라도 켜져 있으면 워터마크 토글 보이기
|
|
self.toggle_visibility(True, [(self.watermark_toggle, self.watermark_toggle_label)])
|
|
|
|
# 워터마크 토글이 보이게 될 때 상태 확인
|
|
if self.watermark_toggle.isChecked():
|
|
# 워터마크 토글이 ON 상태이면 워터마크 레이아웃도 보이게 함
|
|
self.toggle_visibility(True, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
|
|
else:
|
|
# 워터마크 토글이 OFF 상태이면 워터마크 레이아웃 숨김
|
|
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
|
|
else:
|
|
# 모두 꺼져 있으면 워터마크 토글과 레이아웃 숨기기
|
|
self.toggle_visibility(False, [(self.watermark_toggle, self.watermark_toggle_label)])
|
|
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
|
|
|
|
def toggle_visibility(self, is_checked, toggle_items):
|
|
"""
|
|
토글 상태에 따라 여러 필드의 visibility를 제어하는 범용 메서드
|
|
:param is_checked: 토글 상태 (True/False)
|
|
:param toggle_items: 토글 필드와 레이블 목록 [(필드, 레이블), ...]
|
|
"""
|
|
for item, label in toggle_items:
|
|
item.setVisible(is_checked)
|
|
if label:
|
|
label.setVisible(is_checked)
|
|
|
|
|
|
# def update_api_fields_visibility(self, is_checked):
|
|
# """
|
|
# use_API 토글 버튼 상태에 따라 clientID 및 clientSecretKey 필드를 보이거나 숨기는 메서드
|
|
# :param is_checked: use_API 토글 상태 (True/False)
|
|
# """
|
|
# self.toggle_visibility(is_checked, [
|
|
# (self.client_id_input, self.client_id_label),
|
|
# (self.client_secret_input, self.client_secret_label)
|
|
# ])
|
|
|
|
def update_widget_positions(self, use_api_row):
|
|
"""
|
|
위젯의 위치를 동적으로 업데이트 (재배치)
|
|
:param use_api_row: use_API 토글이 켜졌을 때 clientID와 clientSecret이 위치할 행
|
|
"""
|
|
current_row = use_api_row
|
|
|
|
# 1. use_API 관련 필드 위치 업데이트
|
|
self.toggle_layout.addWidget(self.client_id_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.client_id_input, current_row, 1, 1, 2)
|
|
self.toggle_layout.addWidget(self.client_secret_label, current_row, 3)
|
|
self.toggle_layout.addWidget(self.client_secret_input, current_row, 4, 1, 2)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.optionTrnas_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.optionTrnas_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.optionIMGTrans_toggle_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.optionIMGTrans_toggle, current_row, 3)
|
|
self.toggle_layout.addWidget(self.optionAutoSelect_toggle_label, current_row, 4)
|
|
self.toggle_layout.addWidget(self.optionAutoSelect_toggle, current_row, 5)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.price_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.price_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.tag_toggle_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.tag_toggle, current_row, 3)
|
|
self.toggle_layout.addWidget(self.thumb_toggle_label, current_row, 4)
|
|
self.toggle_layout.addWidget(self.thumb_toggle, current_row, 5)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.detail_Option_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.detail_Option_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.detail_IMGTrans_toggle_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.detail_IMGTrans_toggle, current_row, 3)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.debug_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.debug_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.ed_mode_toggle_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.ed_mode_toggle, current_row, 3)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.recovery_mode_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.recovery_mode_toggle, current_row, 1)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.discord_notify_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.discord_notify_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.webhook_input, current_row, 2, 1, 3) # 2열부터 3칸 차지
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.watermark_toggle_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.watermark_toggle, current_row, 1)
|
|
self.toggle_layout.addWidget(self.watermark_text_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.watermark_text_input, current_row, 3)
|
|
self.toggle_layout.addWidget(self.opacity_percent_label, current_row, 4)
|
|
self.toggle_layout.addWidget(self.opacity_percent_input, current_row, 5)
|
|
# self.watermark_layout.addWidget(self.watermark_text_label, 2)
|
|
# self.watermark_layout.addWidget(self.watermark_text_input, 3)
|
|
# self.watermark_layout.addWidget(self.opacity_percent_label, 4)
|
|
# self.watermark_layout.addWidget(self.opacity_percent_input, 5)
|
|
# self.watermark_layout.addWidget(self.watermark_confirm_button, 1)
|
|
# self.toggle_layout.addWidget(self.opacity_percent_label, current_row, 2)
|
|
# self.toggle_layout.addWidget(self.opacity_percent_input, current_row, 3)
|
|
|
|
# self.toggle_layout.addLayout(self.watermark_layout, current_row, 0, 1, 4)
|
|
|
|
current_row += 1
|
|
self.toggle_layout.addWidget(self.max_option_count_label, current_row, 0)
|
|
self.toggle_layout.addWidget(self.max_option_count_input, current_row, 1)
|
|
self.toggle_layout.addWidget(self.group_selector_label, current_row, 2)
|
|
self.toggle_layout.addWidget(self.group_selector, current_row, 3)
|
|
self.toggle_layout.addWidget(self.selected_group_label, current_row, 4)
|
|
|
|
def update_group_items(self, is_admin: bool):
|
|
"""관리자 여부에 따라 그룹 선택 항목 변경"""
|
|
self.group_selector.clear() # 기존 아이템 제거
|
|
|
|
if is_admin:
|
|
# 관리자 계정: 20개 그룹
|
|
self.group_selector.addItems([f"{i}번" for i in range(1, 21)])
|
|
else:
|
|
# 직원 계정: 3개 그룹
|
|
self.group_selector.addItems(["1번그룹", "2번그룹", "3번그룹"])
|
|
|
|
self.group_selector.setCurrentIndex(0) # 기본값 설정
|
|
|
|
def on_group_selected(self):
|
|
"""그룹 선택 변경 시 호출"""
|
|
import re
|
|
try:
|
|
# 정규식으로 숫자만 추출
|
|
match = re.search(r'\d+', self.group_selector.currentText())
|
|
if match:
|
|
self.toggle_states['group_index'] = int(match.group())
|
|
self.logger.log(f"선택된 그룹이 변경되었습니다: {self.toggle_states['group_index']}", level=logging.DEBUG)
|
|
|
|
else:
|
|
# 숫자가 없을 경우 처리
|
|
self.logger.log(f"선택된 그룹에 숫자가 없습니다: {self.group_selector.currentText()}", level=logging.DEBUG)
|
|
|
|
self.toggle_states['group_index'] = None
|
|
except Exception as e:
|
|
# 기타 예외 처리
|
|
self.logger.log(f"그룹 선택 처리 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
|
|
self.toggle_states['group_index'] = None
|
|
|
|
def on_toggle_clicked_generic(self, key, is_checked):
|
|
"""토글 클릭 시 상태 업데이트 및 저장"""
|
|
self.toggle_states[key] = is_checked
|
|
|
|
if is_checked:
|
|
status_text = "활성화"
|
|
else:
|
|
status_text = "비활성화"
|
|
|
|
label_text = ""
|
|
|
|
# key에 따라 라벨 텍스트를 설정
|
|
if key == 'title':
|
|
label_text = self.title_toggle_label.text()
|
|
elif key == 'optionTrnas':
|
|
label_text = self.optionTrnas_toggle_label.text()
|
|
elif key == 'optionIMGTrans':
|
|
label_text = self.optionIMGTrans_toggle_label.text()
|
|
elif key == 'optionAutoSelect':
|
|
label_text = self.optionAutoSelect_toggle_label.text()
|
|
elif key == 'price':
|
|
label_text = self.price_toggle_label.text()
|
|
elif key == 'thumb':
|
|
label_text = self.thumb_toggle_label.text()
|
|
elif key == 'tag':
|
|
label_text = self.tag_toggle_label.text()
|
|
elif key == 'detail_Option':
|
|
label_text = self.detail_Option_toggle_label.text()
|
|
elif key == 'detail_IMGTrans':
|
|
label_text = self.detail_IMGTrans_toggle_label.text()
|
|
elif key == 'debug_mode':
|
|
label_text = self.debug_toggle_label.text()
|
|
elif key == 'discord':
|
|
label_text = self.discord_notify_toggle_label.text()
|
|
elif key == 'recovery_mode':
|
|
label_text = self.recovery_mode_toggle_label.text()
|
|
elif key == 'ed_mode':
|
|
label_text = self.ed_mode_toggle_label.text()
|
|
elif key == 'use_lens':
|
|
label_text = self.use_lens_toggle_label.text()
|
|
elif key == 'use_API':
|
|
label_text = self.use_API_toggle_label.text()
|
|
elif key == 'watermark':
|
|
label_text = self.watermark_toggle_label.text()
|
|
|
|
# 이미지 번역 관련 토글이 하나라도 켜져 있으면 워터마크 토글 보이기
|
|
if key in ['optionIMGTrans', 'detail_IMGTrans', 'thumb']:
|
|
self.update_watermark_visibility()
|
|
|
|
# 워터마크 토글이 켜져 있으면 watermark_layout 보이기
|
|
if key == 'watermark':
|
|
self.toggle_visibility(is_checked, [
|
|
(self.watermark_text_input, self.watermark_text_label),
|
|
(self.opacity_percent_input, self.opacity_percent_label)
|
|
])
|
|
|
|
# key에 따라 라벨 텍스트를 설정
|
|
if key == 'use_lens':
|
|
self.on_lens_toggle_clicked(is_checked)
|
|
|
|
# discord 알림 토글 상태에 따라 webhook 입력 필드 확장
|
|
if key == 'discord':
|
|
self.toggle_visibility(is_checked, [(self.webhook_input)])
|
|
|
|
# key에 따라 라벨 텍스트를 설정
|
|
if key == 'use_API':
|
|
self.toggle_visibility(is_checked, [(self.client_id_input, self.client_id_label), (self.client_secret_input, self.client_secret_label)])
|
|
|
|
# use_API 토글 상태에 따라 clientID와 clientSecret 표시 여부
|
|
# self.client_id_label.setVisible(is_checked)
|
|
# self.client_id_input.setVisible(is_checked)
|
|
# self.client_secret_label.setVisible(is_checked)
|
|
# self.client_secret_input.setVisible(is_checked)
|
|
|
|
# 위젯 위치 업데이트
|
|
base_row = 1 if is_checked else 2
|
|
self.update_widget_positions(use_api_row=base_row)
|
|
|
|
self.logger.log(f"{label_text} 버튼 - {status_text} 선택", level=logging.DEBUG)
|
|
|
|
self.save_toggle_settings()
|
|
|
|
def on_admin_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)
|
|
|
|
self.update_group_items(is_admin=is_checked)
|
|
|
|
def on_lens_toggle_clicked(self, is_checked):
|
|
"""렌즈 토글 상태에 따라 API사용 토들 표시/숨김"""
|
|
if is_checked:
|
|
self.use_API_toggle.setVisible(True)
|
|
else:
|
|
self.use_API_toggle.setVisible(False)
|
|
|
|
# def on_vd_mode_for_detail_imageTrans_clicked(self, is_checked):
|
|
# """상페이미지 번역여부에 따라 VD 모드 선택 필드를 표시/숨김"""
|
|
# if is_checked:
|
|
# self.vd_mode_toggle.setVisible(True)
|
|
# self.vd_mode_toggle_label.setVisible(True)
|
|
# else:
|
|
# self.vd_mode_toggle.setVisible(False)
|
|
# self.vd_mode_toggle_label.setVisible(False)
|
|
|
|
|
|
def set_layout_visibility(self, changelayout, visible):
|
|
"""레이아웃에 포함된 모든 위젯의 가시성을 설정"""
|
|
for i in range(changelayout.count()):
|
|
widget = changelayout.itemAt(i).widget()
|
|
if widget:
|
|
widget.setVisible(visible)
|
|
|
|
# def on_cmb_test_button_clicked(self, test_cat):
|
|
# """크무비 설정 실행 버튼 클릭 시 호출"""
|
|
# self.logger.log('크무비 테스트 버튼 클릭됨', level=logging.DEBUG)
|
|
|
|
# text, ok = QInputDialog.getText(self, "카테고리 입력 테스트", "카테고리를 형식에 맞게 입력하세요:")
|
|
# if ok and text: # 사용자가 확인 버튼을 누르고 텍스트를 입력한 경우
|
|
# stage = self.cmb_diag.get_crmobi_stage(text)
|
|
# self.logger.log(f"{stage}", level=logging.DEBUG)
|
|
|
|
def on_forbbidenWord_button_clicked(self):
|
|
"""금지어 관리 버튼 클릭 시 호출"""
|
|
self.logger.log("금지어 관리 버튼 클릭됨", level=logging.DEBUG)
|
|
self.keyword_manager.exec()
|
|
|
|
def on_manual_button_clicked(self):
|
|
"""매뉴얼 버튼 클릭 시 호출"""
|
|
self.logger.log("매뉴얼 버튼 클릭됨", level=logging.DEBUG)
|
|
|
|
user_manual_widget = UserManualDialog(self)
|
|
user_manual_widget.exec()
|
|
|
|
def on_detail_text_button_clicked(self):
|
|
"""매뉴얼 버튼 클릭 시 호출"""
|
|
try:
|
|
self.logger.log("상페텍스트 버튼 클릭됨", level=logging.DEBUG)
|
|
self.detail_text_widget.show()
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"상페텍스트 관리 중 오류 발생 {e}", level=logging.ERROR, exc_info=True)
|
|
|
|
|
|
def on_cmb_button_clicked(self):
|
|
"""크무비 설정 실행 버튼 클릭 시 호출"""
|
|
self.logger.log('크무비 설정 버튼 클릭됨', level=logging.DEBUG)
|
|
self.price_setting_diag.show()
|
|
|
|
def save_settings(self):
|
|
"""QSettings에 사용자 정보 저장"""
|
|
self.logger.log(f"현재 설정을 저장합니다.", level=logging.DEBUG)
|
|
|
|
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())
|
|
self.settings.setValue("watermark_text", self.watermark_text_input.text())
|
|
self.settings.setValue("opacity_percent", self.opacity_percent_input.value())
|
|
self.settings.setValue("max_option_count", self.max_option_count_input.value())
|
|
|
|
self.settings.setValue("api/toggle", self.use_API_toggle.isChecked())
|
|
# API 필드 저장
|
|
if self.use_API_toggle.isChecked():
|
|
self.settings.setValue("api/client_id", self.client_id_input.text())
|
|
self.settings.setValue("api/client_secret", self.client_secret_input.text())
|
|
|
|
self.settings.setValue("discord", self.discord_notify_toggle.isChecked())
|
|
self.settings.setValue("discord_webhook", self.webhook_input.text())
|
|
|
|
|
|
|
|
def load_settings(self):
|
|
"""QSettings에서 사용자 정보 불러오기"""
|
|
self.admin_id_input.setText(self.settings.value("admin/id", "", type=str))
|
|
self.admin_pw_input.setText(self.settings.value("admin/pw", "", type=str))
|
|
self.user_id_input.setText(self.settings.value("user/id", "", type=str))
|
|
self.user_pw_input.setText(self.settings.value("user/pw", "", type=str))
|
|
|
|
admin_toggle_state = self.settings.value("admin/toggle", False, type=bool)
|
|
self.admin_toggle.setChecked(admin_toggle_state)
|
|
self.on_admin_toggle_clicked(admin_toggle_state)
|
|
|
|
self.watermark_text_input.setText(self.settings.value("watermark_text", "", type=str))
|
|
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
|
|
self.opacity_percent_input.setValue(self.settings.value("opacity_percent", 20, type=int))
|
|
self.toggle_states['opacity_percent'] = int(self.opacity_percent_input.text())
|
|
self.max_option_count_input.setValue(self.settings.value("max_option_count", 20, type=int))
|
|
self.toggle_states['max_option_count'] = int(self.max_option_count_input.text())
|
|
|
|
# 디스코드 설정 로드
|
|
self.toggle_states['discord'] = self.settings.value("discord", False, type=bool)
|
|
self.toggle_states['discord_webhook'] = self.settings.value("discord_webhook", "", type=str)
|
|
|
|
# UI 요소에 로드된 값 적용 (추가 필요)
|
|
if hasattr(self, 'discord_notify_toggle'):
|
|
self.discord_notify_toggle.setChecked(self.toggle_states['discord'])
|
|
if hasattr(self, 'webhook_input'):
|
|
self.webhook_input.setText(self.toggle_states['discord_webhook'])
|
|
self.webhook_input.setVisible(self.toggle_states['discord'])
|
|
|
|
# use_API 토글 상태 로드
|
|
api_toggle_state = self.settings.value("api/toggle", False, type=bool)
|
|
self.use_API_toggle.setChecked(api_toggle_state)
|
|
|
|
# self.on_toggle_clicked_generic('use_API', api_toggle_state)
|
|
|
|
# API 필드 로드
|
|
self.client_id_input.setText(self.settings.value("api/client_id", "", type=str))
|
|
self.toggle_states['clientID'] = self.client_id_input.text()
|
|
self.client_secret_input.setText(self.settings.value("api/client_secret", "", type=str))
|
|
self.toggle_states['clientSecret'] = self.client_secret_input.text()
|
|
|
|
self.load_toggle_settings()
|
|
|
|
|
|
def get_toggle_states(self):
|
|
"""
|
|
현재 UI 상태를 기반으로 toggle_states를 업데이트하고 반환
|
|
:return: 업데이트된 toggle_states 딕셔너리
|
|
"""
|
|
# 각 토글 및 입력 필드 상태를 toggle_states에 업데이트
|
|
self.toggle_states['title'] = self.title_toggle.isChecked()
|
|
self.toggle_states['use_lens'] = self.use_lens_toggle.isChecked()
|
|
self.toggle_states['use_API'] = self.use_API_toggle.isChecked()
|
|
self.toggle_states['clientID'] = bool(self.client_id_input.text().strip())
|
|
self.toggle_states['clientSecret'] = bool(self.client_secret_input.text().strip())
|
|
self.toggle_states['optionTrnas'] = self.optionTrnas_toggle.isChecked()
|
|
self.toggle_states['optionIMGTrans'] = self.optionIMGTrans_toggle.isChecked()
|
|
self.toggle_states['optionAutoSelect'] = self.optionAutoSelect_toggle.isChecked()
|
|
self.toggle_states['price'] = self.price_toggle.isChecked()
|
|
self.toggle_states['thumb'] = self.thumb_toggle.isChecked()
|
|
self.toggle_states['tag'] = self.tag_toggle.isChecked()
|
|
self.toggle_states['detail_Option'] = self.detail_Option_toggle.isChecked()
|
|
self.toggle_states['detail_IMGTrans'] = self.detail_IMGTrans_toggle.isChecked()
|
|
self.toggle_states['debug_mode'] = self.debug_toggle.isChecked()
|
|
self.toggle_states['recovery_mode'] = self.recovery_mode_toggle.isChecked()
|
|
self.toggle_states['ed_mode'] = self.ed_mode_toggle.isChecked()
|
|
self.toggle_states['watermark'] = self.watermark_toggle.isChecked()
|
|
|
|
# 워터마크 텍스트, 투명도, 최대 옵션 수 업데이트
|
|
self.toggle_states['watermark_text'] = self.watermark_text_input.text()
|
|
self.toggle_states['opacity_percent'] = self.opacity_percent_input.value()
|
|
self.toggle_states['max_option_count'] = self.max_option_count_input.value()
|
|
|
|
# 업데이트된 toggle_states 반환
|
|
return self.toggle_states
|
|
|
|
|
|
def update_total_progress(self, current_value, total_value):
|
|
if current_value == 0 or total_value <= 0:
|
|
self.reset_stages()
|
|
self.total_progress_bar.setValue(0)
|
|
self.total_progress_bar.setFormat(f"상품 {current_value}/{total_value}개 완료 [{0}%]")
|
|
self.logger.log(f"전체 진행률: {current_value}/{total_value} (0%)", level=logging.DEBUG)
|
|
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}%]")
|
|
self.logger.log(f"전체 진행률: {current_value}/{total_value} ({percentage}%)", level=logging.DEBUG)
|
|
|
|
|
|
# def update_total_progress(self, current_value, total_value):
|
|
# percentage = int((current_value / total_value) * 100)
|
|
# self.total_progress_bar.setValue(percentage)
|
|
# self.total_progress_bar.setFormat(f"상품 {current_value}/{total_value}개 완료 [{percentage}%]")
|
|
# self.logger.log(f"전체 진행률: {current_value}/{total_value} ({percentage}%)", level=logging.DEBUG)
|
|
|
|
|
|
def update_detail_progress(self, current_value, total_value):
|
|
if total_value <= 0:
|
|
# 전체 작업 수가 0이면 초기 상태로 설정
|
|
self.detail_progress_bar.setValue(0)
|
|
self.detail_progress_bar.setFormat("수정 대기")
|
|
else:
|
|
percentage = int((current_value / total_value) * 100)
|
|
self.detail_progress_bar.setValue(percentage)
|
|
# 진행률 포맷: "이미지 번역: 3/10 (30%) 완료"
|
|
self.detail_progress_bar.setFormat(f"이미지 번역: {current_value}/{total_value} ({percentage}%) 완료")
|
|
|
|
|
|
@Slot()
|
|
def start_browser_thread(self):
|
|
"""브라우저 스레드 시작 및 GUI 상태 전달"""
|
|
self.start_chrome_button.setEnabled(False)
|
|
|
|
self.browser_controller.start()
|
|
|
|
time.sleep(1)
|
|
|
|
if self.browser_controller.isRunning():
|
|
# 스레드를 처음 시작하여 이벤트 루프를 실행
|
|
# self.browser_controller.start() # QThread의 start() 호출로 run() 실행
|
|
self.logger.log("브라우저 스레드가 시작되었습니다.", level=logging.DEBUG)
|
|
|
|
self.browser_controller.login_infos = {
|
|
'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(),
|
|
'is_admin': self.admin_toggle.isChecked(),
|
|
}
|
|
|
|
# 로그인 정보 저장
|
|
self.save_settings()
|
|
|
|
# 스레드 시작
|
|
self.browser_controller.start_browser_task()
|
|
else:
|
|
self.logger.log("브라우저 스레드가 실행중이지 않습니다.", level=logging.WARNING)
|
|
|
|
@Slot()
|
|
def on_browser_started(self):
|
|
"""브라우저 시작 완료 시 처리할 로직"""
|
|
self.logger.log("브라우저가 성공적으로 시작되었습니다.", level=logging.INFO)
|
|
# 버튼 상태 활성화&비활성화
|
|
self.PercentyJob_button.setEnabled(True)
|
|
self.pause_button.setEnabled(True)
|
|
# self.start_chrome_button.setEnabled(False)
|
|
|
|
@Slot(str)
|
|
def on_browser_error(self, error_message):
|
|
"""브라우저 오류 발생 시 처리할 로직"""
|
|
self.logger.log(f"브라우저 시작 중 오류 발생: {error_message}", level=logging.ERROR)
|
|
|
|
# def closeEvent(self, event):
|
|
# """창 닫기 시 스레드 종료"""
|
|
# self.logger.log('프로그램을 종료합니다...', level=logging.INFO)
|
|
# self.save_settings()
|
|
# asyncio.run(self.browser_controller.close_browser()) # 브라우저 종료
|
|
|
|
# if self.browser_controller.isRunning():
|
|
# self.browser_controller.stop() # 리소스 정리
|
|
# # self.browser_controller.wait() # 스레드가 종료될 때까지 대기
|
|
# event.accept()
|
|
# super().close()
|
|
|
|
def closeEvent(self, event):
|
|
"""창 닫기 시 스레드 및 리소스 종료"""
|
|
self.logger.log('프로그램을 종료합니다...', level=logging.INFO)
|
|
self.supabase_manager.decrement_concurrent(self.sp_user_id)
|
|
|
|
|
|
# 현재 설정 저장
|
|
self.save_settings()
|
|
|
|
|
|
# Playwright 및 이벤트 루프 정리
|
|
try:
|
|
asyncio.run(self.cleanup_resources())
|
|
except Exception as e:
|
|
self.logger.log(f"Playwright 리소스 정리 중 오류 발생: {e}", level=logging.ERROR)
|
|
|
|
# 브라우저 컨트롤러 스레드 종료
|
|
if self.browser_controller.isRunning():
|
|
self.browser_controller.terminate()
|
|
self.browser_controller.wait(3000) # 3초 대기
|
|
if self.browser_controller.isRunning():
|
|
self.logger.log('스레드가 종료되지 않아 강제 종료를 시도합니다.', level=logging.WARNING)
|
|
self.browser_controller.terminate() # 강제 종료
|
|
|
|
# Qt 메인 이벤트 루프 종료
|
|
QApplication.quit()
|
|
event.accept()
|
|
super().closeEvent(event)
|
|
|
|
async def cleanup_resources(self):
|
|
"""Playwright 및 이벤트 루프 정리"""
|
|
try:
|
|
self.logger.log("Playwright 리소스 정리를 시작합니다.", level=logging.INFO)
|
|
|
|
# 모든 페이지 닫기
|
|
if self.browser_controller.browser:
|
|
self.logger.log("열린 페이지를 닫습니다...", level=logging.INFO)
|
|
for page in self.browser_controller.browser.pages:
|
|
try:
|
|
await asyncio.wait_for(page.close(), timeout=1) # 페이지 닫기에 타임아웃 적용
|
|
self.logger.log(f"페이지 {page.url} 닫기 완료.", level=logging.INFO)
|
|
except asyncio.TimeoutError:
|
|
self.logger.log(f"페이지 {page.url} 닫기 타임아웃 발생. 강제 종료를 시도합니다.", level=logging.WARNING)
|
|
|
|
# 브라우저 닫기
|
|
self.logger.log("브라우저를 닫습니다...", level=logging.INFO)
|
|
try:
|
|
await asyncio.wait_for(self.browser_controller.browser.close(), timeout=1)
|
|
self.logger.log("브라우저 종료 완료.", level=logging.INFO)
|
|
except asyncio.TimeoutError:
|
|
self.logger.log("브라우저 종료가 타임아웃되었습니다. 강제 종료를 시도합니다.", level=logging.WARNING)
|
|
self.browser_controller.force_terminate_browser()
|
|
|
|
|
|
# Playwright 종료
|
|
if self.browser_controller.playwright:
|
|
self.logger.log('Playwright 종료 중...', level=logging.INFO)
|
|
await self.browser_controller.playwright.stop()
|
|
self.logger.log('Playwright 종료 완료.', level=logging.INFO)
|
|
|
|
# 이벤트 루프 종료
|
|
if self.browser_controller.loop and not self.browser_controller.loop.is_closed():
|
|
self.browser_controller.loop.call_soon_threadsafe(self.browser_controller.loop.stop)
|
|
self.logger.log('이벤트 루프 종료 완료.', level=logging.INFO)
|
|
|
|
except Exception as e:
|
|
self.logger.log(f"리소스 정리 중 오류 발생: {e}", level=logging.ERROR)
|
|
|
|
@Slot()
|
|
def on_start_PercentyJob_clicked(self):
|
|
|
|
# 프로그래스바 초기화
|
|
self.update_total_progress(0,0)
|
|
|
|
# 예시: 토글 상태에 따라 활성화된 스테이지 목록 생성 (실제 구현은 토글 상태에 맞춰서 수정)
|
|
active_stages = []
|
|
if self.title_toggle.isChecked():
|
|
active_stages.append("상품명")
|
|
if self.optionTrnas_toggle.isChecked() or self.optionAutoSelect_toggle.isChecked() or self.optionIMGTrans_toggle.isChecked():
|
|
active_stages.append("옵션")
|
|
if self.price_toggle.isChecked():
|
|
active_stages.append("가격")
|
|
if self.thumb_toggle.isChecked():
|
|
active_stages.append("썸네일")
|
|
if self.tag_toggle.isChecked():
|
|
active_stages.append("태그")
|
|
if self.detail_Option_toggle.isChecked() or self.detail_IMGTrans_toggle.isChecked():
|
|
active_stages.append("상페")
|
|
|
|
# 스테이지 타임라인 업데이트
|
|
self.update_stage_timeline(active_stages)
|
|
|
|
|
|
"""상품수정 스레드 시작 및 상태 전달"""
|
|
if self.browser_controller.isRunning():
|
|
# 스레드 시작
|
|
self.browser_controller.start_PercentyJob_task()
|
|
self.logger.log("상품수정 작업 스레드가 시작되었습니다.", level=logging.INFO)
|
|
else:
|
|
self.logger.log("브라우저 스레드가 없습니다.", level=logging.INFO)
|
|
|
|
|
|
@Slot()
|
|
def on_PercentyJob_started(self):
|
|
"""상품수정 작업이 시작되었을 때 처리할 로직"""
|
|
self.job_start_time = datetime.now()
|
|
self.logger.log("상품수정 작업이 시작되었습니다.", level=logging.INFO)
|
|
self.PercentyJob_button.setEnabled(False)
|
|
|
|
# 디스코드 알림 전송 (토글이 활성화된 경우에만)
|
|
if self.discord_notify_toggle.isChecked():
|
|
# 현재 시간 포맷팅
|
|
start_time = self.job_start_time.strftime("%Y-%m-%d %H:%M:%S")
|
|
|
|
# 선택된 그룹
|
|
selected_group = self.group_selector.currentText()
|
|
|
|
# 수정 대상 확인
|
|
modification_targets = []
|
|
if self.toggle_states.get('title', False):
|
|
modification_targets.append("상품명")
|
|
if self.toggle_states.get('optionTrnas', False):
|
|
modification_targets.append("옵션명AI번역")
|
|
if self.toggle_states.get('optionIMGTrans', False):
|
|
modification_targets.append("옵션-이미지번역")
|
|
if self.toggle_states.get('optionAutoSelect', False):
|
|
modification_targets.append("옵션-자동선택")
|
|
if self.toggle_states.get('price', False):
|
|
modification_targets.append("가격")
|
|
if self.toggle_states.get('thumb', False):
|
|
modification_targets.append("썸네일")
|
|
if self.toggle_states.get('tag', False):
|
|
modification_targets.append("태그")
|
|
if self.toggle_states.get('detail_Option', False):
|
|
modification_targets.append("상페 설명 및 옵션명 추가")
|
|
if self.toggle_states.get('thumb', False):
|
|
modification_targets.append("상페 이미지 워터마크")
|
|
if self.toggle_states.get('group_index', False):
|
|
modification_targets.append(self.selected_group_label.text())
|
|
# self.selected_group_label.text()
|
|
|
|
# 수정 대상이 없으면 기본값 설정
|
|
if not modification_targets:
|
|
modification_targets = ["상품 정보"]
|
|
|
|
# 디스코드 알림 전송
|
|
self.discord_manager.send_job_start_notification(
|
|
self.discord_notify_toggle.isChecked(),
|
|
self.job_start_time,
|
|
self.group_selector.currentText(),
|
|
modification_targets
|
|
)
|
|
|
|
@Slot()
|
|
def on_PercentyJob_completed(self, total_products):
|
|
"""상품수정 완료 시 처리할 로직"""
|
|
|
|
self.total_progress_bar.setValue(100)
|
|
|
|
# 디스코드 알림 전송
|
|
if self.discord_notify_toggle.isChecked():
|
|
self.discord_manager.send_job_complete_notification(
|
|
self.discord_notify_toggle.isChecked(),
|
|
self.job_start_time,
|
|
self.group_selector.currentText(),
|
|
total_products
|
|
)
|
|
|
|
self.PercentyJob_button.setEnabled(True)
|
|
|
|
self.logger.log("상품수정 작업이 완료되었습니다.", level=logging.INFO)
|
|
QMessageBox.information(self, "작업 완료", f"총 {total_products}개의 상품 수정이 완료되었습니다.")
|
|
|
|
@Slot(str)
|
|
def on_PercentyJob_error(self, error_message):
|
|
"""상품수정 중 오류 발생 시 처리할 로직"""
|
|
self.logger.log(f"상품수정 작업 중 오류 발생: {error_message}", level=logging.ERROR)
|
|
self.PercentyJob_button.setEnabled(True)
|
|
|
|
@Slot(bool)
|
|
def set_progress_visibility(self, visible):
|
|
self.detail_progress_bar.setVisible(visible)
|
|
self.detail_progress_bar.setValue(0)
|
|
|
|
@Slot(bool)
|
|
def update_detail_progress_value(self, current, total):
|
|
self.update_detail_progress(current, total)
|
|
|
|
@Slot(bool)
|
|
def percentyJob_button_Enable(self, Enable):
|
|
self.PercentyJob_button.setEnabled(Enable)
|
|
|
|
|
|
|
|
def initialize_user_session(self):
|
|
"""
|
|
사용자 세션을 초기화하고, 로그인 후 추가 검증(예: 사용 기간 확인, 할인 이벤트 안내 등)을 진행합니다.
|
|
이 메서드는 AutoPercentyGUI의 __init__의 마지막 부분에서 호출하는 것이 좋습니다.
|
|
"""
|
|
# 예시로, 회원가입 시 저장된 full_user_info를 기반으로 사용 기간과 할인 이벤트 메시지를 확인
|
|
valid = self.supabase_manager.check_membership_validity(self.user_info)
|
|
if not valid:
|
|
QMessageBox.warning(self, "기간 만료", "사용 기간이 만료되었습니다. 재결제가 필요합니다.")
|
|
# 프로그램의 주요 기능 사용을 제한하는 로직을 추가할 수 있음.
|
|
else:
|
|
discount_msg = self.supabase_manager.get_membership_message(self.user_info)
|
|
if discount_msg:
|
|
QMessageBox.information(self, "재결제 할인 안내", discount_msg)
|
|
|
|
# 동시 접속 제한 검사 (예: can_login 메서드를 통해)
|
|
if not self.supabase_manager.can_login(self.user_info):
|
|
QMessageBox.warning(self, "동시 접속 제한", "동시 접속 제한을 초과하였습니다.")
|
|
# 로그인 후 추가 동작을 막거나, 프로그램 종료 처리
|
|
|
|
# 마지막 로그인 시간 업데이트는 로그인 성공 후 이미 처리되었거나,
|
|
# 별도로 update_last_login(user_id)를 호출할 수 있습니다.
|
|
self.supabase_manager.update_last_login(self.sp_user_id)
|