-
Notifications
You must be signed in to change notification settings - Fork 76
Expand file tree
/
Copy pathdaoyan_chaijiantools.py
More file actions
186 lines (158 loc) · 7.05 KB
/
daoyan_chaijiantools.py
File metadata and controls
186 lines (158 loc) · 7.05 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import os
import sys
from PySide6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QPushButton,
QLabel, QGraphicsView, QGraphicsScene, QGraphicsPixmapItem,
QGraphicsRectItem, QMessageBox, QWidget)
from PySide6.QtGui import QPixmap, QColor, QPen, QPainter, QImage, QBrush
from PySide6.QtCore import Qt, QRectF, Signal, QPointF, QSize
class CropRectItem(QGraphicsRectItem):
"""可调整大小的裁剪框"""
def __init__(self, rect=QRectF(0, 0, 100, 100), parent=None):
super().__init__(rect, parent)
self.setFlags(QGraphicsRectItem.ItemIsMovable |
QGraphicsRectItem.ItemIsSelectable |
QGraphicsRectItem.ItemSendsGeometryChanges)
self.setPen(QPen(QColor(255, 255, 255), 1, Qt.DashLine))
self.setBrush(QBrush(QColor(255, 255, 255, 50)))
self.setAcceptHoverEvents(True)
self.handle_size = 20
self.handles = {}
self.update_handles()
self.current_handle = None
self.mouse_press_pos = None
self.mouse_press_rect = None
def update_handles(self):
r = self.rect()
self.handles = {
'tl': QRectF(r.left(), r.top(), self.handle_size, self.handle_size),
'tr': QRectF(r.right() - self.handle_size, r.top(), self.handle_size, self.handle_size),
'bl': QRectF(r.left(), r.bottom() - self.handle_size, self.handle_size, self.handle_size),
'br': QRectF(r.right() - self.handle_size, r.bottom() - self.handle_size, self.handle_size, self.handle_size),
}
def paint(self, painter, option, widget=None):
super().paint(painter, option, widget)
# 绘制手柄
painter.setPen(Qt.NoPen)
painter.setBrush(QColor(0, 120, 215))
self.update_handles()
for handle in self.handles.values():
painter.drawRect(handle)
def hoverMoveEvent(self, event):
pos = event.pos()
cursor = Qt.ArrowCursor
for handle_name, rect in self.handles.items():
if rect.contains(pos):
if handle_name in ['tl', 'br']:
cursor = Qt.SizeFDiagCursor
else:
cursor = Qt.SizeBDiagCursor
break
self.setCursor(cursor)
super().hoverMoveEvent(event)
def mousePressEvent(self, event):
self.mouse_press_pos = event.pos()
self.mouse_press_rect = self.rect()
self.current_handle = None
for handle_name, rect in self.handles.items():
if rect.contains(event.pos()):
self.current_handle = handle_name
break
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if self.current_handle:
# 调整大小
diff = event.pos() - self.mouse_press_pos
rect = self.mouse_press_rect
new_rect = QRectF(rect)
if self.current_handle == 'tl':
new_rect.setTopLeft(rect.topLeft() + diff)
elif self.current_handle == 'tr':
new_rect.setTopRight(rect.topRight() + diff)
elif self.current_handle == 'bl':
new_rect.setBottomLeft(rect.bottomLeft() + diff)
elif self.current_handle == 'br':
new_rect.setBottomRight(rect.bottomRight() + diff)
self.setRect(new_rect.normalized())
self.update_handles()
else:
super().mouseMoveEvent(event)
class CropDialog(QDialog):
def __init__(self, image_path, parent=None):
super().__init__(parent)
self.setWindowTitle("裁剪工具")
# Ensure minimum size to avoid "tadpole" look
self.setMinimumSize(800, 600)
self.resize(1200, 900)
# Force window flags to ensure top-level behavior
self.setWindowFlags(Qt.Window | Qt.WindowMinMaxButtonsHint | Qt.WindowCloseButtonHint)
self.setWindowState(Qt.WindowMaximized)
self.image_path = image_path
self.cropped_pixmap = None
layout = QVBoxLayout(self)
# 顶部工具栏
toolbar = QHBoxLayout()
self.btn_crop = QPushButton("裁剪并保存")
self.btn_crop.clicked.connect(self.do_crop)
self.btn_cancel = QPushButton("取消")
self.btn_cancel.clicked.connect(self.reject)
toolbar.addStretch()
toolbar.addWidget(self.btn_crop)
toolbar.addWidget(self.btn_cancel)
layout.addLayout(toolbar)
# 视图
self.scene = QGraphicsScene()
self.view = QGraphicsView(self.scene)
self.view.setRenderHint(QPainter.Antialiasing)
self.view.setDragMode(QGraphicsView.RubberBandDrag)
layout.addWidget(self.view)
# 加载图片
self.load_image()
def showEvent(self, event):
super().showEvent(event)
if hasattr(self, 'pixmap_item'):
self.view.fitInView(self.pixmap_item, Qt.KeepAspectRatio)
def load_image(self):
if not os.path.exists(self.image_path):
QMessageBox.critical(self, "错误", "图片文件不存在")
self.reject()
return
self.pixmap = QPixmap(self.image_path)
self.pixmap_item = QGraphicsPixmapItem(self.pixmap)
self.scene.addItem(self.pixmap_item)
# 默认裁剪框
rect_size = min(self.pixmap.width(), self.pixmap.height()) * 0.8
cx = (self.pixmap.width() - rect_size) / 2
cy = (self.pixmap.height() - rect_size) / 2
self.crop_rect = CropRectItem(QRectF(cx, cy, rect_size, rect_size))
self.scene.addItem(self.crop_rect)
self.view.setSceneRect(self.pixmap.rect())
def do_crop(self):
# 获取裁剪区域(相对于图片)
rect = self.crop_rect.rect()
# 考虑crop_rect在场景中的位置(如果它被移动了)
pos = self.crop_rect.pos()
final_rect = QRectF(pos.x() + rect.x(), pos.y() + rect.y(), rect.width(), rect.height())
# 转换为整数坐标
x = int(final_rect.x())
y = int(final_rect.y())
w = int(final_rect.width())
h = int(final_rect.height())
# 裁剪
cropped = self.pixmap.copy(x, y, w, h)
# 保存回原路径(或者保存为新文件并返回)
# 这里直接覆盖原文件,或者保存为 _cropped 版本
# 根据用户需求:"返回原来的分镜图位置进行替换"
# 建议覆盖或者更新引用。这里我们覆盖原文件。
try:
cropped.save(self.image_path)
self.cropped_pixmap = cropped
QMessageBox.information(self, "成功", "裁剪完成")
self.accept()
except Exception as e:
QMessageBox.critical(self, "错误", f"保存失败: {e}")
if __name__ == "__main__":
from PySide6.QtWidgets import QApplication
app = QApplication(sys.argv)
# 测试
# dialog = CropDialog("path/to/image.png")
# dialog.exec()