Skip to content

Commit a0cbbbb

Browse files
committed
send_message_by_enter
1 parent 9f369c3 commit a0cbbbb

File tree

7 files changed

+167
-69
lines changed

7 files changed

+167
-69
lines changed
19.2 KB
Binary file not shown.

src/Sample/Resources/help.html

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1-
<p style="text-align: center;">====<span style='font-family: "Arial Black", Gadget, sans-serif; font-size: 18px;'><u>Holo Bot v1.1.1</u></span>====</p>
2-
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<style>
5+
body {
6+
font-family: "Arial Black", Gadget, sans-serif;
7+
text-align: center;
8+
}
9+
p {
10+
font-size: 18px;
11+
}
12+
span {
13+
text-decoration: underline;
14+
}
15+
table {
16+
margin-left: auto;
17+
margin-right: auto;
18+
text-align: center;
19+
border-collapse: collapse;
20+
border: 1px solid black;
21+
}
22+
th, td {
23+
padding: 10px;
24+
border: 1px solid black;
25+
}
26+
th {
27+
background-color: lightgray;
28+
text-align: center;
29+
}
30+
</style>
31+
</head>
32+
<body>
33+
<p>====<span>Holo Bot v1.1.1</span>====</p>
34+
<pre>
335
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢲⣦⣤⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
436
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⣿⣿⣿⠷⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
537
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠿⠁⢀⣴⣶⣶⡀⠀⠀⠀⠀⠀⠀⠀
@@ -23,34 +55,38 @@
2355
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣧⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣿⣿⣿⣿⡇⠀⠀⠀⠀⠀⠀
2456
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠛⠻⠛⢿⣿⣧⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⢹⣿⣿⣄⠀⠀⠀⠀⠀
2557
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠿⠿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠀
26-
27-
<table style="margin-left:auto; margin-right:auto; text-align:center;" border="1">
28-
<thead>
29-
<tr>
30-
<th>Action</th>
31-
<th>Key</th>
32-
</tr>
33-
</thead>
34-
<tbody>
35-
<tr>
36-
<td>Interact</td>
37-
<td>Mouse Left</td>
38-
</tr>
39-
<tr>
40-
<td>Move</td>
41-
<td>Mouse Middle</td>
42-
</tr>
43-
<tr>
44-
<td>Menu</td>
45-
<td>Mouse Right</td>
46-
</tr>
47-
<tr>
48-
<td>Resize</td>
49-
<td>Mouse Wheel</td>
50-
</tr>
51-
<tr>
52-
<td>Exit Holo Mode</td>
53-
<td>ESC</td>
54-
</tr>
55-
</tbody>
56-
</table>
58+
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉⠉⠀⠀⠀⠀⠀
59+
</pre>
60+
<p>Project URL: <a href="https://github.com/sucv/HoloLive2dChatbot">https://github.com/sucv/HoloLive2dChatbot</a></p>
61+
<table>
62+
<thead>
63+
<tr>
64+
<th>Action</th>
65+
<th>Key</th>
66+
</tr>
67+
</thead>
68+
<tbody>
69+
<tr>
70+
<td>Interact</td>
71+
<td>Mouse Left</td>
72+
</tr>
73+
<tr>
74+
<td>Move</td>
75+
<td>Mouse Middle</td>
76+
</tr>
77+
<tr>
78+
<td>Menu</td>
79+
<td>Mouse Right</td>
80+
</tr>
81+
<tr>
82+
<td>Resize</td>
83+
<td>Mouse Wheel</td>
84+
</tr>
85+
<tr>
86+
<td>Exit Holo Mode</td>
87+
<td>ESC</td>
88+
</tr>
89+
</tbody>
90+
</table>
91+
</body>
92+
</html>

src/Sample/communicate.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,57 @@
11
#include <QJsonDocument>
22
#include <QThread>
3-
3+
#include <QMessageBox>
44
#include "OpenAI/openai.hpp"
55
#include "communicate.h"
66

77
using namespace Microsoft::CognitiveServices::Speech;
88
using namespace Microsoft::CognitiveServices::Speech::Audio;
99

