This commit is contained in:
SeungJu Lim 2025-02-02 21:00:40 +09:00
parent 3882a2607f
commit b6a578468d
15 changed files with 954 additions and 15 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
output.pdf
captures/
dist/
build/

1
.python-version Normal file
View File

@ -0,0 +1 @@
3.11.9

View File

@ -1,23 +1,16 @@
# Template # Ebook Snipping Tool
This project is a code for ~ This project is a code for Ebook snipping and PDF converting
## Prerequisites ## Prerequisites
- foo - Python 3.11.9
- bar - pip
- ... - mss
- pyautogui
- opencv-python
- pyqt5
## Installation ## Installation
1. Clone this repository to your local machine. 1. Clone this repository to your local machine.
2. Install dependencies using ~
```batch
npm install
```
## Usage
To run the script, execute the following command:
```batch
foobar
```
## Contributing ## Contributing
Feel free to contribute to this project by opening issues or pull requests. Any feedback or suggestions are welcome! Feel free to contribute to this project by opening issues or pull requests. Any feedback or suggestions are welcome!

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

260
main.py Normal file
View File

@ -0,0 +1,260 @@
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_())

47
main.spec Normal file
View File

@ -0,0 +1,47 @@
# -*- mode: python ; coding: utf-8 -*-
import os
import sys
program_name = 'Ebook Snipper'
icon_path = os.path.abspath('./assets/icon.ico')
a = Analysis(
['main.py'],
pathex=['.'],
binaries=[],
datas=[
('ui/mainWindow.ui', 'ui'),
('assets/icon.ico', 'assets'),
],
hiddenimports=[],
hookspath=[],
hooksconfig={},
runtime_hooks=[],
excludes=[],
noarchive=False,
optimize=0,
)
pyz = PYZ(a.pure)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
name=program_name,
debug=True,
bootloader_ignore_signals=False,
strip=False,
upx=True,
upx_exclude=[],
runtime_tmpdir=None,
console=False,
disable_windowed_traceback=False,
argv_emulation=False,
target_arch=None,
codesign_identity=None,
entitlements_file=None,
icon=icon_path,
)

438
ui/mainwindow.ui Normal file
View File

@ -0,0 +1,438 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>436</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>340</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Ebook Snipping Tool</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_8">
<item row="0" column="0">
<widget class="QFrame" name="frame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_1st">
<property name="title">
<string>1. 영역 지정</string>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="btn_coords">
<property name="text">
<string>캡쳐 영역 설정 (취소: esc)</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_2nd">
<property name="title">
<string>2. 페이지 넘김 버튼 위치 지정</string>
</property>
<layout class="QGridLayout" name="gridLayout_7">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QPushButton" name="btn_nextbtn">
<property name="text">
<string>버튼 위치 설정</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3rd">
<property name="title">
<string>3. 파라미터 지정</string>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="groupBox_3rd_1">
<property name="title">
<string>캡쳐 영역</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_9">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_3">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label_x">
<property name="text">
<string>X:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_x">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_y">
<property name="text">
<string>Y:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_y">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_w">
<property name="text">
<string>Width:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_w">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="label_h">
<property name="text">
<string>Height:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_h">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="groupBox_3rd_2">
<property name="title">
<string>버튼 좌표</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_12">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_11">
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_btn_x">
<property name="text">
<string>X:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_btn_x">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<widget class="QLabel" name="label_btn_y">
<property name="text">
<string>Y:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_btn_y">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QGroupBox" name="groupBox_3rd_3">
<property name="title">
<string>조건</string>
</property>
<property name="flat">
<bool>true</bool>
</property>
<layout class="QGridLayout" name="gridLayout_6">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_delay">
<property name="text">
<string>딜레이 시간(sec)</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_delay">
<property name="maximum">
<number>20000</number>
</property>
<property name="value">
<number>2</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_page">
<property name="text">
<string>페이지 수</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="spinBox_pages">
<property name="maximum">
<number>20000</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="groupBox_4th">
<property name="title">
<string>4. 캡쳐</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<property name="spacing">
<number>3</number>
</property>
<item row="0" column="0">
<layout class="QGridLayout" name="gridLayout_5">
<item row="0" column="0">
<widget class="QPushButton" name="btn_start">
<property name="text">
<string>시작 (alt + F10)</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="btn_stop">
<property name="text">
<string>중지 (alt + F11)</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>340</width>
<height>21</height>
</rect>
</property>
<property name="nativeMenuBar">
<bool>false</bool>
</property>
</widget>
</widget>
<tabstops>
<tabstop>spinBox_x</tabstop>
<tabstop>spinBox_y</tabstop>
<tabstop>spinBox_w</tabstop>
<tabstop>spinBox_h</tabstop>
<tabstop>spinBox_delay</tabstop>
<tabstop>spinBox_pages</tabstop>
<tabstop>btn_start</tabstop>
<tabstop>btn_stop</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

Binary file not shown.

Binary file not shown.

70
utils/utils.py Normal file
View File

