NDD(notepad--)的AI机器人插件制作过程

简介: NDD(notepad--)的AI机器人插件制作过程

想要体验的小伙伴可以点赞加评论,首页关注公众号,回复666获取体验码。


无需注册和配置,只需我把动态库发您,放入插件目录即可,使用我的后台服务可以直接使用。


ndd-chat-ai: NDD(NotePad--)的聊天机器人插件


实现效果截图:




环境准备


首先得有一个AI的后台接口服务。好在我已具备,部署在了免费的replit。如果你有公有云服务器资源,可以也部署一个AI的后台接口服务。如果没有,免费的repit也很好用。


下载NDD源码,准备好编译环境(msvc2019工具链+QT5.12以上版本)。


以下是我的replit后台截图:  



NDD插件制作


插件主要功能实现类


按照NDD插件制作说明制作一个插件,点击菜单后弹出一个QDockWidget停靠窗口(实用工具窗口)。停靠在主窗口的左侧。NDDMyPlugin主功能实现类如下:


//
// Created by Administrator on 2023/3/19.
//
#ifndef HELLOWORLD_NDDMYPLUGIN_H
#define HELLOWORLD_NDDMYPLUGIN_H
#include <QAction>
#include <QObject>
#include <QWidget>
#include <qsciscintilla.h>
class QDockWidget;
class NDDMyPlugin  : public QObject{
Q_OBJECT
public:
    explicit NDDMyPlugin(QWidget *mainWidget, const QString &pluginPath, QsciScintilla *pEdit,
    QObject *parent = nullptr);
    ~NDDMyPlugin() override = default;
    void getViewMenu(QMenu *menu);
    void setScintilla(const std::function<QsciScintilla *()> &cb);
private:
    QWidget *mainWidget_;
    QDockWidget *dockWidget_;
private:
    std::function<QsciScintilla *()> scintillaCallback_;
};
#endif //HELLOWORLD_NDDMYPLUGIN_H


//
// Created by yangyongzhen 2023/3/19.
//
#include "NDDMyPlugin.h"
#include "mysetting.h"
#include <docktitlewidget.h>
#include <QDockWidget>
#include <QHeaderView>
#include <QMainWindow>
#include <QMenuBar>
NDDMyPlugin::NDDMyPlugin(QWidget *mainWidget, const QString &pluginPath, QsciScintilla *pEdit, QObject *parent)
        : QObject(parent),
          dockWidget_(new QDockWidget("AI窗口")),
          mainWidget_(mainWidget)
{
    dockWidget_->setFeatures(QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable);
    dockWidget_->setAllowedAreas(Qt::LeftDockWidgetArea);
    dockWidget_->hide();
    auto dockWidgetTitle = new DockTitleWidget;
    //dockWidget_->setTitleBarWidget(dockWidgetTitle);
    dockWidget_->setWidget(dockWidgetTitle);
    auto mainWindow = dynamic_cast<QMainWindow *>(mainWidget);
    mainWindow->addDockWidget(Qt::LeftDockWidgetArea, dockWidget_);
}
void NDDMyPlugin::getViewMenu(QMenu *menu)
{
    menu->addAction("Show Chat Window", this, [this] {
        dockWidget_->show();
    });
    menu->addAction(
            "快捷按键(Ctrl+F8)", this, [this] {
                }, Qt::CTRL + Qt::Key_F8);
    menu->addAction("Settings", this, [this] {
        //参数设置
        MySettingDlg* p = new MySettingDlg(mainWidget_,scintillaCallback_());
        //主窗口关闭时,子窗口也关闭。避免空指针操作
        p->setWindowFlag(Qt::Window);
        p->show();
    });
}
void NDDMyPlugin::setScintilla(const std::function<QsciScintilla *()> &cb)
{
    if(scintillaCallback_== nullptr){
        scintillaCallback_ = cb;
    }
}


插件接口类


该类主要是插件框架层接口的实现。这个简单,基本就是按NDD插件的说明文档制作。