1010
Mailman::Mailman(ChatWidget *chatWidget) : m_chatWidget(chatWidget) {
11+
// Save the chat history to Qt Standard model.
1112
connect(this, SIGNAL(updateChatModel(nlohmann::json,QString)), m_chatWidget, SLOT(update(nlohmann::json,QString)));
1213
}
1314

1415

1516
void Mailman::talk(QString prompts, QString azureSetting) {
17+
18+
// Get the current prompts.
1619
static QRegularExpression fingNewLineChar("[\\n]");
1720
std::string cleanprompt = prompts.remove(fingNewLineChar).toStdString();
1821

22+
// Format the prompts following OPENAI's standard.
1923
jsonBody = initJsonBody(cleanprompt);
2024

25+
// Read the variables from System Setting.
2126
auto speechKey = GetEnvironmentVariable("SPEECH_KEY");
2227
auto speechRegion = GetEnvironmentVariable("SPEECH_REGION");
2328

29+
// If the variable is not set, then quit the thread.
2430
if ((std::size(speechKey) == 0) || (std::size(speechRegion) == 0)) {
2531
std::cout << "Please set both SPEECH_KEY and SPEECH_REGION environment variables." << std::endl;
32+
QMessageBox msgBox;
33+
msgBox.setWindowTitle("Error");
34+
msgBox.setText("Cannot find SPEECH_KEY and SPEECH_REGION environment variables. Check your system environment variable setting!");
35+
msgBox.setIcon(QMessageBox::Information);
36+
msgBox.addButton("OK", QMessageBox::AcceptRole);
37+
38+
QThread::currentThread()->quit();
39+
QThread::currentThread()->wait();
2640
}
27-
2841
auto speechConfig = SpeechConfig::FromSubscription(speechKey, speechRegion);
2942

43+
// Get the preferred voice.
3044
QString language = azureSetting.split("-")[0] + "-" + azureSetting.split("-")[1];
3145
QString voice = azureSetting;
3246
speechConfig->SetSpeechRecognitionLanguage(language.toStdString());
3347
speechConfig->SetSpeechSynthesisVoiceName(voice.toStdString());
3448

35-
49+
// Initialize streaming...
3650
auto audioConfig = AudioConfig::FromDefaultMicrophoneInput();
3751
auto recognizer = SpeechRecognizer::FromConfig(speechConfig, audioConfig);
3852
auto speechSynthesizer = SpeechSynthesizer::FromConfig(speechConfig);
3953

40-
54+
// Start streaming...
4155
while (!QThread::currentThread()->isInterruptionRequested())
4256
{
4357
auto voiceInput = recognizer->RecognizeOnceAsync().get();
@@ -49,21 +63,29 @@ void Mailman::talk(QString prompts, QString azureSetting) {
4963

5064
nlohmann::json userInputJson;
5165

52-
// Add key-value pairs to the JSON object
66+
// The json contains Expression and Motion to animate the Live2D model and Content for conversation.
5367
userInputJson["Expression"] = "";
5468
userInputJson["Motion"] = "";
5569
userInputJson["Content"] = userInput;
70+
71+
// Save the json to Qt StandardItemModel. So that the chat bubble can be filled in with words.
5672
emit updateChatModel(userInputJson, "Outgoing");
5773

74+
// Format the user input following OpenAI's standard.
5875
nlohmann::json userMessage = getNewMessage("user", userInput);
76+
77+
// Stitch it with the main json.
5978
jsonBody = insertMessage(jsonBody, userMessage);
6079

80+
// Send POST request to OpenAI.
6181
openai::start();
6282
nlohmann::json completion = openai::chat().create(jsonBody);
6383

84+
// Get the response from OpenAI. Always use the top-1 response.
6485
std::string gptResponse = completion["choices"][0]["message"]["content"];
65-
nlohmann::json gptResponseJson = parseJsonString(gptResponse);
6686

87+
// Parse the returned data as if it is json. Sometime the data may not be json, so we manually make sure it is.
88+
nlohmann::json gptResponseJson = parseJsonString(gptResponse);
6789
expression = gptResponseJson.contains("Expression") ? gptResponseJson["Expression"] : "";
6890
motion = gptResponseJson.contains("Motion") ? gptResponseJson["Motion"] : "";
6991
content = gptResponseJson.contains("Content") ? gptResponseJson["Content"].get<std::string>() : gptResponse;
@@ -73,16 +95,21 @@ void Mailman::talk(QString prompts, QString azureSetting) {
7395
reconstructedJson["Motion"] = motion;
7496
reconstructedJson["Content"] = content;
7597

98+
// Send the expression and motion to animate the Live2D model.
7699
emit sendResponseMove(expression, motion);
100+
101+
// Save the json to Qt StandardItemModel.
77102
emit updateChatModel(reconstructedJson, "Incoming");
78103

104+
// Stitch the json with the main json.
79105
nlohmann::json gptMessage = getNewMessage("assistant", reconstructedJson.dump());
80106
jsonBody = insertMessage(jsonBody, gptMessage);
81107

108+
// Voice the reply.
82109
auto chatGptVoiceOutput = speechSynthesizer->SpeakTextAsync(content).get();}
83110
}
84111

85-
112+
// Stop the thread.
86113
QThread::currentThread()->quit();
87114
QThread::currentThread()->wait();
88115
}
@@ -99,6 +126,7 @@ nlohmann::json Mailman::getNewMessage(std::string role, std::string content)
99126
}
100127

