This commit is contained in:
9700X_PC 2024-11-15 01:26:15 +09:00
parent 806502d926
commit 1f89ffa3a7
7 changed files with 313 additions and 46 deletions

3
.gitignore vendored
View File

@ -3,10 +3,11 @@ Lib/
Scripts/
__pycache__/
build/
dist/
pyvenv.cfg
*.log
*.log.*
*.pyc
browsers/user_data
browsers/whale/user_data
browsers/whale/cache
browsers/whale/cache

View File

@ -7,7 +7,7 @@ import requests
import numpy as np
import cv2
import time
import os
import os, sys
from datetime import datetime
import random
import pywinauto
@ -18,13 +18,25 @@ class ClipboardImageManager:
self.debug = debug_flag # 디버그 플래그를 클래스 변수로 사용
# 프로그램이 위치한 경로 기준으로 폰트 경로 설정
self.font_path = os.path.join(os.path.dirname(__file__), 'src', 'font', 'HakgyoansimDunggeunmisoTTFB.ttf')
self.base_path = self.get_base_dir()
self.font_path = os.path.join(self.base_path, 'src', 'font', 'HakgyoansimDunggeunmisoTTFB.ttf')
# 폰트 로드
self.font = ImageFont.truetype(self.font_path, watermark_font_size)
# self.debug = True
def get_base_dir(self):
"""
실행 환경에 따라 base_dir을 설정하는 메서드.
cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정.
"""
if getattr(sys, 'frozen', False): # 패키징된 경우
base_dir = os.path.dirname(sys.executable)
else: # 일반 Python 실행 환경
base_dir = os.path.dirname(os.path.abspath(__file__))
return base_dir
def get_clipboard_data(self):
"""클립보드의 텍스트 또는 이미지 데이터를 가져옵니다."""
self.logger.debug("클립보드의 텍스트 또는 이미지 데이터를 가져옵니다")

15
gui.py
View File

@ -107,9 +107,6 @@ class AutoPercentyGUI(QWidget):
# 이전에 저장된 설정 불러오기
self.load_settings()
# 로거 초기화
self.add_text_edit_logger()
# 프로그래스바 초기화
self.update_total_progress(0,0)
@ -142,17 +139,7 @@ class AutoPercentyGUI(QWidget):
except Exception as e:
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):

View File

@ -1,7 +1,8 @@
import logging
import os
from logging.handlers import RotatingFileHandler
from PyQt5.QtCore import pyqtSignal, QObject
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):
"""로거 설정을 위한 함수"""
@ -23,34 +24,84 @@ 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):
appendHtml = pyqtSignal(str) # HTML 메시지를 전달할 시그널 정의
scrollToBottom = pyqtSignal() # 스크롤을 최하단으로 이동시키는 시그널
"""
PySide6 QTextEdit와 연동하여 로그 메시지를 출력하는 로깅 핸들러.
"""
appendHtml = Signal(str) # HTML 메시지를 전달하는 시그널
scrollToBottom = Signal() # 텍스트 영역의 스크롤을 최하단으로 이동시키는 시그널
def __init__(self):
logging.Handler.__init__(self)
QObject.__init__(self)
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):
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() # 스크롤 시그널 발생
"""
로그 레코드를 처리하고 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()
logging.Handler.close(self)
super().close()
def flush(self):
pass # 필요 시 정리 작업 수행
"""
필요 출력 버퍼를 비움.
"""
pass

56
logger_module_qt5.py Normal file
View File

@ -0,0 +1,56 @@
# 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 # 필요 시 정리 작업 수행

148
main.spec Normal file
View File

@ -0,0 +1,148 @@
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'], # 프로젝트 메인 스크립트 이름
pathex=['.'], # 현재 경로
binaries=[],
datas=[
('leensoo1nt.json', '.'),
('prompt.json', '.'),
('config.ini', '.'),
('userDB.db', '.'),
('src/initialDB.db', 'src'),
('src/Percenty_SS_Code.json', 'src')
],
hiddenimports=[
'playwright.async_api',
'PySide6.QtCore',
'PySide6.QtGui',
'PySide6.QtWidgets',
'win32gui',
'win32con',
'pyautogui',
'bs4',
'numpy',
'cv2',
'sqlalchemy',
'sqlalchemy.orm',
'sqlalchemy.exc',
'logging.handlers',
'PIL',
'pywinauto',
'vertexai.generative_models',
'pyperclip',
'ctypes',
'pandas'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='AutoPercenty3', # 실행 파일 이름
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False, # 콘솔 창 비활성화
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='AutoPercenty3' # 최종 배포 폴더 이름
)
# -*- mode: python ; coding: utf-8 -*-
block_cipher = None
a = Analysis(
['main.py'], # 프로젝트 메인 스크립트 이름
pathex=['.'], # 현재 경로
binaries=[],
datas=[
('leensoo1nt.json', '.'),
('prompt.json', '.'),
('config.ini', '.'),
('userDB.db', '.'),
('src/initialDB.db', 'src'),
('src/Percenty_SS_Code.json', 'src')
],
hiddenimports=[
'playwright.async_api',
'PySide6.QtCore',
'PySide6.QtGui',
'PySide6.QtWidgets',
'win32gui',
'win32con',
'pyautogui',
'bs4',
'numpy',
'cv2',
'sqlalchemy',
'sqlalchemy.orm',
'sqlalchemy.exc',
'logging.handlers',
'PIL',
'PIL.Image',
'PIL.ImageTk',
'pywinauto',
'vertexai.generative_models',
'pyperclip',
'ctypes',
'pandas'
],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=['PyQt5'],
win_no_prefer_redirects=False,
win_private_assemblies=False,
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz,
a.scripts,
[],
exclude_binaries=True,
name='AutoPercenty3', # 실행 파일 이름
debug=False,
bootloader_ignore_signals=False,
strip=False,
upx=True,
console=False, # 콘솔 창 비활성화
)
coll = COLLECT(
exe,
a.binaries,
a.zipfiles,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='AutoPercenty3' # 최종 배포 폴더 이름
)

View File

@ -1,5 +1,5 @@
import time
import os
import os, sys
import re
import pyperclip
from pywinauto import Application, findwindows, clipboard, timings
@ -20,10 +20,22 @@ class WhaleTranslator:
self.min_image_width = 200
self.min_image_height = 150
def get_base_dir(self):
"""
실행 환경에 따라 base_dir을 설정하는 메서드.
cx_Freeze로 패키징된 경우 실행 파일의 경로, 일반 Python 환경일 경우 __file__을 기준으로 설정.
"""
if getattr(sys, 'frozen', False): # 패키징된 경우
base_dir = os.path.dirname(sys.executable)
else: # 일반 Python 실행 환경
base_dir = os.path.dirname(os.path.abspath(__file__))
return base_dir
def start_whale_browser(self):
whale_exe_path = os.path.join(os.getcwd(), "browsers", "whale", "whale.exe")
user_data_dir = os.path.join(os.getcwd(), "browsers", "whale", "user_data")
cache_dir = os.path.join(os.getcwd(), "browsers", "whale", "cache")
base_path = self.get_base_dir()
whale_exe_path = os.path.join(base_path, "browsers", "whale", "whale.exe")
user_data_dir = os.path.join(base_path, "browsers", "whale", "user_data")
cache_dir = os.path.join(base_path, "browsers", "whale", "cache")
self.whale_app = Application(backend="uia").start(
f'"{whale_exe_path}" --incognito --user-data-dir="{user_data_dir}" --disk-cache-dir="{cache_dir}"'