261 lines
7.8 KiB
Python
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_())
|