101128
void Mailman::chat(QString prompts, QString userInput) {
129+
// This member is largely the same as talk(), but it is only a one-turn process and has no while loop.
102130
static QRegularExpression fingNewLineChar("[\\n]");
103131
std::string cleanprompt = prompts.remove(fingNewLineChar).toStdString();
104132

@@ -141,6 +169,7 @@ void Mailman::chat(QString prompts, QString userInput) {
141169

142170
nlohmann::json Mailman::insertMessage(nlohmann::json json, nlohmann::json newJson)
143171
{
172+
// Stitch the reply from ChatGPT so that it can track the context. Without stitching, the prompt will be forgotten in no time.
144173
if (newJson.size())
145174
{
146175
if(newJson.is_array())

src/Sample/editdialog.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ using namespace LAppDefine;
1717
EditDialog::EditDialog(MainWindow *mainWindow, QWidget *parent)
1818
: QDialog(parent), m_mainWindow(mainWindow)
1919
{
20+
// The combobox for live2d models, prompt files, and available voices.
2021
modelComboBox = new QComboBox(this);
22+
promptComboBox = new QComboBox(modelComboBox);
23+
languageComboBox = new QComboBox(this);
24+
2125
connect(modelComboBox, &QComboBox::activated, m_mainWindow, &MainWindow::loadModel);
2226
connect(this, &EditDialog::sendPrompt, m_mainWindow, &MainWindow::getPrompt);
23-
promptComboBox = new QComboBox(modelComboBox);
2427
connect(modelComboBox, SIGNAL(currentIndexChanged(int)), m_mainWindow, SLOT(updateModelIndex(int)));
2528
connect(modelComboBox, SIGNAL(currentIndexChanged(int)), promptComboBox, SLOT(clear()));
2629
connect(modelComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(populateComboBox()));
27-
2830
connect(promptComboBox, &QComboBox::activated, this, &EditDialog::loadPrompts);
29-
30-
languageComboBox = new QComboBox(this);
31-
3231
connect(languageComboBox, &QComboBox::currentTextChanged, m_mainWindow, &MainWindow::updateVoiceLanguage);
32+
3333
initAzureSetting();
3434

3535
modelLabel = new QLabel(tr("&Model"));
@@ -64,12 +64,14 @@ EditDialog::EditDialog(MainWindow *mainWindow, QWidget *parent)
6464
}
6565
}
6666

67+
// Send the updated prompt to mainwindow.
6768
emit sendPrompt(promptTextEdit->toPlainText());
6869
promptComboBox->clear();
6970
populateComboBox();
7071

7172
this->accept(); // Accept the dialog
7273
});
74+
7375
connect(buttonBox, &QDialogButtonBox::rejected, this, [&](){
7476
loadPrompts(0);
7577
this->reject();
@@ -173,7 +175,7 @@ void EditDialog::populateComboBox()
173175
promptComboBox->addItem(txtFiles[i]);
174176
}
175177
loadPrompts(0);
176-
qDebug() << promptTextEdit->toPlainText() << "prompt";
178+
177179
emit sendPrompt(promptTextEdit->toPlainText());
178180
}
179181

@@ -204,9 +206,9 @@ void EditDialog::loadPrompts(int promptIndex)
204206

205207
void EditDialog::initPrompts(int modelIndex)
206208
{
207-
209+
// Initialize the prompt by reading the model json of Live2D model.
210+
// The initialized prompt is expected to be further modified manually to better behave as intended.
208211
QJsonObject jsonObject, motions, expressions;
209-
210212
QList<QString> expressionList, motionList, rules;
211213

212214

src/Sample/listviewdelegate.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
#include "listviewdelegate.h"
22

3+
// Use this custom QTextEdit class to capture the Enter key and shift enter key.
4+
// It improves the keyboard-based chat experience a lot.
5+
ChatTextEdit::ChatTextEdit(QWidget *parent) : QTextEdit(parent){}
6+
7+
void ChatTextEdit::keyPressEvent(QKeyEvent* event)
8+
{
9+
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
10+
{
11+
if (event->modifiers() == Qt::ShiftModifier)
12+
{
13+
// Shift+Enter pressed, handle custom behavior here
14+
// For example, add a new line without emitting the enterKeyPressed() signal
15+
QTextEdit::keyPressEvent(event); // Let the base class handle the key event
16+
}
17+
else
18+
{
19+
// Enter pressed, emit the enterKeyPressed() signal
20+
emit enterKeyPressed();
21+
setFocus();
22+
}
23+
}
24+
else
25+
{
26+
QTextEdit::keyPressEvent(event); // Let the base class handle the key event
27+
}
28+
}
29+
30+
331
ChatWidget::ChatWidget(QWidget* parent) : QWidget(parent) {
432
listView = new QListView();
5-
// listView->setStyleSheet("border: none");
633
chatModel = new QStandardItemModel();
7-
textEdit = new QTextEdit();
8-
// textEdit->setStyleSheet("border: none");
9-
// textEdit->setMaximumSize(200,450);
10-
pushButton = new QPushButton(tr("Send"));
11-
pushButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
12-
connect(pushButton, &QPushButton::clicked, this, &ChatWidget::send);
34+
textEdit = new ChatTextEdit();
35+
// connect(textEdit, &ChatTextEdit::enterKeyPressed, pushButton, &QPushButton::click);
36+
connect(textEdit, &ChatTextEdit::enterKeyPressed, this, &ChatWidget::send);
1337

1438
listView->setModel(chatModel);
1539

16-
1740
// create a view and set our data
1841
listView->setResizeMode(QListView::Adjust);
1942
listView->setWordWrap(true);
@@ -29,14 +52,7 @@ ChatWidget::ChatWidget(QWidget* parent) : QWidget(parent) {
2952

3053
QVBoxLayout* layout = new QVBoxLayout();
3154
layout->addWidget(listView);
32-
// layout->addWidget(separator);
3355
layout->addWidget(textEdit);
34-
35-
auto buttonLayout = new QHBoxLayout();
36-
buttonLayout->addStretch();
37-
buttonLayout->addWidget(pushButton);
38-
layout->addLayout(buttonLayout);
39-
4056
setLayout(layout);
4157
connect(chatModel, &QStandardItemModel::rowsInserted, listView, &QListView::scrollToBottom);
4258
}
@@ -72,8 +88,8 @@ void ChatWidget::send()
7288
if (userInput != "")
7389
{
7490
emit sendUserInput(userInput);
91+
textEdit->setFocus();
7592
}
76-
7793
}
7894

7995
void ChatWidget::update(nlohmann::json input, QString role)

0 commit comments

Comments
 (0)