diff --git a/Snap_canvas.py b/Snap_canvas.py index ca9af1b..f762b35 100644 --- a/Snap_canvas.py +++ b/Snap_canvas.py @@ -42,9 +42,8 @@ class PyQtCanvasNode: print("Converted QImage to NumPy array") # Define the save directory - save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "Comfyui_Snap-Processing", "save") - if not os.path.exists(save_directory): - os.makedirs(save_directory) + save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "ComfyUI-Snap_Processing", "save") + # Create a unique file name using the seed and current timestamp timestamp = int(time.time()) diff --git a/Snapload.py b/Snapload.py index 4e49a33..f1b1995 100644 --- a/Snapload.py +++ b/Snapload.py @@ -11,7 +11,7 @@ class Snapload: @classmethod def INPUT_TYPES(s): return {"required": - {"image": ("STRING", {"default": r"123"})}, + {"image": ("STRING", {"default": r"请转换为输入并SnapCanvas的string输出"})}, } CATEGORY = "Snap Processing" @@ -44,7 +44,7 @@ class Snapload: def _resolve_path(image) -> Path: # 如果未提供图像路径,则使用默认路径 if not image: - default_path = Path("ComfyUI/custom_nodes/Comfyui_Snap-Processing/save/output.png").resolve() + default_path = Path("ComfyUI/custom_nodes/ComfyUI-Snap_Processing/save/output.png").resolve() else: default_path = Path(folder_paths.get_annotated_filepath(image)).resolve() diff --git a/__pycache__/Snap_canvas.cpython-311.pyc b/__pycache__/Snap_canvas.cpython-311.pyc index b2e2d5d..cf46284 100644 Binary files a/__pycache__/Snap_canvas.cpython-311.pyc and b/__pycache__/Snap_canvas.cpython-311.pyc differ diff --git a/__pycache__/Snapload.cpython-311.pyc b/__pycache__/Snapload.cpython-311.pyc index d13256d..bf92f5a 100644 Binary files a/__pycache__/Snapload.cpython-311.pyc and b/__pycache__/Snapload.cpython-311.pyc differ diff --git a/__pycache__/__init__.cpython-311.pyc b/__pycache__/__init__.cpython-311.pyc index a111703..b241dfc 100644 Binary files a/__pycache__/__init__.cpython-311.pyc and b/__pycache__/__init__.cpython-311.pyc differ diff --git a/__pycache__/area_calculator.cpython-311.pyc b/__pycache__/area_calculator.cpython-311.pyc index bb9f53a..c6a89b3 100644 Binary files a/__pycache__/area_calculator.cpython-311.pyc and b/__pycache__/area_calculator.cpython-311.pyc differ diff --git a/save/output.png b/save/output.png deleted file mode 100644 index e5101fd..0000000 Binary files a/save/output.png and /dev/null differ diff --git a/save/output_image.png b/save/output_image.png deleted file mode 100644 index b5f736e..0000000 Binary files a/save/output_image.png and /dev/null differ diff --git a/save/output_image_seed102188696742965_1727592804.png b/save/output_image_seed102188696742965_1727592804.png deleted file mode 100644 index f975c12..0000000 Binary files a/save/output_image_seed102188696742965_1727592804.png and /dev/null differ diff --git a/save/output_image_seed165231885671399_1727680395.png b/save/output_image_seed165231885671399_1727680395.png deleted file mode 100644 index 1a9ee45..0000000 Binary files a/save/output_image_seed165231885671399_1727680395.png and /dev/null differ diff --git a/save/output_image_seed341874800874179_1727592868.png b/save/output_image_seed341874800874179_1727592868.png deleted file mode 100644 index 4862458..0000000 Binary files a/save/output_image_seed341874800874179_1727592868.png and /dev/null differ diff --git a/save/output_image_seed364149889408431_1727679313.png b/save/output_image_seed364149889408431_1727679313.png deleted file mode 100644 index bd994ee..0000000 Binary files a/save/output_image_seed364149889408431_1727679313.png and /dev/null differ diff --git a/save/output_image_seed473256537738175_1727592751.png b/save/output_image_seed473256537738175_1727592751.png deleted file mode 100644 index d753361..0000000 Binary files a/save/output_image_seed473256537738175_1727592751.png and /dev/null differ diff --git a/save/output_image_seed518072437279080_1727592788.png b/save/output_image_seed518072437279080_1727592788.png deleted file mode 100644 index 990c729..0000000 Binary files a/save/output_image_seed518072437279080_1727592788.png and /dev/null differ diff --git a/save/output_image_seed554760322076771_1727679945.png b/save/output_image_seed554760322076771_1727679945.png deleted file mode 100644 index fe9cbd5..0000000 Binary files a/save/output_image_seed554760322076771_1727679945.png and /dev/null differ diff --git a/save/output_image_seed575326176570079_1727593596.png b/save/output_image_seed575326176570079_1727593596.png deleted file mode 100644 index bd9d9ec..0000000 Binary files a/save/output_image_seed575326176570079_1727593596.png and /dev/null differ diff --git a/save/output_image_seed612536980664326_1727592812.png b/save/output_image_seed612536980664326_1727592812.png deleted file mode 100644 index f7d26fa..0000000 Binary files a/save/output_image_seed612536980664326_1727592812.png and /dev/null differ diff --git a/save/output_image_seed688705875868338_1727681556.png b/save/output_image_seed688705875868338_1727681556.png deleted file mode 100644 index 7b20442..0000000 Binary files a/save/output_image_seed688705875868338_1727681556.png and /dev/null differ diff --git a/save/output_image_seed703932794060439_1727593988.png b/save/output_image_seed703932794060439_1727593988.png deleted file mode 100644 index c938c7d..0000000 Binary files a/save/output_image_seed703932794060439_1727593988.png and /dev/null differ diff --git a/save/output_image_seed727213827209089_1727593767.png b/save/output_image_seed727213827209089_1727593767.png deleted file mode 100644 index 0e70400..0000000 Binary files a/save/output_image_seed727213827209089_1727593767.png and /dev/null differ diff --git a/save/output_image_seed75858144998317_1727592796.png b/save/output_image_seed75858144998317_1727592796.png deleted file mode 100644 index 6cd7ae6..0000000 Binary files a/save/output_image_seed75858144998317_1727592796.png and /dev/null differ diff --git a/save/output_image_seed808646150378520_1727594070.png b/save/output_image_seed808646150378520_1727594070.png deleted file mode 100644 index 783d9ad..0000000 Binary files a/save/output_image_seed808646150378520_1727594070.png and /dev/null differ diff --git a/save/output_image_seed857148206117389_1727679325.png b/save/output_image_seed857148206117389_1727679325.png deleted file mode 100644 index f89fbde..0000000 Binary files a/save/output_image_seed857148206117389_1727679325.png and /dev/null differ diff --git a/save/output_image_seed92050565502526_1727593606.png b/save/output_image_seed92050565502526_1727593606.png deleted file mode 100644 index 27cb025..0000000 Binary files a/save/output_image_seed92050565502526_1727593606.png and /dev/null differ diff --git a/ui/__pycache__/canvas_window.cpython-311.pyc b/ui/__pycache__/canvas_window.cpython-311.pyc index 7266110..965dcce 100644 Binary files a/ui/__pycache__/canvas_window.cpython-311.pyc and b/ui/__pycache__/canvas_window.cpython-311.pyc differ diff --git a/ui/canvas_window.py b/ui/canvas_window.py index 6126d5e..a7ba0f3 100644 --- a/ui/canvas_window.py +++ b/ui/canvas_window.py @@ -1,10 +1,11 @@ import os +import sys import time from PyQt5.QtWidgets import ( - QDialog, QPushButton, QVBoxLayout, QLineEdit, QHBoxLayout, - QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem + QApplication, QDialog, QPushButton, QVBoxLayout, QLineEdit, QHBoxLayout, + QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QMessageBox ) -from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter +from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QTransform, QPen from PyQt5.QtCore import pyqtSignal, Qt, QRectF @@ -12,52 +13,73 @@ class CanvasWindow(QDialog): save_signal = pyqtSignal() close_signal = pyqtSignal() - def __init__(self, input_image): + def __init__(self, input_image, canvas_width=512, canvas_height=512): super().__init__() self.setWindowTitle("PyQt5 Image Canvas") self.input_image = input_image - self.canvas_width = 512 - self.canvas_height = 512 + self.canvas_width = canvas_width + self.canvas_height = canvas_height self.modified_image = None self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.setStyleSheet("border: 1px solid black;") - self.view.setFixedSize(self.canvas_width + 2, self.canvas_height + 2) - self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) - self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + # 设置滚动条策略为需要时显示 + self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) + self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) + # 创建白色画布 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) + # 添加可调整大小和可旋转的输入图像 scaled_pixmap = QPixmap.fromImage(self.input_image).scaled( self.canvas_width, self.canvas_height, Qt.KeepAspectRatio, Qt.SmoothTransformation ) self.input_pixmap_item = ResizablePixmapItem(scaled_pixmap, self.canvas_width, self.canvas_height) self.scene.addItem(self.input_pixmap_item) - save_button = QPushButton("Save and Close", self) + # 设置旋转锚点为图像中心 + self.input_pixmap_item.setTransformOriginPoint(self.input_pixmap_item.boundingRect().center()) + + # 添加画布边界线 + self.add_canvas_border() + + # 创建按钮和输入框 + save_button = QPushButton("保存并关闭", self) save_button.clicked.connect(self.save_and_close) + rotate_label = QLineEdit(self) + rotate_label.setPlaceholderText("旋转角度 (°)") + rotate_label.setFixedWidth(100) + self.rotate_input = rotate_label + + rotate_button = QPushButton("旋转", self) + rotate_button.clicked.connect(self.rotate_image) + self.canvas_width_input = QLineEdit(self) - self.canvas_width_input.setPlaceholderText("Canvas Width") + self.canvas_width_input.setPlaceholderText("画布宽度") self.canvas_width_input.setText(str(self.canvas_width)) self.canvas_height_input = QLineEdit(self) - self.canvas_height_input.setPlaceholderText("Canvas Height") + self.canvas_height_input.setPlaceholderText("画布高度") self.canvas_height_input.setText(str(self.canvas_height)) - size_button = QPushButton("Set Canvas Size", self) + size_button = QPushButton("设置画布大小", self) size_button.clicked.connect(self.set_canvas_size) + # 布局设置 hbox = QHBoxLayout() hbox.addWidget(self.canvas_width_input) hbox.addWidget(self.canvas_height_input) hbox.addWidget(size_button) + hbox.addWidget(self.rotate_input) + hbox.addWidget(rotate_button) vbox = QVBoxLayout() vbox.addWidget(self.view) @@ -69,28 +91,50 @@ class CanvasWindow(QDialog): self.scene.setSceneRect(0, 0, self.canvas_width, self.canvas_height) self.view.setSceneRect(0, 0, self.canvas_width, self.canvas_height) + # 设置窗口最小大小 + self.setMinimumSize(400, 300) + + 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 save_and_close(self): self.save_image() self.accept() def save_image(self): + # 创建一个与画布大小相同的图像 image = QImage(self.canvas_width, self.canvas_height, QImage.Format_RGB888) image.fill(QColor('white')) painter = QPainter(image) target_rect = QRectF(0, 0, self.canvas_width, self.canvas_height) - source_rect = self.scene.sceneRect() - self.scene.render(painter, target_rect, source_rect) + + # 隐藏边界线 + 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 - save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "Comfyui_Snap-Processing", "save") - if not os.path.exists(save_directory): - os.makedirs(save_directory) + # 保存路径 + save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "ComfyUI-Snap_Processing", "save") timestamp = int(time.time() * 1000) - save_path = os.path.join(save_directory, "output.png") + save_path = os.path.join(save_directory, f"output_{timestamp}.png") image.save(save_path) self.save_signal.emit() @@ -103,18 +147,36 @@ class CanvasWindow(QDialog): width = int(self.canvas_width_input.text()) height = int(self.canvas_height_input.text()) except ValueError: + 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.view.setFixedSize(self.canvas_width + 2, self.canvas_height + 2) + + # 更新边界线 + 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 get_modified_image(self): return self.modified_image @@ -125,7 +187,6 @@ class ResizablePixmapItem(QGraphicsPixmapItem): super().__init__(pixmap) self.canvas_width = canvas_width self.canvas_height = canvas_height - self.original_pixmap = pixmap self.setFlags( QGraphicsItem.ItemIsSelectable | QGraphicsItem.ItemIsMovable | @@ -133,62 +194,69 @@ class ResizablePixmapItem(QGraphicsPixmapItem): ) self.setAcceptHoverEvents(True) self.is_resizing = False - self.resize_handle_size = 10 - self.aspect_ratio = pixmap.width() / pixmap.height() - self.setCursor(Qt.ArrowCursor) + 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.setTransformOriginPoint(self.boundingRect().center()) def hoverMoveEvent(self, event): if self.is_in_resize_area(event.pos()): self.setCursor(Qt.SizeFDiagCursor) else: - self.setCursor(Qt.ArrowCursor) + 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_factor = 0.1 - new_width = self.boundingRect().width() + offset.x() * scale_factor - new_height = new_width / self.aspect_ratio + # 调整缩放灵敏度 + scale_change = offset.x() / 500.0 # 增加分母以减少灵敏度 + scale_factor = 1 + scale_change + if scale_factor <= 0: + return # 防止缩放为零或负值 - self.prepareGeometryChange() - - if 10 < new_width <= self.canvas_width * 2 and 10 < new_height <= self.canvas_height * 2: - scaled_pixmap = self.original_pixmap.scaled( - int(new_width), int(new_height), - Qt.KeepAspectRatio, Qt.SmoothTransformation - ) - self.setPixmap(scaled_pixmap) + 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() - self.prepareGeometryChange() - def is_in_resize_area(self, pos): rect = self.boundingRect() resize_rect = QRectF( @@ -209,18 +277,51 @@ class ResizablePixmapItem(QGraphicsPixmapItem): 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_width = self.boundingRect().width() * factor - new_height = new_width / self.aspect_ratio + new_scale = self.current_scale * factor - if self.min_scale * self.canvas_width <= new_width <= self.max_scale * self.canvas_width and \ - self.min_scale * self.canvas_height <= new_height <= self.max_scale * self.canvas_height: - scaled_pixmap = self.original_pixmap.scaled( - int(new_width), int(new_height), Qt.KeepAspectRatio, Qt.SmoothTransformation - ) - self.setPixmap(scaled_pixmap) + 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) + + +# 示例用法 +def run_pyqt_gui(input_image_path, canvas_width=512, canvas_height=512): + app = QApplication(sys.argv) + + input_image = QImage(input_image_path) + if input_image.isNull(): + QMessageBox.critical(None, "加载失败", "无法加载图像。请检查图像路径是否正确。") + sys.exit(1) + + dialog = CanvasWindow(input_image, canvas_width, canvas_height) + dialog.show() # 使用 show 而不是 exec_ 以避免阻塞 + + # 运行应用程序的事件循环 + app.exec_() + + modified_image = dialog.get_modified_image() + if modified_image: + save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "Comfyui-Snap_Processing", "save") + + timestamp = int(time.time() * 1000) + save_path = os.path.join(save_directory, f"modified_output_{timestamp}.png") + modified_image.save(save_path) + else: + QMessageBox.information(None, "信息", "没有修改后的图像可用。") + + +if __name__ == "__main__": + # 替换为您实际的图像路径 + image_path = "path_to_your_image.png" # 请替换为实际图像路径 + run_pyqt_gui(image_path)