459 lines
20 KiB
Python
459 lines
20 KiB
Python
import sys
|
|
import asyncio
|
|
from PySide6 import QtWidgets, QtCore, QtGui, QtWebEngineWidgets
|
|
from PySide6.QtCore import QUrl, QTimer
|
|
from PySide6.QtGui import QIcon, QAction
|
|
from PySide6.QtWidgets import QToolBar, QLineEdit, QMessageBox, QHBoxLayout, QWidget, QDialog, QFormLayout, QDialogButtonBox
|
|
from playwright.async_api import async_playwright
|
|
|
|
# chrome_sync 모듈 import (플레이스홀더)
|
|
import chrome_sync
|
|
|
|
# Playwright 자동화 작업을 위한 QThread 클래스
|
|
class PlaywrightThread(QtCore.QThread):
|
|
result_signal = QtCore.Signal(str)
|
|
|
|
def run(self):
|
|
asyncio.run(self.run_playwright())
|
|
|
|
async def run_playwright(self):
|
|
async with async_playwright() as p:
|
|
browser = await p.chromium.launch(headless=False)
|
|
page = await browser.new_page()
|
|
await page.goto("https://example.com")
|
|
screenshot_path = "screenshot.png"
|
|
await page.screenshot(path=screenshot_path)
|
|
await browser.close()
|
|
self.result_signal.emit(f"Playwright 자동화 완료: 스크린샷 저장 - {screenshot_path}")
|
|
|
|
# 각 브라우저 탭에 들어갈 위젯 클래스 (QWebEngineView 포함)
|
|
class BrowserTab(QtWidgets.QWidget):
|
|
def __init__(self, url, parent=None):
|
|
super().__init__(parent)
|
|
layout = QtWidgets.QVBoxLayout(self)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
self.web_view = QtWebEngineWidgets.QWebEngineView()
|
|
layout.addWidget(self.web_view)
|
|
self.web_view.setUrl(QUrl(url))
|
|
|
|
# 북마크 수정/추가 다이얼로그
|
|
class BookmarkEditDialog(QDialog):
|
|
def __init__(self, bookmark=None, parent=None):
|
|
super().__init__(parent)
|
|
self.setWindowTitle("북마크 수정" if bookmark else "새 북마크 추가")
|
|
layout = QFormLayout(self)
|
|
self.name_edit = QLineEdit()
|
|
self.url_edit = QLineEdit()
|
|
self.folder_edit = QLineEdit()
|
|
if bookmark:
|
|
self.name_edit.setText(bookmark["name"])
|
|
self.url_edit.setText(bookmark["url"])
|
|
self.folder_edit.setText(bookmark.get("folder", ""))
|
|
layout.addRow("이름:", self.name_edit)
|
|
layout.addRow("주소:", self.url_edit)
|
|
layout.addRow("폴더 경로:", self.folder_edit)
|
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
button_box.accepted.connect(self.accept)
|
|
button_box.rejected.connect(self.reject)
|
|
layout.addWidget(button_box)
|
|
|
|
def getData(self):
|
|
return {
|
|
"name": self.name_edit.text().strip(),
|
|
"url": self.url_edit.text().strip(),
|
|
"folder": self.folder_edit.text().strip()
|
|
}
|
|
|
|
# 북마크 버튼 (QToolButton 확장) - 우클릭 시 수정 메뉴 제공
|
|
class BookmarkButton(QtWidgets.QToolButton):
|
|
def __init__(self, bookmark, edit_callback, parent=None):
|
|
super().__init__(parent)
|
|
self.bookmark = bookmark
|
|
self.edit_callback = edit_callback
|
|
self.setText(bookmark["name"])
|
|
self.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
|
|
|
def show_context_menu(self, pos):
|
|
menu = QtWidgets.QMenu(self)
|
|
edit_action = menu.addAction("북마크 수정")
|
|
action = menu.exec(self.mapToGlobal(pos))
|
|
if action == edit_action:
|
|
self.edit_callback(self)
|
|
|
|
# 도구 패널(QTabWidget) - 세로 탭
|
|
class ToolPanel(QtWidgets.QTabWidget):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
self.setTabPosition(QtWidgets.QTabWidget.West)
|
|
self.setMinimumWidth(250)
|
|
self.auto_hide = False
|
|
self.hide_timer = QTimer(self)
|
|
self.hide_timer.setInterval(500)
|
|
self.hide_timer.setSingleShot(True)
|
|
self.hide_timer.timeout.connect(self.hide_panel)
|
|
self.installEventFilter(self)
|
|
|
|
def eventFilter(self, obj, event):
|
|
if self.auto_hide:
|
|
if event.type() == QtCore.QEvent.Leave:
|
|
self.hide_timer.start()
|
|
elif event.type() == QtCore.QEvent.Enter:
|
|
self.hide_timer.stop()
|
|
return super().eventFilter(obj, event)
|
|
|
|
def hide_panel(self):
|
|
self.hide()
|
|
|
|
def show_panel(self):
|
|
self.show()
|
|
|
|
# 문자전송 탭의 간단한 예제 위젯
|
|
class MessageTab(QtWidgets.QWidget):
|
|
def __init__(self, parent=None):
|
|
super().__init__(parent)
|
|
layout = QtWidgets.QVBoxLayout(self)
|
|
layout.addWidget(QtWidgets.QLabel("문자전송 기능"))
|
|
self.message_input = QtWidgets.QLineEdit()
|
|
self.message_input.setPlaceholderText("보낼 메시지를 입력하세요")
|
|
self.send_button = QtWidgets.QPushButton("전송")
|
|
self.send_button.clicked.connect(self.send_message)
|
|
layout.addWidget(self.message_input)
|
|
layout.addWidget(self.send_button)
|
|
self.status_label = QtWidgets.QLabel("")
|
|
layout.addWidget(self.status_label)
|
|
|
|
def send_message(self):
|
|
message = self.message_input.text()
|
|
# 실제 문자 전송 API 호출 구현 필요
|
|
self.status_label.setText(f"전송됨: {message}")
|
|
|
|
# 메인 윈도우 클래스
|
|
class MainWindow(QtWidgets.QMainWindow):
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.setWindowTitle("크롬과 유사한 커스텀 브라우저")
|
|
self.resize(1920, 1080)
|
|
|
|
# 북마크 리스트 (각 항목은 dict: name, url, folder)
|
|
self.bookmarks = []
|
|
|
|
# 메인 레이아웃: 브라우저 영역과 도구 패널 (세로 탭)
|
|
central_widget = QtWidgets.QWidget()
|
|
self.setCentralWidget(central_widget)
|
|
self.main_layout = QtWidgets.QHBoxLayout(central_widget)
|
|
self.main_layout.setContentsMargins(0, 0, 0, 0)
|
|
|
|
# 브라우저 영역 (네비게이션 바, 북마크 바, 브라우저 탭)
|
|
self.browser_area = QtWidgets.QWidget()
|
|
self.init_browser_area()
|
|
self.main_layout.addWidget(self.browser_area, 8)
|
|
|
|
# 도구 패널
|
|
self.tool_panel = ToolPanel()
|
|
self.init_tool_panel()
|
|
self.main_layout.addWidget(self.tool_panel, 2)
|
|
|
|
def init_browser_area(self):
|
|
layout = QtWidgets.QVBoxLayout(self.browser_area)
|
|
layout.setContentsMargins(0, 0, 0, 0)
|
|
layout.setSpacing(0)
|
|
|
|
# 1. 네비게이션 바 (아이콘 버튼 + 주소창 등)
|
|
self.nav_toolbar = QToolBar()
|
|
self.nav_toolbar.setIconSize(QtCore.QSize(24, 24))
|
|
self.nav_toolbar.setMovable(False)
|
|
layout.addWidget(self.nav_toolbar)
|
|
|
|
# 뒤로가기
|
|
back_icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowBack)
|
|
back_action = QAction(back_icon, "", self)
|
|
back_action.triggered.connect(self.navigate_back)
|
|
self.nav_toolbar.addAction(back_action)
|
|
|
|
# 앞으로가기
|
|
forward_icon = self.style().standardIcon(QtWidgets.QStyle.SP_ArrowForward)
|
|
forward_action = QAction(forward_icon, "", self)
|
|
forward_action.triggered.connect(self.navigate_forward)
|
|
self.nav_toolbar.addAction(forward_action)
|
|
|
|
# 새로고침
|
|
refresh_icon = self.style().standardIcon(QtWidgets.QStyle.SP_BrowserReload)
|
|
refresh_action = QAction(refresh_icon, "", self)
|
|
refresh_action.triggered.connect(self.refresh_page)
|
|
self.nav_toolbar.addAction(refresh_action)
|
|
|
|
# 주소창
|
|
self.url_bar = QLineEdit()
|
|
self.url_bar.returnPressed.connect(self.load_url_from_bar)
|
|
self.url_bar.setMinimumWidth(400)
|
|
self.nav_toolbar.addWidget(self.url_bar)
|
|
|
|
self.nav_toolbar.addSeparator()
|
|
|
|
# 북마크 추가 (아이콘)
|
|
bookmark_icon = self.style().standardIcon(QtWidgets.QStyle.SP_DialogYesButton)
|
|
bookmark_action = QAction(bookmark_icon, "", self)
|
|
bookmark_action.triggered.connect(self.new_bookmark)
|
|
self.nav_toolbar.addAction(bookmark_action)
|
|
|
|
# 확장 프로그램 (아이콘)
|
|
ext_icon = self.style().standardIcon(QtWidgets.QStyle.SP_DirIcon)
|
|
ext_action = QAction(ext_icon, "", self)
|
|
ext_action.triggered.connect(self.show_extensions)
|
|
self.nav_toolbar.addAction(ext_action)
|
|
|
|
# 2. 북마크 바 (주소창 바로 아래) - 오른쪽 끝에 도구 패널 토글 버튼 포함
|
|
self.bookmark_bar_widget = QWidget()
|
|
bookmark_layout = QHBoxLayout(self.bookmark_bar_widget)
|
|
bookmark_layout.setContentsMargins(2, 2, 2, 2)
|
|
self.bookmark_toolbar = QToolBar()
|
|
self.bookmark_toolbar.setMovable(False)
|
|
self.bookmark_toolbar.setIconSize(QtCore.QSize(20, 20))
|
|
bookmark_layout.addWidget(self.bookmark_toolbar)
|
|
bookmark_layout.addStretch()
|
|
self.tool_toggle_button = QtWidgets.QToolButton()
|
|
toggle_icon = self.style().standardIcon(QtWidgets.QStyle.SP_ComputerIcon)
|
|
self.tool_toggle_button.setIcon(toggle_icon)
|
|
self.tool_toggle_button.setCheckable(True)
|
|
self.tool_toggle_button.setChecked(True)
|
|
self.tool_toggle_button.clicked.connect(self.toggle_tool_panel)
|
|
self.tool_toggle_button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
self.tool_toggle_button.customContextMenuRequested.connect(self.show_toggle_menu)
|
|
bookmark_layout.addWidget(self.tool_toggle_button)
|
|
layout.addWidget(self.bookmark_bar_widget)
|
|
self.load_sync_bookmarks()
|
|
|
|
# 3. 브라우저 탭 영역
|
|
self.browser_tabs = QtWidgets.QTabWidget()
|
|
self.browser_tabs.setTabsClosable(True)
|
|
self.browser_tabs.tabCloseRequested.connect(self.close_current_tab)
|
|
self.browser_tabs.currentChanged.connect(self.update_url_bar)
|
|
layout.addWidget(self.browser_tabs)
|
|
|
|
# 첫 탭 추가
|
|
self.add_new_tab("https://www.google.com", "Google")
|
|
|
|
def init_tool_panel(self):
|
|
# 도구 패널 탭 추가 (세로 탭)
|
|
# 배송비 계산기 탭
|
|
self.shipping_tab = QtWidgets.QWidget()
|
|
self.init_shipping_tab()
|
|
self.tool_panel.addTab(self.shipping_tab, "배송비")
|
|
# 금지어 관리 탭
|
|
self.prohibited_tab = QtWidgets.QWidget()
|
|
self.init_prohibited_tab()
|
|
self.tool_panel.addTab(self.prohibited_tab, "금지어")
|
|
# 카테고리 관리 탭
|
|
self.category_tab = QtWidgets.QWidget()
|
|
self.init_category_tab()
|
|
self.tool_panel.addTab(self.category_tab, "카테고리")
|
|
# Playwright 매크로 탭
|
|
self.playwright_tab = QtWidgets.QWidget()
|
|
self.init_playwright_tab()
|
|
self.tool_panel.addTab(self.playwright_tab, "Playwright")
|
|
# 문자전송 탭
|
|
self.message_tab = MessageTab()
|
|
self.tool_panel.addTab(self.message_tab, "문자전송")
|
|
|
|
def add_new_tab(self, url="https://www.google.com", label="New Tab"):
|
|
new_tab = BrowserTab(url)
|
|
index = self.browser_tabs.addTab(new_tab, label)
|
|
self.browser_tabs.setCurrentIndex(index)
|
|
new_tab.web_view.urlChanged.connect(lambda qurl, tab=new_tab: self.update_tab_title(tab, qurl))
|
|
new_tab.web_view.loadFinished.connect(lambda ok, tab=new_tab: self.update_url_bar())
|
|
|
|
def close_current_tab(self, index):
|
|
if self.browser_tabs.count() > 1:
|
|
self.browser_tabs.removeTab(index)
|
|
|
|
def update_tab_title(self, tab, qurl):
|
|
title = tab.web_view.title() or qurl.toString()
|
|
index = self.browser_tabs.indexOf(tab)
|
|
self.browser_tabs.setTabText(index, title)
|
|
if self.browser_tabs.currentWidget() == tab:
|
|
self.url_bar.setText(qurl.toString())
|
|
|
|
def update_url_bar(self):
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
url = current_tab.web_view.url().toString()
|
|
self.url_bar.setText(url)
|
|
|
|
def load_url_from_bar(self):
|
|
url_text = self.url_bar.text().strip()
|
|
if url_text and not url_text.startswith("http"):
|
|
url_text = "http://" + url_text
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
current_tab.web_view.setUrl(QUrl(url_text))
|
|
|
|
def navigate_back(self):
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
current_tab.web_view.back()
|
|
|
|
def navigate_forward(self):
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
current_tab.web_view.forward()
|
|
|
|
def refresh_page(self):
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
current_tab.web_view.reload()
|
|
|
|
# 북마크 추가 버튼 클릭 시: 새 북마크 다이얼로그 띄움
|
|
def new_bookmark(self):
|
|
dialog = BookmarkEditDialog(parent=self)
|
|
if dialog.exec() == QDialog.Accepted:
|
|
data = dialog.getData()
|
|
self.bookmarks.append(data)
|
|
self.add_bookmark_button(data)
|
|
|
|
# 북마크 바에 버튼 추가
|
|
def add_bookmark_button(self, bookmark):
|
|
btn = BookmarkButton(bookmark, edit_callback=self.edit_bookmark)
|
|
btn.clicked.connect(lambda checked, url=bookmark["url"]: self.open_bookmark(url))
|
|
self.bookmark_toolbar.addWidget(btn)
|
|
|
|
# 우클릭으로 북마크 수정 호출
|
|
def edit_bookmark(self, btn: BookmarkButton):
|
|
dialog = BookmarkEditDialog(bookmark=btn.bookmark, parent=self)
|
|
if dialog.exec() == QDialog.Accepted:
|
|
data = dialog.getData()
|
|
btn.bookmark = data
|
|
btn.setText(data["name"])
|
|
# 업데이트된 데이터를 bookmarks 리스트에도 반영
|
|
for bm in self.bookmarks:
|
|
if bm["url"] == btn.bookmark["url"]:
|
|
bm.update(data)
|
|
break
|
|
|
|
def open_bookmark(self, url):
|
|
current_tab = self.browser_tabs.currentWidget()
|
|
if current_tab:
|
|
current_tab.web_view.setUrl(QUrl(url))
|
|
|
|
def load_sync_bookmarks(self):
|
|
# chrome_sync 모듈을 통해 동기화된 북마크 가져오기 (플레이스홀더)
|
|
sync_bookmarks = chrome_sync.get_chrome_bookmarks()
|
|
for bm in sync_bookmarks:
|
|
if bm not in self.bookmarks:
|
|
self.bookmarks.append(bm)
|
|
self.add_bookmark_button(bm)
|
|
|
|
def show_extensions(self):
|
|
# chrome_sync 모듈을 통해 확장 프로그램 가져오기 (플레이스홀더)
|
|
ext_list = chrome_sync.get_chrome_extensions()
|
|
msg = "\n".join(ext_list)
|
|
QMessageBox.information(self, "확장 프로그램", f"사용 가능한 확장 프로그램:\n{msg}")
|
|
|
|
def init_shipping_tab(self):
|
|
layout = QtWidgets.QVBoxLayout(self.shipping_tab)
|
|
layout.addWidget(QtWidgets.QLabel("배송비 계산기"))
|
|
self.weight_input = QtWidgets.QLineEdit()
|
|
self.weight_input.setPlaceholderText("무게 입력")
|
|
self.size_input = QtWidgets.QLineEdit()
|
|
self.size_input.setPlaceholderText("크기 입력")
|
|
self.calculate_button = QtWidgets.QPushButton("계산")
|
|
self.calculate_result = QtWidgets.QLabel("결과:")
|
|
self.calculate_button.clicked.connect(self.calculate_shipping)
|
|
layout.addWidget(self.weight_input)
|
|
layout.addWidget(self.size_input)
|
|
layout.addWidget(self.calculate_button)
|
|
layout.addWidget(self.calculate_result)
|
|
|
|
def calculate_shipping(self):
|
|
try:
|
|
shipping_cost = float(self.weight_input.text()) * 0.5 + float(self.size_input.text()) * 0.3
|
|
self.calculate_result.setText(f"결과: {shipping_cost:.2f}")
|
|
except ValueError:
|
|
self.calculate_result.setText("올바른 숫자를 입력하세요.")
|
|
|
|
def init_prohibited_tab(self):
|
|
layout = QtWidgets.QVBoxLayout(self.prohibited_tab)
|
|
layout.addWidget(QtWidgets.QLabel("금지어 관리"))
|
|
self.prohibited_table = QtWidgets.QTableWidget(0, 3)
|
|
self.prohibited_table.setHorizontalHeaderLabels(["단어", "등급", "비고"])
|
|
layout.addWidget(self.prohibited_table)
|
|
btn_layout = QtWidgets.QHBoxLayout()
|
|
self.add_word_button = QtWidgets.QPushButton("추가")
|
|
self.remove_word_button = QtWidgets.QPushButton("삭제")
|
|
btn_layout.addWidget(self.add_word_button)
|
|
btn_layout.addWidget(self.remove_word_button)
|
|
layout.addLayout(btn_layout)
|
|
self.add_word_button.clicked.connect(self.add_prohibited_word)
|
|
self.remove_word_button.clicked.connect(self.remove_prohibited_word)
|
|
|
|
def add_prohibited_word(self):
|
|
row = self.prohibited_table.rowCount()
|
|
self.prohibited_table.insertRow(row)
|
|
self.prohibited_table.setItem(row, 0, QtWidgets.QTableWidgetItem("dummy_word"))
|
|
self.prohibited_table.setItem(row, 1, QtWidgets.QTableWidgetItem("1"))
|
|
self.prohibited_table.setItem(row, 2, QtWidgets.QTableWidgetItem("추가됨"))
|
|
|
|
def remove_prohibited_word(self):
|
|
row = self.prohibited_table.currentRow()
|
|
if row >= 0:
|
|
self.prohibited_table.removeRow(row)
|
|
|
|
def init_category_tab(self):
|
|
layout = QtWidgets.QVBoxLayout(self.category_tab)
|
|
layout.addWidget(QtWidgets.QLabel("카테고리 관리"))
|
|
self.category_table = QtWidgets.QTableWidget(0, 4)
|
|
self.category_table.setHorizontalHeaderLabels(["카테고리", "필터링", "금지여부", "추가배송비"])
|
|
layout.addWidget(self.category_table)
|
|
for cat in ["전자제품", "의류", "식품"]:
|
|
row = self.category_table.rowCount()
|
|
self.category_table.insertRow(row)
|
|
self.category_table.setItem(row, 0, QtWidgets.QTableWidgetItem(cat))
|
|
self.category_table.setItem(row, 1, QtWidgets.QTableWidgetItem("필터링"))
|
|
self.category_table.setItem(row, 2, QtWidgets.QTableWidgetItem("미금지"))
|
|
self.category_table.setItem(row, 3, QtWidgets.QTableWidgetItem("0"))
|
|
|
|
def init_playwright_tab(self):
|
|
layout = QtWidgets.QVBoxLayout(self.playwright_tab)
|
|
layout.addWidget(QtWidgets.QLabel("Playwright 매크로"))
|
|
self.playwright_run_button = QtWidgets.QPushButton("실행")
|
|
self.playwright_output = QtWidgets.QTextEdit()
|
|
self.playwright_output.setReadOnly(True)
|
|
layout.addWidget(self.playwright_run_button)
|
|
layout.addWidget(self.playwright_output)
|
|
self.playwright_run_button.clicked.connect(self.run_playwright_macro)
|
|
|
|
def run_playwright_macro(self):
|
|
self.playwright_run_button.setEnabled(False)
|
|
self.playwright_output.append("Playwright 실행 중...")
|
|
self.thread = PlaywrightThread()
|
|
self.thread.result_signal.connect(self.handle_playwright_result)
|
|
self.thread.finished.connect(lambda: self.playwright_run_button.setEnabled(True))
|
|
self.thread.start()
|
|
|
|
def handle_playwright_result(self, result):
|
|
self.playwright_output.append(result)
|
|
|
|
def toggle_tool_panel(self):
|
|
if self.tool_toggle_button.isChecked():
|
|
self.tool_panel.show_panel()
|
|
else:
|
|
self.tool_panel.hide()
|
|
|
|
def show_toggle_menu(self, pos):
|
|
menu = QtWidgets.QMenu()
|
|
action = QtWidgets.QAction("자동 숨김 모드", self)
|
|
action.setCheckable(True)
|
|
action.setChecked(self.tool_panel.auto_hide)
|
|
action.triggered.connect(self.toggle_auto_hide)
|
|
menu.addAction(action)
|
|
menu.exec(self.tool_toggle_button.mapToGlobal(pos))
|
|
|
|
def toggle_auto_hide(self, checked):
|
|
self.tool_panel.auto_hide = checked
|
|
|
|
if __name__ == "__main__":
|
|
app = QtWidgets.QApplication(sys.argv)
|
|
window = MainWindow()
|
|
window.show()
|
|
sys.exit(app.exec())
|