AP_Browser/test_code.py

259 lines
12 KiB
Python

import os
import sys
import time
import logging
# --- pywinauto 관련 ---
from pywinauto import Application, findwindows, timings
from pywinauto.controls.hwndwrapper import HwndWrapper
# --- Selenium 관련 ---
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
# --- PySide6 관련 ---
from PySide6.QtCore import Qt, QTimer, QEvent
from PySide6.QtWidgets import (
QApplication, QMainWindow, QFrame, QSplitter,
QVBoxLayout, QPushButton
)
# --- Win32 API (임베딩에 사용) ---
import win32gui
import win32con
# ============================================================
# 간단한 Logger 클래스
# ============================================================
class Logger:
def __init__(self, log_file="app.log", logger_name="App_Logger", level=logging.DEBUG):
self.logger = logging.getLogger(logger_name)
self.logger.setLevel(level)
if not self.logger.handlers:
handler = logging.FileHandler(log_file, encoding="utf-8")
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
handler.setFormatter(formatter)
self.logger.addHandler(handler)
def log(self, msg, level=logging.INFO, exc_info=False):
self.logger.log(level, msg, exc_info=exc_info)
# ============================================================
# WhaleController 클래스
# ============================================================
class WhaleController:
def __init__(self):
self.logger = Logger(log_file="app.log", logger_name="App_Logger", level=logging.DEBUG)
self.logger.log("로그기록이 설정되었습니다.", level=logging.INFO)
self.driver = None
self.browser_pid = None
self.whale_window = None
def find_whale(self):
"""pywinauto를 사용하여 웨일 브라우저 창을 찾아 제어"""
try:
if not self.browser_pid:
self.logger.log("브라우저 PID를 찾을 수 없습니다. 먼저 Selenium으로 실행해야 합니다.", level=logging.WARNING)
return
# 최대 10초 동안 'whale'이 포함된 창이 나타나기를 기다림
timings.wait_until(10, 0.5, lambda: any("whale" in window.name.lower() for window in findwindows.find_elements()))
windows = findwindows.find_elements()
for window in windows:
if "whale" in window.name.lower() or window.process_id == self.browser_pid:
self.logger.log(f"찾은 창: {window.name}, PID: {window.process_id}", level=logging.DEBUG)
if window.name == 'whale://new-tab-page-third-party/ - Whale' or "whale" in window.name.lower():
self.logger.log("웨일 브라우저를 찾았음: " + window.name, level=logging.INFO)
whale_app = Application(backend="uia").connect(process=window.process_id)
self.whale_window = whale_app.top_window()
# 초기 위치 및 크기 조정 (나중에 GUI 내 임베딩 시 재조정됨)
hwnd_wrapper = HwndWrapper(self.whale_window.handle)
hwnd_wrapper.move_window(x=1, y=1, width=1280, height=720)
self.whale_window.set_focus()
break
except Exception as e:
self.logger.log(f"웨일 제어 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
def get_base_dir(self):
"""
실행 환경에 따라 base_dir 설정.
cx_Freeze 패키징 여부에 따라 __file__ 또는 sys.executable 기준.
"""
if getattr(sys, 'frozen', False):
base_dir = os.path.dirname(sys.executable)
internal_dir = os.path.join(base_dir, '_internal')
if os.path.exists(internal_dir):
return internal_dir
else:
base_dir = os.path.dirname(os.path.abspath(__file__))
return base_dir
def start_whale_Browser(self):
"""웨일 브라우저 실행 및 Selenium 제어"""
try:
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")
extension_path = os.path.join(base_path, "browsers", "whale", "extensions", "gadfmnjdnhkncfcibhfleoojcdimdcbd", "1.1.11_0")
chromedriver_path = os.path.join(base_path, "browsers", "chromedriver_128.0.6613.137.exe")
chrome_service = Service(chromedriver_path)
chrome_options = Options()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")
chrome_options.add_argument("--remote-debugging-port=9222")
chrome_options.add_argument(f"--user-data-dir={user_data_dir}")
chrome_options.add_argument(f"--disk-cache-dir={cache_dir}")
chrome_options.add_argument(f"--load-extension={extension_path}")
chrome_options.binary_location = whale_exe_path
chrome_options.add_argument(
"user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/130.0.0.0 Whale/4.29.282.14 Safari/537.36"
)
self.driver = webdriver.Chrome(service=chrome_service, options=chrome_options)
time.sleep(0.2)
self.browser_pid = self.driver.service.process.pid
self.logger.log(f"웨일 브라우저 PID: {self.browser_pid}", level=logging.INFO)
self.find_whale()
except Exception as e:
self.logger.log(f"웨일 브라우저 시작 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
def close_whale_br(self):
"""브라우저 종료"""
try:
self.logger.log("브라우저를 종료합니다.", level=logging.DEBUG)
if self.driver:
self.driver.quit()
else:
self.logger.log("브라우저 객체를 찾을 수 없습니다.", level=logging.WARNING)
except Exception as e:
self.logger.log(f"브라우저 종료 중 오류 발생: {e}", level=logging.ERROR, exc_info=True)
# ============================================================
# 메인 GUI 프로그램 (PySide6)
# ============================================================
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("커스텀 도구 포함 웹GUI 프로그램")
self.resize(1400, 800)
# QSplitter로 브라우저 임베딩 영역과 도구 패널 분리
splitter = QSplitter(Qt.Horizontal, self)
self.setCentralWidget(splitter)
# [1] 브라우저 임베딩 영역
self.browser_container = QFrame()
self.browser_container.setStyleSheet("background-color: #ffffff;")
splitter.addWidget(self.browser_container)
# [2] 커스텀 도구 패널
self.tools_panel = QFrame()
self.tools_panel.setMinimumWidth(300)
splitter.addWidget(self.tools_panel)
tools_layout = QVBoxLayout(self.tools_panel)
self.btn_close = QPushButton("브라우저 종료")
self.btn_refresh = QPushButton("브라우저 새로고침")
self.btn_custom = QPushButton("커스텀 도구 실행")
tools_layout.addWidget(self.btn_close)
tools_layout.addWidget(self.btn_refresh)
tools_layout.addWidget(self.btn_custom)
tools_layout.addStretch()
# 버튼 이벤트 연결
self.btn_close.clicked.connect(self.close_browser)
self.btn_refresh.clicked.connect(self.refresh_browser)
self.btn_custom.clicked.connect(self.custom_tool_action)
# WhaleController 인스턴스 생성
self.whale_controller = WhaleController()
self.embedded_browser_hwnd = None
# 브라우저 컨테이너에 이벤트 필터 설치: Resize 이벤트 발생 시 임베딩된 브라우저 창 크기를 조정
self.browser_container.installEventFilter(self)
QTimer.singleShot(500, self.start_whale_browser)
def eventFilter(self, obj, event):
if obj == self.browser_container and event.type() == QEvent.Resize:
self.resize_embedded_browser()
return super().eventFilter(obj, event)
def start_whale_browser(self):
"""웨일 브라우저 실행 후 임베딩"""
self.whale_controller.start_whale_Browser()
# 약간의 지연 후 embed_browser() 호출하여 웨일 창을 찾아 임베딩
QTimer.singleShot(1000, self.embed_browser)
def embed_browser(self):
"""pywinauto로 찾은 웨일 브라우저 창을 컨테이너에 임베딩"""
try:
if self.whale_controller.whale_window is None:
print("웨일 브라우저 창을 찾지 못했습니다.")
return
browser_hwnd = self.whale_controller.whale_window.handle
self.embedded_browser_hwnd = browser_hwnd
# 컨테이너의 윈도우 핸들을 부모로 지정
container_hwnd = int(self.browser_container.winId())
win32gui.SetParent(browser_hwnd, container_hwnd)
# 창 스타일 수정 (제목표시줄, 테두리 등 제거)
style = win32gui.GetWindowLong(browser_hwnd, win32con.GWL_STYLE)
style = style & ~(win32con.WS_CAPTION | win32con.WS_THICKFRAME |
win32con.WS_MINIMIZE | win32con.WS_MAXIMIZE | win32con.WS_SYSMENU)
win32gui.SetWindowLong(browser_hwnd, win32con.GWL_STYLE, style)
self.resize_embedded_browser()
except Exception as e:
print("브라우저 임베딩 중 오류:", e)
def resize_embedded_browser(self):
"""
브라우저 컨테이너의 현재 크기를 기준으로 임베딩된 브라우저 창을
컨테이너 크기의 1.245배 크기로 조정합니다.
단, 왼쪽 위 꼭지점은 항상 (0,0)에 고정되고, 오른쪽과 아랫쪽만 변경됩니다.
"""
if self.embedded_browser_hwnd:
container_size = self.browser_container.size()
container_width = container_size.width()
container_height = container_size.height()
new_width = int(container_width * 1.245)
new_height = int(container_height * 1.245)
# 왼쪽 위 꼭지점은 항상 (0,0)
win32gui.MoveWindow(self.embedded_browser_hwnd, 0, 0, new_width, new_height, True)
def close_browser(self):
"""커스텀 도구: 브라우저 종료"""
self.whale_controller.close_whale_br()
def refresh_browser(self):
"""커스텀 도구: 브라우저 새로고침"""
if self.whale_controller.driver:
self.whale_controller.driver.refresh()
def custom_tool_action(self):
"""커스텀 도구: 추가 자동화 작업 실행 (예시)"""
print("커스텀 도구 작업이 실행되었습니다.")
def resizeEvent(self, event):
"""메인 윈도우 크기가 변경될 경우에도 임베딩된 브라우저 창을 업데이트"""
super().resizeEvent(event)
self.resize_embedded_browser()
if __name__ == "__main__":
app = QApplication(sys.argv)
main_win = MainWindow()
main_win.show()
sys.exit(app.exec())