diff --git a/PainterNode/README.md b/PainterNode/README.md
index 9b2d168..9572293 100644
--- a/PainterNode/README.md
+++ b/PainterNode/README.md
@@ -4,6 +4,8 @@
## Changelog:
+> 2025.07.29 - Add tool Crop canvas
+
> 2024.05.10 - Implement piping in an image ([issue in an image](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/issues/24)) ([example Piping in an image](#piping))
> 2024.04.11 - Implement **MyPaint** brush tool ([issue MyPaint Brush make](https://github.com/AlekPet/ComfyUI_Custom_Nodes_AlekPet/issues/36))
diff --git a/PainterNode/css/painter_node_styles.css b/PainterNode/css/painter_node_styles.css
index 787d0b5..6ae099f 100644
--- a/PainterNode/css/painter_node_styles.css
+++ b/PainterNode/css/painter_node_styles.css
@@ -48,8 +48,12 @@
min-height: 55px;
}
-.property_brushesBox > button {
+.property_brushesBox > button,
+.property_cropBox > button {
width: auto;
+ display: flex;
+ align-items: center;
+ justify-content: center;
}
.painter_drawning_box_property select {
diff --git a/PainterNode/js/painter_node.js b/PainterNode/js/painter_node.js
index 11b9a94..606b7a4 100644
--- a/PainterNode/js/painter_node.js
+++ b/PainterNode/js/painter_node.js
@@ -32,7 +32,7 @@ import { MyPaintManager } from "./lib/painternode/manager_mypaint.js";
// ================= FUNCTIONS ================
-const DEBUG = !false;
+const DEBUG = false;
const extensionName = "alekpet.PainterNode";
// Save settings in JSON file on the extension folder [big data settings includes images] if true else localStorage
@@ -140,6 +140,11 @@ class Painter {
this.storageCls = this.node.storageCls;
+ // [Crop] Object
+ this.crop_object = null;
+ this.crop_disabled = false;
+ this.mouseDown = null;
+
this.fonts = {
Arial: { type: "default" },
"Times New Roman": { type: "default" },
@@ -287,11 +292,12 @@ class Painter {
-
+
+
@@ -666,7 +672,16 @@ class Painter {
if (await comfyuiDesktopConfirm("Reset canvas size by default 512x512?")) {
this.setCanvasSize(512, 512);
}
+ this.cropReset();
+
this.canvas.clear();
+
+ const image = new fabric.Image("");
+ this.canvas.setBackgroundImage(
+ image,
+ this.canvas.renderAll.bind(this.canvas)
+ );
+
this.canvas.backgroundColor = this.bgColor.value || "#000000";
this.canvas.requestRenderAll();
@@ -689,9 +704,12 @@ class Painter {
}),
obEl = makeElement("button"),
countType = objectNames.filter((t) => t == type).length + 1,
- text_value = !o.hasOwnProperty("mypaintlib")
- ? type + `_${countType}`
- : `mypaint_${countType}`;
+ addTypeCheck = o.hasOwnProperty("mypaintlib")
+ ? "mypaint"
+ : o.hasOwnProperty("cropimage")
+ ? "cropimage"
+ : type,
+ text_value = addTypeCheck + `_${countType}`;
obEl.setAttribute("painter_object", text_value);
obEl.textContent = text_value;
@@ -763,6 +781,16 @@ class Painter {
this.drawning = true;
}
+ // [Crop] Crop
+ if (this.type === "CropMove" || this.type === "Crop") {
+ this.type = "Crop";
+ this.canvas.isDrawingMode = false;
+ this.drawning = true;
+ this.mouseDown = null;
+ }
+
+ this.cropReset();
+
if (this.drawning) {
this.canvas.isDrawingMode = false;
this.drawning = false;
@@ -771,9 +799,15 @@ class Painter {
this.canvas.isDrawingMode = this.drawning = true;
if (
- !["Brush", "Erase", "BrushSymmetry", "Image", "Textbox"].includes(
- this.type
- )
+ ![
+ "Brush",
+ "Erase",
+ "BrushSymmetry",
+ "Image",
+ "Textbox",
+ "Crop",
+ "CropMove",
+ ].includes(this.type)
)
this.canvas.isDrawingMode = false;
}
@@ -901,6 +935,7 @@ class Painter {
changePropertyBrush(type = "Brush") {
if (["Brush", "BrushSymmetry", "BrushMyPaint"].includes(type)) {
if (type === "Brush" || type === "BrushSymmetry") {
+ // nope
}
if (type === "BrushMyPaint") {
@@ -925,6 +960,8 @@ class Painter {
let a_obs = this.canvas.getActiveObjects();
if (a_obs) {
a_obs.forEach((a_o) => {
+ if (a_o.hasOwnProperty("cropimage") && !this.mode) return;
+
this.setActiveStyle(
"strokeWidth",
parseInt(this.strokeWidth.value, 10),
@@ -962,15 +999,15 @@ class Painter {
}) {
let shape = null;
- if (type == "Rect") {
+ if (type === "Rect") {
shape = new fabric.Rect();
- } else if (type == "Circle") {
+ } else if (type === "Circle") {
shape = new fabric.Circle();
- } else if (type == "Triangle") {
+ } else if (type === "Triangle") {
shape = new fabric.Triangle();
- } else if (type == "Line") {
+ } else if (type === "Line") {
shape = new fabric.Line(points);
- } else if (type == "Path") {
+ } else if (type === "Path") {
shape = new fabric.Path(path);
}
@@ -992,6 +1029,126 @@ class Painter {
return shape;
}
+ // [Crop] Crop canvas
+ cropReset() {
+ if (this.crop_object) {
+ this.canvas.remove(this.crop_object);
+ this.crop_object = null;
+ this.crop_disabled = false;
+ this.mouseDown = null;
+ }
+ }
+
+ createCrop(left, top) {
+ const crop_object = new fabric.Rect();
+ Object.assign(crop_object, {
+ originX: "left",
+ originY: "top",
+ strokeWidth: 1,
+ stroke: "#ccc",
+ left: left,
+ top: top,
+ transparentCorners: false,
+ hasBorders: false,
+ hasControls: true,
+ fill: "transparent",
+ strokeDashArray: [5, 5],
+ absolutePositioned: true,
+ name: "crop",
+ visible: false,
+ });
+
+ this.canvas.add(crop_object);
+ return crop_object;
+ }
+
+ cropCanvas() {
+ this.crop_disabled = true;
+ this.crop_object.visible = false;
+ const croppedImg = new Image();
+
+ croppedImg.src = this.canvas.toDataURL({
+ left: this.crop_object.left,
+ top: this.crop_object.top,
+ width: this.crop_object.width,
+ height: this.crop_object.height,
+ });
+
+ croppedImg.onload = async () => {
+ this.canvas.clear();
+ this.canvas.backgroundColor = this.bgColor.value || "#000000";
+ this.canvas.requestRenderAll();
+
+ const image = new fabric.Image(croppedImg);
+ image.left = this.crop_object.left;
+ image.top = this.crop_object.top;
+ image.selectable = true;
+
+ if (
+ await comfyuiDesktopConfirm(
+ "Resize the canvas to fit the updated clip size?"
+ )
+ ) {
+ image.left = 0;
+ image.top = 0;
+ this.setCanvasSize(this.crop_object.width, this.crop_object.height);
+ }
+
+ this.canvas.isDrawingMode = false;
+ this.drawning = false;
+ this.type = "CropMove";
+ this.cropReset();
+
+ image.setCoords();
+ this.canvas.add(image);
+ this.canvas.sendToBack(image);
+ this.canvas.renderAll();
+ this.saveSettingsPainterNode();
+ this.uploadPaintFile(this.node.name);
+ };
+ }
+
+ cropCanvasPath() {
+ const croppedImg = new Image();
+
+ this.crop_object.visible = false;
+ croppedImg.src = this.canvas.toDataURL();
+
+ croppedImg.onload = async () => {
+ this.crop_object.visible = true;
+ this.canvas.clear();
+ this.canvas.backgroundColor = this.bgColor.value || "#000000";
+
+ this.canvas.requestRenderAll();
+
+ const image = new fabric.Image(croppedImg);
+ image.set({
+ selectable: true,
+ evented: true,
+ cropimage: true,
+ strokeWidth: 0,
+ stroke: null,
+ });
+
+ image.setCoords();
+ this.canvas.add(image);
+
+ this.crop_object.set({ fill: this.canvas.backgroundColor });
+ image.clipPath = this.crop_object;
+
+ this.canvas.isDrawingMode = false;
+ this.drawning = false;
+ this.type = "CropMove";
+ this.cropReset();
+
+ this.canvas.sendToBack(image);
+ this.canvas.renderAll();
+
+ this.saveSettingsPainterNode();
+ this.uploadPaintFile(this.node.name);
+ };
+ }
+
// Toolbars
createFontToolbar() {
const property_textbox = makeElement("div", {
@@ -1086,6 +1243,7 @@ class Painter {
this.painter_drawning_box_property.append(property_textbox);
}
+ // Brush Toolbar
createBrushesToolbar() {
// First panel
const property_brushesBox = makeElement("div", {
@@ -1145,11 +1303,83 @@ class Painter {
app.graph.setDirtyCanvas(true, false);
}
+
+ // Crop Toolbar
+ createCropToolbar() {
+ const property_crop = makeElement("div", {
+ style: {
+ display: "flex",
+ gap: "5px",
+ },
+ class: ["property_cropBox", "comfy-menu-btns"],
+ });
+
+ const cropImg = makeElement("img");
+ cropImg.src =
+ "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' version='1.0' width='21px' height='24px' viewBox='0 0 300 300'%3E%3Cg transform='translate(0.000000,300.000000) scale(0.100000,-0.100000)' fill='%23ffffff' stroke='none'%3E%3Cpath d='M664 2836 l-34 -34 0 -1052 0 -1052 34 -34 34 -34 1052 0 1052 0 34 34 c30 30 34 40 34 86 0 46 -4 56 -34 86 l-34 34 -966 0 -966 0 0 966 0 966 -34 34 c-30 30 -40 34 -86 34 -46 0 -56 -4 -86 -34z m137 -25 l24 -19 5 -957 5 -957 28 -24 28 -24 945 0 945 0 24 -25 c33 -32 33 -78 0 -110 l-24 -25 -1031 0 -1031 0 -24 25 -25 24 0 1031 0 1031 25 24 c29 30 74 32 106 6z'/%3E%3Cpath d='M164 2336 c-50 -50 -50 -122 0 -172 l34 -34 106 0 c123 0 155 11 180 64 20 43 20 69 0 112 -25 53 -57 64 -180 64 l-106 0 -34 -34z m258 -20 c27 -20 39 -62 27 -93 -17 -45 -37 -52 -137 -53 -86 0 -95 2 -117 25 -16 15 -25 36 -25 55 0 19 9 40 25 55 22 23 31 25 117 25 60 -1 99 -5 110 -14z'/%3E%3Cpath d='M1059 2355 c-29 -15 -59 -70 -59 -105 0 -36 31 -90 61 -105 23 -13 112 -15 549 -15 l520 0 0 -520 c0 -576 -1 -563 64 -594 43 -20 69 -20 112 0 66 31 64 9 64 680 l0 606 -34 34 -34 34 -609 0 c-506 -1 -612 -3 -634 -15z m1246 -50 l25 -24 0 -591 c0 -359 -4 -598 -10 -609 -5 -10 -23 -24 -40 -31 -37 -15 -81 0 -99 33 -8 15 -12 175 -14 529 l-2 507 -23 23 -23 23 -507 2 c-354 2 -514 6 -529 14 -33 18 -48 62 -33 99 7 17 20 35 29 40 9 6 255 10 609 10 l593 0 24 -25z'/%3E%3Cpath d='M2192 483 c-51 -25 -62 -57 -62 -179 l0 -106 34 -34 c30 -30 40 -34 86 -34 46 0 56 4 86 34 l34 34 0 106 c0 123 -11 155 -64 180 -43 20 -72 20 -114 -1z m116 -56 c20 -18 22 -30 22 -114 0 -86 -2 -96 -25 -118 -32 -33 -78 -33 -110 0 -23 22 -25 31 -25 117 1 99 8 120 51 137 30 12 57 5 87 -22z'/%3E%3C/g%3E%3C/svg%3E";
+
+ const cropAction1 = makeElement("button", {
+ dataset: { prop: "prop_cropAction1" },
+ title: "Crop",
+ children: [
+ makeElement("span", {
+ styke: { padding: "5px" },
+ textContent: "Crop",
+ }),
+ cropImg,
+ ],
+ onclick: (e) => {
+ if (
+ this.crop_object &&
+ this.canvas.getActiveObject() === this.crop_object
+ ) {
+ this.cropCanvas();
+ } else {
+ app.extensionManager.toast.add({
+ severity: "warn",
+ summary: "Warning",
+ detail: "No area selected for canvas cropping!",
+ life: 5000,
+ });
+ }
+ },
+ });
+
+ const cropAction2 = makeElement("button", {
+ dataset: { prop: "prop_cropAction2" },
+ title: "Crop 2",
+ children: [
+ makeElement("span", {
+ styke: { padding: "5px" },
+ textContent: "Crop path",
+ }),
+ cropImg.cloneNode(true),
+ ],
+ onclick: (e) => {
+ if (
+ this.crop_object &&
+ this.canvas.getActiveObject() === this.crop_object
+ ) {
+ this.cropCanvasPath();
+ } else {
+ app.extensionManager.toast.add({
+ severity: "warn",
+ summary: "Warning",
+ detail: "No area selected for canvas cropping!",
+ life: 5000,
+ });
+ }
+ },
+ });
+
+ property_crop.append(cropAction1, cropAction2);
+ this.painter_drawning_box_property.append(property_crop);
+ }
// end - Toolbars
selectPropertyToolbar(type) {
this.painter_drawning_box_property.innerHTML = "";
- if (["Textbox", "Brush"].includes(this.type)) {
+ if (["Textbox", "Brush", "Crop"].includes(this.type)) {
this.painter_drawning_box_property.style.display = "flex";
switch (this.type) {
@@ -1159,6 +1389,9 @@ class Painter {
case "Brush":
this.createBrushesToolbar();
break;
+ case "Crop":
+ this.createCropToolbar();
+ break;
}
} else {
this.painter_drawning_box_property.style.display = "";
@@ -1198,7 +1431,9 @@ class Painter {
{ width: new_width, height: new_height }
);
- this.node.title = `${this.node.type} - ${new_width}x${new_height}`;
+ this.node.title = `${this.node.type} - ${Math.floor(
+ new_width
+ )}x${Math.floor(new_height)}`;
this.canvas.renderAll();
app.graph.setDirtyCanvas(true, false);
this.node.onResize();
@@ -1221,6 +1456,22 @@ class Painter {
if (currentTarget) {
this.type = currentTarget;
+ // [Crop] Crop
+ if (this.type === "CropMove") {
+ this.type = "Crop";
+ this.canvas.isDrawingMode = false;
+ this.drawning = true;
+ this.mouseDown = null;
+ }
+
+ if (currentTarget !== "Crop") {
+ this.cropReset();
+ } else {
+ // [Crop] Create crop object
+ this.crop_disabled = false;
+ this.crop_object = this.createCrop(0, 0);
+ }
+
// Set default brush width if width < 1 (for fabricjs)
this.setDefaultValuesInputs();
@@ -1491,9 +1742,14 @@ class Painter {
}
}
};
+
// Event input bgcolor
this.reset_set_bg = () => {
- this.canvas.setBackgroundImage(null);
+ const image = new fabric.Image("");
+ this.canvas.setBackgroundImage(
+ image,
+ this.canvas.renderAll.bind(this.canvas)
+ );
this.canvas.backgroundColor = this.bgColor.value;
this.canvas.renderAll();
this.uploadPaintFile(this.node.name);
@@ -1646,6 +1902,9 @@ class Painter {
this.fillColor.onchange =
this.fillColorTransparent.onchange =
() => {
+ // [Crop]
+ if (this.type === "Crop") return;
+
if (this.canvas.getActiveObject()) {
this.uploadPaintFile(this.node.name);
}
@@ -1659,6 +1918,9 @@ class Painter {
};
this.strokeWidth.onchange = () => {
+ // [Crop]
+ if (this.type === "Crop") return;
+
if (
["Brush", "BrushMyPaint", "BrushSymmetry", "Textbox", "Image"].includes(
this.type
@@ -1677,6 +1939,11 @@ class Painter {
let targets = this.canvas.getActiveObjects();
if (!targets || targets.length == 0) return;
+ // [Crop]
+ if (["Crop", "CropMove"].includes(this.type)) {
+ return;
+ }
+
// Selected tools
const setProps = (style, value) => {
const propEl = this.painter_drawning_box_property.querySelector(
@@ -1753,8 +2020,24 @@ class Painter {
},
// Mouse button down event
"mouse:down": (o) => {
- if (!this.canvas.isDrawingMode && this.bringFrontSelected)
- this.canvas.bringToFront(this.canvas.getActiveObject());
+ const { x: left, y: top } = this.canvas.getPointer(o.e);
+ const activeObject = this.canvas.getActiveObject();
+
+ if (
+ !this.canvas.isDrawingMode &&
+ this.bringFrontSelected &&
+ !["Crop", "CropMove"].includes(this.type)
+ ) {
+ if (
+ activeObject &&
+ activeObject?.hasOwnProperty("cropimage") &&
+ !this.mode
+ ) {
+ this.canvas.bringToFront(activeObject);
+ } else {
+ this.canvas.bringToFront(activeObject);
+ }
+ }
this.canvas.isDrawingMode = this.drawning;
if (!this.canvas.isDrawingMode) {
@@ -1769,33 +2052,52 @@ class Painter {
["Brush", "Erase", "BrushMyPaint", "BrushSymmetry"].includes(
this.type
)
- )
+ ) {
return;
+ }
- if (this.type != "Textbox") {
- let { x: left, y: top } = this.canvas.getPointer(o.e),
- colors = ["red", "blue", "green", "yellow", "purple", "orange"],
- strokeWidth = +this.strokeWidth.value,
- stroke =
- strokeWidth == 0
- ? "transparent"
- : toRGBA(
- this.strokeColor.value,
- this.strokeColorTransparent.value
- ) || colors[Math.floor(Math.random() * colors.length)],
- fill = toRGBA(
- this.fillColor.value,
- this.fillColorTransparent.value
- ),
- shape = this.shapeCreate({
- type: this.type,
- left,
- top,
- stroke,
- fill,
- strokeWidth,
- points: [left, top, left, top],
- });
+ // [Crop] Mouse down
+ if (this.type === "Crop") {
+ if (!this.crop_object) {
+ // [Crop] Create crop object
+ this.crop_object = this.createCrop(left, top);
+ }
+
+ if (!this.crop_disabled) {
+ this.crop_object.set({ left, top });
+ this.crop_object.visible = true;
+ this.crop_object.crop_startX = left;
+ this.crop_object.crop_startY = top;
+ this.mouseDown = o.e;
+ this.canvas.setActiveObject(this.crop_object);
+ }
+ return;
+ }
+
+ if (this.type !== "Textbox") {
+ const colors = ["red", "blue", "green", "yellow", "purple", "orange"];
+ const strokeWidth = +this.strokeWidth.value;
+ const stroke =
+ strokeWidth == 0
+ ? "transparent"
+ : toRGBA(
+ this.strokeColor.value,
+ this.strokeColorTransparent.value
+ ) || colors[Math.floor(Math.random() * colors.length)];
+ const fill = toRGBA(
+ this.fillColor.value,
+ this.fillColorTransparent.value
+ );
+
+ const shape = this.shapeCreate({
+ type: this.type,
+ left,
+ top,
+ stroke,
+ fill,
+ strokeWidth,
+ points: [left, top, left, top],
+ });
this.originX = left;
this.originY = top;
@@ -1845,7 +2147,7 @@ class Painter {
activeObj.set({ top: pointer.y });
}
- if (this.type == "Circle") {
+ if (this.type === "Circle") {
let radius =
Math.max(
Math.abs(this.originY - pointer.y),
@@ -1854,8 +2156,49 @@ class Painter {
if (radius > activeObj.strokeWidth)
radius -= activeObj.strokeWidth / 2;
activeObj.set({ radius: radius });
- } else if (this.type == "Line") {
+ } else if (this.type === "Line") {
activeObj.set({ x2: pointer.x, y2: pointer.y });
+ } else if (
+ this.type === "Crop" &&
+ this.mouseDown &&
+ !this.crop_disabled
+ ) {
+ const newWidth = pointer.x - this.crop_object.crop_startX;
+ const newHeight = pointer.y - this.crop_object.crop_startY;
+
+ const minSize = 10;
+
+ const absWidth = Math.max(minSize, Math.abs(newWidth));
+ const absHeight = Math.max(minSize, Math.abs(newHeight));
+
+ const finalLeft =
+ newWidth > 0
+ ? this.crop_object.crop_startX
+ : this.crop_object.crop_startX + newWidth;
+ const finalTop =
+ newHeight > 0
+ ? this.crop_object.crop_startY
+ : this.crop_object.crop_startY + newHeight;
+
+ const boundedLeft = Math.max(0, finalLeft);
+ const boundedTop = Math.max(0, finalTop);
+ const boundedWidth = Math.min(
+ absWidth,
+ this.canvas.width - boundedLeft
+ );
+ const boundedHeight = Math.min(
+ absHeight,
+ this.canvas.height - boundedTop
+ );
+
+ this.crop_object.set({
+ left: boundedLeft,
+ top: boundedTop,
+ width: boundedWidth,
+ height: boundedHeight,
+ originX: "left",
+ originY: "top",
+ });
} else {
activeObj.set({ width: Math.abs(this.originX - pointer.x) });
activeObj.set({ height: Math.abs(this.originY - pointer.y) });
@@ -1866,10 +2209,33 @@ class Painter {
// Mouse button up event
"mouse:up": (o) => {
- this.canvas._objects.forEach((object) => {
- if (!object.hasOwnProperty("controls")) {
- object.controls = {
- ...object.controls,
+ this.mouseDown = null;
+
+ const activeObject = this.canvas.getActiveObject();
+
+ if (activeObject) {
+ if (this.type === "Crop" && !this.mode) {
+ // activeObject.controls = {
+ // ...activeObject.controls,
+ // removeControl: new fabric.Control({
+ // x: 0.5,
+ // y: -0.5,
+ // offsetY: -16,
+ // offsetX: 16,
+ // cursorStyle: "pointer",
+ // mouseUpHandler: (e) => e.stopPropagation(),
+ // mouseDownHandler: (eventData, transform) => {
+ // eventData.stopPropagation();
+ // this.cropCanvas();
+ // return true;
+ // },
+ // render: renderIcon(cropImg),
+ // cornerSize: 24,
+ // }),
+ // };
+ } else {
+ activeObject.controls = {
+ ...activeObject.controls,
removeControl: new fabric.Control({
x: 0.5,
y: -0.5,
@@ -1882,10 +2248,10 @@ class Painter {
}),
};
}
- });
- this.canvas.getActiveObject()?.setCoords();
- this.canvas.getActiveObjects()?.forEach((a) => a.setCoords());
+ activeObject.setCoords();
+ this.canvas.requestRenderAll();
+ }
if (
![
@@ -1900,7 +2266,8 @@ class Painter {
this.canvas.isDrawingMode = false;
// Skip BrushMyPaint mouseup is empty objects array, loading canvas as image, upload when object add to canvas
- if (!["BrushMyPaint"].includes(this.type)) {
+ // [Crop] Skip
+ if (!["BrushMyPaint", "Crop"].includes(this.type)) {
this.canvas.renderAll();
this.addToHistory();
this.uploadPaintFile(this.node.name);
@@ -2216,6 +2583,10 @@ class Painter {
this.canvas.getActiveObjects().forEach((a_obs) => {
a_obs.hasControls = false;
a_obs.hasBorders = false;
+
+ if (a_obs?.name === "crop") {
+ a_obs.visible = false;
+ }
});
}
}
@@ -2251,9 +2622,21 @@ class Painter {
this.canvas.getActiveObjects().forEach((a_obs) => {
a_obs.hasControls = true;
a_obs.hasBorders = true;
+ if (a_obs?.name === "crop") {
+ activeObj.visible = true;
+ activeObj.hasBorders = false;
+ }
});
this.canvas.renderAll();
+ } else {
+ if (activeObj?.name === "crop") {
+ activeObj.visible = true;
+ activeObj.hasControls = true;
+ activeObj.hasBorders = false;
+ }
}
+ this.canvas.renderAll();
+
this.saveSettingsPainterNode();
res(true);
})
@@ -2271,7 +2654,12 @@ class Painter {
// ================= CREATE PAINTER WIDGET ============
function PainterWidget(node, inputName, inputData, app) {
- node.addWidget("button", "Clear Canvas", "clear_painer", () => {
+ node.addWidget("button", "Clear Canvas", "clear_painer", async () => {
+ if (
+ !(await comfyuiDesktopConfirm("Do you really want to clear the canvas?"))
+ ) {
+ return;
+ }
node.painter.list_objects_panel__items.innerHTML = "";
node.painter.clearCanvas();
});
@@ -2419,7 +2807,13 @@ function PainterWidget(node, inputName, inputData, app) {
) {
node.painter.setCanvasSize(w, h);
} else {
- node.title = `${node.type} - ${node.painter.storageCls.settings_painter_node.settings.currentCanvasSize.width}x${node.painter.storageCls.settings_painter_node.settings.currentCanvasSize.height}`;
+ node.title = `${node.type} - ${Math.floor(
+ node.painter.storageCls.settings_painter_node.settings
+ .currentCanvasSize.width
+ )}x${Math.floor(
+ node.painter.storageCls.settings_painter_node.settings
+ .currentCanvasSize.height
+ )}`;
}
const img_ = new fabric.Image(img, {
diff --git a/PainterNode/painter_node.py b/PainterNode/painter_node.py
index 26e008f..443a82f 100644
--- a/PainterNode/painter_node.py
+++ b/PainterNode/painter_node.py
@@ -372,7 +372,7 @@ class PainterNode(object):
DESCRIPTION = "PainterNode allows you to draw in the node window, for later use in the ControlNet or in any other node."
CATEGORY = "AlekPet Nodes/image"
- def painter_execute(self, image, unique_id, update_node=True, images=None):
+ async def painter_execute(self, image, unique_id, update_node=True, images=None):
# Piping image input
if unique_id not in PAINTER_DICT:
PAINTER_DICT[unique_id] = self
@@ -391,7 +391,7 @@ class PainterNode(object):
PromptServer.instance.send_sync(
"alekpet_get_image", {"unique_id": unique_id, "images": input_images}
)
- if not asyncio.run(wait_canvas_change(unique_id)):
+ if await wait_canvas_change(unique_id):
print(f"Painter_{unique_id}: Failed to get image!")
else:
print(f"Painter_{unique_id}: Image received, canvas changed!")
diff --git a/pyproject.toml b/pyproject.toml
index 9e3ba5e..02ea437 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,7 +1,7 @@
[project]
name = "comfyui_custom_nodes_alekpet"
description = "Nodes: PoseNode, PainterNode, TranslateTextNode, TranslateCLIPTextEncodeNode, DeepTranslatorTextNode, DeepTranslatorCLIPTextEncodeNode, ArgosTranslateTextNode, ArgosTranslateCLIPTextEncodeNode, ChatGLM4TranslateCLIPTextEncodeNode, ChatGLM4TranslateTextNode, ChatGLM4InstructNode, ChatGLM4InstructMediaNode, PreviewTextNode, HexToHueNode, ColorsCorrectNode, IDENode."
-version = "1.0.74"
+version = "1.0.75"
license = { file = "LICENSE" }
[project.urls]