Qt模仿人工智能聊天助手

这份代码是参照网上的一个开源项目写的,主要实现了简易人工智能的聊天功能。原项目通过C++调用后台python进程来获取聊天的内容。我通过修改部分逻辑后,使用了基于纯C++来调用后台结果来返回聊天的数据。

实现的主要思路如下:

1.用户在输入框输入消息后,点击发送,调用槽函数on_btnSend_clicked(),然后通过布局器以及绘图功能绘制出文本框与气泡,显示在聊天窗口;
2. 当我方消息发送成功后,程序会自动调用后台接口,将我们的消息以GET请求方法发送给后台。服务器会根据我们的消息去自动生成json格式的回复反馈给程序。
3. 程序接收到json字符串后,会对其进行解析,提取出文本消息字段后,通过信号与槽将文本字符串发给ui类。ui类再将消息显示即可。

效果如下:
在这里插入图片描述
源代码如下:

//common.h
#ifndef COMMON_H
#define COMMON_H

enum E_COLOR {
    eWechatGreen,
    eWhite
};

enum E_WHOSAY {
    eMe,
    eFriend
};

#define WECHATGREEN 152, 225, 101
#define WHITE 255,255,255

#endif // COMMON_H
//custommsgitem.h
#ifndef CUSTOMMSGITEM_H
#define CUSTOMMSGITEM_H

#include <QWidget>
#include "common.h"

namespace Ui {
class CustomMsgItem;
}
class QLabel;
class CustomMsgItem : public QWidget
{
    Q_OBJECT

public:
    explicit CustomMsgItem(QWidget *parent = 0, E_WHOSAY = E_WHOSAY::eMe);
    ~CustomMsgItem();

    void paintEvent(QPaintEvent *);

    void setMsg(QString strMsg);
    QString getMsg();

    void setMsgBackgroudColor(E_COLOR);

private:
    Ui::CustomMsgItem *ui;
    QString m_strMsg;
    QLabel *m_pLbMsg;
    QColor m_colorBackgroundMsg;

    E_WHOSAY m_eWhoSay;
};

#endif // CUSTOMMSGITEM_H

//iknow.h
#ifndef IKNOW_H
#define IKNOW_H

#include <QString>
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>

class QProcess;
class IKnow : public QObject
{
    Q_OBJECT
public:
    IKnow();
    ~IKnow();
    QString answer();
    void setQuestion(QString);
    bool findAnswer();
    void exec();

signals:
    void signalFindAnswer(QString&);
	void sgnAnswreString(QString&);

private slots:
    void recvStandOut();
protected slots:
	void OnGetReplyFromUrlRtn(QNetworkReply* pRply);

private:
    QProcess *m_pProc;
    QString m_strAnswer;
    QString m_strQuestion;
	QNetworkAccessManager* m_pNetAcsMgr;
};

#endif // IKNOW_H

