Qt(发音同“cute”)是一个跨平台的C++应用程序开发框架,由挪威公司Trolltech(奇趣科技)于1995年首次发布,现由The Qt Company维护。它包含丰富的类库和工具,支持Windows、Linux、macOS、Android、iOS等多种操作系统平台。
主要特点:
- 跨平台能力:通过一套代码即可部署到多个平台
- 丰富的GUI组件:包含超过400个C++类,提供按钮、表格、图表等UI元素
- 信号与槽机制:独特的对象通信方式,比回调函数更安全高效
- 模块化设计:核心模块包括QtCore、QtGui、QtWidgets等
- 开发工具链:包含Qt Creator IDE、qmake、Qt Designer等工具
Qt分为商业版和开源版(LGPL/GPL协议),被广泛应用于:
- 工业自动化(如西门子HMI)
- 医疗设备(超声波仪器界面)
- 车载系统(大众MMI系统)
- 知名软件(Autodesk Maya、VirtualBox、WPS Office)
ComfyUI 是一个基于 Python 的开源项目,专为 Stable Diffusion 等 AI 图像生成模型设计的图形化用户界面(GUI)。它通过节点式工作流的方式,让用户可以更灵活地控制和定制 AI 图像生成过程。相比其他同类工具(如 AUTOMATIC1111 的 WebUI),ComfyUI 具有以下核心特点:
-
节点式工作流:采用可视化编程界面,每个功能模块(如加载模型、文本编码、图像生成等)都是一个可拖拽连接的节点,用户可以自由组合这些节点来构建个性化工作流。
-
高性能与低资源占用:优化了显存管理,即使在低配置硬件上(如 4GB 显存显卡)也能流畅运行,同时支持多 GPU 并行计算。
-
高级控制能力:
- 支持多模型混合使用(如同时加载 SD1.5 和 SDXL)
- 可精确控制生成过程的每个环节(提示词权重、采样步骤、CFG 值等)
- 提供 Latent Space 操作等专业级功能
-
扩展性:
- 通过插件系统支持 ControlNet、LoRA 等扩展功能
- 兼容主流模型格式(.ckpt/.safetensors)
- 支持自定义节点开发
典型应用场景包括:
- 艺术家创作复杂的概念艺术作品
- 开发者测试和优化模型工作流
- 研究人员进行可控性图像生成实验
安装方式:
git clone https://github.com/comfyanonymous/ComfyUI
cd ComfyUI
pip install -r requirements.txt
目前该项目在 GitHub 上获得超过 15k stars,因其专业性和灵活性成为 AI 图像生成领域的重要工具之一。
最基础的ai背景生成实现
部署comfyui
1.自己下载
2.现成的
也可以去b站有很多大神的现成的
下面是优化后的版本:
上方图片展示了最基础的工作流搭建流程,可完成简单的图片生成任务。具体操作步骤如下:
- 自行创建工作流或从网络获取现成的工作流模板
- 将工作流保存为API JSON文件
- 点击左下角的设置齿轮图标
- 进入开发者选项并勾选启用
如果问题仍未解决,可以进入ComfyUI的快捷键设置界面直接配置快捷键。
使用ai,让他分析api json文件,并生成简易的调试工具
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonObject>
#include <QTimer>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_generateButton_clicked();
void onPromptSubmitted();
void onHistoryChecked();
void onImageDownloaded();
void handleNetworkError(QNetworkReply::NetworkError error);
private:
Ui::MainWindow *ui;
QNetworkAccessManager *networkManager;
QString currentPromptId;
QString comfyuiServer = "http://127.0.0.1:8188";
void loadWorkflowTemplate();
void submitPromptToComfyUI();
void pollForCompletion();
void downloadGeneratedImage(const QString &filename);
QJsonObject findImageNodeOutput(const QJsonObject &historyObj);
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QNetworkRequest>
#include <QJsonDocument>
#include <QJsonArray>
#include <QMessageBox>
#include <QFile>
#include <QPixmap>
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
, networkManager(new QNetworkAccessManager(this))
{
ui->setupUi(this);
// 设置初始值
ui->positivePromptEdit->setText("沙滩,海岸");
ui->negativePromptEdit->setText("一棵树");
ui->seedSpinBox->setValue(840893832511446);
ui->stepsSpinBox->setValue(20);
ui->widthSpinBox->setValue(512);
ui->heightSpinBox->setValue(512);
// 连接网络错误信号
connect(networkManager, &QNetworkAccessManager::finished,
this, [this](QNetworkReply *reply) {
if (reply->error() != QNetworkReply::NoError) {
handleNetworkError(reply->error());
}
reply->deleteLater();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_generateButton_clicked()
{
ui->generateButton->setEnabled(false);
ui->statusLabel->setText("正在提交生成任务...");
submitPromptToComfyUI();
}
void MainWindow::submitPromptToComfyUI()
{
// 构建工作流JSON
QJsonObject workflow;
// 节点3: KSampler
QJsonObject node3;
node3["inputs"] = QJsonObject{
{"seed", ui->seedSpinBox->value()},
{"steps", ui->stepsSpinBox->value()},
{"cfg", 8},
{"sampler_name", "euler"},
{"scheduler", "normal"},
{"denoise", 1},
{"model", QJsonArray{"4", 0}},
{"positive", QJsonArray{"6", 0}},
{"negative", QJsonArray{"7", 0}},
{"latent_image", QJsonArray{"5", 0}}
};
node3["class_type"] = "KSampler";
workflow["3"] = node3;
// 节点4: CheckpointLoader
QJsonObject node4;
node4["inputs"] = QJsonObject{
{"ckpt_name", "v1-5-pruned-emaonly.safetensors"}
};
node4["class_type"] = "CheckpointLoaderSimple";
workflow["4"] = node4;
// 节点5: EmptyLatentImage
QJsonObject node5;
node5["inputs"] = QJsonObject{
{"width", ui->widthSpinBox->value()},
{"height", ui->heightSpinBox->value()},
{"batch_size", 1}
};
node5["class_type"] = "EmptyLatentImage";
workflow["5"] = node5;
// 节点6: 正面提示词
QJsonObject node6;
node6["inputs"] = QJsonObject{
{"text", ui->positivePromptEdit->toPlainText()},
{"clip", QJsonArray{"4", 1}}
};
node6["class_type"] = "CLIPTextEncode";
workflow["6"] = node6;
// 节点7: 负面提示词
QJsonObject node7;
node7["inputs"] = QJsonObject{
{"text", ui->negativePromptEdit->toPlainText()},
{"clip", QJsonArray{"4", 1}}
};
node7["class_type"] = "CLIPTextEncode";
workflow["7"] = node7;
// 节点8: VAEDecode
QJsonObject node8;
node8["inputs"] = QJsonObject{
{"samples", QJsonArray{"3", 0}},
{"vae", QJsonArray{"4", 2}}
};
node8["class_type"] = "VAEDecode";
workflow["8"] = node8;
// 节点9: SaveImage
QJsonObject node9;
node9["inputs"] = QJsonObject{
{"filename_prefix", "ComfyUI"},
{"images", QJsonArray{"8", 0}}
};
node9["class_type"] = "SaveImage";
workflow["9"] = node9;
// 提交到ComfyUI
QUrl url(comfyuiServer + "/prompt");
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonObject payload;
payload["prompt"] = workflow;
QNetworkReply *reply = networkManager->post(request,
QJsonDocument(payload).toJson());
connect(reply, &QNetworkReply::finished, this, &MainWindow::onPromptSubmitted);
}
void MainWindow::onPromptSubmitted()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) return;
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(response);
QJsonObject obj = doc.object();
if (obj.contains("prompt_id")) {
currentPromptId = obj["prompt_id"].toString();
ui->statusLabel->setText("任务已提交,ID: " + currentPromptId);
ui->promptIdLabel->setText("Prompt ID: " + currentPromptId);
// 开始轮询任务状态
QTimer::singleShot(1000, this, &MainWindow::pollForCompletion);
} else {
ui->statusLabel->setText("提交失败: 未收到任务ID");
ui->generateButton->setEnabled(true);
}
} else {
ui->statusLabel->setText("网络错误: " + reply->errorString());
ui->generateButton->setEnabled(true);
}
}
void MainWindow::pollForCompletion()
{
if (currentPromptId.isEmpty()) {
ui->statusLabel->setText("错误: 没有有效的任务ID");
ui->generateButton->setEnabled(true);
return;
}
QUrl url(comfyuiServer + "/history/" + currentPromptId);
QNetworkRequest request(url);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, &MainWindow::onHistoryChecked);
}
void MainWindow::onHistoryChecked()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) return;
if (reply->error() == QNetworkReply::NoError) {
QByteArray response = reply->readAll();
QJsonDocument doc = QJsonDocument::fromJson(response);
QJsonObject historyObj = doc.object();
if (historyObj.contains(currentPromptId)) {
// 任务完成
QJsonObject outputInfo = findImageNodeOutput(historyObj);
if (!outputInfo.isEmpty()) {
QString filename = outputInfo["filename"].toString();
ui->statusLabel->setText("任务完成,下载图片: " + filename);
downloadGeneratedImage(filename);
} else {
ui->statusLabel->setText("错误: 未找到图片输出信息");
ui->generateButton->setEnabled(true);
}
} else {
// 任务仍在进行中,继续轮询
ui->statusLabel->setText("生成中...");
QTimer::singleShot(1000, this, &MainWindow::pollForCompletion);
}
} else {
ui->statusLabel->setText("查询状态失败: " + reply->errorString());
ui->generateButton->setEnabled(true);
}
}
QJsonObject MainWindow::findImageNodeOutput(const QJsonObject &historyObj)
{
QJsonObject promptHistory = historyObj[currentPromptId].toObject();
QJsonObject outputs = promptHistory["outputs"].toObject();
// 查找包含图片的节点(通常是节点9)
for (auto it = outputs.begin(); it != outputs.end(); ++it) {
QJsonObject nodeOutput = it.value().toObject();
if (nodeOutput.contains("images")) {
QJsonArray images = nodeOutput["images"].toArray();
if (!images.isEmpty()) {
return images[0].toObject();
}
}
}
return QJsonObject();
}
void MainWindow::downloadGeneratedImage(const QString &filename)
{
QUrl url(comfyuiServer + "/view?filename=" + filename + "&type=output");
QNetworkRequest request(url);
QNetworkReply *reply = networkManager->get(request);
connect(reply, &QNetworkReply::finished, this, &MainWindow::onImageDownloaded);
}
void MainWindow::onImageDownloaded()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (!reply) return;
ui->generateButton->setEnabled(true);
if (reply->error() == QNetworkReply::NoError) {
QByteArray imageData = reply->readAll();
QPixmap pixmap;
if (pixmap.loadFromData(imageData)) {
// 缩放图片以适应显示区域
QPixmap scaledPixmap = pixmap.scaled(ui->imageLabel->size(),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
ui->imageLabel->setPixmap(scaledPixmap);
ui->statusLabel->setText("图片显示完成!");
} else {
ui->statusLabel->setText("错误: 图片数据格式不支持");
}
} else {
ui->statusLabel->setText("下载图片失败: " + reply->errorString());
}
}
void MainWindow::handleNetworkError(QNetworkReply::NetworkError error)
{
qDebug() << "Network error:" << error;
}
.ui文件
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>ComfyUI 客户端调试工具</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>生成参数</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>正面提示词:</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QTextEdit" name="positivePromptEdit"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>负面提示词:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QTextEdit" name="negativePromptEdit"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>种子:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QSpinBox" name="seedSpinBox">
<property name="maximum">
<number>999999999999999</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>步数:</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="stepsSpinBox">
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>100</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_5">
<property name="text">
<string>宽度:</string>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QSpinBox" name="widthSpinBox">
<property name="minimum">
<number>64</number>
</property>
<property name="maximum">
<number>2048</number>
</property>
<property name="singleStep">
<number>64</number>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_6">
<property name="text">
<string>高度:</string>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QSpinBox" name="heightSpinBox">
<property name="minimum">
<number>64</number>
</property>
<property name="maximum">
<number>2048</number>
</property>
<property name="singleStep">
<number>64</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="generateButton">
<property name="text">
<string>生成图片</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="statusLabel">
<property name="text">
<string>就绪</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="promptIdLabel">
<property name="text">
<string>Prompt ID: </string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="imageLabel">
<property name="minimumSize">
<size>
<width>512</width>
<height>512</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Box</enum>
</property>
<property name="text">
<string>生成的图片将显示在这里</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
在确保127.0.0.1:8188网址可以打开情况下,运行qt文件
红色对列出就会有出现一个1,代表有一个图片在生产中(这就代表成功)
个人见解,有错误的大佬可以指出
转载自CSDN-专业IT技术社区
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2401_84694122/article/details/152135671