@ -0,0 +1,70 @@
import os
import sys
import subprocess
from PIL import Image
def resource_path(relative_path): # for Pyinstaller
if hasattr(sys, '_MEIPASS'):
return os.path.join(sys._MEIPASS, relative_path)
return os.path.join(os.path.abspath("."), relative_path)
def terminate_process(process_name):
try:
print(f"[INFO] Attempting to terminate process: {process_name}")
subprocess.run(f"taskkill /F /IM {process_name}", shell=True, check=True)
print(f"[INFO] Successfully terminated process: {process_name}")
return 0
except subprocess.CalledProcessError as e:
if "128" in str(e): print(f"[WARN] Process '{process_name}' not found or already terminated.")
else: print(f"[ERROR] Failed to terminate process '{process_name}'. Error: {e}")
except Exception as e:
print(f"[ERROR] Unexpected error while terminating '{process_name}'. Error: {e}")
return 1
def start_process(process_name):
try:
print(f"[INFO] Attempting to start process: {process_name}")
subprocess.run(f"start {process_name}", shell=True, check=True)
print(f"[INFO] Successfully started process: {process_name}")
return 0
except FileNotFoundError:
print(f"[ERROR] Process '{process_name}' not found. Please check the path or name.")
except subprocess.CalledProcessError as e:
print(f"[ERROR] Failed to start process '{process_name}'. Error: {e}")
except Exception as e:
print(f"[ERROR] Unexpected error while starting '{process_name}'. Error: {e}")
return 1
"""
Pyinstaller splash screen related
"""
def check_alpha(img):
"""Check if the image contains translucent pixels."""
alpha_channel = img.getchannel('A')
alpha_data = alpha_channel.load()
for y in range(img.height):
for x in range(img.width):
alpha_value = alpha_data[x, y]
if 0 < alpha_value < 255: # Check if translucent
return True
return False
def processing_image(img_path):
img = Image.open(img_path).convert("RGBA") # Ensure image is in RGBA format
print("Contain translucency pixels (Before):", check_alpha(img))
# Remove translucency: Set all non-opaque pixels (alpha != 255) to transparent (alpha = 0)
pixels = img.load()
for y in range(img.height):
for x in range(img.width):
r, g, b, a = pixels[x, y]
if a != 255: # If alpha is not fully opaque
pixels[x, y] = (r, g, b, 0) # Make pixel fully transparent
print("Contain translucency pixels (After):", check_alpha(img))
# Save the modified image
output_path = img_path.replace(".png", "_transparent.png")
img.save(output_path, "PNG")
print(f"Processed image saved to {output_path}")
return output_path

126
widget.py Normal file
View File

@ -0,0 +1,126 @@
import sys
from utils.utils import resource_path
from PyQt5.QtWidgets import QApplication, QWidget, QRubberBand, QMessageBox
from PyQt5.QtCore import Qt, QRect, QPoint, QSize
from PyQt5.QtGui import QGuiApplication, QCursor
class SnippingWidget(QWidget):
def __init__(self, mainwindow):
super().__init__()
self.setWindowTitle("Screen Snipper")
self.parent = mainwindow
self.setWindowIcon(self.parent.icon)
self.rectMode = True
# ✅ 최상단 유지 및 입력 포커스 확보
self.setWindowFlags(
Qt.FramelessWindowHint
| Qt.WindowStaysOnTopHint
| Qt.SplashScreen # ✅ 최상단 유지 및 입력 포커스 확보
)
self.setWindowOpacity(0.3) # ✅ 창을 불투명하게 유지
self.setAttribute(Qt.WA_NoSystemBackground, False) # ✅ 배경 활성화
self.clipboard = QApplication.clipboard()
# ✅ 전체 화면 설정
screen_geometry = QGuiApplication.primaryScreen().geometry()
self.setGeometry(screen_geometry)
self.activateWindow()
self.raise_()
# ✅ 드래그 초기화
self.origin = QPoint()
self.rubberBand = QRubberBand(QRubberBand.Rectangle, self)
def showEvent(self, event):
self.setCursor(Qt.CrossCursor) # ✅ 십자 모양 커서로 변경
super().showEvent(event)
def closeEvent(self, event):
self.unsetCursor()
super().closeEvent(event)
def mousePressEvent(self, event):
if event.button() == Qt.LeftButton:
if self.rectMode == True:
self.origin = event.pos()
self.rubberBand.setGeometry(QRect(self.origin, QSize()))
self.rubberBand.show()
event.accept()
def mouseMoveEvent(self, event):
if self.rectMode == True:
if not self.origin.isNull():
rect = QRect(self.origin, event.pos()).normalized()
self.rubberBand.setGeometry(rect)
event.accept()
def mouseReleaseEvent(self, event):
if event.button() == Qt.LeftButton:
""" Button Coordinate Setting Mode """
if self.rectMode == False:
pos = event.pos()
btn_x, btn_y = pos.x(), pos.y()
self.parent.coords["btn_x"] = btn_x
self.parent.coords["btn_y"] = btn_y
self.parent.update_btn_coord()
self.close_overlay()
QMessageBox.information(
self,
"버튼 좌표 지정 완료",
f"x: {btn_x}, y: {btn_y}",
)
""" Snipping Area Setting Mode """
if self.rectMode == True:
selected_rect = self.rubberBand.geometry()
self.rubberBand.hide()
QApplication.processEvents()
self.close_overlay()
# 멀티 모니터 지원
screen = QGuiApplication.screenAt(event.globalPos())
if not screen:
screen = QGuiApplication.primaryScreen()
# screenshot = screen.grabWindow(
# 0,
# selected_rect.x(),
# selected_rect.y(),
# selected_rect.width(),
# selected_rect.height(),
# )
# # ✅ 캡처 이미지 저장 및 클립보드 복사
# screenshot.save("screenshot.png", "PNG")
# self.clipboard.setPixmap(screenshot)
x, y, w, h = selected_rect.getRect()
self.parent.coords["x"] = x
self.parent.coords["y"] = y
self.parent.coords["w"] = w
self.parent.coords["h"] = h
self.parent.update_rect_coords()
QMessageBox.information(
self,
"영역 지정 완료",
f"x: {x}, y: {y}, width: {w}, height: {h}",
)
def close_overlay(self):
self.origin = QPoint()
self.close()
def keyPressEvent(self, event):
if event.key() == Qt.Key_Escape:
self.rubberBand.hide()
self.origin = QPoint()
self.close()
event.accept()