ebook-snipping-tool/main.py

261 lines
7.8 KiB
Python

import os
import sys
from utils.utils import resource_path
from PyQt5.QtWidgets import *
from PyQt5 import uic, QtGui, QtCore
from widget import SnippingWidget
from functools import partial
import mss
from PIL import Image
import pyautogui
import cv2
import numpy as np
"""
교보ebook 응용프로그램 전용
"""
class EbookSnipper(QMainWindow):
def __init__(self):
super().__init__()
os.environ["QT_AUTO_SCREEN_SCALE_FACTOR"] = "1"
self.capture_dir = "./captures"
uic.loadUi(resource_path("./ui/MainWindow.ui"), self)
self.coords = {
"x": 0,
"y": 0,
"w": 0,
"h": 0,
"btn_x": 0,
"btn_y": 0,
}
self.params = {"delay": 0, "pages": 0}
# Icon Setting
self.icon = QtGui.QIcon(resource_path("./assets/icon.ico"))
if self.icon.isNull():
print("Failed to load icon")
self.setWindowIcon(self.icon)
self.widget = SnippingWidget(self)
self.current_page = 0
self.is_snipping = False
# Initialize
self._initUi()
self.close_spash_screen()
def _initUi(self):
self.params["delay"] = self.spinBox_delay.value()
self.params["pages"] = self.spinBox_pages.value()
self.btn_coords.clicked.connect(lambda: self.start_setSnippingArea())
self.btn_nextbtn.clicked.connect(lambda: self.start_setNextBtnCoord())
for key, spinbox in {
"x": self.spinBox_x,
"y": self.spinBox_y,
"w": self.spinBox_w,
"h": self.spinBox_h,
"btn_x": self.spinBox_btn_x,
"btn_y": self.spinBox_btn_y,
}.items():
spinbox.valueChanged.connect(partial(self.coords_updownbutton_event, key))
for key, spinbox in {
"delay": self.spinBox_delay,
"pages": self.spinBox_pages,
}.items():
spinbox.valueChanged.connect(partial(self.params_updownbutton_event, key))
self.btn_start.clicked.connect(self.start_snipping)
self.btn_stop.clicked.connect(self.stop_snipping)
QShortcut(QtGui.QKeySequence("Alt+F10"), self, self.start_snipping)
QShortcut(QtGui.QKeySequence("Alt+F11"), self, self.stop_snipping)
"""
Pyinstaller Splash Screen Terminator
"""
def close_spash_screen(self):
if hasattr(sys, "_MEIPASS"):
import pyi_splash # type: ignore
pyi_splash.close()
"""
Snipping Area Mode
"""
def start_setSnippingArea(self):
self.widget.rectMode = True
self.widget.showFullScreen()
def update_rect_coords(self, coords=None):
self.spinbox_blocksignal(True)
if coords == None:
coords = self.coords
if self.spinBox_x.value() != coords["x"]:
self.spinBox_x.setValue(coords["x"])
if self.spinBox_y.value() != coords["y"]:
self.spinBox_y.setValue(coords["y"])
if self.spinBox_w.value() != coords["w"]:
self.spinBox_w.setValue(coords["w"])
if self.spinBox_h.value() != coords["h"]:
self.spinBox_h.setValue(coords["h"])
self.spinbox_blocksignal(False)
"""
Next Button Coord Mode
"""
def start_setNextBtnCoord(self):
self.widget.rectMode = False
self.widget.showFullScreen()
def update_btn_coord(self, coords=None):
self.spinbox_blocksignal(True)
if coords == None:
coords = self.coords
if self.spinBox_btn_x.value() != coords["btn_x"]:
self.spinBox_btn_x.setValue(coords["btn_x"])
if self.spinBox_btn_y.value() != coords["btn_y"]:
self.spinBox_btn_y.setValue(coords["btn_y"])
self.spinbox_blocksignal(True)
"""
Event
"""
def coords_updownbutton_event(self, key, value):
self.coords[key] = value
print(self.coords)
def params_updownbutton_event(self, key, value):
self.params[key] = value
print(self.params)
def spinbox_blocksignal(self, boolean):
# if boolean == True: print(f"Before: {self.coords}")
# else: print(f"After: {self.coords}")
self.spinBox_x.blockSignals(boolean)
self.spinBox_y.blockSignals(boolean)
self.spinBox_w.blockSignals(boolean)
self.spinBox_h.blockSignals(boolean)
self.spinBox_btn_x.blockSignals(boolean)
self.spinBox_btn_y.blockSignals(boolean)
"""
Snipping Process
"""
def stop_snipping(self):
if self.is_snipping:
self.is_snipping = False
print("Snipping has been stopped")
else:
return
def start_snipping(self):
if self.is_snipping:
print("Already processing")
return
print("Start snipping")
self.is_snipping = True
self.current_page = 0
self.capture_next_page()
def capture_next_page(self):
if self.current_page >= self.params["pages"]:
self.current_page = 0
self.is_snipping = False
print("Snipping complete")
result = QMessageBox.question(
self,
"캡쳐 완료",
f"{self.params['pages']} 페이지 캡쳐가 완료되었습니다. PDF로 변환 하시겠습니까?",
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No, # 기본 선택 버튼
)
if result == QMessageBox.Yes:
self.images_to_pdf()
return
if not self.is_snipping:
QMessageBox.information(
self,
"캡쳐 중단",
f"캡쳐가 중단되었습니다. [{self.current_page}/{self.params['pages']}]",
)
return
# capture
self.capture()
# click next button
pyautogui.click(x=self.coords["btn_x"], y=self.coords["btn_y"])
# next page process
self.current_page += 1
QtCore.QTimer.singleShot(self.params["delay"] * 1000, self.capture_next_page)
def capture(self):
with mss.mss() as sct:
monitor = {
"left": self.coords["x"],
"top": self.coords["y"],
"width": self.coords["w"],
"height": self.coords["h"],
}
screenshot = sct.grab(monitor)
img = np.array(screenshot)
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) # BGRA → BGR 변환
# ✅ 이미지 저장
if not os.path.exists(self.capture_dir):
os.makedirs(self.capture_dir)
cv2.imwrite(f"{self.capture_dir}/{self.current_page}.png", img)
print(f"> Page {self.current_page + 1} has been captured")
def images_to_pdf(self):
image_files = [
f
for f in os.listdir(self.capture_dir)
if f.lower().endswith((".png", ".jpg", ".jpeg"))
]
image_files.sort()
if not image_files:
QMessageBox.warning(
self,
"이미지 없음",
f"변환할 이미지가 없습니다.",
)
return
first_image = Image.open(
os.path.join(self.capture_dir, image_files[0])
).convert("RGB")
image_list = []
for file in image_files[1:]:
img = Image.open(os.path.join(self.capture_dir, file)).convert("RGB")
image_list.append(img)
first_image.save("./output.pdf", save_all=True, append_images=image_list)
QMessageBox.information(
self,
"PDF 변환 완료",
f"PDF 변환이 완료되었습니다.",
)
if __name__ == "__main__":
app = QApplication(sys.argv)
main_window = EbookSnipper()
main_window.show()
sys.exit(app.exec_())