Add a description of what you changed
This commit is contained in:
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 87 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 464 KiB After Width: | Height: | Size: 36 KiB |
+116
-154
@@ -1,16 +1,39 @@
|
||||
# 文件路径: ComfyUI/custom_nodes/ComfyUI-Snap_Processing/ui/canvas_window.py
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QDialog, QPushButton, QVBoxLayout, QLineEdit, QHBoxLayout,
|
||||
QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QMessageBox
|
||||
QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QMessageBox,
|
||||
QMenuBar, QMenu, QAction
|
||||
)
|
||||
from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QTransform, QPen
|
||||
from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QTransform
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, QRectF, QPointF
|
||||
|
||||
|
||||
class CustomGraphicsView(QGraphicsView):
|
||||
def __init__(self, scene, parent=None):
|
||||
super().__init__(scene, parent)
|
||||
self.zoom_step = 1.1
|
||||
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
|
||||
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
|
||||
self.setViewportUpdateMode(QGraphicsView.FullViewportUpdate)
|
||||
self.setFocusPolicy(Qt.StrongFocus) # 确保视图可以接收焦点
|
||||
|
||||
def wheelEvent(self, event):
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
# 修改为无需按下 Alt 键即可缩放视图
|
||||
if modifiers == Qt.NoModifier:
|
||||
delta = event.angleDelta().y()
|
||||
if delta > 0:
|
||||
factor = self.zoom_step
|
||||
else:
|
||||
factor = 1 / self.zoom_step
|
||||
self.scale(factor, factor)
|
||||
event.accept()
|
||||
else:
|
||||
super().wheelEvent(event)
|
||||
|
||||
|
||||
class CanvasWindow(QDialog):
|
||||
save_signal = pyqtSignal()
|
||||
close_signal = pyqtSignal()
|
||||
@@ -18,7 +41,8 @@ class CanvasWindow(QDialog):
|
||||
def __init__(self, input_image, canvas_width=512, canvas_height=512):
|
||||
super().__init__()
|
||||
|
||||
self.setWindowTitle("PyQt5 Image Canvas")
|
||||
self.setWindowTitle("Snap Canvas")
|
||||
self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
|
||||
self.input_image = input_image
|
||||
self.canvas_width = canvas_width
|
||||
self.canvas_height = canvas_height
|
||||
@@ -29,32 +53,43 @@ class CanvasWindow(QDialog):
|
||||
self.scale_factor = 1.0 # 初始缩放倍数
|
||||
|
||||
self.scene = QGraphicsScene()
|
||||
self.view = QGraphicsView(self.scene)
|
||||
self.view.setStyleSheet("border: 1px solid black;")
|
||||
# 使用自定义的视图
|
||||
self.view = CustomGraphicsView(self.scene)
|
||||
self.view.setStyleSheet("border: 1px solid black; background-color: #404040;") # 设置深灰色背景区分画布
|
||||
|
||||
# 设置滚动条策略为需要时显示
|
||||
self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
|
||||
|
||||
# 启用拖动画布
|
||||
self.view.setDragMode(QGraphicsView.ScrollHandDrag)
|
||||
|
||||
# 创建白色画布
|
||||
self.canvas = QImage(self.canvas_width, self.canvas_height, QImage.Format_RGB888)
|
||||
self.canvas.fill(QColor('white'))
|
||||
|
||||
# 添加画布背景
|
||||
# 添加画布到场景
|
||||
self.canvas_pixmap_item = QGraphicsPixmapItem(QPixmap.fromImage(self.canvas))
|
||||
self.scene.addItem(self.canvas_pixmap_item)
|
||||
|
||||
# 添加可调整大小和可旋转的输入图像
|
||||
# 直接显示原始尺寸,不进行适应画布
|
||||
original_pixmap = QPixmap.fromImage(self.input_image)
|
||||
self.input_pixmap_item = ResizablePixmapItem(original_pixmap, self.canvas_width, self.canvas_height)
|
||||
self.input_pixmap_item = ResizablePixmapItem(original_pixmap)
|
||||
self.input_pixmap_item.setPos(self.canvas_width / 2, self.canvas_height / 2) # 将图像放置在画布中央
|
||||
self.scene.addItem(self.input_pixmap_item)
|
||||
|
||||
# 设置旋转锚点为图像中心
|
||||
self.input_pixmap_item.setTransformOriginPoint(self.input_pixmap_item.boundingRect().center())
|
||||
|
||||
# 添加画布边界线
|
||||
self.add_canvas_border()
|
||||
# 创建菜单栏
|
||||
self.menu_bar = QMenuBar(self)
|
||||
help_menu = QMenu("使用说明(Help)", self)
|
||||
self.menu_bar.addMenu(help_menu)
|
||||
|
||||
# 创建“使用方法”动作
|
||||
usage_action = QAction("使用方法", self)
|
||||
usage_action.triggered.connect(self.show_usage)
|
||||
help_menu.addAction(usage_action)
|
||||
|
||||
# 创建按钮和输入框
|
||||
save_button = QPushButton("保存并关闭", self)
|
||||
@@ -97,6 +132,7 @@ class CanvasWindow(QDialog):
|
||||
hbox.addWidget(scale_button)
|
||||
|
||||
vbox = QVBoxLayout()
|
||||
vbox.setMenuBar(self.menu_bar) # 将菜单栏添加到布局中
|
||||
vbox.addWidget(self.view)
|
||||
vbox.addLayout(hbox)
|
||||
vbox.addWidget(save_button)
|
||||
@@ -109,16 +145,18 @@ class CanvasWindow(QDialog):
|
||||
# 设置窗口最小大小
|
||||
self.setMinimumSize(600, 400)
|
||||
|
||||
def add_canvas_border(self):
|
||||
"""添加画布边界线以可视化画布边缘,但不包含在输出中"""
|
||||
pen = QPen(QColor('red'))
|
||||
pen.setWidth(2)
|
||||
border_rect = QRectF(0, 0, self.canvas_width, self.canvas_height)
|
||||
self.border_item = self.scene.addRect(border_rect, pen)
|
||||
self.border_item.setZValue(1) # 确保边界线在最上层
|
||||
self.border_item.setFlag(QGraphicsItem.ItemIsSelectable, False)
|
||||
self.border_item.setFlag(QGraphicsItem.ItemIsMovable, False)
|
||||
self.border_item.setFlag(QGraphicsItem.ItemSendsGeometryChanges, False)
|
||||
def show_usage(self):
|
||||
usage_text = (
|
||||
"使用方法:\n"
|
||||
"1. 滚动鼠标滚轮可缩放视图。\n"
|
||||
"2. 按住 Ctrl 键并滚动鼠标滚轮可缩放图像。\n"
|
||||
"3. 在输入框中输入旋转角度(度数),点击“旋转”按钮可旋转图像。\n"
|
||||
"4. 在输入框中输入缩放倍数,点击“缩放”按钮可缩放图像。\n"
|
||||
"5. 拖动图像可移动其位置。\n"
|
||||
"6. 使用上方输入框可调整画布大小。\n"
|
||||
"7. 点击“保存并关闭”按钮可保存当前图像。\n"
|
||||
)
|
||||
QMessageBox.information(self, "使用方法", usage_text)
|
||||
|
||||
def save_and_close(self):
|
||||
self.save_image()
|
||||
@@ -132,15 +170,9 @@ class CanvasWindow(QDialog):
|
||||
painter = QPainter(image)
|
||||
target_rect = QRectF(0, 0, self.canvas_width, self.canvas_height)
|
||||
|
||||
# 隐藏边界线
|
||||
self.border_item.setVisible(False)
|
||||
|
||||
# 渲染场景
|
||||
self.scene.render(painter, target_rect, self.scene.sceneRect(), Qt.IgnoreAspectRatio)
|
||||
|
||||
# 显示边界线
|
||||
self.border_item.setVisible(True)
|
||||
|
||||
painter.end()
|
||||
|
||||
self.modified_image = image
|
||||
@@ -151,7 +183,7 @@ class CanvasWindow(QDialog):
|
||||
self.top_left_y = int(top_left_point.y())
|
||||
|
||||
# 获取缩放倍数
|
||||
self.scale_factor = self.input_pixmap_item.get_current_scale()
|
||||
self.scale_factor = self.input_pixmap_item.current_scale
|
||||
|
||||
def closeEvent(self, event):
|
||||
self.close_signal.emit()
|
||||
@@ -165,32 +197,24 @@ class CanvasWindow(QDialog):
|
||||
QMessageBox.warning(self, "输入错误", "画布宽度和高度必须是整数。")
|
||||
return
|
||||
|
||||
# 更新画布宽度和高度
|
||||
self.canvas_width = width
|
||||
self.canvas_height = height
|
||||
|
||||
# 更新画布图像
|
||||
# 更新场景和视图的画布大小
|
||||
self.canvas = QImage(self.canvas_width, self.canvas_height, QImage.Format_RGB888)
|
||||
self.canvas.fill(QColor('white'))
|
||||
self.canvas_pixmap_item.setPixmap(QPixmap.fromImage(self.canvas))
|
||||
|
||||
# 更新场景和视图
|
||||
self.scene.setSceneRect(0, 0, self.canvas_width, self.canvas_height)
|
||||
self.view.setSceneRect(0, 0, self.canvas_width, self.canvas_height)
|
||||
|
||||
# 更新边界线
|
||||
self.scene.removeItem(self.border_item)
|
||||
self.add_canvas_border()
|
||||
|
||||
# 更新视图大小以适应新的画布大小
|
||||
self.view.updateGeometry()
|
||||
|
||||
def rotate_image(self):
|
||||
try:
|
||||
angle = float(self.rotate_input.text())
|
||||
except ValueError:
|
||||
QMessageBox.warning(self, "输入错误", "旋转角度必须是数字。")
|
||||
return
|
||||
|
||||
self.input_pixmap_item.rotate_pixmap(angle)
|
||||
|
||||
def scale_image(self):
|
||||
@@ -199,9 +223,8 @@ class CanvasWindow(QDialog):
|
||||
if scale_factor <= 0:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
QMessageBox.warning(self, "输入错误", "缩放倍数必须是一个正数。")
|
||||
QMessageBox.warning(self, "输入错误", "缩放倍数必须是正数。")
|
||||
return
|
||||
|
||||
self.input_pixmap_item.scale_pixmap(scale_factor)
|
||||
|
||||
def get_modified_image(self):
|
||||
@@ -218,127 +241,66 @@ class CanvasWindow(QDialog):
|
||||
|
||||
|
||||
class ResizablePixmapItem(QGraphicsPixmapItem):
|
||||
def __init__(self, pixmap, canvas_width, canvas_height):
|
||||
def __init__(self, pixmap):
|
||||
super().__init__(pixmap)
|
||||
self.canvas_width = canvas_width
|
||||
self.canvas_height = canvas_height
|
||||
self.original_pixmap = pixmap.copy() # 保持原始图像的副本
|
||||
self.original_size = pixmap.size()
|
||||
self.setFlags(
|
||||
QGraphicsItem.ItemIsSelectable |
|
||||
QGraphicsItem.ItemIsMovable |
|
||||
QGraphicsItem.ItemSendsGeometryChanges
|
||||
QGraphicsItem.ItemIsMovable
|
||||
)
|
||||
self.setTransformationMode(Qt.SmoothTransformation)
|
||||
self.setAcceptHoverEvents(True)
|
||||
self.is_resizing = False
|
||||
self.resize_handle_size = 15 # 增大缩放手柄大小,便于操作
|
||||
self.aspect_ratio = pixmap.width() / pixmap.height() if pixmap.height() != 0 else 1
|
||||
self.setCursor(Qt.OpenHandCursor)
|
||||
self.min_scale = 0.1
|
||||
self.max_scale = 10
|
||||
self.current_scale = 1.0
|
||||
self.current_rotation = 0 # 当前旋转角度
|
||||
|
||||
# 设置旋转锚点为图像中心
|
||||
self.setAcceptDrops(False)
|
||||
self.setTransformOriginPoint(self.boundingRect().center())
|
||||
|
||||
def hoverMoveEvent(self, event):
|
||||
if self.is_in_resize_area(event.pos()):
|
||||
self.setCursor(Qt.SizeFDiagCursor)
|
||||
else:
|
||||
self.setCursor(Qt.OpenHandCursor)
|
||||
super().hoverMoveEvent(event)
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
if event.button() == Qt.LeftButton and self.is_in_resize_area(event.pos()):
|
||||
self.is_resizing = True
|
||||
self.resize_start_pos = event.pos()
|
||||
self.setCursor(Qt.SizeFDiagCursor)
|
||||
event.accept()
|
||||
else:
|
||||
self.is_resizing = False
|
||||
self.setCursor(Qt.ClosedHandCursor)
|
||||
super().mousePressEvent(event)
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
if self.is_resizing:
|
||||
offset = event.pos() - self.resize_start_pos
|
||||
self.resize_image(offset)
|
||||
event.accept()
|
||||
else:
|
||||
super().mouseMoveEvent(event)
|
||||
|
||||
def mouseReleaseEvent(self, event):
|
||||
if self.is_resizing:
|
||||
self.is_resizing = False
|
||||
self.setCursor(Qt.OpenHandCursor)
|
||||
event.accept()
|
||||
else:
|
||||
self.setCursor(Qt.OpenHandCursor)
|
||||
super().mouseReleaseEvent(event)
|
||||
|
||||
def boundingRect(self):
|
||||
return QRectF(0, 0, self.pixmap().width(), self.pixmap().height())
|
||||
|
||||
def resize_image(self, offset):
|
||||
# 调整缩放灵敏度
|
||||
scale_change = offset.x() / 500.0 # 增加分母以减少灵敏度
|
||||
scale_factor = 1 + scale_change
|
||||
if scale_factor <= 0:
|
||||
return # 防止缩放为零或负值
|
||||
|
||||
new_scale = self.current_scale * scale_factor
|
||||
|
||||
if self.min_scale <= new_scale <= self.max_scale:
|
||||
self.current_scale = new_scale
|
||||
self.setScale(self.current_scale)
|
||||
self.update()
|
||||
|
||||
def is_in_resize_area(self, pos):
|
||||
rect = self.boundingRect()
|
||||
resize_rect = QRectF(
|
||||
rect.right() - self.resize_handle_size,
|
||||
rect.bottom() - self.resize_handle_size,
|
||||
self.resize_handle_size,
|
||||
self.resize_handle_size
|
||||
)
|
||||
return resize_rect.contains(pos)
|
||||
|
||||
def wheelEvent(self, event):
|
||||
try:
|
||||
if hasattr(event, 'angleDelta'):
|
||||
delta = event.angleDelta().y()
|
||||
elif hasattr(event, 'delta'):
|
||||
delta = event.delta()
|
||||
else:
|
||||
delta = 0
|
||||
|
||||
factor = 1.1 if delta > 0 else 0.9
|
||||
self.resize_with_factor(factor)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def resize_with_factor(self, factor):
|
||||
new_scale = self.current_scale * factor
|
||||
|
||||
if self.min_scale <= new_scale <= self.max_scale:
|
||||
self.current_scale = new_scale
|
||||
self.setScale(self.current_scale)
|
||||
self.update()
|
||||
|
||||
def rotate_pixmap(self, angle):
|
||||
"""根据输入角度旋转图像,并保持旋转锚点为中心"""
|
||||
self.current_rotation = (self.current_rotation + angle) % 360
|
||||
self.setRotation(self.current_rotation)
|
||||
self.current_scale = 1.0
|
||||
self.current_rotation = 0.0
|
||||
self.min_scale = 0.1
|
||||
self.max_scale = 10.0
|
||||
|
||||
def scale_pixmap(self, scale_factor):
|
||||
"""手动设置缩放倍数"""
|
||||
if scale_factor <= 0:
|
||||
return
|
||||
if self.min_scale <= scale_factor <= self.max_scale:
|
||||
self.current_scale = scale_factor
|
||||
self.setScale(self.current_scale)
|
||||
self.update()
|
||||
self.update_transform()
|
||||
else:
|
||||
QMessageBox.warning(None, "无效的缩放", f"缩放倍数必须在 {self.min_scale} 和 {self.max_scale} 之间。")
|
||||
|
||||
def get_current_scale(self):
|
||||
return self.current_scale
|
||||
def rotate_pixmap(self, angle):
|
||||
self.current_rotation = angle % 360
|
||||
self.update_transform()
|
||||
|
||||
def update_transform(self):
|
||||
transform = QTransform()
|
||||
center = self.boundingRect().center()
|
||||
transform.translate(center.x(), center.y())
|
||||
transform.rotate(self.current_rotation)
|
||||
transform.scale(self.current_scale, self.current_scale)
|
||||
transform.translate(-center.x(), -center.y())
|
||||
self.setTransform(transform)
|
||||
|
||||
def wheelEvent(self, event):
|
||||
if QApplication.keyboardModifiers() == Qt.ControlModifier:
|
||||
delta = event.delta() # 修改这里,使用 delta()
|
||||
if delta > 0:
|
||||
factor = 1.1
|
||||
else:
|
||||
factor = 0.9
|
||||
new_scale = self.current_scale * factor
|
||||
self.scale_pixmap(new_scale)
|
||||
event.accept()
|
||||
else:
|
||||
event.ignore() # 确保事件能够传递给视图进行缩放
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
# 示例输入图像,可以替换为您自己的图像路径
|
||||
input_image_path = "your_image.png"
|
||||
if not os.path.exists(input_image_path):
|
||||
# 创建一个示例图像
|
||||
input_image = QImage(200, 200, QImage.Format_RGB888)
|
||||
input_image.fill(QColor('blue'))
|
||||
else:
|
||||
input_image = QImage(input_image_path)
|
||||
|
||||
window = CanvasWindow(input_image)
|
||||
window.show()
|
||||
sys.exit(app.exec_())
|
||||
|
||||
Reference in New Issue
Block a user