브라우저 제어 클래스 광고 닫기 기능 개선: ESC 키를 두 번 전송하여 다이얼로그를 닫는 메서드를 개선하고, 새로운 다이얼로그 처리 로직을 추가하여 다양한 상황에서 광고를 효과적으로 닫을 수 있도록 수정함. UI 요소 및 레이아웃 조정, 키워드 관리 다이얼로그의 크기 및 스타일 개선.
|
|
@ -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,15 +594,205 @@ 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',
|
||||||
|
keyCode: 27,
|
||||||
|
which: 27,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
document.dispatchEvent(escEvent);
|
||||||
|
return true;
|
||||||
|
})()
|
||||||
|
""")
|
||||||
|
time.sleep(1)
|
||||||
|
await self.page.evaluate("""
|
||||||
|
(() => {
|
||||||
|
const escEvent = new KeyboardEvent('keydown', {
|
||||||
|
key: 'Escape',
|
||||||
|
code: 'Escape',
|
||||||
|
keyCode: 27,
|
||||||
|
which: 27,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
});
|
||||||
|
document.dispatchEvent(escEvent);
|
||||||
|
return true;
|
||||||
|
})()
|
||||||
|
""")
|
||||||
|
self.logger.log("ESC 키를 전송하여 다이얼로그를 닫았습니다.", level=logging.INFO)
|
||||||
|
except Exception as e:
|
||||||
|
self.logger.log(f"ESC 키 전송 중 오류: {e}", level=logging.ERROR, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
|
async def close_ad_if_exists_new(self):
|
||||||
|
"""
|
||||||
|
다이얼로그가 존재하면 해당 다이얼로그 내부의 "닫기" 버튼을 클릭하고,
|
||||||
|
다이얼로그를 찾지 못하거나 내부에 닫기 버튼이 없으면 페이지 전체에서 "닫기" 버튼을 검색하여 클릭하는 메서드
|
||||||
|
"""
|
||||||
|
# 새로운 다이얼로그의 닫기(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:
|
||||||
|
# 다이얼로그가 나타날 때까지 최대 3초 대기
|
||||||
|
dialog_element = await self.page.wait_for_selector(self.close_ad_dialog_locator, timeout=3000, state='visible')
|
||||||
|
self.logger.log("다이얼로그가 발견되었습니다. 내부의 닫기 버튼을 찾습니다.", level=logging.INFO)
|
||||||
|
|
||||||
|
if dialog_element:
|
||||||
|
# 다이얼로그 내부에서 "닫기" 텍스트를 가진 버튼을 xpath로 찾음
|
||||||
|
close_button = await dialog_element.query_selector("xpath=.//button[span[text()='닫기']]")
|
||||||
|
if close_button:
|
||||||
|
await close_button.click()
|
||||||
|
self.logger.log("다이얼로그 내부의 닫기 버튼을 클릭하여 닫았습니다.", level=logging.INFO)
|
||||||
|
return # 닫기 성공 시 함수 종료
|
||||||
|
else:
|
||||||
|
self.logger.log("다이얼로그 내부에서 닫기 버튼을 찾지 못했습니다.", level=logging.WARNING)
|
||||||
|
else:
|
||||||
|
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", {
|
await self.page.dispatch_event("body", "keydown", {
|
||||||
"key": "Escape",
|
"key": "Escape",
|
||||||
"code": "Escape",
|
"code": "Escape",
|
||||||
|
|
@ -614,82 +806,18 @@ class BrowserController(QThread):
|
||||||
"keyCode": 27,
|
"keyCode": 27,
|
||||||
"which": 27
|
"which": 27
|
||||||
})
|
})
|
||||||
|
|
||||||
await self.page.wait_for_timeout(100)
|
await self.page.wait_for_timeout(100)
|
||||||
|
|
||||||
# ESC 키를 두 번째 전송
|
self.logger.log("ECS를 전송하여 닫았습니다.", level=logging.INFO)
|
||||||
esc_script = """
|
return
|
||||||
() => {
|
|
||||||
const dispatchEsc = () => {
|
|
||||||
const eventDown = new KeyboardEvent('keydown', {
|
|
||||||
key: 'Escape',
|
|
||||||
code: 'Escape',
|
|
||||||
keyCode: 27,
|
|
||||||
which: 27,
|
|
||||||
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"닫기 중 오류: {e}", level=logging.ERROR, exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
async def close_ad_if_exists_new(self):
|
|
||||||
"""
|
|
||||||
다이얼로그가 존재하면 해당 다이얼로그 내부의 "닫기" 버튼을 클릭하고,
|
|
||||||
다이얼로그를 찾지 못하거나 내부에 닫기 버튼이 없으면 페이지 전체에서 "닫기" 버튼을 검색하여 클릭하는 메서드
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# 다이얼로그가 나타날 때까지 최대 3초 대기
|
|
||||||
await self.page.wait_for_selector(self.close_ad_dialog_locator, timeout=3000, state='visible')
|
|
||||||
self.logger.log("다이얼로그가 발견되었습니다. 내부의 닫기 버튼을 찾습니다.", level=logging.INFO)
|
|
||||||
|
|
||||||
# 다이얼로그 엘리먼트를 먼저 가져옴
|
|
||||||
dialog_element = await self.page.query_selector(self.close_ad_dialog_locator)
|
|
||||||
if dialog_element:
|
|
||||||
# 다이얼로그 내부에서 "닫기" 텍스트를 가진 버튼을 xpath로 찾음
|
|
||||||
close_button = await dialog_element.query_selector("xpath=.//button[span[text()='닫기']]")
|
|
||||||
if close_button:
|
|
||||||
await close_button.click()
|
|
||||||
self.logger.log("다이얼로그 내부의 닫기 버튼을 클릭하여 닫았습니다.", level=logging.INFO)
|
|
||||||
return # 닫기 성공 시 함수 종료
|
|
||||||
else:
|
|
||||||
self.logger.log("다이얼로그 내부에서 닫기 버튼을 찾지 못했습니다.", level=logging.WARNING)
|
|
||||||
else:
|
|
||||||
self.logger.log("대기 후에도 다이얼로그 엘리먼트를 찾지 못했습니다.", level=logging.WARNING)
|
|
||||||
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:
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 66 KiB |
|
After Width: | Height: | Size: 66 KiB |
39
mainUI_SP.py
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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': "",
|
||||||
|
|
@ -619,7 +619,7 @@ class AutoPercentyGUI(QMainWindow):
|
||||||
""",
|
""",
|
||||||
"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":
|
||||||
|
|
@ -774,7 +777,6 @@ class AutoPercentyGUI(QMainWindow):
|
||||||
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)
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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("공통금지어")
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 164 KiB |
|
After Width: | Height: | Size: 555 KiB |
|
After Width: | Height: | Size: 459 KiB |
|
After Width: | Height: | Size: 339 KiB |
|
After Width: | Height: | Size: 628 KiB |