errorString());ui->statusLabel->setText("下载图片失败: " + reply->errorString());ui->statusLabel->setText("任务完成,下载图片: " + filename);ui->statusLabel->setText("网络错误: " + reply->errorString()); 。 惊觉,一个优质的创作社区和技术社区,在这里,用户每天都可以在这里找到技术世界的头条内容。讨论编程、设计、硬件、游戏等令人激动的话题。本网站取自:横钗整鬓,倚醉唱清词,房户静,酒杯深。帘幕明残照。扬州一梦,未尽还惊觉。《蓦山溪·韵高格妙》" />
关注

qt+comfyui实现最基础的ai背景生成工具

Qt(发音同“cute”)是一个跨平台的C++应用程序开发框架,由挪威公司Trolltech(奇趣科技)于1995年首次发布,现由The Qt Company维护。它包含丰富的类库和工具,支持Windows、Linux、macOS、Android、iOS等多种操作系统平台。

主要特点:

  1. 跨平台能力:通过一套代码即可部署到多个平台
  2. 丰富的GUI组件:包含超过400个C++类,提供按钮、表格、图表等UI元素
  3. 信号与槽机制:独特的对象通信方式,比回调函数更安全高效
  4. 模块化设计:核心模块包括QtCore、QtGui、QtWidgets等
  5. 开发工具链:包含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 具有以下核心特点:

  1. 节点式工作流:采用可视化编程界面,每个功能模块(如加载模型、文本编码、图像生成等)都是一个可拖拽连接的节点,用户可以自由组合这些节点来构建个性化工作流。

  2. 高性能与低资源占用:优化了显存管理,即使在低配置硬件上(如 4GB 显存显卡)也能流畅运行,同时支持多 GPU 并行计算。

  3. 高级控制能力

    • 支持多模型混合使用(如同时加载 SD1.5 和 SDXL)
    • 可精确控制生成过程的每个环节(提示词权重、采样步骤、CFG 值等)
    • 提供 Latent Space 操作等专业级功能
  4. 扩展性

    • 通过插件系统支持 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.自己下载

github-comfyui本地运行-CSDN博客

2.现成的

也可以去b站有很多大神的现成的

下面是优化后的版本:


上方图片展示了最基础的工作流搭建流程,可完成简单的图片生成任务。具体操作步骤如下:

  1. 自行创建工作流或从网络获取现成的工作流模板
  2. 将工作流保存为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

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--