diff --git a/Snap_canvas.py b/Snap_canvas.py index f762b35..0271e66 100644 --- a/Snap_canvas.py +++ b/Snap_canvas.py @@ -1,85 +1,91 @@ +# 文件路径: ComfyUI/custom_nodes/ComfyUI-Snap_Processing/Snap_canvas.py + from PyQt5.QtCore import QEventLoop -from PyQt5.QtWidgets import QApplication +from PyQt5.QtWidgets import QApplication, QMessageBox from PyQt5.QtGui import QImage import time import os -import random import torch from PIL import Image import numpy as np from io import BytesIO from .ui.canvas_window import CanvasWindow + class PyQtCanvasNode: @staticmethod def INPUT_TYPES(): return { "required": { - "image": ("IMAGE",), # Required image input + "image": ("IMAGE",), # 必需的图像输入 "seed": ("INT", {"default": 0, "min": 0, "max": 0xffffffffffffffff}), } } - RETURN_TYPES = ("IMAGE", "INT", "STRING") # Added "STRING" to the return types + RETURN_TYPES = ("IMAGE", "INT", "STRING", "INT", "INT", "FLOAT") # IMAGE, seed, path, x, y, scale_factor + RETURN_NAMES = ("image", "seed", "string", "X", "Y", "factor") FUNCTION = "activate_pyqt" CATEGORY = "Snap Processing" def activate_pyqt(self, image, seed): try: - # Use the user-provided seed value + # 使用用户提供的种子值 print(f"Received seed: {seed}") - # Convert tensor to QImage + # 将张量转换为 QImage qimage = self.tensor_to_qimage(image) print("Converted tensor to QImage") - # Run PyQt GUI and block the main thread until the user finishes - modified_image = self.run_pyqt_gui(qimage) + # 运行 PyQt GUI 并阻塞主线程,直到用户完成操作 + modified_image, x, y, scale_factor = self.run_pyqt_gui(qimage) print("PyQt GUI closed") - # Convert the modified QImage to a NumPy array + # 将修改后的 QImage 转换为 NumPy 数组 numpy_array = self.qimage_to_numpy(modified_image) print("Converted QImage to NumPy array") - # Define the save directory - save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "ComfyUI-Snap_Processing", "save") + save_directory = os.path.join(os.getcwd(), "canvas") + # 如果目录不存在,则创建该目录 + if not os.path.exists(save_directory): + os.makedirs(save_directory) - # Create a unique file name using the seed and current timestamp - timestamp = int(time.time()) - png_image_filename = f"output_image_seed{seed}_{timestamp}.png" + # 使用固定文件名 'output.png' + png_image_filename = "output.png" png_image_path = os.path.join(save_directory, png_image_filename) + + # 保存图像 self.numpy_to_png(numpy_array, png_image_path) - print(f"Saved image to {png_image_path}") + print(f"已将图像保存到 {png_image_path}") - # Get the absolute path for consistency - absolute_png_image_path = os.path.abspath(png_image_path) - - # Return the modified image, seed, and the file path - return (self.qimage_to_tensor(modified_image), seed, absolute_png_image_path) + # 返回修改后的图像和其他参数 + return (self.qimage_to_tensor(modified_image), seed, png_image_path, x, y, scale_factor) except Exception as e: - print(f"Error in activate_pyqt: {e}") + print(f"激活 PyQt 时出错: {e}") raise e def run_pyqt_gui(self, input_image): try: app = QApplication.instance() if app is None: - app = QApplication([]) # Create the global application + app = QApplication([]) # 创建全局应用程序 - # Always create a new CanvasWindow instance + # 始终创建一个新的 CanvasWindow 实例 dialog = CanvasWindow(input_image) dialog.save_signal.connect(self.on_save) dialog.close_signal.connect(self.on_close) print("PyQt CanvasWindow shown") - dialog.exec_() # Block until the dialog is closed + dialog.exec_() # 阻塞,直到对话框关闭 print("Dialog closed") modified_image = dialog.get_modified_image() - dialog.deleteLater() # Ensure the window is properly destroyed + x = dialog.get_top_left_x() + y = dialog.get_top_left_y() + scale_factor = dialog.get_scale_factor() + dialog.deleteLater() # 确保窗口被正确销毁 print("CanvasWindow deleted") - return modified_image + return modified_image, x, y, scale_factor except Exception as e: print(f"Error in run_pyqt_gui: {e}") raise e diff --git a/Snapload.py b/Snapload.py index f1b1995..b812b00 100644 --- a/Snapload.py +++ b/Snapload.py @@ -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("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 cf46284..61578b5 100644 Binary files a/__pycache__/Snap_canvas.cpython-311.pyc and b/__pycache__/Snap_canvas.cpython-311.pyc differ diff --git a/save/output_1727966231783.png b/save/output_1727966231783.png new file mode 100644 index 0000000..8f69729 Binary files /dev/null and b/save/output_1727966231783.png differ diff --git a/save/output_1727966269871.png b/save/output_1727966269871.png new file mode 100644 index 0000000..e6a9e90 Binary files /dev/null and b/save/output_1727966269871.png differ diff --git a/save/output_1727966582162.png b/save/output_1727966582162.png new file mode 100644 index 0000000..5d039d5 Binary files /dev/null and b/save/output_1727966582162.png differ diff --git a/save/output_1727966896008.png b/save/output_1727966896008.png new file mode 100644 index 0000000..398c9e8 Binary files /dev/null and b/save/output_1727966896008.png differ diff --git a/save/output_1727966909228.png b/save/output_1727966909228.png new file mode 100644 index 0000000..7b23789 Binary files /dev/null and b/save/output_1727966909228.png differ diff --git a/save/output_1727967145059.png b/save/output_1727967145059.png new file mode 100644 index 0000000..d64b44a Binary files /dev/null and b/save/output_1727967145059.png differ diff --git a/save/output_1727967224096.png b/save/output_1727967224096.png new file mode 100644 index 0000000..dc559f7 Binary files /dev/null and b/save/output_1727967224096.png differ diff --git a/save/output_1727967262925.png b/save/output_1727967262925.png new file mode 100644 index 0000000..e030593 Binary files /dev/null and b/save/output_1727967262925.png differ diff --git a/save/output_image_seed0_1727966896.png b/save/output_image_seed0_1727966896.png new file mode 100644 index 0000000..73b4356 Binary files /dev/null and b/save/output_image_seed0_1727966896.png differ diff --git a/save/output_image_seed195677953338875_1727967145.png b/save/output_image_seed195677953338875_1727967145.png new file mode 100644 index 0000000..be5e143 Binary files /dev/null and b/save/output_image_seed195677953338875_1727967145.png differ diff --git a/save/output_image_seed1_1727962356.png b/save/output_image_seed1_1727962356.png new file mode 100644 index 0000000..d30c6d3 Binary files /dev/null and b/save/output_image_seed1_1727962356.png differ diff --git a/save/output_image_seed1_1727963797.png b/save/output_image_seed1_1727963797.png new file mode 100644 index 0000000..f9fa61d Binary files /dev/null and b/save/output_image_seed1_1727963797.png differ diff --git a/save/output_image_seed1_1727964255.png b/save/output_image_seed1_1727964255.png new file mode 100644 index 0000000..ba4624b Binary files /dev/null and b/save/output_image_seed1_1727964255.png differ diff --git a/save/output_image_seed1_1727966269.png b/save/output_image_seed1_1727966269.png new file mode 100644 index 0000000..6142721 Binary files /dev/null and b/save/output_image_seed1_1727966269.png differ diff --git a/save/output_image_seed1_1727966582.png b/save/output_image_seed1_1727966582.png new file mode 100644 index 0000000..b6a33a4 Binary files /dev/null and b/save/output_image_seed1_1727966582.png differ diff --git a/save/output_image_seed2_1727962390.png b/save/output_image_seed2_1727962390.png new file mode 100644 index 0000000..b71c5db Binary files /dev/null and b/save/output_image_seed2_1727962390.png differ diff --git a/save/output_image_seed2_1727966231.png b/save/output_image_seed2_1727966231.png new file mode 100644 index 0000000..947aec0 Binary files /dev/null and b/save/output_image_seed2_1727966231.png differ diff --git a/save/output_image_seed483086821900913_1727966909.png b/save/output_image_seed483086821900913_1727966909.png new file mode 100644 index 0000000..ed63327 Binary files /dev/null and b/save/output_image_seed483086821900913_1727966909.png differ diff --git a/save/output_image_seed729033948006093_1727967224.png b/save/output_image_seed729033948006093_1727967224.png new file mode 100644 index 0000000..f11ef75 Binary files /dev/null and b/save/output_image_seed729033948006093_1727967224.png differ diff --git a/save/output_image_seed928041820751391_1727967263.png b/save/output_image_seed928041820751391_1727967263.png new file mode 100644 index 0000000..ff1cba8 Binary files /dev/null and b/save/output_image_seed928041820751391_1727967263.png differ diff --git a/ui/__pycache__/canvas_window.cpython-311.pyc b/ui/__pycache__/canvas_window.cpython-311.pyc index 965dcce..5a13d18 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 a7ba0f3..4f2a85e 100644 --- a/ui/canvas_window.py +++ b/ui/canvas_window.py @@ -1,3 +1,5 @@ +# 文件路径: ComfyUI/custom_nodes/ComfyUI-Snap_Processing/ui/canvas_window.py + import os import sys import time @@ -6,7 +8,7 @@ from PyQt5.QtWidgets import ( QGraphicsView, QGraphicsScene, QGraphicsPixmapItem, QGraphicsItem, QMessageBox ) from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QTransform, QPen -from PyQt5.QtCore import pyqtSignal, Qt, QRectF +from PyQt5.QtCore import pyqtSignal, Qt, QRectF, QPointF class CanvasWindow(QDialog): @@ -22,6 +24,10 @@ class CanvasWindow(QDialog): self.canvas_height = canvas_height self.modified_image = None + self.top_left_x = 0 + self.top_left_y = 0 + self.scale_factor = 1.0 # 初始缩放倍数 + self.scene = QGraphicsScene() self.view = QGraphicsView(self.scene) self.view.setStyleSheet("border: 1px solid black;") @@ -39,10 +45,9 @@ class CanvasWindow(QDialog): 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) + # 直接显示原始尺寸,不进行适应画布 + original_pixmap = QPixmap.fromImage(self.input_image) + self.input_pixmap_item = ResizablePixmapItem(original_pixmap, self.canvas_width, self.canvas_height) self.scene.addItem(self.input_pixmap_item) # 设置旋转锚点为图像中心 @@ -63,6 +68,14 @@ class CanvasWindow(QDialog): rotate_button = QPushButton("旋转", self) rotate_button.clicked.connect(self.rotate_image) + scale_label = QLineEdit(self) + scale_label.setPlaceholderText("缩放倍数") + scale_label.setFixedWidth(100) + self.scale_input = scale_label + + scale_button = QPushButton("缩放", self) + scale_button.clicked.connect(self.scale_image) + self.canvas_width_input = QLineEdit(self) self.canvas_width_input.setPlaceholderText("画布宽度") self.canvas_width_input.setText(str(self.canvas_width)) @@ -80,6 +93,8 @@ class CanvasWindow(QDialog): hbox.addWidget(size_button) hbox.addWidget(self.rotate_input) hbox.addWidget(rotate_button) + hbox.addWidget(self.scale_input) + hbox.addWidget(scale_button) vbox = QVBoxLayout() vbox.addWidget(self.view) @@ -92,10 +107,10 @@ class CanvasWindow(QDialog): self.view.setSceneRect(0, 0, self.canvas_width, self.canvas_height) # 设置窗口最小大小 - self.setMinimumSize(400, 300) + 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) @@ -130,13 +145,13 @@ class CanvasWindow(QDialog): self.modified_image = image - # 保存路径 - save_directory = os.path.join(os.getcwd(), "ComfyUI", "custom_nodes", "ComfyUI-Snap_Processing", "save") + # 获取图像左上角的坐标 + top_left_point = self.input_pixmap_item.mapToScene(0, 0) + self.top_left_x = int(top_left_point.x()) + self.top_left_y = int(top_left_point.y()) - timestamp = int(time.time() * 1000) - save_path = os.path.join(save_directory, f"output_{timestamp}.png") - image.save(save_path) - self.save_signal.emit() + # 获取缩放倍数 + self.scale_factor = self.input_pixmap_item.get_current_scale() def closeEvent(self, event): self.close_signal.emit() @@ -178,15 +193,37 @@ class CanvasWindow(QDialog): self.input_pixmap_item.rotate_pixmap(angle) + def scale_image(self): + try: + scale_factor = float(self.scale_input.text()) + if scale_factor <= 0: + raise ValueError + except ValueError: + QMessageBox.warning(self, "输入错误", "缩放倍数必须是一个正数。") + return + + self.input_pixmap_item.scale_pixmap(scale_factor) + def get_modified_image(self): return self.modified_image + def get_top_left_x(self): + return self.top_left_x + + def get_top_left_y(self): + return self.top_left_y + + def get_scale_factor(self): + return self.scale_factor + class ResizablePixmapItem(QGraphicsPixmapItem): def __init__(self, pixmap, canvas_width, canvas_height): 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 | @@ -290,38 +327,18 @@ class ResizablePixmapItem(QGraphicsPixmapItem): self.update() def rotate_pixmap(self, angle): - """根据输入角度旋转图像""" + """根据输入角度旋转图像,并保持旋转锚点为中心""" self.current_rotation = (self.current_rotation + angle) % 360 self.setRotation(self.current_rotation) + 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() -# 示例用法 -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) + def get_current_scale(self): + return self.current_scale