브라우저 제어 클래스 광고 닫기 기능 개선: ESC 키를 두 번 전송하여 다이얼로그를 닫는 메서드를 개선하고, 새로운 다이얼로그 처리 로직을 추가하여 다양한 상황에서 광고를 효과적으로 닫을 수 있도록 수정함. UI 요소 및 레이아웃 조정, 키워드 관리 다이얼로그의 크기 및 스타일 개선.

This commit is contained in:
Envy_PC 2025-05-08 00:40:32 +09:00
parent 456dd6eb2e
commit 08f7c36de8
11 changed files with 267 additions and 115 deletions

View File

@ -318,8 +318,8 @@ class BrowserController(QThread):
# self.logger.log(f'크롬 창 핸들: {self.chrome_hwnd}', level=logging.DEBUG) # self.logger.log(f'크롬 창 핸들: {self.chrome_hwnd}', level=logging.DEBUG)
await self.login() await self.login()
await self.close_ad_if_exists_with_ESC_Key()
await self.close_ad_if_exists_new() await self.close_ad_if_exists_new()
await self.close_ad_if_exists()
id_ed_mode = self.toggle_states.get('ed_mode', False) id_ed_mode = self.toggle_states.get('ed_mode', False)
@ -330,6 +330,8 @@ class BrowserController(QThread):
self.logger.log('신규 상품 등록 페이지로 이동 중...', level=logging.INFO) self.logger.log('신규 상품 등록 페이지로 이동 중...', level=logging.INFO)
await self.go_to_new_product_page() await self.go_to_new_product_page()
await self.close_ad_if_exists_with_ESC_Key()
group_index = self.toggle_states['group_index'] group_index = self.toggle_states['group_index']
self.logger.log('선택한 그룹 인덱스로 이동', level=logging.INFO) self.logger.log('선택한 그룹 인덱스로 이동', level=logging.INFO)
@ -592,61 +594,42 @@ class BrowserController(QThread):
return image_urls return image_urls
async def close_ad_if_exists(self): async def close_ad_if_exists_with_ESC_Key(self):
"""ESC 키를 두 번 전송하여 다이얼로그를 닫는 메서드""" """ESC 키를 두 번 전송하여 다이얼로그를 닫는 메서드"""
try: try:
# ESC 키를 한 번 전송 # JavaScript로 ESC 키 이벤트 발생
time.sleep(2) await self.page.evaluate("""
await self.page.keyboard.press("Escape") (() => {
# 잠시 대기 (필요시, 다이얼로그 애니메이션 등 고려) const escEvent = new KeyboardEvent('keydown', {
await self.page.wait_for_timeout(100) key: 'Escape',
code: 'Escape',
await self.page.dispatch_event("body", "keydown", { keyCode: 27,
"key": "Escape", which: 27,
"code": "Escape", bubbles: true,
"keyCode": 27, cancelable: true
"which": 27 });
}) document.dispatchEvent(escEvent);
await self.page.wait_for_timeout(50) return true;
await self.page.dispatch_event("body", "keyup", { })()
"key": "Escape", """)
"code": "Escape", time.sleep(1)
"keyCode": 27, await self.page.evaluate("""
"which": 27 (() => {
}) const escEvent = new KeyboardEvent('keydown', {
key: 'Escape',
await self.page.wait_for_timeout(100) code: 'Escape',
keyCode: 27,
# ESC 키를 두 번째 전송 which: 27,
esc_script = """ bubbles: true,
() => { cancelable: true
const dispatchEsc = () => { });
const eventDown = new KeyboardEvent('keydown', { document.dispatchEvent(escEvent);
key: 'Escape', return true;
code: 'Escape', })()
keyCode: 27, """)
which: 27, self.logger.log("ESC 키를 전송하여 다이얼로그를 닫았습니다.", level=logging.INFO)
bubbles: true
});
document.dispatchEvent(eventDown);
const eventUp = new KeyboardEvent('keyup', {
key: 'Escape',
code: 'Escape',
keyCode: 27,
which: 27,
bubbles: true
});
document.dispatchEvent(eventUp);
};
dispatchEsc();
}
"""
# 첫 번째 ESC 전송
await self.page.evaluate(esc_script)
self.logger.log("ESC 키를 두 번 전송하여 다이얼로그를 닫았습니다.", level=logging.INFO)
except Exception as e: except Exception as e:
self.logger.log(f"ESC 키 전송 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"ESC 키 전송 중 오류: {e}", level=logging.ERROR, exc_info=True)
async def close_ad_if_exists_new(self): async def close_ad_if_exists_new(self):
@ -654,13 +637,148 @@ class BrowserController(QThread):
다이얼로그가 존재하면 해당 다이얼로그 내부의 "닫기" 버튼을 클릭하고, 다이얼로그가 존재하면 해당 다이얼로그 내부의 "닫기" 버튼을 클릭하고,
다이얼로그를 찾지 못하거나 내부에 닫기 버튼이 없으면 페이지 전체에서 "닫기" 버튼을 검색하여 클릭하는 메서드 다이얼로그를 찾지 못하거나 내부에 닫기 버튼이 없으면 페이지 전체에서 "닫기" 버튼을 검색하여 클릭하는 메서드
""" """
# 새로운 다이얼로그의 닫기(X) 버튼 먼저 시도
self.logger.log("새로운 다이얼로그의 닫기(X) 버튼 먼저 시도", level=logging.INFO)
# 1. 모달 다이얼로그 처리 (ant-modal-wrap)
try:
# 모달 처리 JavaScript 코드
modal_js = """
() => {
// 모달 찾기
const modalWraps = document.querySelectorAll('.ant-modal-wrap.ant-modal-centered');
if (modalWraps.length === 0) return 0;
console.log(`${modalWraps.length}개의 모달 발견`);
let closed = 0;
// 모달에 대해
for (const modal of modalWraps) {
try {
// 1. 닫기 버튼 찾기
const closeBtn = modal.querySelector('.ant-modal-close');
if (closeBtn) {
closeBtn.click();
console.log('모달 닫기 버튼 클릭');
closed++;
continue;
}
// 2. '닫기' 텍스트 버튼 찾기
const buttons = modal.querySelectorAll('button');
let found = false;
for (const btn of buttons) {
if (btn.textContent.includes('닫기')) {
btn.click();
console.log('닫기 텍스트 버튼 클릭');
closed++;
found = true;
break;
}
}
if (found) continue;
// 3. 마지막 수단: 모달 숨기기
modal.style.display = 'none';
const mask = document.querySelector('.ant-modal-mask');
if (mask) mask.style.display = 'none';
console.log('모달 강제 숨김');
closed++;
} catch (e) {
console.error('모달 처리 오류:', e);
}
}
return closed;
}
"""
modal_count = await self.page.evaluate(modal_js)
if modal_count > 0:
self.logger.log(f"{modal_count}개의 모달 다이얼로그 닫기 처리됨", level=logging.INFO)
await self.page.wait_for_timeout(1000) # 모달이 사라질 때까지 대기
return True
except Exception as e:
self.logger.log(f"모달 다이얼로그 처리 중 오류: {e}", level=logging.INFO)
# 2. drawer-close 버튼 처리
try:
drawer_js = """
() => {
const drawerCloseButtons = Array.from(document.querySelectorAll('button.ant-drawer-close'));
let count = 0;
drawerCloseButtons.forEach(button => {
try {
button.click();
console.log('drawer-close 버튼 클릭');
count++;
} catch (e) {
console.error('drawer-close 클릭 오류:', e);
}
});
return count;
}
"""
drawer_count = await self.page.evaluate(drawer_js)
if drawer_count > 0:
self.logger.log(f"{drawer_count}개의 drawer-close 버튼 클릭됨", level=logging.INFO)
await self.page.wait_for_timeout(1000)
return True
except Exception as e:
self.logger.log(f"drawer-close 버튼 처리 중 오류: {e}", level=logging.INFO)
# 3. 배경 클릭 시도
try:
backdrop_js = """
() => {
const backdropSelectors = [
'.ant-modal-mask',
'.ant-drawer-mask',
'.modal-backdrop',
'.dialog-backdrop',
'[role="dialog"] + div',
'.overlay',
'.modal-overlay'
];
let clicked = 0;
for (const selector of backdropSelectors) {
const backdrops = document.querySelectorAll(selector);
for (const backdrop of backdrops) {
try {
backdrop.click();
console.log(`${selector} 배경 클릭`);
clicked++;
} catch (e) {
console.error(`${selector} 클릭 오류:`, e);
}
}
}
return clicked;
}
"""
backdrop_count = await self.page.evaluate(backdrop_js)
if backdrop_count > 0:
self.logger.log(f"{backdrop_count}개의 배경 요소 클릭됨", level=logging.INFO)
await self.page.wait_for_timeout(1000)
return True
except Exception as e:
self.logger.log(f"배경 클릭 처리 중 오류: {e}", level=logging.INFO)
# 4. ESC 키 시도
try:
await self.page.keyboard.down("Escape")
await self.page.wait_for_timeout(100)
await self.page.keyboard.up("Escape")
self.logger.log("ESC 키 전송 (down-up 방식)", level=logging.INFO)
await self.page.wait_for_timeout(1000)
except Exception as e:
self.logger.log(f"ESC 키 전송 중 오류: {e}", level=logging.INFO)
# 5. 기존 다이얼로그 처리 (이전 코드)
try: try:
# 다이얼로그가 나타날 때까지 최대 3초 대기 # 다이얼로그가 나타날 때까지 최대 3초 대기
await self.page.wait_for_selector(self.close_ad_dialog_locator, timeout=3000, state='visible') dialog_element = await self.page.wait_for_selector(self.close_ad_dialog_locator, timeout=3000, state='visible')
self.logger.log("다이얼로그가 발견되었습니다. 내부의 닫기 버튼을 찾습니다.", level=logging.INFO) self.logger.log("다이얼로그가 발견되었습니다. 내부의 닫기 버튼을 찾습니다.", level=logging.INFO)
# 다이얼로그 엘리먼트를 먼저 가져옴
dialog_element = await self.page.query_selector(self.close_ad_dialog_locator)
if dialog_element: if dialog_element:
# 다이얼로그 내부에서 "닫기" 텍스트를 가진 버튼을 xpath로 찾음 # 다이얼로그 내부에서 "닫기" 텍스트를 가진 버튼을 xpath로 찾음
close_button = await dialog_element.query_selector("xpath=.//button[span[text()='닫기']]") close_button = await dialog_element.query_selector("xpath=.//button[span[text()='닫기']]")
@ -672,24 +790,34 @@ class BrowserController(QThread):
self.logger.log("다이얼로그 내부에서 닫기 버튼을 찾지 못했습니다.", level=logging.WARNING) self.logger.log("다이얼로그 내부에서 닫기 버튼을 찾지 못했습니다.", level=logging.WARNING)
else: else:
self.logger.log("대기 후에도 다이얼로그 엘리먼트를 찾지 못했습니다.", level=logging.WARNING) self.logger.log("대기 후에도 다이얼로그 엘리먼트를 찾지 못했습니다.", level=logging.WARNING)
try:
await self.page.keyboard.press("Escape")
await self.page.wait_for_timeout(100)
await self.page.dispatch_event("body", "keydown", {
"key": "Escape",
"code": "Escape",
"keyCode": 27,
"which": 27
})
await self.page.wait_for_timeout(50)
await self.page.dispatch_event("body", "keyup", {
"key": "Escape",
"code": "Escape",
"keyCode": 27,
"which": 27
})
await self.page.wait_for_timeout(100)
self.logger.log("ECS를 전송하여 닫았습니다.", level=logging.INFO)
return
except Exception as e:
self.logger.log(f"닫기 중 오류: {e}", level=logging.ERROR, exc_info=True)
except TimeoutError: except TimeoutError:
self.logger.log("다이얼로그가 나타나지 않았습니다.", level=logging.INFO) self.logger.log("다이얼로그가 나타나지 않았습니다.", level=logging.INFO)
except Exception as e: except Exception as e:
self.logger.log(f"다이얼로그 닫기 시도 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"다이얼로그 닫기 시도 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
# 다이얼로그에서 닫기 버튼을 찾지 못한 경우, 페이지 전체에서 "닫기" 버튼을 검색
self.logger.log("페이지 전체에서 '닫기' 버튼을 검색합니다.", level=logging.INFO)
try:
global_close_button = await self.page.query_selector("xpath=//button[span[text()='닫기']]")
if global_close_button:
await global_close_button.click()
self.logger.log("페이지 전체에서 '닫기' 버튼을 클릭하였습니다.", level=logging.INFO)
else:
self.logger.log("페이지 전체에서 '닫기' 버튼을 찾지 못했습니다.", level=logging.WARNING)
except Exception as e:
self.logger.log(f"글로벌 '닫기' 버튼 검색 및 클릭 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
async def close_ad_if_exists_ori(self): async def close_ad_if_exists_ori(self):
"""광고 다이얼로그가 있으면 닫기 버튼을 클릭하는 메서드""" """광고 다이얼로그가 있으면 닫기 버튼을 클릭하는 메서드"""
try: try:

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View File

@ -1,6 +1,6 @@
from PySide6.QtWidgets import QApplication, QFrame, QTabWidget, QScrollBar, QInputDialog, QMainWindow, QWidget, QMessageBox, QSpinBox, QPushButton, QGroupBox, QFormLayout, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy, QComboBox, QDialog from PySide6.QtWidgets import QApplication, QFrame, QTabWidget, QScrollBar, QInputDialog, QMainWindow, QWidget, QMessageBox, QSpinBox, QPushButton, QGroupBox, QFormLayout, QVBoxLayout, QGridLayout, QTextEdit, QLabel, QLineEdit, QHBoxLayout, QProgressBar, QSizePolicy, QComboBox, QDialog, QGraphicsDropShadowEffect
from PySide6.QtCore import Qt, Signal, Slot, QRect, QSettings, QTimer, QThreadPool from PySide6.QtCore import Qt, Signal, Slot, QRect, QSettings, QTimer, QThreadPool
from PySide6.QtGui import QGuiApplication, QFont, QIcon, QPixmap, QTextOption from PySide6.QtGui import QGuiApplication, QFont, QIcon, QPixmap, QTextOption, QColor
from typing import Dict, Any, List, Optional, Tuple from typing import Dict, Any, List, Optional, Tuple
from functools import partial from functools import partial
@ -281,7 +281,7 @@ class AutoPercentyGUI(QMainWindow):
self.log_display.append(message) self.log_display.append(message)
# 스크롤을 항상 아래로 # 스크롤을 항상 아래로
self.log_display.verticalScrollBar().setValue( self.log_display.verticalScrollBar().setValue(
self.log_display.verticalScrollBar().maximum() self.log_display.verticalScrollBar().maximum()
) )
except Exception as e: except Exception as e:
self.logger.log(f"로그 추가 중 오류 발생: {str(e)}", level=logging.ERROR, exc_info=True) self.logger.log(f"로그 추가 중 오류 발생: {str(e)}", level=logging.ERROR, exc_info=True)
@ -494,7 +494,7 @@ class AutoPercentyGUI(QMainWindow):
'detail_IMGTrans': False, 'detail_IMGTrans': False,
'detail_IMGTrans_type': False, 'detail_IMGTrans_type': False,
'debug_mode': False, 'debug_mode': False,
# 'ed_mode': False, 'ed_mode': False,
'discord': False, 'discord': False,
'watermark': False, 'watermark': False,
'clientID': "", 'clientID': "",
@ -599,11 +599,11 @@ class AutoPercentyGUI(QMainWindow):
"default": """ "default": """
QGroupBox { QGroupBox {
border: 1px solid #cccccc; border: 1px solid #cccccc;
border-radius: 4px; border-radius: 4px;
background-color: #f9f9f9; background-color: #f9f9f9;
color: #666666; color: #666666;
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
margin: 10px; margin: 10px;
padding: 10px; padding: 10px;
} }
@ -613,13 +613,13 @@ class AutoPercentyGUI(QMainWindow):
padding: 0 5px; padding: 0 5px;
background: transparent; background: transparent;
color: #666666; color: #666666;
font-weight: bold; font-weight: bold;
font-size: 12px; font-size: 12px;
} }
""", """,
"modern": """ "modern": """
QGroupBox { QGroupBox {
border: none; border: 1px solid rgba(0,0,0,0.1);
border-radius: 8px; border-radius: 8px;
background-color: #ffffff; background-color: #ffffff;
color: #333333; color: #333333;
@ -627,7 +627,6 @@ class AutoPercentyGUI(QMainWindow):
font-weight: 600; font-weight: 600;
margin: 12px; margin: 12px;
padding: 15px; padding: 15px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
} }
QGroupBox::title { QGroupBox::title {
subcontrol-origin: margin; subcontrol-origin: margin;
@ -661,7 +660,7 @@ class AutoPercentyGUI(QMainWindow):
""", """,
"neumorphism": """ "neumorphism": """
QGroupBox { QGroupBox {
border: none; border: 1px solid rgba(0,0,0,0.1);
border-radius: 10px; border-radius: 10px;
background-color: #e0e0e0; background-color: #e0e0e0;
color: #333333; color: #333333;
@ -669,9 +668,6 @@ class AutoPercentyGUI(QMainWindow):
font-weight: bold; font-weight: bold;
margin: 10px; margin: 10px;
padding: 15px; padding: 15px;
box-shadow:
inset 2px 2px 5px rgba(255,255,255,0.8),
inset -2px -2px 5px rgba(0,0,0,0.1);
} }
QGroupBox::title { QGroupBox::title {
subcontrol-origin: border; subcontrol-origin: border;
@ -709,6 +705,13 @@ class AutoPercentyGUI(QMainWindow):
sheet = styles.get(theme, styles["default"]) sheet = styles.get(theme, styles["default"])
group_box.setStyleSheet(sheet) group_box.setStyleSheet(sheet)
# 그림자 효과 추가
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(15)
shadow.setColor(QColor(0, 0, 0, 30))
shadow.setOffset(0, 2)
group_box.setGraphicsEffect(shadow)
# 레이아웃 마진/간격 조정 (테마별로 느낌 달리) # 레이아웃 마진/간격 조정 (테마별로 느낌 달리)
if layout: if layout:
if theme == "modern": if theme == "modern":
@ -745,18 +748,18 @@ class AutoPercentyGUI(QMainWindow):
min-width: 40px; min-width: 40px;
min-height: 20px; min-height: 20px;
padding: 0; padding: 0;
text-align: center; text-align: center;
font-size: 12px; font-size: 12px;
color: #333333; color: #333333;
} }
QPushButton:checked { QPushButton:checked {
background-color: #e0e0e0; background-color: #e0e0e0;
color: #0078d7; color: #0078d7;
} }
QPushButton:hover { QPushButton:hover {
background-color: #e8e8e8; background-color: #e8e8e8;
} }
QPushButton:pressed { QPushButton:pressed {
background-color: #d0d0d0; background-color: #d0d0d0;
} }
QPushButton:disabled { QPushButton:disabled {
@ -767,14 +770,13 @@ class AutoPercentyGUI(QMainWindow):
"modern": """ "modern": """
QPushButton { QPushButton {
background-color: #61afef; background-color: #61afef;
border: none; border: none;
border-radius: 14px; border-radius: 14px;
min-width: 44px; min-width: 44px;
min-height: 24px; min-height: 24px;
padding: 0; padding: 0;
font-size: 12px; font-size: 12px;
color: #555555; color: #555555;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
} }
QPushButton:checked { QPushButton:checked {
background-color: qlineargradient( background-color: qlineargradient(
@ -1449,7 +1451,7 @@ class AutoPercentyGUI(QMainWindow):
self.max_option_count_input.setMaximum(50) self.max_option_count_input.setMaximum(50)
self.max_option_count_input.setValue(self.toggle_states.get("max_option_count", 20)) self.max_option_count_input.setValue(self.toggle_states.get("max_option_count", 20))
self.max_option_count_input.valueChanged.connect(self.update_max_option_count) self.max_option_count_input.valueChanged.connect(self.update_max_option_count)
self.max_option_count_input.setFixedWidth(60) self.max_option_count_input.setFixedWidth(90)
self.max_option_widget.enterEvent = lambda e: self.show_manual_html( self.max_option_widget.enterEvent = lambda e: self.show_manual_html(
self.option_manual_group, self.option_manual_group,
"🔢 최대 옵션 수 설정", "🔢 최대 옵션 수 설정",
@ -2025,7 +2027,6 @@ class AutoPercentyGUI(QMainWindow):
self.watermark_widget.leaveEvent = lambda e: self.reset_manual(self.detail_manual_group, self.detail_manual_label) self.watermark_widget.leaveEvent = lambda e: self.reset_manual(self.detail_manual_group, self.detail_manual_label)
self.watermark_toggle_layout.addWidget(self.watermark_toggle_label) self.watermark_toggle_layout.addWidget(self.watermark_toggle_label)
self.watermark_toggle_layout.addStretch()
self.watermark_toggle_layout.addWidget(self.watermark_toggle) self.watermark_toggle_layout.addWidget(self.watermark_toggle)
self.detail_toggle_layout.addWidget(self.watermark_widget) self.detail_toggle_layout.addWidget(self.watermark_widget)
@ -2081,7 +2082,6 @@ class AutoPercentyGUI(QMainWindow):
self.opacity_widget.leaveEvent = lambda e: self.reset_manual(self.detail_manual_group, self.detail_manual_label) self.opacity_widget.leaveEvent = lambda e: self.reset_manual(self.detail_manual_group, self.detail_manual_label)
self.opacity_layout.addWidget(self.opacity_percent_label) self.opacity_layout.addWidget(self.opacity_percent_label)
self.opacity_layout.addStretch()
self.opacity_layout.addWidget(self.opacity_percent_input) self.opacity_layout.addWidget(self.opacity_percent_input)
self.detail_toggle_layout.addWidget(self.opacity_widget) self.detail_toggle_layout.addWidget(self.opacity_widget)
@ -2190,7 +2190,9 @@ class AutoPercentyGUI(QMainWindow):
self.webhook_widget.leaveEvent = lambda e: self.reset_manual(self.etc_manual_group, self.etc_manual_label) self.webhook_widget.leaveEvent = lambda e: self.reset_manual(self.etc_manual_group, self.etc_manual_label)
self.webhook_layout.addWidget(self.webhook_label) self.webhook_layout.addWidget(self.webhook_label)
self.webhook_layout.addStretch()
self.webhook_layout.addWidget(self.webhook_input) self.webhook_layout.addWidget(self.webhook_input)
self.webhook_layout.addStretch()
self.etc_toggle_layout.addWidget(self.webhook_widget) self.etc_toggle_layout.addWidget(self.webhook_widget)
@ -2434,7 +2436,7 @@ class AutoPercentyGUI(QMainWindow):
button.setFixedHeight(height) button.setFixedHeight(height)
if theme == "blue": if theme == "blue":
button.setStyleSheet(""" button.setStyleSheet("""
QPushButton { QPushButton {
background-color: #2196F3; background-color: #2196F3;
color: white; color: white;
border: none; border: none;
@ -3146,7 +3148,7 @@ class AutoPercentyGUI(QMainWindow):
for key, value in self.toggle_states.items(): for key, value in self.toggle_states.items():
self.settings.setValue(f"toggle/{key}", value) self.settings.setValue(f"toggle/{key}", value)
# 각 토글 상태가 변경되었을 때 UI 업데이트 # 각 토글 상태가 변경되었을 때 UI 업데이트
self.update_toggle_ui(key) self.update_toggle_ui(key)
def update_toggle_ui(self, key): def update_toggle_ui(self, key):
"""토글 상태에 따라 UI 업데이트""" """토글 상태에 따라 UI 업데이트"""
@ -3261,9 +3263,15 @@ class AutoPercentyGUI(QMainWindow):
if self.watermark_toggle.isChecked(): if self.watermark_toggle.isChecked():
# 워터마크 토글이 ON 상태이면 워터마크 레이아웃도 보이게 함 # 워터마크 토글이 ON 상태이면 워터마크 레이아웃도 보이게 함
self.toggle_visibility(True, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)]) self.toggle_visibility(True, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
self.watermark_text_input.setFocus()
self.watermark_text_input.setEnabled(True)
self.opacity_percent_input.setEnabled(True)
else: else:
# 워터마크 토글이 OFF 상태이면 워터마크 레이아웃 숨김 # 워터마크 토글이 OFF 상태이면 워터마크 레이아웃 숨김
self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)]) self.toggle_visibility(False, [(self.watermark_text_input, self.watermark_text_label), (self.opacity_percent_input, self.opacity_percent_label)])
self.watermark_text_input.setFocus()
self.watermark_text_input.setEnabled(False)
self.opacity_percent_input.setEnabled(False)
else: else:
# 모두 꺼져 있으면 워터마크 토글과 레이아웃 숨기기 # 모두 꺼져 있으면 워터마크 토글과 레이아웃 숨기기
self.toggle_visibility(False, [(self.watermark_toggle, self.watermark_toggle_label)]) self.toggle_visibility(False, [(self.watermark_toggle, self.watermark_toggle_label)])
@ -3442,6 +3450,7 @@ class AutoPercentyGUI(QMainWindow):
elif key == 'detail_Option': elif key == 'detail_Option':
label_text = self.detail_Option_toggle_label.text() label_text = self.detail_Option_toggle_label.text()
self.detail_Option_toggle.setEnabled(is_checked) self.detail_Option_toggle.setEnabled(is_checked)
self.detail_text_button.setEnabled(is_checked)
elif key == 'detail_IMGTrans': elif key == 'detail_IMGTrans':
label_text = self.detail_IMGTrans_toggle_label.text() label_text = self.detail_IMGTrans_toggle_label.text()
# elif key == 'detail_IMGTans_type': # elif key == 'detail_IMGTans_type':
@ -3738,6 +3747,9 @@ class AutoPercentyGUI(QMainWindow):
self.toggle_states['discord'] = self.settings.value("discord", False, type=bool) self.toggle_states['discord'] = self.settings.value("discord", False, type=bool)
self.toggle_states['discord_webhook'] = self.settings.value("discord_webhook", "", type=str) self.toggle_states['discord_webhook'] = self.settings.value("discord_webhook", "", type=str)
# 상세 텍스트 버튼 활성화 여부 설정
self.detail_text_button.setEnabled(self.settings.value("detail_text_button", False, type=bool))
# UI 요소에 로드된 값 적용 # UI 요소에 로드된 값 적용
if hasattr(self, 'discord_notify_toggle'): if hasattr(self, 'discord_notify_toggle'):
self.discord_notify_toggle.setChecked(self.toggle_states['discord']) self.discord_notify_toggle.setChecked(self.toggle_states['discord'])
@ -3793,7 +3805,6 @@ class AutoPercentyGUI(QMainWindow):
self.logger.log(f"토글 상태 저장 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) self.logger.log(f"토글 상태 저장 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
return {} return {}
def update_total_progress(self, current_value, total_value): def update_total_progress(self, current_value, total_value):
if current_value == 0 or total_value <= 0: if current_value == 0 or total_value <= 0:
self.reset_stages() self.reset_stages()

View File

@ -4,7 +4,7 @@ from PySide6.QtWidgets import (
QMenu, QInputDialog,QProgressDialog, QApplication, QGroupBox, QFormLayout, QTextEdit QMenu, QInputDialog,QProgressDialog, QApplication, QGroupBox, QFormLayout, QTextEdit
) )
from PySide6.QtGui import QPixmap, QKeySequence, QShortcut, QColor, QFont, QCursor, Qt from PySide6.QtGui import QPixmap, QKeySequence, QShortcut, QColor, QFont, QCursor, Qt
from PySide6.QtCore import QTimer, Signal from PySide6.QtCore import QTimer, Signal, QSize
import logging import logging
import re import re
@ -37,7 +37,7 @@ class KeywordManager(QDialog):
self.browser_dialog.setWindowModality(Qt.ApplicationModal) self.browser_dialog.setWindowModality(Qt.ApplicationModal)
self.setWindowTitle("금지어 관리") self.setWindowTitle("금지어 관리")
self.setGeometry(200, 200, 800, 600) self.setGeometry(100, 100, 450, 750)
self._setup_shortcuts() self._setup_shortcuts()
self.setStyleSheet(""" self.setStyleSheet("""
@ -613,7 +613,8 @@ class KeywordManager(QDialog):
item_widget = self._create_item_widget(keyword, grade, source, status) item_widget = self._create_item_widget(keyword, grade, source, status)
list_item = QListWidgetItem(self.keyword_list) list_item = QListWidgetItem(self.keyword_list)
list_item.setSizeHint(item_widget.sizeHint()) # list_item.setSizeHint(item_widget.sizeHint())
list_item.setSizeHint(QSize(-1,50))
list_item.setData(1, {"keyword": keyword, "grade": grade, "source": source}) list_item.setData(1, {"keyword": keyword, "grade": grade, "source": source})
self.set_item_colors(list_item, grade) self.set_item_colors(list_item, grade)
self.keyword_list.setItemWidget(list_item, item_widget) self.keyword_list.setItemWidget(list_item, item_widget)
@ -628,10 +629,10 @@ class KeywordManager(QDialog):
try: try:
item_widget = QWidget() # 단순 위젯 생성 item_widget = QWidget() # 단순 위젯 생성
layout = QHBoxLayout(item_widget) layout = QHBoxLayout(item_widget)
layout.setContentsMargins(6, 6, 6, 6) layout.setContentsMargins(3, 3, 3, 3)
# 키워드 라벨 # 키워드 라벨
keyword_label = QLabel(f"{keyword} ({grade}) [{status}]") keyword_label = QLabel(f"{keyword} ({grade}) [{status}]")
font = QFont("Segoe UI", 12) font = QFont("Segoe UI", 11)
if source == "local": if source == "local":
font.setBold(True) font.setBold(True)
font.setUnderline(True) font.setUnderline(True)
@ -641,7 +642,7 @@ class KeywordManager(QDialog):
layout.addWidget(keyword_label) layout.addWidget(keyword_label)
# 검증 버튼 # 검증 버튼
verify_button = QPushButton() verify_button = QPushButton()
verify_button.setFixedSize(80, 40) verify_button.setFixedSize(80, 30)
# 키워드 ID 초기화 (None으로) # 키워드 ID 초기화 (None으로)
keyword_id = None keyword_id = None
@ -658,7 +659,7 @@ class KeywordManager(QDialog):
if not keyword_id: if not keyword_id:
self.logger.log(f"공통 금지어 '{keyword}'의 ID가 없습니다. record: {record}", level=logging.WARNING) self.logger.log(f"공통 금지어 '{keyword}'의 ID가 없습니다. record: {record}", level=logging.WARNING)
elif status == "안전": elif status == "안전":
verify_button.setText("안전\n(재검증)") verify_button.setText("안전(재검증)")
else: else:
verify_button.setText("검증") verify_button.setText("검증")
@ -691,7 +692,7 @@ class KeywordManager(QDialog):
# 스스검색 버튼 (네이버 검색) # 스스검색 버튼 (네이버 검색)
search_button = QPushButton("스스검색") search_button = QPushButton("스스검색")
search_button.setFixedSize(100, 40) search_button.setFixedSize(100, 30)
search_button.setToolTip("해당 키워드로 네이버쇼핑 검색 실행") search_button.setToolTip("해당 키워드로 네이버쇼핑 검색 실행")
search_button.clicked.connect(lambda: self.search_keyword(keyword)) search_button.clicked.connect(lambda: self.search_keyword(keyword))
layout.addWidget(search_button) layout.addWidget(search_button)
@ -1671,6 +1672,18 @@ class KeywordManager(QDialog):
keyword_label.setStyleSheet("font-size: 18px; font-weight: bold;") keyword_label.setStyleSheet("font-size: 18px; font-weight: bold;")
title_layout.addWidget(keyword_label, 1) # 1은 stretch factor title_layout.addWidget(keyword_label, 1) # 1은 stretch factor
# 단축키안내 라벨
shortcut_label = QLabel(
"✔️숫자키(1~9)로 탭이동 가능 ✔️키보드 상하좌우키로 이동가능 ✔️ ESC = 닫기\n"
"✔️유사군코드의 S는 서비스업, G는 상품군"
)
shortcut_label.setWordWrap(True) # 텍스트 줄바꿈 활성화
shortcut_label.setStyleSheet("font-weight: bold; color: #555; margin-bottom: 12px;")
# keyword_label.setAlignment(Qt.AlignCenter)
# keyword_label.setStyleSheet("font-size: 18px; font-weight: bold;")
layout.addWidget(shortcut_label)
# 공통금지어 표시 라벨 (공통금지어인 경우만) # 공통금지어 표시 라벨 (공통금지어인 경우만)
if is_common: if is_common:
common_label = QLabel("공통금지어") common_label = QLabel("공통금지어")

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 KiB