feat: 🚀 debug node

Still needs some work, especially in widget drawing, but already
useful as is, so it will do for now
This commit is contained in:
melMass
2023-07-21 23:39:20 +02:00
parent 67d3783ac9
commit b27b8ef91f
2 changed files with 241 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
from ..utils import tensor2pil
from ..log import log
import io, base64
import torch
class Debug:
@classmethod
def INPUT_TYPES(cls):
return {
"required": {"anything_1": ("*")},
}
RETURN_TYPES = ("STRING",)
FUNCTION = "do_debug"
CATEGORY = "mtb/debug"
OUTPUT_NODE = True
def do_debug(self, **kwargs):
output = {
"ui": {"b64_images": [], "text": []},
"result": ("A"),
}
for k, v in kwargs.items():
anything = v
text = ""
if isinstance(anything, torch.Tensor):
log.debug(f"Tensor: {anything.shape}")
# write the images to temp
image = tensor2pil(anything)
b64_imgs = []
for im in image:
buffered = io.BytesIO()
im.save(buffered, format="JPEG")
b64_imgs.append(
base64.b64encode(buffered.getvalue()).decode("utf-8")
)
output["ui"]["b64_images"] += b64_imgs
log.debug(f"Input {k} contains {len(b64_imgs)} images")
elif isinstance(anything, bool):
log.debug(f"Input {k} contains boolean: {anything}")
output["ui"]["text"] += ["True" if anything else "False"]
else:
text = str(anything)
log.debug(f"Input {k} contains text: {text}")
output["ui"]["text"] += [text]
return output
__nodes__ = [Debug]
+187
View File
@@ -0,0 +1,187 @@
import { app } from "/scripts/app.js";
import * as shared from '/extensions/mtb/comfy_shared.js'
import { log } from '/extensions/mtb/comfy_shared.js'
// TODO: respect inputs order...
const DEBUG_WIDGETS = {
"image": (val, index) => {
const w = {
name: `anything_${index}`,
type: "image",
value: val,
draw: function (ctx,
node,
widgetWidth,
widgetY,
height) {
const [cw, ch] = this.computeSize(widgetWidth)
shared.offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, ch)
},
computeSize: function (width) {
const ratio = this.inputRatio || 1;
if (width) {
return [width, width / ratio + 4]
}
return [128, 128]
},
onRemove: function () {
if (this.inputEl) {
this.inputEl.remove();
}
}
}
w.inputEl = document.createElement("img");
w.inputEl.src = "data:image/jpeg;base64," + w.value;
w.inputEl.onload = function () {
w.inputRatio = w.inputEl.naturalWidth / w.inputEl.naturalHeight;
}
document.body.appendChild(w.inputEl);
return w
},
"text": (val, index) => {
const w = {
name: `anything_${index}`,
type: "debug_text",
val: val,
draw: function (ctx,
node,
widgetWidth,
widgetY,
height) {
// const [cw, ch] = this.computeSize(widgetWidth)
shared.offsetDOMWidget(this, ctx, node, widgetWidth, widgetY, height)
},
computeSize: function (width) {
const value = this.inputEl.innerHTML
if (!value) {
return [32, 32]
}
if (!width) {
log(`No width ${this.parent.size}`)
}
const fontSize = 25; // Assuming 1rem = 16px
const oldFont = app.ctx.font
app.ctx.font = `${fontSize}px Arial`;
const words = value.split(" ");
const lines = [];
let currentLine = "";
for (const word of words) {
const testLine = currentLine.length === 0 ? word : `${currentLine} ${word}`;
const testWidth = app.ctx.measureText(testLine).width;
// log(`Testing line ${testLine}, width: ${testWidth}, width: ${width}, ratio: ${testWidth / width}`)
if (testWidth > width) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = testLine;
}
}
app.ctx.font = oldFont;
lines.push(currentLine);
// Step 3: Calculate the widget width and height
const textHeight = lines.length * (fontSize + 2); // You can adjust the line height (2 in this case)
const maxLineWidth = lines.reduce((maxWidth, line) => Math.max(maxWidth, app.ctx.measureText(line).width), 0);
const widgetWidth = Math.max(width || this.width || 32, maxLineWidth);
const widgetHeight = textHeight + 10; // Additional padding for spacing
return [widgetWidth, widgetHeight + 4]
},
onRemove: function () {
if (this.inputEl) {
this.inputEl.remove();
}
}
}
w.inputEl = document.createElement("p");
w.inputEl.style.textAlign = "center";
w.inputEl.style.fontSize = "1.5em";
w.inputEl.style.color = "var(--input-text)";
w.inputEl.style.fontFamily = "monospace";
w.inputEl.innerHTML = val
document.body.appendChild(w.inputEl);
return w
}
}
app.registerExtension({
name: "mtb.Debug",
async beforeRegisterNodeDef(nodeType, nodeData, app) {
if (nodeData.name === "Debug (mtb)") {
const onConnectionsChange = nodeType.prototype.onConnectionsChange;
nodeType.prototype.onConnectionsChange = function (type, index, connected, link_info) {
const r = onConnectionsChange ? onConnectionsChange.apply(this, arguments) : undefined;
// TODO: remove all widgets on disconnect once computed
shared.dynamic_connection(this, index, connected, "anything_", "*")
//- infer type
if (link_info) {
const fromNode = this.graph._nodes.find((otherNode) => otherNode.id == link_info.origin_id);
const type = fromNode.outputs[link_info.origin_slot].type;
this.inputs[index].type = type;
// this.inputs[index].label = type.toLowerCase()
}
//- restore dynamic input
if (!connected) {
this.inputs[index].type = "*";
this.inputs[index].label = `anything_${index + 1}`
}
}
const onExecuted = nodeType.prototype.onExecuted;
nodeType.prototype.onExecuted = function (message) {
log(message)
onExecuted?.apply(this, arguments);
log(message)
if (this.widgets) {
// const pos = this.widgets.findIndex((w) => w.name === "anything_1");
// if (pos !== -1) {
for (let i = 0; i < this.widgets.length; i++) {
this.widgets[i].onRemove?.();
}
this.widgets.length = 0;
}
let widgetI = 1
if (message.text) {
for (const txt of message.text) {
const w = this.addCustomWidget(DEBUG_WIDGETS["text"](txt, widgetI))
w.parent = this;
widgetI++;
}
}
if (message.b64_images) {
for (const img of message.b64_images) {
const w = this.addCustomWidget(DEBUG_WIDGETS["image"](img, widgetI))
w.parent = this;
widgetI++;
}
// this.onResize?.(this.size);
// this.resize?.(this.size)
this.setSize(this.computeSize())
};
this.onRemoved = function () {
// When removing this node we need to remove the input from the DOM
for (let y in this.widgets) {
if (this.widgets[y].canvas) {
this.widgets[y].canvas.remove();
}
this.widgets[y].onRemove?.();
}
}
}
}
}
}
);