```cpp
//topaopao.h
#ifndef TOPAOPAO_H
#define TOPAOPAO_H

#include <QWidget>

namespace Ui {
class ToPaoPao;
}
class QVBoxLayout;
class IKnow;
class ToPaoPao : public QWidget
{
    Q_OBJECT

public:
    explicit ToPaoPao(QWidget *parent = 0);
    ~ToPaoPao();

    void paintEvent(QPaintEvent *);

    bool eventFilter(QObject *, QEvent *);

private slots:
    void on_btnSend_clicked();
    void slotFindAnswer(QString&);
    void slider();
	void OnGetRetString(QString&);

private:
    Ui::ToPaoPao *ui;
    QVBoxLayout *m_pVboxLayout;
    IKnow *m_pIknow;
	QString m_strRet;
};

#endif // TOPAOPAO_H
//custommsgitem.cpp
#include "custommsgitem.h"
#include "ui_custommsgitem.h"

#include <QPainter>
#include <QLabel>
#include <QDebug>

CustomMsgItem::CustomMsgItem(QWidget *parent, E_WHOSAY eWhoSay) :
    QWidget(parent),
    ui(new Ui::CustomMsgItem),
    m_strMsg(""),
    m_colorBackgroundMsg(WECHATGREEN),
    m_eWhoSay(eWhoSay)
{
    ui->setupUi(this);

    m_pLbMsg = new QLabel(this);
    m_pLbMsg->setTextInteractionFlags(Qt::TextSelectableByMouse);
    ui->horizontalLayout->addWidget(m_pLbMsg);
    m_pLbMsg->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    QFont font;
    font.setPixelSize(20);
    m_pLbMsg->setFont(font);
    m_pLbMsg->setWordWrap(true);
}

CustomMsgItem::~CustomMsgItem()
{
    delete ui;
}

void CustomMsgItem::paintEvent(QPaintEvent *)
{
    m_pLbMsg->setText(m_strMsg);
    //根据当前文字长度设置气泡长度
    QFontMetrics fontMetrics(m_pLbMsg->font());

    //如果当前字体长度大于窗体宽度的二分之一
    //就不断分割
    if (fontMetrics.width(m_pLbMsg->text()) > this->parentWidget()->width() / 2) {
        setFixedWidth(30 + this->parentWidget()->width() / 2);
        int iLine = (fontMetrics.width(m_pLbMsg->text()) % (this->parentWidget()->width() / 2)) == 0 ? 0 : fontMetrics.height();
        setFixedHeight(30 + fontMetrics.height() * ((fontMetrics.width(m_pLbMsg->text()) / (this->parentWidget()->width() / 2))) + iLine);
    } else {
        setFixedWidth(fontMetrics.width(m_pLbMsg->text()) + 30);
        setFixedHeight(fontMetrics.height() + 25);
    }


    QPainter painter(this);

    QRectF rectangle;
    QPainterPath trianglePath;
    if (m_eWhoSay == E_WHOSAY::eMe) {
        rectangle = QRectF(0, 0, width() - 5, height());
        trianglePath = QPainterPath(QPointF(width() - 5, height() / 2 - 7));
        trianglePath.lineTo(width(), height() / 2);
        trianglePath.lineTo(width() - 5, height() / 2 + 7);
        trianglePath.lineTo(width() - 5, height() / 2 - 7);
    } else if (m_eWhoSay == E_WHOSAY::eFriend) {
        m_colorBackgroundMsg.setRgb(WHITE);
        rectangle = QRectF(5, 0, width() - 5, height());
        trianglePath = QPainterPath(QPointF(5, height() / 2 - 7));
        trianglePath.lineTo(0, height() / 2);
        trianglePath.lineTo(5, height() / 2 + 7);
        trianglePath.lineTo(5, height() / 2 - 7);
    }

    painter.setPen(Qt::NoPen);
    painter.setBrush(m_colorBackgroundMsg);
    //画矩形
    painter.drawRoundedRect(rectangle, 5.0, 5.0);
    //画三角形
    painter.drawPath(trianglePath);
}

void CustomMsgItem::setMsg(QString strMsg)
{
    this->m_strMsg = strMsg;
}

QString CustomMsgItem::getMsg()
{
    return m_strMsg;
}

void CustomMsgItem::setMsgBackgroudColor(E_COLOR eColor)
{
    if (eColor == E_COLOR::eWechatGreen) {
        m_colorBackgroundMsg.setRgb(WECHATGREEN);
    } else if (eColor == E_COLOR::eWhite) {
        m_colorBackgroundMsg.setRgb(WHITE);
    }
}
//iknow.cpp
#include "iknow.h"

#include <QProcess>
#include <QDebug>
#include <QTextCodec>
#include <QCoreApplication>
#include <QByteArray>
#include <QJsonParseError>
#include <QJsonDocument>
#include <QJsonObject>

IKnow::IKnow()
{
	m_pNetAcsMgr = new QNetworkAccessManager(this);
    QString strPorc = QCoreApplication::applicationDirPath() + "/IKNOW.exe";

    m_pProc = new QProcess();
    m_pProc->setReadChannel(QProcess::StandardOutput);
    m_pProc->start(strPorc);

    connect(m_pProc, SIGNAL(readyReadStandardOutput()), this, SLOT(recvStandOut()));
}

IKnow::~IKnow()
{
    m_pProc->kill();
    delete m_pProc;
    m_pProc = nullptr;
}

void IKnow::recvStandOut()
{
    exec();
}

QString IKnow::answer()
{
    return m_strQuestion;
}

void IKnow::setQuestion(QString strQuestion)
{
    //向IKNOW.exe发送消息
    /*QByteArray byte = strQuestion.toLocal8Bit();
    const char *pChar = byte.data();
    m_pProc->write(pChar);
    m_pProc->write("\r\n");
    qDebug() << m_pProc->waitForBytesWritten(2000);*/
	m_strQuestion = strQuestion;
}

void IKnow::OnGetReplyFromUrlRtn(QNetworkReply* pRply)
{
	int iError = pRply->error();
	if (iError == QNetworkReply::NoError)
	{
		QByteArray bytes = pRply->readAll();
		QString jsonString = QString::fromUtf8(bytes);

		QJsonParseError jsonParseError;
		QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes, &jsonParseError);
		if (!jsonDoc.isNull() && jsonParseError.error == QJsonParseError::NoError)
		{
			if (jsonDoc.isArray())
			{
				
			}
			else
			{
				QJsonObject jsonObject = jsonDoc.object();
				QString strRet = jsonObject["content"].toString();
				//用换车替换{br}
				strRet.replace("{br}", QChar('\n'));
				emit signalFindAnswer(strRet);
			}
		}
		else
		{
		}
	}
}

bool IKnow::findAnswer()
{
    //收到IKNOW.exe的消息
	// 方案1.根据C++调用python进程来获取答案
    /*QByteArray byte = m_pProc->readAllStandardOutput();
    QTextCodec *pTextCodec = QTextCodec::codecForName("GBK");
    QString strMsg = pTextCodec->toUnicode(byte);
    if (strMsg.isEmpty()) {
        return false;
    }
    //用换车替换{br}
    strMsg.replace("{br}", QChar('\n'));

    m_strQuestion = strMsg;
    return true;*/

	//方案2.C++独立请求后台接口来得到答案
	QObject::connect(m_pNetAcsMgr, SIGNAL(finished(QNetworkReply*)), this, SLOT(OnGetReplyFromUrlRtn(QNetworkReply*)));
	QString strUrl = QString("http://api.qingyunke.com/api.php?key=free&appid=0&msg=%1").arg(m_strQuestion);
	QUrl url(strUrl);
	QNetworkRequest req(url);
	req.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json;charset=utf-8"));
	m_pNetAcsMgr->get(req);
	return true;
}

void IKnow::exec()
{
    if (findAnswer()) {
        //emit signalFindAnswer();
    }
}
//topaopao.cpp
#include "topaopao.h"
#include "ui_topaopao.h"
#include "custommsgitem.h"
#include "common.h"
#include "iknow.h"

#include <QPlainTextEdit>
#include <QPropertyAnimation>
#include <QDebug>
#include <QScrollBar>
#include <QThread>

ToPaoPao::ToPaoPao(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::ToPaoPao)
{
    ui->setupUi(this);

    setFocus();

    m_pVboxLayout = new QVBoxLayout();
    ui->scrollAreaWidgetContents_2->setLayout(m_pVboxLayout);

    m_pIknow = new IKnow();
    connect(m_pIknow, SIGNAL(signalFindAnswer(QString&)), this, SLOT(slotFindAnswer(QString&)));
	connect(m_pIknow, SIGNAL(sgnAnswreString(QString&)), this, SLOT(OnGetRetString(QString&)));

    ui->texteditInput->installEventFilter(this);

	QString strQuestion = ui->texteditInput->toPlainText();
	m_pIknow->setQuestion(strQuestion);

    connect(ui->scrollArea->verticalScrollBar(), &QAbstractSlider::rangeChanged, this, &ToPaoPao::slider);
}

ToPaoPao::~ToPaoPao()
{
    delete m_pIknow;
    delete m_pVboxLayout;
    delete ui;
}

void ToPaoPao::slider()
{
    ui->scrollArea->verticalScrollBar()->setValue(ui->scrollArea->verticalScrollBar()->maximum());
}

void ToPaoPao::paintEvent(QPaintEvent *event)
{
    QWidget::paintEvent(event);
}

bool ToPaoPao::eventFilter(QObject *pObj, QEvent *pEvent)
{
    if (pObj == ui->texteditInput) {
        QKeyEvent *pKeyEvent = dynamic_cast<QKeyEvent *>(pEvent);
        if (pKeyEvent) {
            if (pKeyEvent->key() == Qt::Key_Return || pKeyEvent->key() == Qt::Key_Return) {
                on_btnSend_clicked();
            }
        }
    }
    return QWidget::eventFilter(pObj, pEvent);
}

void ToPaoPao::slotFindAnswer(QString& strAnswer)
{
    QHBoxLayout *pHboxLayout = new QHBoxLayout();
    CustomMsgItem *pItem = new CustomMsgItem(ui->scrollAreaWidgetContents_2, E_WHOSAY::eFriend);
    pHboxLayout->addWidget(pItem);
    pHboxLayout->insertStretch(1);
	pItem->setMsg(strAnswer);
    //pItem->setMsg(m_pIknow->answer());
    m_pVboxLayout->addLayout(pHboxLayout);
}

void ToPaoPao::OnGetRetString(QString& strRet)
{
	m_strRet = strRet;
}

void ToPaoPao::on_btnSend_clicked()
{
    QString strInput = ui->texteditInput->toPlainText();
    //去除回车
    strInput.remove(QChar('\n'));
    ui->texteditInput->clear();

    //判断输入框是否为空
    if (strInput.isEmpty()) {
        return ;
    }

    QHBoxLayout *pHboxLayout = new QHBoxLayout();

    CustomMsgItem *pItem = new CustomMsgItem(ui->scrollAreaWidgetContents_2, E_WHOSAY::eMe);
    pHboxLayout->insertStretch(0);
    pHboxLayout->addWidget(pItem);
    pItem->setMsg(strInput);
    m_pVboxLayout->addLayout(pHboxLayout);

    m_pIknow->setQuestion(strInput);
	m_pIknow->findAnswer();
}




  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值