#include <qobject.h>
#include <qstring.h>
#include <pluginGl.h>
#include <functional>
#include <qsciscintilla.h>
#include "qttestclass.h"
#include "NDDMyPlugin.h"
#define NDD_EXPORTDLL
#if defined(Q_OS_WIN)
  #if defined(NDD_EXPORTDLL)
    #define NDD_EXPORT __declspec(dllexport)
  #else
    #define NDD_EXPORT __declspec(dllimport)
  #endif
#else
  #define NDD_EXPORT __attribute__((visibility("default")))
#endif
#ifdef __cplusplus
  extern "C" {
#endif
  NDD_EXPORT bool NDD_PROC_IDENTIFY(NDD_PROC_DATA* pProcData);
  NDD_EXPORT int NDD_PROC_MAIN(QWidget* pNotepad, const QString& strFileName, std::function<QsciScintilla* ()>getCurEdit, NDD_PROC_DATA* procData);
#ifdef __cplusplus
  }
#endif
NDDMyPlugin *nddMyPlugin = nullptr;
static NDD_PROC_DATA s_procData;
static QWidget* s_pMainNotepad = nullptr;
std::function<QsciScintilla* ()> s_getCurEdit;
bool NDD_PROC_IDENTIFY(NDD_PROC_DATA* pProcData)
{
  if(pProcData == NULL)
  {
    return false;
  }
  pProcData->m_strPlugName = QObject::tr("Chat AI Plug");
  pProcData->m_strComment = QObject::tr("chat tool use openai GPT-3.5");
  pProcData->m_version = QString("v1.0");
  pProcData->m_auther = QString("yangyongzhen");
  pProcData->m_menuType = 1;
  return true;
}
//则点击菜单栏按钮时,会自动调用到该插件的入口点函数。
//pNotepad:就是CCNotepad的主界面指针
//strFileName:当前插件DLL的全路径,如果不关心,则可以不使用
//getCurEdit:从NDD主程序传递过来的仿函数,通过该函数获取当前编辑框操作对象QsciScintilla
//pProcData:如果pProcData->m_menuType = 0 ,则该指针为空;如果pProcData->m_menuType = 1,则该指针有值。目前需要关心s_procData.m_rootMenu
//开发者可以在该菜单下面,自行创建二级菜单
int NDD_PROC_MAIN(QWidget* pNotepad, const QString &strFileName, std::function<QsciScintilla*()>getCurEdit, NDD_PROC_DATA* pProcData)
{
  //务必拷贝一份pProcData,在外面会释放。
    if (pProcData == nullptr)
    {
        return 1;
    }
    s_pMainNotepad = pNotepad;
    s_procData = *pProcData;
    s_getCurEdit = getCurEdit;
  //如果pProcData->m_menuType = 1;是自己要创建二级菜单的场景。则通过s_procData.m_rootMenu 获取该插件的菜单根节点。
  //插件开发者自行在s_procData.m_rootMenu下添加新的二级菜单项目
    //QMenu* menu = s_procData.m_rootMenu;
    if (!nddMyPlugin)
    {
        nddMyPlugin = new NDDMyPlugin(s_pMainNotepad, strFileName, nullptr, s_pMainNotepad);
        nddMyPlugin->getViewMenu(s_procData.m_rootMenu);
        nddMyPlugin->setScintilla(s_getCurEdit);
    }
  return 0;
}


cmake编译脚本


cmake_minimum_required(VERSION 3.16)
project(mychatai)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_PREFIX_PATH "D:/Qt5.12.11/Qt5.12.11/5.12.11/msvc2015_64/lib/cmake")
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets Concurrent Network PrintSupport XmlPatterns)
add_definitions(-D_UNICODE -DUNICODE)
# win下需要开启UNICODE进行支持TCHAR
if(CMAKE_HOST_WIN32)
    add_definitions(-D_UNICODE -DUNICODE)
