update
parent
2de9d78f41
commit
9c121c6ba9
|
@ -65,6 +65,10 @@
|
||||||
|
|
||||||
# 部分效果预览
|
# 部分效果预览
|
||||||
|
|
||||||
|
## 内置一个ChatGPT聊天Demo
|
||||||
|
|
||||||
|
![](doc/preview/chatpgt.png)
|
||||||
|
|
||||||
## 各种Button按钮
|
## 各种Button按钮
|
||||||
|
|
||||||
![](doc/preview/buttons.png)
|
![](doc/preview/buttons.png)
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 382 KiB |
|
@ -15,6 +15,7 @@ Window {
|
||||||
"/":"qrc:/page/MainPage.qml",
|
"/":"qrc:/page/MainPage.qml",
|
||||||
"/about":"qrc:/page/AboutPage.qml",
|
"/about":"qrc:/page/AboutPage.qml",
|
||||||
"/login":"qrc:/page/LoginPage.qml",
|
"/login":"qrc:/page/LoginPage.qml",
|
||||||
|
"/chat":"qrc:/page/ChatPage.qml",
|
||||||
}
|
}
|
||||||
FluApp.initialRoute = "/"
|
FluApp.initialRoute = "/"
|
||||||
FluApp.run()
|
FluApp.run()
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include "ChatController.h"
|
||||||
|
|
||||||
|
ChatController::ChatController(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
isLoading(false);
|
||||||
|
networkManager = new QNetworkAccessManager(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatController::sendMessage(const QString& text){
|
||||||
|
isLoading(true);
|
||||||
|
QUrl apiUrl("https://api.openai.com/v1/engines/text-davinci-003/completions");
|
||||||
|
QNetworkRequest request(apiUrl);
|
||||||
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
|
request.setRawHeader("Authorization", "Bearer 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");
|
||||||
|
QJsonObject requestData;
|
||||||
|
requestData.insert("prompt", text);
|
||||||
|
requestData.insert("max_tokens", 1000);
|
||||||
|
requestData.insert("temperature", 0.5);
|
||||||
|
QJsonDocument requestDoc(requestData);
|
||||||
|
QByteArray requestDataBytes = requestDoc.toJson();
|
||||||
|
QNetworkReply* reply = networkManager->post(request, requestDataBytes);
|
||||||
|
connect(reply, &QNetworkReply::finished,this, [=]() {
|
||||||
|
QString responseString = QString::fromUtf8(reply->readAll());
|
||||||
|
qDebug() << responseString;
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(responseString.toUtf8());
|
||||||
|
QJsonObject jsonObj = doc.object();
|
||||||
|
QString text = jsonObj.value("choices").toArray().at(0).toObject().value("text").toString();
|
||||||
|
if(text.isEmpty()){
|
||||||
|
text = "不好意思,我似乎听不懂您的意思";
|
||||||
|
}
|
||||||
|
responseData(text);
|
||||||
|
reply->deleteLater();
|
||||||
|
isLoading(false);
|
||||||
|
});
|
||||||
|
connect(reply, QOverload<QNetworkReply::NetworkError>::of(&QNetworkReply::errorOccurred), this, [=](QNetworkReply::NetworkError) {
|
||||||
|
qDebug() << "Network error occurred: " << reply->errorString();
|
||||||
|
reply->deleteLater();
|
||||||
|
isLoading(false);
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
#ifndef CHATCONTROLLER_H
|
||||||
|
#define CHATCONTROLLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QByteArray>
|
||||||
|
#include "stdafx.h"
|
||||||
|
|
||||||
|
class ChatController : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY_AUTO(bool,isLoading)
|
||||||
|
Q_PROPERTY_AUTO(QString,responseData);
|
||||||
|
public:
|
||||||
|
explicit ChatController(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
Q_INVOKABLE void sendMessage(const QString& text);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QNetworkAccessManager* networkManager;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CHATCONTROLLER_H
|
|
@ -1,9 +1,10 @@
|
||||||
QT += quick concurrent
|
QT += quick concurrent network
|
||||||
CONFIG += c++11
|
CONFIG += c++11
|
||||||
|
|
||||||
DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT
|
DEFINES += QT_DEPRECATED_WARNINGS QT_NO_WARNING_OUTPUT
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
|
ChatController.cpp \
|
||||||
main.cpp
|
main.cpp
|
||||||
|
|
||||||
RESOURCES += qml.qrc
|
RESOURCES += qml.qrc
|
||||||
|
@ -24,3 +25,6 @@ CONFIG(debug,debug|release) {
|
||||||
} else {
|
} else {
|
||||||
DESTDIR = $$absolute_path($${_PRO_FILE_PWD_}/../bin/release)
|
DESTDIR = $$absolute_path($${_PRO_FILE_PWD_}/../bin/release)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
ChatController.h
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QQuickWindow>
|
#include <QQuickWindow>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
|
#include "ChatController.h"
|
||||||
|
|
||||||
QMap<QString, QVariant> properties(){
|
QMap<QString, QVariant> properties(){
|
||||||
QMap<QString, QVariant> map;
|
QMap<QString, QVariant> map;
|
||||||
|
@ -20,6 +21,9 @@ int main(int argc, char *argv[])
|
||||||
// QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
|
// QQuickWindow::setSceneGraphBackend(QSGRendererInterface::Software);
|
||||||
QGuiApplication app(argc, argv);
|
QGuiApplication app(argc, argv);
|
||||||
QQmlApplicationEngine engine;
|
QQmlApplicationEngine engine;
|
||||||
|
|
||||||
|
qmlRegisterType<ChatController>("Controller",1,0,"ChatController");
|
||||||
|
|
||||||
QMapIterator<QString, QVariant> iterator(properties());
|
QMapIterator<QString, QVariant> iterator(properties());
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
iterator.next();
|
iterator.next();
|
||||||
|
|
|
@ -34,7 +34,7 @@ FluWindow {
|
||||||
fontStyle: FluText.Title
|
fontStyle: FluText.Title
|
||||||
}
|
}
|
||||||
FluText{
|
FluText{
|
||||||
text:"v1.0.9"
|
text:"v1.0.10"
|
||||||
fontStyle: FluText.Body
|
fontStyle: FluText.Body
|
||||||
Layout.alignment: Qt.AlignBottom
|
Layout.alignment: Qt.AlignBottom
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,230 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import FluentUI 1.0
|
||||||
|
import Controller 1.0
|
||||||
|
|
||||||
|
FluWindow {
|
||||||
|
|
||||||
|
width: 680
|
||||||
|
height: 600
|
||||||
|
minimumWidth: 500
|
||||||
|
minimumHeight: 600
|
||||||
|
|
||||||
|
title:"ChatGPT"
|
||||||
|
|
||||||
|
ChatController{
|
||||||
|
id:controller
|
||||||
|
|
||||||
|
onResponseDataChanged: {
|
||||||
|
appendMessage(false,responseData)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ListModel{
|
||||||
|
id:model_message
|
||||||
|
ListElement{
|
||||||
|
isMy:false
|
||||||
|
text:"欢迎使用ChatGPT"
|
||||||
|
}
|
||||||
|
ListElement{
|
||||||
|
isMy:true
|
||||||
|
text:"好的,3Q"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluAppBar{
|
||||||
|
id:appbar
|
||||||
|
title:"ChatGPT"
|
||||||
|
}
|
||||||
|
|
||||||
|
Component{
|
||||||
|
id:com_text
|
||||||
|
TextEdit {
|
||||||
|
text: modelData.text
|
||||||
|
wrapMode: Text.WrapAnywhere
|
||||||
|
readOnly: true
|
||||||
|
textFormat: Text.RichText
|
||||||
|
selectByMouse: true
|
||||||
|
selectByKeyboard: true
|
||||||
|
selectedTextColor: color
|
||||||
|
color:FluColors.Black
|
||||||
|
selectionColor: {
|
||||||
|
if(FluTheme.isDark){
|
||||||
|
return FluTheme.primaryColor.lighter
|
||||||
|
}else{
|
||||||
|
return FluTheme.primaryColor.dark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
width: Math.min(list_message.width-200,600,implicitWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluArea{
|
||||||
|
id:layout_content
|
||||||
|
anchors{
|
||||||
|
top: appbar.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
bottom: layout_bottom.top
|
||||||
|
margins: 10
|
||||||
|
}
|
||||||
|
color: FluTheme.isDark ? Qt.rgba(39/255,39/255,39/255,1) : Qt.rgba(245/255,245/255,245/255,1)
|
||||||
|
|
||||||
|
ListView{
|
||||||
|
id:list_message
|
||||||
|
anchors.fill: parent
|
||||||
|
model:model_message
|
||||||
|
clip: true
|
||||||
|
ScrollBar.vertical: FluScrollBar {}
|
||||||
|
preferredHighlightBegin: 0
|
||||||
|
preferredHighlightEnd: 0
|
||||||
|
highlightMoveDuration: 0
|
||||||
|
header:Item{
|
||||||
|
width: list_message.width
|
||||||
|
height:20
|
||||||
|
}
|
||||||
|
footer:Item{
|
||||||
|
width: list_message.width
|
||||||
|
height:20
|
||||||
|
}
|
||||||
|
delegate: Item{
|
||||||
|
width: ListView.view.width
|
||||||
|
height: childrenRect.height
|
||||||
|
|
||||||
|
FluRectangle{
|
||||||
|
id:item_avatar
|
||||||
|
width: 30
|
||||||
|
height: 30
|
||||||
|
radius:[15,15,15,15]
|
||||||
|
anchors{
|
||||||
|
right: isMy ? parent.right : undefined
|
||||||
|
rightMargin: isMy ? 20 : undefined
|
||||||
|
left: isMy ? undefined : parent.left
|
||||||
|
leftMargin: isMy ? undefined : 20
|
||||||
|
top:parent.top
|
||||||
|
}
|
||||||
|
Image {
|
||||||
|
asynchronous: true
|
||||||
|
anchors.fill: parent
|
||||||
|
sourceSize: Qt.size(100,100)
|
||||||
|
source: isMy ? "qrc:/res/svg/avatar_2.svg" : "qrc:/res/image/logo_openai.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle{
|
||||||
|
id:item_layout_content
|
||||||
|
color: isMy ? "#FF95EC69" : "#FFFFFF"
|
||||||
|
width: item_msg_loader.width+10
|
||||||
|
height: item_msg_loader.height+10
|
||||||
|
radius: 3
|
||||||
|
anchors{
|
||||||
|
top: item_avatar.top
|
||||||
|
right: isMy ? item_avatar.left : undefined
|
||||||
|
rightMargin: isMy ? 10 : undefined
|
||||||
|
left: isMy ? undefined : item_avatar.right
|
||||||
|
leftMargin: isMy ? undefined : 10
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader{
|
||||||
|
id:item_msg_loader
|
||||||
|
property var modelData: model
|
||||||
|
anchors.centerIn: parent
|
||||||
|
sourceComponent: com_text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Item{
|
||||||
|
id:item_layout_bottom
|
||||||
|
width: parent.width
|
||||||
|
anchors.top: item_layout_content.bottom
|
||||||
|
height: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluArea{
|
||||||
|
id:layout_bottom
|
||||||
|
height: 90
|
||||||
|
anchors{
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 10
|
||||||
|
left: parent.left
|
||||||
|
right: parent.right
|
||||||
|
leftMargin: 10
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScrollView{
|
||||||
|
anchors{
|
||||||
|
bottom: parent.bottom
|
||||||
|
left: parent.left
|
||||||
|
right: button_send.left
|
||||||
|
bottomMargin: 10
|
||||||
|
leftMargin: 10
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
height: Math.min(textbox.implicitHeight,64)
|
||||||
|
FluMultiLineTextBox{
|
||||||
|
id:textbox
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FluFilledButton{
|
||||||
|
id:button_send
|
||||||
|
text:controller.isLoading ? timer_loading.loadingText :"发送"
|
||||||
|
anchors{
|
||||||
|
bottom: parent.bottom
|
||||||
|
right: parent.right
|
||||||
|
bottomMargin: 10
|
||||||
|
rightMargin: 10
|
||||||
|
}
|
||||||
|
width: 60
|
||||||
|
disabled: controller.isLoading
|
||||||
|
onClicked:{
|
||||||
|
var text = textbox.text
|
||||||
|
appendMessage(true,text)
|
||||||
|
controller.sendMessage(text)
|
||||||
|
textbox.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer{
|
||||||
|
id:timer_loading
|
||||||
|
property int count : 0
|
||||||
|
property string loadingText : ""
|
||||||
|
interval: 500
|
||||||
|
running: controller.isLoading
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
switch(count%3){
|
||||||
|
case 0:
|
||||||
|
loadingText = "."
|
||||||
|
break
|
||||||
|
case 1:
|
||||||
|
loadingText = ".."
|
||||||
|
break
|
||||||
|
case 2:
|
||||||
|
loadingText = "..."
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
loadingText = ""
|
||||||
|
break
|
||||||
|
}
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function appendMessage(isMy,text){
|
||||||
|
model_message.append({isMy:isMy,text:text})
|
||||||
|
list_message.positionViewAtEnd()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -204,6 +204,35 @@ FluWindow {
|
||||||
items:original_items
|
items:original_items
|
||||||
footerItems:footer_items
|
footerItems:footer_items
|
||||||
|
|
||||||
|
actions:[
|
||||||
|
Image {
|
||||||
|
width: 30
|
||||||
|
height: 30
|
||||||
|
Layout.preferredWidth: 30
|
||||||
|
Layout.preferredHeight: 30
|
||||||
|
sourceSize: Qt.size(60,60)
|
||||||
|
source: "qrc:/res/image/logo_openai.png"
|
||||||
|
Layout.rightMargin: 5
|
||||||
|
MouseArea{
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
FluApp.navigate("/chat")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FluText{
|
||||||
|
text:"夜间模式"
|
||||||
|
fontStyle: FluText.Body
|
||||||
|
},
|
||||||
|
FluToggleSwitch{
|
||||||
|
selected: FluTheme.isDark
|
||||||
|
clickFunc:function(){
|
||||||
|
FluTheme.isDark = !FluTheme.isDark
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
nav_view.setCurrentIndex(1)
|
nav_view.setCurrentIndex(1)
|
||||||
nav_view.push("qrc:/T_Buttons.qml")
|
nav_view.push("qrc:/T_Buttons.qml")
|
||||||
|
|
|
@ -38,5 +38,7 @@
|
||||||
<file>res/image/banner_1.jpg</file>
|
<file>res/image/banner_1.jpg</file>
|
||||||
<file>res/image/banner_2.jpg</file>
|
<file>res/image/banner_2.jpg</file>
|
||||||
<file>res/image/banner_3.jpg</file>
|
<file>res/image/banner_3.jpg</file>
|
||||||
|
<file>res/image/logo_openai.png</file>
|
||||||
|
<file>page/ChatPage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -16,6 +16,8 @@ Item {
|
||||||
|
|
||||||
property bool displaMinimalNav : false
|
property bool displaMinimalNav : false
|
||||||
|
|
||||||
|
property alias actions: layout_actions.data
|
||||||
|
|
||||||
onDisplayModeChanged: {
|
onDisplayModeChanged: {
|
||||||
if(displayMode === FluNavigationView.Minimal){
|
if(displayMode === FluNavigationView.Minimal){
|
||||||
anim_navi.enabled = false
|
anim_navi.enabled = false
|
||||||
|
@ -172,22 +174,13 @@ Item {
|
||||||
|
|
||||||
|
|
||||||
RowLayout{
|
RowLayout{
|
||||||
|
id:layout_actions
|
||||||
anchors{
|
anchors{
|
||||||
right: parent.right
|
right: parent.right
|
||||||
rightMargin: 14
|
rightMargin: 14
|
||||||
verticalCenter: parent.verticalCenter
|
verticalCenter: parent.verticalCenter
|
||||||
}
|
}
|
||||||
spacing: 5
|
spacing: 5
|
||||||
FluText{
|
|
||||||
text:"夜间模式"
|
|
||||||
fontStyle: FluText.Body
|
|
||||||
}
|
|
||||||
FluToggleSwitch{
|
|
||||||
selected: FluTheme.isDark
|
|
||||||
clickFunc:function(){
|
|
||||||
FluTheme.isDark = !FluTheme.isDark
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -377,11 +370,8 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function push(url){
|
function push(url){
|
||||||
nav_swipe.push(url)
|
nav_swipe.push(url)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,6 @@ Item{
|
||||||
id:root
|
id:root
|
||||||
property var radius:[0,0,0,0]
|
property var radius:[0,0,0,0]
|
||||||
property color color : "#FFFFFF"
|
property color color : "#FFFFFF"
|
||||||
property color borderColor:"red"
|
|
||||||
property int borderWidth: 1
|
|
||||||
property bool shadow: true
|
property bool shadow: true
|
||||||
default property alias contentItem: container.data
|
default property alias contentItem: container.data
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue