diff --git a/browser_control.py b/browser_control.py index b8c675e4..d03c8dc5 100644 --- a/browser_control.py +++ b/browser_control.py @@ -318,8 +318,8 @@ class BrowserController(QThread): # self.logger.log(f'크롬 창 핸들: {self.chrome_hwnd}', level=logging.DEBUG) 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() id_ed_mode = self.toggle_states.get('ed_mode', False) @@ -330,6 +330,8 @@ class BrowserController(QThread): self.logger.log('신규 상품 등록 페이지로 이동 중...', level=logging.INFO) await self.go_to_new_product_page() + await self.close_ad_if_exists_with_ESC_Key() + group_index = self.toggle_states['group_index'] self.logger.log('선택한 그룹 인덱스로 이동', level=logging.INFO) @@ -592,61 +594,42 @@ class BrowserController(QThread): return image_urls - async def close_ad_if_exists(self): + async def close_ad_if_exists_with_ESC_Key(self): """ESC 키를 두 번 전송하여 다이얼로그를 닫는 메서드""" try: - # ESC 키를 한 번 전송 - time.sleep(2) - 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) - - # ESC 키를 두 번째 전송 - esc_script = """ - () => { - 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) + # JavaScript로 ESC 키 이벤트 발생 + 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; + })() + """) + 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) + self.logger.log(f"ESC 키 전송 중 오류: {e}", level=logging.ERROR, exc_info=True) 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: # 다이얼로그가 나타날 때까지 최대 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) - - # 다이얼로그 엘리먼트를 먼저 가져옴 - 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()='닫기']]") @@ -672,24 +790,34 @@ class BrowserController(QThread): 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", { + "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: self.logger.log("다이얼로그가 나타나지 않았습니다.", level=logging.INFO) except Exception as e: 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): """광고 다이얼로그가 있으면 닫기 버튼을 클릭하는 메서드""" try: diff --git a/img/마술화분_0_drawing_4020200188626.jpg b/img/마술화분_0_drawing_4020200188626.jpg new file mode 100644 index 00000000..77ed20c1 Binary files /dev/null and b/img/마술화분_0_drawing_4020200188626.jpg differ diff --git a/img/마술화분_1_drawing_4020200188625.jpg b/img/마술화분_1_drawing_4020200188625.jpg new file mode 100644 index 00000000..77ed20c1 Binary files /dev/null and b/img/마술화분_1_drawing_4020200188625.jpg differ diff --git a/mainUI_SP.py b/mainUI_SP.py index a70f2e68..11ac29d1 100644 --- a/mainUI_SP.py +++ b/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.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 functools import partial @@ -58,7 +58,7 @@ class AutoPercentyGUI(QMainWindow): """ super().__init__() self.logger = logger - + # 로그 경로 설정 self.log_paths = log_paths self.initial_setting = False @@ -88,7 +88,7 @@ class AutoPercentyGUI(QMainWindow): self.initUI() self.initialize_user_session() - + self.logger.log(f"로그기록이 설정되었습니다.", level=logging.INFO) # update_log 인자가 있다면 표시 @@ -281,7 +281,7 @@ class AutoPercentyGUI(QMainWindow): self.log_display.append(message) # 스크롤을 항상 아래로 self.log_display.verticalScrollBar().setValue( - self.log_display.verticalScrollBar().maximum() + self.log_display.verticalScrollBar().maximum() ) except Exception as e: self.logger.log(f"로그 추가 중 오류 발생: {str(e)}", level=logging.ERROR, exc_info=True) @@ -305,7 +305,7 @@ class AutoPercentyGUI(QMainWindow): except Exception as e: self.logger.log(f"로그 다이얼로그 표시 중 오류 발생: {str(e)}", level=logging.ERROR, exc_info=True) QMessageBox.critical(self, "오류", f"로그 다이얼로그를 표시할 수 없습니다: {str(e)}") - + def load_config(self, file_path: str) -> configparser.ConfigParser: """ config.ini 파일을 읽어서 ConfigParser 객체로 반환 @@ -494,7 +494,7 @@ class AutoPercentyGUI(QMainWindow): 'detail_IMGTrans': False, 'detail_IMGTrans_type': False, 'debug_mode': False, - # 'ed_mode': False, + 'ed_mode': False, 'discord': False, 'watermark': False, 'clientID': "", @@ -599,11 +599,11 @@ class AutoPercentyGUI(QMainWindow): "default": """ QGroupBox { border: 1px solid #cccccc; - border-radius: 4px; + border-radius: 4px; background-color: #f9f9f9; color: #666666; font-weight: bold; - font-size: 12px; + font-size: 12px; margin: 10px; padding: 10px; } @@ -613,13 +613,13 @@ class AutoPercentyGUI(QMainWindow): padding: 0 5px; background: transparent; color: #666666; - font-weight: bold; + font-weight: bold; font-size: 12px; } """, "modern": """ QGroupBox { - border: none; + border: 1px solid rgba(0,0,0,0.1); border-radius: 8px; background-color: #ffffff; color: #333333; @@ -627,7 +627,6 @@ class AutoPercentyGUI(QMainWindow): font-weight: 600; margin: 12px; padding: 15px; - box-shadow: 0 2px 8px rgba(0,0,0,0.1); } QGroupBox::title { subcontrol-origin: margin; @@ -661,7 +660,7 @@ class AutoPercentyGUI(QMainWindow): """, "neumorphism": """ QGroupBox { - border: none; + border: 1px solid rgba(0,0,0,0.1); border-radius: 10px; background-color: #e0e0e0; color: #333333; @@ -669,9 +668,6 @@ class AutoPercentyGUI(QMainWindow): font-weight: bold; margin: 10px; 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 { subcontrol-origin: border; @@ -709,6 +705,13 @@ class AutoPercentyGUI(QMainWindow): sheet = styles.get(theme, styles["default"]) 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 theme == "modern": @@ -745,18 +748,18 @@ class AutoPercentyGUI(QMainWindow): min-width: 40px; min-height: 20px; padding: 0; - text-align: center; + text-align: center; font-size: 12px; color: #333333; } QPushButton:checked { background-color: #e0e0e0; color: #0078d7; - } - QPushButton:hover { + } + QPushButton:hover { background-color: #e8e8e8; - } - QPushButton:pressed { + } + QPushButton:pressed { background-color: #d0d0d0; } QPushButton:disabled { @@ -767,14 +770,13 @@ class AutoPercentyGUI(QMainWindow): "modern": """ QPushButton { background-color: #61afef; - border: none; + border: none; border-radius: 14px; min-width: 44px; min-height: 24px; padding: 0; font-size: 12px; color: #555555; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); } QPushButton:checked { background-color: qlineargradient( @@ -1449,7 +1451,7 @@ class AutoPercentyGUI(QMainWindow): 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.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.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_toggle_layout.addWidget(self.watermark_toggle_label) - self.watermark_toggle_layout.addStretch() self.watermark_toggle_layout.addWidget(self.watermark_toggle) 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_layout.addWidget(self.opacity_percent_label) - self.opacity_layout.addStretch() self.opacity_layout.addWidget(self.opacity_percent_input) 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_layout.addWidget(self.webhook_label) + self.webhook_layout.addStretch() self.webhook_layout.addWidget(self.webhook_input) + self.webhook_layout.addStretch() self.etc_toggle_layout.addWidget(self.webhook_widget) @@ -2434,7 +2436,7 @@ class AutoPercentyGUI(QMainWindow): button.setFixedHeight(height) if theme == "blue": button.setStyleSheet(""" - QPushButton { + QPushButton { background-color: #2196F3; color: white; border: none; @@ -2445,13 +2447,13 @@ class AutoPercentyGUI(QMainWindow): min-width: 100px; } QPushButton:hover { - background-color: #1976D2; + background-color: #1976D2; } QPushButton:pressed { background-color: #0D47A1; } QPushButton:disabled { - background-color: #BDBDBD; + background-color: #BDBDBD; color: #757575; } QPushButton::icon { @@ -3146,7 +3148,7 @@ class AutoPercentyGUI(QMainWindow): for key, value in self.toggle_states.items(): self.settings.setValue(f"toggle/{key}", value) # 각 토글 상태가 변경되었을 때 UI 업데이트 - self.update_toggle_ui(key) + self.update_toggle_ui(key) def update_toggle_ui(self, key): """토글 상태에 따라 UI 업데이트""" @@ -3261,9 +3263,15 @@ class AutoPercentyGUI(QMainWindow): 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)]) + self.watermark_text_input.setFocus() + self.watermark_text_input.setEnabled(True) + self.opacity_percent_input.setEnabled(True) else: # 워터마크 토글이 OFF 상태이면 워터마크 레이아웃 숨김 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: # 모두 꺼져 있으면 워터마크 토글과 레이아웃 숨기기 self.toggle_visibility(False, [(self.watermark_toggle, self.watermark_toggle_label)]) @@ -3442,6 +3450,7 @@ class AutoPercentyGUI(QMainWindow): elif key == 'detail_Option': label_text = self.detail_Option_toggle_label.text() self.detail_Option_toggle.setEnabled(is_checked) + self.detail_text_button.setEnabled(is_checked) elif key == 'detail_IMGTrans': label_text = self.detail_IMGTrans_toggle_label.text() # elif key == 'detail_IMGTans_type': @@ -3483,11 +3492,11 @@ class AutoPercentyGUI(QMainWindow): elif key == 'remove_overprice': # 가격초과제외 토글 label_text = self.remove_overprice_toggle_label.text() - + # 이미지 번역 관련 토글이 하나라도 켜져 있으면 워터마크 토글 보이기 if key in ['optionIMGTrans', 'detail_IMGTrans', 'thumb']: self.update_watermark_visibility() - + # 워터마크 토글이 켜져 있으면 watermark_layout 보이기 if key == 'watermark': self.toggle_visibility(is_checked, [ @@ -3682,7 +3691,7 @@ class AutoPercentyGUI(QMainWindow): 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("thumb_rmb_count", self.thumb_rmb_count_input.value()) - + # 새로 추가된 토글 버튼 상태 저장 self.settings.setValue("cat_rec", self.cat_rec_toggle.isChecked()) self.settings.setValue("fixed_keywords", self.keyword_fix_toggle.isChecked()) @@ -3738,6 +3747,9 @@ class AutoPercentyGUI(QMainWindow): self.toggle_states['discord'] = self.settings.value("discord", False, type=bool) 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 요소에 로드된 값 적용 if hasattr(self, 'discord_notify_toggle'): self.discord_notify_toggle.setChecked(self.toggle_states['discord']) @@ -3778,7 +3790,7 @@ class AutoPercentyGUI(QMainWindow): self.toggle_states['cat_rec'] = self.cat_rec_toggle.isChecked() # 카테 추천 토글 상태 저장 self.toggle_states['fixed_keywords'] = self.keyword_fix_toggle.isChecked() # 키고정 토글 상태 저장 self.toggle_states['remove_overprice'] = self.remove_overprice_toggle.isChecked() # 가격초과제외 토글 상태 저장 - + # 기타 설정 값들도 저장 self.toggle_states['discord_webhook'] = self.webhook_input.text() self.toggle_states['watermark_text'] = self.watermark_text_input.text() @@ -3793,7 +3805,6 @@ class AutoPercentyGUI(QMainWindow): self.logger.log(f"토글 상태 저장 중 오류 발생: {e}", level=logging.ERROR, exc_info=True) return {} - def update_total_progress(self, current_value, total_value): if current_value == 0 or total_value <= 0: self.reset_stages() @@ -3877,7 +3888,7 @@ class AutoPercentyGUI(QMainWindow): self.sync_unwanted_words_to_supabase() self.logger.log('프로그램을 종료합니다...', level=logging.INFO) - + # 현재 설정 저장 self.save_settings() diff --git a/src/keyword/keyword_manager.py b/src/keyword/keyword_manager.py index fb31d027..bade73c1 100644 --- a/src/keyword/keyword_manager.py +++ b/src/keyword/keyword_manager.py @@ -4,7 +4,7 @@ from PySide6.QtWidgets import ( QMenu, QInputDialog,QProgressDialog, QApplication, QGroupBox, QFormLayout, QTextEdit ) 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 re @@ -37,7 +37,7 @@ class KeywordManager(QDialog): self.browser_dialog.setWindowModality(Qt.ApplicationModal) self.setWindowTitle("금지어 관리") - self.setGeometry(200, 200, 800, 600) + self.setGeometry(100, 100, 450, 750) self._setup_shortcuts() self.setStyleSheet(""" @@ -613,7 +613,8 @@ class KeywordManager(QDialog): item_widget = self._create_item_widget(keyword, grade, source, status) 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}) self.set_item_colors(list_item, grade) self.keyword_list.setItemWidget(list_item, item_widget) @@ -628,10 +629,10 @@ class KeywordManager(QDialog): try: item_widget = QWidget() # 단순 위젯 생성 layout = QHBoxLayout(item_widget) - layout.setContentsMargins(6, 6, 6, 6) + layout.setContentsMargins(3, 3, 3, 3) # 키워드 라벨 keyword_label = QLabel(f"{keyword} ({grade}) [{status}]") - font = QFont("Segoe UI", 12) + font = QFont("Segoe UI", 11) if source == "local": font.setBold(True) font.setUnderline(True) @@ -641,7 +642,7 @@ class KeywordManager(QDialog): layout.addWidget(keyword_label) # 검증 버튼 verify_button = QPushButton() - verify_button.setFixedSize(80, 40) + verify_button.setFixedSize(80, 30) # 키워드 ID 초기화 (None으로) keyword_id = None @@ -658,7 +659,7 @@ class KeywordManager(QDialog): if not keyword_id: self.logger.log(f"공통 금지어 '{keyword}'의 ID가 없습니다. record: {record}", level=logging.WARNING) elif status == "안전": - verify_button.setText("안전\n(재검증)") + verify_button.setText("안전(재검증)") else: verify_button.setText("검증") @@ -691,7 +692,7 @@ class KeywordManager(QDialog): # 스스검색 버튼 (네이버 검색) search_button = QPushButton("스스검색") - search_button.setFixedSize(100, 40) + search_button.setFixedSize(100, 30) search_button.setToolTip("해당 키워드로 네이버쇼핑 검색 실행") search_button.clicked.connect(lambda: self.search_keyword(keyword)) layout.addWidget(search_button) @@ -1670,7 +1671,19 @@ class KeywordManager(QDialog): keyword_label.setAlignment(Qt.AlignCenter) keyword_label.setStyleSheet("font-size: 18px; font-weight: bold;") 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: common_label = QLabel("공통금지어") diff --git a/tmp_images/translated_thumb_1.png b/tmp_images/translated_thumb_1.png new file mode 100644 index 00000000..3925ae87 Binary files /dev/null and b/tmp_images/translated_thumb_1.png differ diff --git a/tmp_images/translated_thumb_2.png b/tmp_images/translated_thumb_2.png new file mode 100644 index 00000000..5b43ac29 Binary files /dev/null and b/tmp_images/translated_thumb_2.png differ diff --git a/tmp_images/translated_thumb_3.png b/tmp_images/translated_thumb_3.png new file mode 100644 index 00000000..22e7e786 Binary files /dev/null and b/tmp_images/translated_thumb_3.png differ diff --git a/tmp_images/translated_thumb_4.png b/tmp_images/translated_thumb_4.png new file mode 100644 index 00000000..c6c9a85e Binary files /dev/null and b/tmp_images/translated_thumb_4.png differ diff --git a/tmp_images/translated_thumb_5.png b/tmp_images/translated_thumb_5.png new file mode 100644 index 00000000..0fc31ca6 Binary files /dev/null and b/tmp_images/translated_thumb_5.png differ diff --git a/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db b/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db index 6f460cb1..b83607f1 100644 Binary files a/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db and b/user_data/user_data_909d2ef8-7053-4006-ab40-49eb49f20383.db differ