endif()
file(GLOB UI_SRC ${PROJECT_SOURCE_DIR}/*.ui)
file(GLOB SRC ${PROJECT_SOURCE_DIR}/*.cpp)
file(GLOB MOC_HEADER ${PROJECT_SOURCE_DIR}/*.h)
# add_executable(${PROJECT_NAME} ${IS_WIN} ${SRC} ${UI_SRC} ${PROJECT_SOURCE_DIR}/src/RealCompare.qrc)
link_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/
)
#find_library(QSCINT_LIB qmyedit_qt5d PATH ${CMAKE_CURRENT_SOURCE_DIR}/)
#add_library( qmyedit_qt5d SHARED IMPORTED )
#set_target_properties( qmyedit_qt5d PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/qmyedit_qt5d.dll )
add_library(${PROJECT_NAME} SHARED ${SRC} ${UI_SRC} ${MOC_HEADER})
target_include_directories(${PROJECT_NAME} PRIVATE
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/../../include
${PROJECT_SOURCE_DIR}/../../qscint/src
${PROJECT_SOURCE_DIR}/../../qscint/src/Qsci
${PROJECT_SOURCE_DIR}/../../qscint/scintilla/src
${PROJECT_SOURCE_DIR}/../../qscint/scintilla/include
${PROJECT_SOURCE_DIR}/../../qscint/scintilla/lexlib
${PROJECT_SOURCE_DIR}/../../qscint/scintilla/boostregex
)
#set(QSCINT_LIB ${CMAKE_CURRENT_SOURCE_DIR}/qmyedit_qt5d.lib)
target_link_libraries(${PROJECT_NAME} PRIVATE
        debug qmyedit_qt5d
        optimized qmyedit_qt5)
link_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}/
)
target_link_libraries(${PROJECT_NAME} PRIVATE  Qt5::Core Qt5::Gui Qt5::Widgets Qt5::Concurrent Qt5::Network  Qt5::PrintSupport Qt5::XmlPatterns)


QT的https访问


void DockTitleWidget::slotBtn_SendClick() {
    //设置头信息
    QNetworkRequest m_url;
    //m_url.setUrl(QUrl("https://pmp.eloam.net/api/ota/findFadVersion"));
    m_url.setUrl(QUrl("https://xxxxx/xxxx"));
    m_url.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    QSslConfiguration m_sslConfig = QSslConfiguration::defaultConfiguration();
    m_sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
    m_sslConfig.setProtocol(QSsl::TlsV1_2);
    m_url.setSslConfiguration(m_sslConfig);
    //char cByte[1024] = "{\"as\":\"123456\", \"ks\": \"123456\", \"productCode\": \"HSPS\", \"version\": \"V1.2.3\"}";
    auto send = QString(u8"{\"user\":\"%1\",\"msg\":\"%2\"}").arg("yang").arg(ui->te_send->toPlainText());
    QByteArray bate = send.toUtf8();
    //QByteArray bate(send);
    //发送数据
    QString fromName = QString(u8"我问:");
    fromName = QString("<font color = blue>%1</font>").arg(fromName);//必须用br作为换行符
    ui->te_recv->append(fromName);
    ui->te_recv->append(ui->te_send->toPlainText());
    m_resp = m_http->post(m_url, bate);
    connect(m_resp, &QNetworkReply::finished, this, &DockTitleWidget::slotNet_Received);
}
void DockTitleWidget::slotNet_Received() {
    QString fromName = QString(u8"\nchat-Ai回答:");
    fromName = QString("<font color = green>%1</font>").arg(fromName);//必须用br作为换行符
    ui->te_recv->append(fromName);
    if (m_resp->error() == QNetworkReply::NoError) {
        QString strReceive = m_resp->readAll();      // 自行解析接口返回数据
        //ui->te_recv->append(ba);
        //QMessageBox::warning(this, "123", ba);
        QJsonParseError json_error;
        QJsonDocument doc = QJsonDocument::fromJson(strReceive.toUtf8(), &json_error);
        if (!doc.isNull() && json_error.error == QJsonParseError::NoError) {
            if (doc.isObject()) {
                QJsonObject object = doc.object();
                if (object.contains("text")) {  // 包含指定的 key
                    QJsonValue value = object.value("text");
                    if (value.isString()) {
                        ui->te_recv->append(value.toString());
                    }
                }
            }
        } else {
            ui->te_recv->append(strReceive);
        }
        //auto recv = QJsonDocument::fromJson(strReceive);
    } else {
        ui->te_recv->append(m_resp->errorString());
        //QMessageBox::warning(this, "123", m_resp->errorString());
    }
}


完整实现


// You may need to build the project (run Qt uic code generator) to get "ui_DockTitleWidget.h" resolved
#pragma execution_character_set("utf-8")
#include "docktitlewidget.h"
#include "ui_DockTitleWidget.h"
#include <QMessageBox>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonParseError>
DockTitleWidget::DockTitleWidget(QWidget *parent) : QWidget(parent), ui(new Ui::DockTitleWidget) {
    ui->setupUi(this);
    ui->te_recv->setStyleSheet(
            "QTextEdit{padding-top:2px;background:#f7f7f7;border:none;border-radius:5px;font-size:12px;color:#292421;"
            "font-family:Microsoft YaHei;padding-left:5px;padding-right:5px;}");//无边框
    ui->te_send->setStyleSheet(
            "QTextEdit{padding-top:2px;background:#f7f7f7;border:none;border-radius:5px;font-size:12px;color:#292421;"
            "font-family:Microsoft YaHei;padding-left:5px;padding-right:5px;}");//无边框
    connect(ui->pb_send, SIGNAL(clicked(bool)), this, SLOT(slotBtn_SendClick()));
    connect(ui->te_recv, SIGNAL(textChanged()), this, SLOT(slotEdtReceiveTextChanged()));
    connect(this,SIGNAL(sigAppendText(QString)),this,SLOT(slotAppendText(QString)));
    m_http = new QNetworkAccessManager();
}
DockTitleWidget::~DockTitleWidget() {
    delete ui;
    delete m_http;
    if (m_resp != nullptr) {
        delete m_resp;
    }
}
void DockTitleWidget::slotBtn_SendClick() {
    //设置头信息
    QNetworkRequest m_url;
    //m_url.setUrl(QUrl("https://pmp.eloam.net/api/ota/findFadVersion"));
    m_url.setUrl(QUrl("https://weixx.xxx.repl.co/xxxx"));
    m_url.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
    QSslConfiguration m_sslConfig = QSslConfiguration::defaultConfiguration();
    m_sslConfig.setPeerVerifyMode(QSslSocket::VerifyNone);
    m_sslConfig.setProtocol(QSsl::TlsV1_2);
    m_url.setSslConfiguration(m_sslConfig);
    //char cByte[1024] = "{\"as\":\"123456\", \"ks\": \"123456\", \"productCode\": \"HSPS\", \"version\": \"V1.2.3\"}";
    auto send = QString(u8"{\"user\":\"%1\",\"msg\":\"%2\"}").arg("yang").arg(ui->te_send->toPlainText());
    QByteArray bate = send.toUtf8();
    //QByteArray bate(send);
    //发送数据
    QString fromName = QString(u8"我问:");
    fromName = QString("<font color = blue>%1</font>").arg(fromName);//必须用br作为换行符
    ui->te_recv->append(fromName);
    ui->te_recv->append(ui->te_send->toPlainText());
    m_resp = m_http->post(m_url, bate);
    connect(m_resp, &QNetworkReply::finished, this, &DockTitleWidget::slotNet_Received);
}
void DockTitleWidget::slotNet_Received() {
    QString fromName = QString(u8"\nchat-Ai回答:");
    fromName = QString("<font color = green>%1</font>").arg(fromName);//必须用br作为换行符
    ui->te_recv->append(fromName);
    if (m_resp->error() == QNetworkReply::NoError) {
        QString strReceive = m_resp->readAll();      // 自行解析接口返回数据
        //ui->te_recv->append(ba);
        //QMessageBox::warning(this, "123", ba);
        QJsonParseError json_error;
        QJsonDocument doc = QJsonDocument::fromJson(strReceive.toUtf8(), &json_error);
        if (!doc.isNull() && json_error.error == QJsonParseError::NoError) {
            if (doc.isObject()) {
                QJsonObject object = doc.object();
                if (object.contains("text")) {  // 包含指定的 key
                    QJsonValue value = object.value("text");
                    if (value.isString()) {
                        ui->te_recv->append(value.toString());
                    }
                }
            }
        } else {
            ui->te_recv->append(strReceive);
        }
        //auto recv = QJsonDocument::fromJson(strReceive);
    } else {
        ui->te_recv->append(m_resp->errorString());
        //QMessageBox::warning(this, "123", m_resp->errorString());
    }
}
void DockTitleWidget::slotEdtReceiveTextChanged() {
    QTextCursor cursor = ui->te_recv->textCursor();
    cursor.movePosition(QTextCursor::End);
    ui->te_recv->setTextCursor(cursor);
}
void DockTitleWidget::slotAppendText(const QString &text) {
}


注意事项


由于使用了https访问网络,默认情况下会缺少两个库(libcrypto-1_1-x64.dll和libssl-1_1-x64.dll)。打包时需要把这两个动态库添加进来,否则运行不起来,运行会报错Process finished with exit code -1073741701 (0xC000007B)。该错误通常是由于所需的库文件(例如.dll文件)不存在或无法加载造成的,有可能是程序需要的特定版本的运行库缺少或者缺失了 32 位或 64 位的运行库。


其他资源


Qt--解析Json_qt json解析_Qt程序员的博客-CSDN博客


qt中的toUtf8, toLatin1, Local8bit, toUcs4_顺其自然~的博客-CSDN博客


各种常见颜色的RGB数值|RGB数值,rgb颜色表,金色rgb,rgb颜色,rgb颜色代码,各种颜色的rgb,常用颜色rgb,金属颜色rgb,rgb颜色对照表

相关文章
|
27天前
|
人工智能 算法 数据可视化
机器人训练师狂喜!Infinite Mobility:上海AI Lab造物神器1秒生成可动家具,成本只要1分钱
上海AI Lab推出的Infinite Mobility采用程序化生成技术,可高效生成22类高质量可交互物体,单个生成仅需1秒且成本低至0.01元,已应用于机器人仿真训练等领域。
69 2
机器人训练师狂喜!Infinite Mobility:上海AI Lab造物神器1秒生成可动家具,成本只要1分钱
|
2月前
|
人工智能 自然语言处理 机器人
9.9K star!大模型原生即时通信机器人平台,这个开源项目让AI对话更智能!
"😎高稳定、🧩支持插件、🦄多模态 - 大模型原生即时通信机器人平台"
|
1天前
|
人工智能 自然语言处理 机器人
2025年AI客服机器人推荐榜单:主流厂商与创新解决方案
本文探讨2025年AI客服机器人的行业趋势,从技术迭代、场景需求到数据安全等角度分析,并提供选型指南。文中强调技术能力(如大模型适配)、场景适配性、数据安全及全周期服务等关键标准,推荐合力亿捷、阿里云、科大讯飞、Salesforce等厂商,助企业理性选择适合的工具。
41 7
|
1天前
|
人工智能 自然语言处理 前端开发
Trae插件Builder模式深度测评:从编程助手到AI工程师的进化
Trae插件最新推出的Builder模式标志着AI辅助编程工具从简单的代码补全向“AI工程师”角色的转变。该模式允许开发者通过自然语言描述项目需求,自动生成完整的项目结构、代码文件和开发流程,支持VSCode、JetBrains IDE及在线Web版本。Builder模式的核心功能包括自然语言项目初始化、智能项目架构设计和多文件协调代码生成,显著提升了开发效率,降低了技术门槛。然而,它在处理复杂业务逻辑和高度定制化需求方面仍有局限。未来,Builder模式将集成云部署、测试套件生成和DevOps流水线等功能
73 1
|
15天前
|
人工智能 程序员
我的通义灵码插件没有AI程序员选项了
通义灵码插件没有AI程序员选项了
|
1月前
|
人工智能 机器人 开发工具
Amazon Nova Act:网页操作全自动!亚马逊黑科技把浏览器变AI机器人,请假/订餐/写邮件一键搞定
Amazon Nova Act是亚马逊AGI实验室推出的通用AI代理系统,通过原子化分解网页操作任务并配合Playwright实现高可靠性浏览器自动化,其配套SDK支持开发者快速构建智能体应用原型。
127 13
Amazon Nova Act:网页操作全自动!亚马逊黑科技把浏览器变AI机器人,请假/订餐/写邮件一键搞定
|
2月前
|
人工智能 自然语言处理 搜索推荐
【2025.3.12】wordpress AI智能插件-新增自动获取搜索引擎下拉关键词,网站SEO必备,自动生成文章、配图,24小时自动发布
Linkreate WordPressAI插件是一款强大的内容生成与优化工具,支持自动化文章生成、SEO优化、长尾关键词生成及管理。具备多语言支持、搜索引擎下拉关键词获取、内容采集、定时任务自动化等功能。同时集成多种AI服务(如DeepSeek、OpenAI等),提供前端AI客服窗口和媒体生成功能,包括文章图片与视频生成。更多功能等待探索。
【2025.3.12】wordpress AI智能插件-新增自动获取搜索引擎下拉关键词,网站SEO必备,自动生成文章、配图,24小时自动发布
|
2月前
|
人工智能 JavaScript 前端开发
一个支持阿里云百炼平台DeepSeek R1大模型(智能体)的Wordpress插件,AI Agent or Chatbot.
这是一个将阿里云DeepSeek AI服务集成到WordPress的聊天机器人插件,支持多轮对话、上下文记忆和自定义界面等功能。用户可通过短代码轻松添加到页面,并支持多种配置选项以满足不同需求。项目采用MIT协议授权,代码仓位于GitHub与Gitee。开发者Chi Leung为长期境外工作,代码注释以英文为主。适合需要在WordPress网站中快速部署AI助手的用户使用。
|
2月前
|
人工智能 自然语言处理 前端开发
【2025.3.08更新】wordpress AI智能插件|自动生成SEO文章/图片/视频+长尾词优化 内置DeepSeek多模型支持与API扩展
Linkreate WordPress AI插件提供强大的自动化文章生成、SEO优化、关键词管理和内容采集功能。它能根据关键词自动生成高质量文章,支持多语言和批量生成,内置长尾关键词生成工具,并可定时自动发布文章。插件还集成了多种AI服务,支持前端AI客服窗口及媒体生成,帮助用户高效管理网站内容,提升SEO效果。
【2025.3.08更新】wordpress AI智能插件|自动生成SEO文章/图片/视频+长尾词优化 内置DeepSeek多模型支持与API扩展
|
2月前
|
人工智能 自然语言处理 搜索推荐
WordPress AI插件 智能化自动生成原创图文文章,从关键词获取、文章生成、文章配图生成、SEO优化、自动推送到各搜索引擎,实现全智能化生成原创图文文章,无需人工干涉
Linkreate WordPress AI插件是一款功能强大的自动化内容生成工具,支持智能图文生成、SEO优化及多语言处理。其核心亮点包括:自动化生成高质量文章、精准标签与摘要、批量生成长尾关键词、定时任务管理以及自动推送至搜索引擎。此外,还提供内容采集、API集成、前端AI客服窗口等功能,支持图片和视频生成,实现网站24小时智能化更新,无需人工干预,大幅提升运营效率。
WordPress AI插件 智能化自动生成原创图文文章,从关键词获取、文章生成、文章配图生成、SEO优化、自动推送到各搜索引擎,实现全智能化生成原创图文文章,无需人工干涉

热门文章

最新文章