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_())