웨일 안전장치
14
gui.py
|
|
@ -107,6 +107,9 @@ class AutoPercentyGUI(QWidget):
|
|||
# 이전에 저장된 설정 불러오기
|
||||
self.load_settings()
|
||||
|
||||
# 로거 초기화
|
||||
self.add_text_edit_logger()
|
||||
|
||||
# 프로그래스바 초기화
|
||||
self.update_total_progress(0,0)
|
||||
|
||||
|
|
@ -140,6 +143,17 @@ class AutoPercentyGUI(QWidget):
|
|||
self.logger.error(f"DB 파일 복사 중 오류 발생: {e}", 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 start_stage(self, stage_index):
|
||||
"""지정한 단계에 깜빡임 효과 적용"""
|
||||
if 0 <= stage_index < len(self.stage_labels):
|
||||
|
|
|
|||
101
logger_module.py
|
|
@ -1,8 +1,7 @@
|
|||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from PySide6.QtCore import Signal, QObject # PyQt5.QtCore.pyqtSignal -> PySide6.QtCore.Signal
|
||||
from PySide6.QtWidgets import QTextEdit
|
||||
from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=5):
|
||||
"""로거 설정을 위한 함수"""
|
||||
|
|
@ -24,84 +23,34 @@ def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, ba
|
|||
logger.addHandler(console_handler)
|
||||
|
||||
return logger
|
||||
|
||||
class QTextEditLogger(logging.Handler, QObject):
|
||||
"""
|
||||
PySide6 QTextEdit와 연동하여 로그 메시지를 출력하는 로깅 핸들러.
|
||||
"""
|
||||
appendHtml = Signal(str) # HTML 메시지를 전달하는 시그널
|
||||
scrollToBottom = Signal() # 텍스트 영역의 스크롤을 최하단으로 이동시키는 시그널
|
||||
appendHtml = pyqtSignal(str) # HTML 메시지를 전달할 시그널 정의
|
||||
scrollToBottom = pyqtSignal() # 스크롤을 최하단으로 이동시키는 시그널
|
||||
|
||||
def __init__(self, text_edit):
|
||||
"""
|
||||
QTextEditLogger 초기화.
|
||||
:param text_edit: 로그 메시지를 표시할 QTextEdit 위젯
|
||||
"""
|
||||
QObject.__init__(self) # QObject 초기화
|
||||
logging.Handler.__init__(self) # logging.Handler 초기화
|
||||
self.text_edit = text_edit # 로그를 출력할 QTextEdit 위젯
|
||||
|
||||
# 시그널 연결
|
||||
self.appendHtml.connect(self.text_edit.append)
|
||||
self.scrollToBottom.connect(self.scroll_to_bottom)
|
||||
def __init__(self):
|
||||
logging.Handler.__init__(self)
|
||||
QObject.__init__(self)
|
||||
|
||||
def emit(self, record):
|
||||
"""
|
||||
로그 레코드를 처리하고 QTextEdit에 출력.
|
||||
:param record: 로그 레코드 (LogRecord 객체)
|
||||
"""
|
||||
try:
|
||||
if not hasattr(record, "getMessage"):
|
||||
raise ValueError(f"Invalid LogRecord: {record}")
|
||||
|
||||
# 로그 레코드를 문자열로 포매팅
|
||||
msg = self.format(record) # format(record)는 getMessage를 호출
|
||||
color = {
|
||||
logging.DEBUG: "black",
|
||||
logging.INFO: "grey",
|
||||
logging.WARNING: "orange",
|
||||
logging.ERROR: "red",
|
||||
logging.CRITICAL: "purple",
|
||||
}.get(record.levelno, "black")
|
||||
|
||||
# HTML 스타일 적용한 메시지 생성
|
||||
html_message = f"<span style=\"color:{color};\">{msg}</span><br/>"
|
||||
|
||||
# 디버깅 메시지 출력
|
||||
print(f"Emitting signal with message: {html_message}")
|
||||
|
||||
# Signal.emit 호출 (문자열 하나만 전달)
|
||||
try:
|
||||
self.appendHtml.emit(html_message)
|
||||
except TypeError as te:
|
||||
print(f"TypeError while emitting appendHtml signal: {te}")
|
||||
except Exception as e:
|
||||
print(f"Error while emitting appendHtml signal: {e}")
|
||||
|
||||
# 스크롤 최하단 이동
|
||||
self.scrollToBottom.emit()
|
||||
|
||||
except ValueError as ve:
|
||||
print(f"ValueError in emit: {ve}")
|
||||
except Exception as e:
|
||||
print(f"Error in emit: {e}")
|
||||
|
||||
def scroll_to_bottom(self):
|
||||
"""
|
||||
QTextEdit의 스크롤을 최하단으로 이동.
|
||||
"""
|
||||
self.text_edit.verticalScrollBar().setValue(
|
||||
self.text_edit.verticalScrollBar().maximum()
|
||||
)
|
||||
|
||||
msg = self.format(record) # 로그 레코드를 문자열로 포매팅
|
||||
|
||||
color = {
|
||||
logging.DEBUG: "black",
|
||||
logging.INFO: "grey",
|
||||
logging.WARNING: "orange",
|
||||
logging.ERROR: "red",
|
||||
logging.CRITICAL: "purple",
|
||||
}.get(record.levelno, "black")
|
||||
|
||||
# HTML 스타일을 적용한 메시지 생성
|
||||
message = f"<span style=\"color:{color};\">{msg}</span><br/>"
|
||||
self.appendHtml.emit(message) # HTML 메시지로 변경
|
||||
self.scrollToBottom.emit() # 스크롤 시그널 발생
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
핸들러를 닫고 필요한 정리 작업 수행.
|
||||
"""
|
||||
self.flush()
|
||||
super().close()
|
||||
|
||||
logging.Handler.close(self)
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
필요 시 출력 버퍼를 비움.
|
||||
"""
|
||||
pass
|
||||
pass # 필요 시 정리 작업 수행
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
import logging
|
||||
import os
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from PySide6.QtCore import Signal, QObject # PyQt5.QtCore.pyqtSignal -> PySide6.QtCore.Signal
|
||||
from PySide6.QtWidgets import QTextEdit
|
||||
|
||||
def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=5):
|
||||
"""로거 설정을 위한 함수"""
|
||||
formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
# RotatingFileHandler를 사용하여 로그 파일 설정
|
||||
handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8')
|
||||
handler.setFormatter(formatter)
|
||||
|
||||
logger = logging.getLogger(name)
|
||||
logger.setLevel(level)
|
||||
logger.addHandler(handler)
|
||||
|
||||
# 콘솔 로그 출력을 위한 핸들러가 이미 추가되었는지 확인
|
||||
if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setFormatter(formatter)
|
||||
console_handler.setLevel(level)
|
||||
logger.addHandler(console_handler)
|
||||
|
||||
return logger
|
||||
class QTextEditLogger(logging.Handler, QObject):
|
||||
"""
|
||||
PySide6 QTextEdit와 연동하여 로그 메시지를 출력하는 로깅 핸들러.
|
||||
"""
|
||||
appendHtml = Signal(str) # HTML 메시지를 전달하는 시그널
|
||||
scrollToBottom = Signal() # 텍스트 영역의 스크롤을 최하단으로 이동시키는 시그널
|
||||
|
||||
def __init__(self, text_edit):
|
||||
"""
|
||||
QTextEditLogger 초기화.
|
||||
:param text_edit: 로그 메시지를 표시할 QTextEdit 위젯
|
||||
"""
|
||||
QObject.__init__(self) # QObject 초기화
|
||||
logging.Handler.__init__(self) # logging.Handler 초기화
|
||||
self.text_edit = text_edit # 로그를 출력할 QTextEdit 위젯
|
||||
|
||||
# 시그널 연결
|
||||
self.appendHtml.connect(self.text_edit.append)
|
||||
self.scrollToBottom.connect(self.scroll_to_bottom)
|
||||
|
||||
def emit(self, record):
|
||||
"""
|
||||
로그 레코드를 처리하고 QTextEdit에 출력.
|
||||
:param record: 로그 레코드 (LogRecord 객체)
|
||||
"""
|
||||
try:
|
||||
if not hasattr(record, "getMessage"):
|
||||
raise ValueError(f"Invalid LogRecord: {record}")
|
||||
|
||||
# 로그 레코드를 문자열로 포매팅
|
||||
msg = self.format(record) # format(record)는 getMessage를 호출
|
||||
color = {
|
||||
logging.DEBUG: "black",
|
||||
logging.INFO: "grey",
|
||||
logging.WARNING: "orange",
|
||||
logging.ERROR: "red",
|
||||
logging.CRITICAL: "purple",
|
||||
}.get(record.levelno, "black")
|
||||
|
||||
# HTML 스타일 적용한 메시지 생성
|
||||
html_message = f"<span style=\"color:{color};\">{msg}</span><br/>"
|
||||
|
||||
# 디버깅 메시지 출력
|
||||
print(f"Emitting signal with message: {html_message}")
|
||||
|
||||
# Signal.emit 호출 (문자열 하나만 전달)
|
||||
try:
|
||||
self.appendHtml.emit(html_message)
|
||||
except TypeError as te:
|
||||
print(f"TypeError while emitting appendHtml signal: {te}")
|
||||
except Exception as e:
|
||||
print(f"Error while emitting appendHtml signal: {e}")
|
||||
|
||||
# 스크롤 최하단 이동
|
||||
self.scrollToBottom.emit()
|
||||
|
||||
except ValueError as ve:
|
||||
print(f"ValueError in emit: {ve}")
|
||||
except Exception as e:
|
||||
print(f"Error in emit: {e}")
|
||||
|
||||
def scroll_to_bottom(self):
|
||||
"""
|
||||
QTextEdit의 스크롤을 최하단으로 이동.
|
||||
"""
|
||||
self.text_edit.verticalScrollBar().setValue(
|
||||
self.text_edit.verticalScrollBar().maximum()
|
||||
)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
핸들러를 닫고 필요한 정리 작업 수행.
|
||||
"""
|
||||
self.flush()
|
||||
super().close()
|
||||
|
||||
def flush(self):
|
||||
"""
|
||||
필요 시 출력 버퍼를 비움.
|
||||
"""
|
||||
pass
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
# import logging
|
||||
# import os
|
||||
# from logging.handlers import RotatingFileHandler
|
||||
# # from PyQt5.QtCore import pyqtSignal, QObject
|
||||
|
||||
# def setup_logger(name, log_file, level=logging.DEBUG, max_bytes=20*1024*1024, backup_count=5):
|
||||
# """로거 설정을 위한 함수"""
|
||||
# formatter = logging.Formatter('%(asctime)s - %(filename)s:%(lineno)d - %(name)s - %(levelname)s - %(message)s')
|
||||
|
||||
# # RotatingFileHandler를 사용하여 로그 파일 설정
|
||||
# handler = RotatingFileHandler(log_file, maxBytes=max_bytes, backupCount=backup_count, encoding='utf-8')
|
||||
# handler.setFormatter(formatter)
|
||||
|
||||
# logger = logging.getLogger(name)
|
||||
# logger.setLevel(level)
|
||||
# logger.addHandler(handler)
|
||||
|
||||
# # 콘솔 로그 출력을 위한 핸들러가 이미 추가되었는지 확인
|
||||
# if not any(isinstance(h, logging.StreamHandler) for h in logger.handlers):
|
||||
# console_handler = logging.StreamHandler()
|
||||
# console_handler.setFormatter(formatter)
|
||||
# console_handler.setLevel(level)
|
||||
# logger.addHandler(console_handler)
|
||||
|
||||
# return logger
|
||||
|
||||
# class QTextEditLogger(logging.Handler, QObject):
|
||||
# appendHtml = pyqtSignal(str) # HTML 메시지를 전달할 시그널 정의
|
||||
# scrollToBottom = pyqtSignal() # 스크롤을 최하단으로 이동시키는 시그널
|
||||
|
||||
# def __init__(self):
|
||||
# logging.Handler.__init__(self)
|
||||
# QObject.__init__(self)
|
||||
|
||||
# def emit(self, record):
|
||||
# msg = self.format(record) # 로그 레코드를 문자열로 포매팅
|
||||
|
||||
# color = {
|
||||
# logging.DEBUG: "black",
|
||||
# logging.INFO: "grey",
|
||||
# logging.WARNING: "orange",
|
||||
# logging.ERROR: "red",
|
||||
# logging.CRITICAL: "purple",
|
||||
# }.get(record.levelno, "black")
|
||||
|
||||
# # HTML 스타일을 적용한 메시지 생성
|
||||
# message = f"<span style=\"color:{color};\">{msg}</span><br/>"
|
||||
# self.appendHtml.emit(message) # HTML 메시지로 변경
|
||||
# self.scrollToBottom.emit() # 스크롤 시그널 발생
|
||||
|
||||
# def close(self):
|
||||
# self.flush()
|
||||
# logging.Handler.close(self)
|
||||
|
||||
# def flush(self):
|
||||
# pass # 필요 시 정리 작업 수행
|
||||
46
setup.py
|
|
@ -6,21 +6,35 @@ from cx_Freeze import setup, Executable
|
|||
base_dir = os.path.dirname(__file__)
|
||||
browsers_dir = os.path.join(base_dir, 'browsers')
|
||||
chromium_dir = os.path.join(browsers_dir, 'chromium-1112')
|
||||
whale_dir = os.path.join(browsers_dir, 'whale')
|
||||
extensions_dir = os.path.join(browsers_dir, 'extensions')
|
||||
user_data_dir = os.path.join(browsers_dir, 'user_data')
|
||||
|
||||
# 필요한 파일 정의
|
||||
include_files = [
|
||||
'config.ini',
|
||||
'leensoo1nt.json',
|
||||
'prompt.json',
|
||||
'userDB.db',
|
||||
('src/initialDB.db', 'src/initialDB.db'),
|
||||
('src/Percenty_SS_Code.json', 'src/Percenty_SS_Code.json'),
|
||||
(chromium_dir, 'browsers/chromium-1112'), # Chromium 브라우저 실행 파일
|
||||
(extensions_dir, 'browsers/extensions'), # 확장 프로그램 폴더
|
||||
(user_data_dir, 'browsers/user_data'), # 사용자 데이터 폴더
|
||||
# # 필요한 파일 정의
|
||||
# include_files = [
|
||||
# 'config.ini',
|
||||
# 'leensoo1nt.json',
|
||||
# 'prompt.json',
|
||||
# 'userDB.db',
|
||||
# ('src/initialDB.db', 'src/initialDB.db'),
|
||||
# ('src/Percenty_SS_Code.json', 'src/Percenty_SS_Code.json'),
|
||||
# (chromium_dir, 'browsers/chromium-1112'), # Chromium 브라우저 실행 파일
|
||||
# (extensions_dir, 'browsers/extensions'), # 확장 프로그램 폴더
|
||||
# (user_data_dir, 'browsers/user_data'), # 사용자 데이터 폴더
|
||||
|
||||
# ]
|
||||
|
||||
include_files = [
|
||||
os.path.abspath('config.ini'),
|
||||
os.path.abspath('leensoo1nt.json'),
|
||||
os.path.abspath('prompt.json'),
|
||||
os.path.abspath('userDB.db'),
|
||||
(os.path.abspath('src/initialDB.db'), 'src/initialDB.db'),
|
||||
(os.path.abspath('src/Percenty_SS_Code.json'), 'src/Percenty_SS_Code.json'),
|
||||
(os.path.abspath(chromium_dir), 'browsers/chromium-1112'),
|
||||
(os.path.abspath(whale_dir), 'browsers/whale'),
|
||||
(os.path.abspath(extensions_dir), 'browsers/extensions'),
|
||||
(os.path.abspath(user_data_dir), 'browsers/user_data'),
|
||||
]
|
||||
|
||||
# 사용된 패키지 정의
|
||||
|
|
@ -42,7 +56,9 @@ build_exe_options = {
|
|||
'excludes': [
|
||||
'tkinter', 'PyQt4', 'PyQt6', 'AppKit', 'Foundation', 'IPython', 'OpenSSL', 'asyncpg',
|
||||
'curses', 'pydantic', 'test', 'unittest', 'matplotlib', 'tensorflow', 'torch', 'scipy'
|
||||
]
|
||||
],
|
||||
'silent': False # 디버그 메시지 활성화
|
||||
|
||||
}
|
||||
|
||||
# 애플리케이션 메인 파일 및 설정
|
||||
|
|
@ -51,13 +67,13 @@ if sys.platform == 'win32':
|
|||
base = 'Win32GUI'
|
||||
|
||||
executables = [
|
||||
Executable('main.py', base=base, target_name='AutoPercenty2.exe')
|
||||
Executable('main.py', base=base, target_name='AutoPercenty3.exe')
|
||||
]
|
||||
|
||||
# Setup 설정
|
||||
setup(
|
||||
name='AutoPercenty2',
|
||||
version='1.1',
|
||||
name='AutoPercenty3',
|
||||
version='3.1',
|
||||
description='자동화도구',
|
||||
options={'build_exe': build_exe_options},
|
||||
executables=executables
|
||||
|
|
|
|||
|
|
@ -0,0 +1,65 @@
|
|||
import os
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
# 필요한 파일 경로 설정
|
||||
base_dir = os.path.dirname(__file__)
|
||||
browsers_dir = os.path.join(base_dir, 'browsers')
|
||||
chromium_dir = os.path.join(browsers_dir, 'chromium-1112')
|
||||
whale_dir = os.path.join(browsers_dir, 'whale')
|
||||
extensions_dir = os.path.join(browsers_dir, 'extensions')
|
||||
user_data_dir = os.path.join(browsers_dir, 'user_data')
|
||||
|
||||
# 포함할 파일 설정
|
||||
include_files = [
|
||||
os.path.abspath('config.ini'),
|
||||
os.path.abspath('leensoo1nt.json'),
|
||||
os.path.abspath('prompt.json'),
|
||||
os.path.abspath('userDB.db'),
|
||||
(os.path.abspath('src/initialDB.db'), 'src/initialDB.db'),
|
||||
(os.path.abspath('src/Percenty_SS_Code.json'), 'src/Percenty_SS_Code.json'),
|
||||
(os.path.abspath('browsers/chromium-1112'), 'browsers/chromium-1112'),
|
||||
(os.path.abspath('browsers/whale'), 'browsers/whale'),
|
||||
(os.path.abspath('browsers/extensions'), 'browsers/extensions'),
|
||||
(os.path.abspath('browsers/user_data'), 'browsers/user_data'),
|
||||
]
|
||||
|
||||
# 빌드 옵션 정의
|
||||
build_exe_options = {
|
||||
'packages': [
|
||||
'ctypes', 'asyncio', 'os', 're', 'time', 'math', 'json', 'logging', 'shutil', 'random',
|
||||
'base64', 'subprocess', 'configparser', 'pyperclip', 'numpy', 'cv2', 'requests',
|
||||
'win32clipboard', 'win32gui', 'win32con', 'win32process', 'PIL', 'bs4', 'pyautogui',
|
||||
'pyvda', 'sqlalchemy', 'sqlalchemy.orm', 'sqlalchemy.exc', 'collections', 'pandas'
|
||||
],
|
||||
'includes': [
|
||||
'PySide6.QtWidgets', 'PySide6.QtCore', 'PySide6.QtGui', 'PyQt5.QtCore',
|
||||
'whale_translator', 'gui', 'logger_module', 'toggleSwitch',
|
||||
'browser_control', 'clipboardImageManager', 'vertexAI', 'option',
|
||||
'price', 'title', 'locatorManager', 'src.cmb_diag', 'src.DatabaseManager',
|
||||
'vertexai.generative_models'
|
||||
],
|
||||
'include_files': include_files,
|
||||
'excludes': [
|
||||
'tkinter', 'PyQt4', 'PyQt6', 'AppKit', 'Foundation', 'IPython', 'OpenSSL', 'asyncpg',
|
||||
'curses', 'pydantic', 'test', 'unittest', 'matplotlib', 'tensorflow', 'torch', 'scipy'
|
||||
],
|
||||
'silent': False # 디버그 메시지 활성화
|
||||
}
|
||||
|
||||
# 실행 파일 정의
|
||||
executables = [
|
||||
Executable(
|
||||
script='main.py', # 메인 파일
|
||||
target_name='MyApp.exe', # 생성될 실행 파일 이름
|
||||
base='Win32GUI' # GUI 애플리케이션의 경우 사용
|
||||
)
|
||||
]
|
||||
|
||||
# setup 함수
|
||||
setup(
|
||||
name="MyApplication",
|
||||
version="1.0",
|
||||
description="My Python application",
|
||||
options={'build_exe': build_exe_options},
|
||||
executables=executables
|
||||
)
|
||||
|
|
@ -516,9 +516,11 @@ class CMBSettingsDialog(QDialog):
|
|||
|
||||
# 쿼리 실행 및 데이터 가져오기
|
||||
rows = self.db_manager.fetchall(query, params)
|
||||
self.logger.debug(f"쿼리 실행 및 데이터 가져오기")
|
||||
|
||||
# id 기준 오름차순 정렬을 위해 정렬
|
||||
rows = sorted(rows, key=lambda x: int(x[0])) # x[0]는 id 열
|
||||
self.logger.debug(f"sorted - 오름차순/내림차순으로 정렬")
|
||||
|
||||
for row_data in rows:
|
||||
row_id, category1, category2, category3, category4, crmobi_stage = row_data
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 403 KiB After Width: | Height: | Size: 696 KiB |
|
Before Width: | Height: | Size: 624 KiB After Width: | Height: | Size: 789 KiB |
|
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 856 KiB |
|
Before Width: | Height: | Size: 401 KiB After Width: | Height: | Size: 535 KiB |
82
whale_new.py
|
|
@ -1,10 +1,12 @@
|
|||
import time
|
||||
import os, sys
|
||||
import re
|
||||
import pyperclip
|
||||
from pywinauto import Application, findwindows, clipboard, timings
|
||||
from pywinauto.controls.hwndwrapper import HwndWrapper
|
||||
from PIL import ImageGrab
|
||||
from pywinauto.findwindows import ElementNotFoundError
|
||||
from pywinauto.timings import wait_until
|
||||
import re
|
||||
|
||||
class WhaleTranslator:
|
||||
def __init__(self, logger):
|
||||
|
|
@ -120,33 +122,61 @@ class WhaleTranslator:
|
|||
|
||||
def navigate_to_url(self, url):
|
||||
"""주소창에 URL을 입력하고 페이지 로딩을 대기"""
|
||||
try:
|
||||
# URL을 클립보드에 복사
|
||||
pyperclip.copy(url)
|
||||
retry_count = 5 # 주소창 찾기 재시도 횟수
|
||||
for attempt in range(retry_count):
|
||||
try:
|
||||
# URL을 클립보드에 복사
|
||||
pyperclip.copy(url)
|
||||
|
||||
# 주소창을 클릭하여 URL 붙여넣기
|
||||
address_bar = self.whale_window.child_window(title="주소창 및 검색창", control_type="Edit")
|
||||
address_bar.click_input()
|
||||
# 주소창을 클릭하여 URL 붙여넣기
|
||||
address_bar = self.whale_window.child_window(title="주소창 및 검색창", control_type="Edit")
|
||||
address_bar.click_input()
|
||||
|
||||
# Ctrl + V로 URL 붙여넣기 후 Enter 키 입력
|
||||
address_bar.type_keys("^v{ENTER}", with_spaces=True)
|
||||
self.logger.debug(f"{url}로 이동 중...")
|
||||
# Ctrl + V로 URL 붙여넣기 후 Enter 키 입력
|
||||
address_bar.type_keys("^v{ENTER}", with_spaces=True)
|
||||
self.logger.debug(f"{url}로 이동 중...")
|
||||
|
||||
# 5초 동안 0.1초 간격으로 이미지 요소가 나타나는지 검사
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 5:
|
||||
try:
|
||||
# 특정 이미지 요소를 찾으면 즉시 반환
|
||||
image_element = self.whale_window.child_window(title="누락된 이미지 설명을 확인하려면 컨텍스트 메뉴를 여세요.", control_type="Image")
|
||||
if image_element.exists(timeout=0.5):
|
||||
self.logger.debug("페이지 로딩 완료: 이미지 요소가 나타났습니다.")
|
||||
return
|
||||
except Exception:
|
||||
pass # 요소가 아직 나타나지 않은 경우 대기
|
||||
# 10초 동안 0.5초 간격으로 이미지 요소가 나타나는지 검사
|
||||
start_time = time.time()
|
||||
while time.time() - start_time < 10:
|
||||
try:
|
||||
# 이미지 요소를 찾으면 즉시 반환
|
||||
image_element = self.whale_window.child_window(
|
||||
title="누락된 이미지 설명을 확인하려면 컨텍스트 메뉴를 여세요.",
|
||||
control_type="Image"
|
||||
)
|
||||
if image_element.exists(timeout=1):
|
||||
self.logger.debug("페이지 로딩 완료: 이미지 요소가 나타났습니다.")
|
||||
return
|
||||
except Exception as e:
|
||||
self.logger.debug(f"이미지 요소 확인 중 오류 발생: {e}")
|
||||
|
||||
self.logger.error("지정된 시간 내에 이미지 요소를 찾지 못했습니다.")
|
||||
except Exception as e:
|
||||
self.logger.error(f"주소창에 접근할 수 없습니다: {e}", exc_info=True)
|
||||
# 안전장치 1: 현재 창 제목 확인
|
||||
window_title = self.whale_window.window_text()
|
||||
if re.match(r".+\.(jpg|png) \(\d+x\d+\)", window_title, re.IGNORECASE):
|
||||
self.logger.info(f"창 제목에 이미지 파일과 해상도가 감지됨: {window_title}")
|
||||
return
|
||||
|
||||
# 안전장치 2: 현재 창의 컨트롤 핸들 요소 출력
|
||||
self.logger.error("지정된 시간 내에 이미지 요소를 찾지 못했습니다. 현재 창의 컨트롤 요소:")
|
||||
with open("debug_controls.txt", "w", encoding="utf-8") as f:
|
||||
original_stdout = os.sys.stdout
|
||||
os.sys.stdout = f
|
||||
self.whale_window.print_control_identifiers()
|
||||
os.sys.stdout = original_stdout
|
||||
self.logger.info("컨트롤 식별자가 debug_controls.txt에 저장되었습니다.")
|
||||
|
||||
except ElementNotFoundError as e:
|
||||
self.logger.error("주소창 요소를 찾을 수 없습니다. 창을 다시 검색합니다.", exc_info=True)
|
||||
self.whale_window = self.find_whale_window() # 창을 다시 찾음
|
||||
if not self.whale_window:
|
||||
self.logger.error("웨일 창을 다시 찾을 수 없습니다. 작업 중단.")
|
||||
return
|
||||
|
||||
except Exception as e:
|
||||
self.logger.error(f"주소창 접근 중 오류 발생: {e}", exc_info=True)
|
||||
|
||||
self.logger.error("주소창 찾기 및 URL 이동 시도 횟수를 초과했습니다.")
|
||||
|
||||
def navigate_to_url_for_typing(self, url):
|
||||
"""주소창에 URL을 입력하고 페이지 로딩을 대기"""
|
||||
|
|
@ -251,6 +281,8 @@ class WhaleTranslator:
|
|||
"""창 제목에서 이미지의 너비와 높이를 추출하여 반환합니다."""
|
||||
try:
|
||||
window_title = self.whale_window.window_text()
|
||||
self.logger.debug(f"Window Title: {window_title}")
|
||||
|
||||
# 창 제목에서 해상도 추출 시도
|
||||
match = re.search(r"\((\d+)×(\d+)\)", window_title)
|
||||
if match:
|
||||
|
|
@ -266,7 +298,7 @@ class WhaleTranslator:
|
|||
return True # 해상도 조건을 만족하면 작업을 진행
|
||||
|
||||
# 해상도가 없다면, 파일 확장자를 확인하여 번역 여부 결정
|
||||
elif window_title.endswith(".jpg") or window_title.endswith(".png"):
|
||||
elif ".jpg" in window_title or ".png" in window_title:
|
||||
self.logger.info("이미지 해상도가 없지만, 파일 확장자가 .jpg 또는 .png입니다. 번역 작업을 진행합니다.")
|
||||
return True # 해상도가 없어도 번역 작업을 수행
|
||||
|
||||
|
|
|
|||