diff --git a/3rdparty/autogui-cpp/CMakeLists.txt b/3rdparty/autogui-cpp/CMakeLists.txt index e688695..2862366 100644 --- a/3rdparty/autogui-cpp/CMakeLists.txt +++ b/3rdparty/autogui-cpp/CMakeLists.txt @@ -29,15 +29,14 @@ if(APPLE) find_library(APPLICATIONSERVICES_LIBRARY ApplicationServices REQUIRED) target_link_libraries(autogui-cpp PUBLIC ${APPLICATIONSERVICES_LIBRARY}) -# Windows平台 + # Windows平台 elseif(WIN32) # 链接User32.lib (提供SendInput, GetCursorPos等API) target_link_libraries(autogui-cpp PUBLIC User32) -# Linux/Unix平台 + # Linux/Unix平台 elseif(UNIX AND NOT APPLE) # Linux需要X11后端(Wayland尚未支持) - # Linux需要X11后端 find_package(X11 REQUIRED) # 查找Xtst库(XTest扩展) find_library(X11_Xtst_LIB Xtst) @@ -49,15 +48,20 @@ elseif(UNIX AND NOT APPLE) if(NOT X11_Xext_LIB) message(FATAL_ERROR "Xext library not found.") endif() + # 查找XRandR库(Xrandr扩展) + find_library(X11_Xrandr_LIB Xrandr) + if(NOT X11_Xrandr_LIB) + message(FATAL_ERROR "XRandR library not found. Install libxrandr-dev.") + endif() # 链接所有必需的X11库 target_link_libraries(autogui-cpp PUBLIC ${X11_LIBRARIES} ${X11_Xtst_LIB} ${X11_Xext_LIB} + ${X11_Xrandr_LIB} ) target_include_directories(autogui-cpp PUBLIC ${X11_INCLUDE_DIR}) endif() - # 集成方式: add_subdirectory # 在你的项目CMakeLists.txt中: # add_subdirectory(autogui-cpp) diff --git a/3rdparty/autogui-cpp/src/Autogui.cpp b/3rdparty/autogui-cpp/src/Autogui.cpp index 5506bc1..a95fcd9 100644 --- a/3rdparty/autogui-cpp/src/Autogui.cpp +++ b/3rdparty/autogui-cpp/src/Autogui.cpp @@ -10,11 +10,10 @@ #include #include #include - +#include #ifndef M_PI # define M_PI 3.14159265358979323846 #endif - namespace AutoGUI { // 内部辅助函数 @@ -33,6 +32,139 @@ Robot::Point getCurrentPosition() { return Robot::Mouse::GetPosition(); } } // namespace +#if defined(__linux__) +#include +#include +#include + +// 获取所有屏幕信息 +std::vector getAllScreens() { + std::vector screens; + Display* display = XOpenDisplay(nullptr); + if (!display) return screens; + + XRRScreenResources* resources = XRRGetScreenResources(display, DefaultRootWindow(display)); + if (resources) { + for (int i = 0; i < resources->noutput; i++) { + XRROutputInfo* output = XRRGetOutputInfo(display, resources, resources->outputs[i]); + if (output && output->connection == RR_Connected && output->crtc) { + XRRCrtcInfo* crtc = XRRGetCrtcInfo(display, resources, output->crtc); + if (crtc) { + ScreenInfo info; + info.id = i; + info.x = crtc->x; + info.y = crtc->y; + info.width = crtc->width; + info.height = crtc->height; + info.isPrimary = (output->name && strcmp(output->name, "primary") == 0); + // 或者使用 XRRGetOutputPrimary 来判断 + screens.push_back(info); + XRRFreeCrtcInfo(crtc); + } + XRRFreeOutputInfo(output); + } + } + XRRFreeScreenResources(resources); + } + XCloseDisplay(display); + return screens; +} + +// 获取当前鼠标所在的屏幕 +ScreenInfo getCurrentScreen() { + auto screens = getAllScreens(); + if (screens.empty()) return {0, 0, 0, 1920, 1080, true}; + + // 获取当前鼠标位置(虚拟桌面绝对坐标) + Robot::Point mouse = position(); + + // 找到包含该点的屏幕 + for (const auto& screen : screens) { + if (mouse.x >= screen.x && mouse.x < screen.x + screen.width && + mouse.y >= screen.y && mouse.y < screen.y + screen.height) { + return screen; + } + } + + // 默认返回主屏或第一个屏幕 + auto it = std::find_if(screens.begin(), screens.end(), + [](const ScreenInfo& s) { return s.isPrimary; }); + return (it != screens.end()) ? *it : screens[0]; +} + +// 相对于当前屏幕移动 +void moveToOnCurrentScreen(int x, int y, double duration) { + const ScreenInfo current = getCurrentScreen(); + // 转换为虚拟桌面绝对坐标 + int absX = current.x + x; + int absY = current.y + y; + moveTo(absX, absY, duration); +} + +// 获取当前屏幕尺寸 +Robot::Point getScreenSize(int screenId) { + if (screenId == -1) { + ScreenInfo current = getCurrentScreen(); + return {current.width, current.height}; + } + auto screens = getAllScreens(); + for (const auto& s : screens) { + if (s.id == screenId) return {s.width, s.height}; + } + return {1920, 1080}; +} + +#endif +#if defined(_WIN32) +#include + + std::vector getAllScreens() { + std::vector screens; + EnumDisplayMonitors(nullptr, nullptr, + [](HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -> BOOL { + auto* screens = reinterpret_cast*>(dwData); + MONITORINFOEX info; + info.cbSize = sizeof(info); + if (GetMonitorInfo(hMonitor, &info)) { + ScreenInfo si; + si.id = screens->size(); + si.x = info.rcMonitor.left; + si.y = info.rcMonitor.top; + si.width = info.rcMonitor.right - info.rcMonitor.left; + si.height = info.rcMonitor.bottom - info.rcMonitor.top; + si.isPrimary = (info.dwFlags & MONITORINFOF_PRIMARY) != 0; + screens->push_back(si); + } + return TRUE; + }, reinterpret_cast(&screens)); + return screens; +} + +ScreenInfo getCurrentScreen() { + // 获取当前鼠标位置 + POINT pt; + GetCursorPos(&pt); + // 查找包含该点的显示器 + HMONITOR hMonitor = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST); + MONITORINFOEX info; + info.cbSize = sizeof(info); + GetMonitorInfo(hMonitor, &info); + + ScreenInfo si; + si.x = info.rcMonitor.left; + si.y = info.rcMonitor.top; + si.width = info.rcMonitor.right - info.rcMonitor.left; + si.height = info.rcMonitor.bottom - info.rcMonitor.top; + return si; +} + +void moveToOnCurrentScreen(int x, int y, double duration) { + ScreenInfo current = getCurrentScreen(); + moveTo(current.x + x, current.y + y, duration); +} +#endif + + // 实现主要API函数 void moveTo(int x, int y, double duration) { Robot::Point target{x, y}; @@ -269,37 +401,84 @@ void sleep(double seconds) { } } +#if defined(_WIN32) +#include + +#elif defined(__APPLE__) +#include + +#elif defined(__linux__) +#include +#include +#endif + // 平台特定函数实现 Robot::Point size() { // 平台特定实现 #ifdef _WIN32 // Windows实现 -#include int width = GetSystemMetrics(SM_CXSCREEN); int height = GetSystemMetrics(SM_CYSCREEN); return {width, height}; #elif defined(__APPLE__) // macOS实现 -#include CGRect mainDisplayBounds = CGDisplayBounds(CGMainDisplayID()); int width = static_cast(CGRectGetWidth(mainDisplayBounds)); int height = static_cast(CGRectGetHeight(mainDisplayBounds)); return {width, height}; #elif defined(__linux__) -// Linux实现(X11) -#include - Display *display = XOpenDisplay(nullptr); - if (display) { - const Screen *screen = DefaultScreenOfDisplay(display); - const int width = WidthOfScreen(screen); - const int height = HeightOfScreen(screen); - XCloseDisplay(display); - return {width, height}; + if (!display) return {1920, 1080}; + + int width = 0, height = 0; + + // 使用 XRRGetScreenResources (兼容 XRandR 1.2+,比 Current 版本兼容性更好) + XRRScreenResources *resources = XRRGetScreenResources(display, DefaultRootWindow(display)); + if (resources) { + RROutput target_output = None; + + // 条件编译:只在 XRandR 1.3+ 时使用 Primary Output 功能 +#if defined(RANDR_MAJOR) && defined(RANDR_MINOR) +#if RANDR_MAJOR > 1 || (RANDR_MAJOR == 1 && RANDR_MINOR >= 3) + target_output = XRRGetOutputPrimary(display, DefaultRootWindow(display)); +#endif +#endif + + // 如果没有获取到主显示器(或版本太低),使用第一个已连接的显示器 + if (target_output == None) { + for (int i = 0; i < resources->noutput; i++) { + XRROutputInfo *info = XRRGetOutputInfo(display, resources, resources->outputs[i]); + if (info) { + if (info->connection == 0) { + target_output = resources->outputs[i]; + XRRFreeOutputInfo(info); + break; + } + XRRFreeOutputInfo(info); + } + } + } + + // 获取选中显示器的分辨率 + if (target_output != None) { + XRROutputInfo *output_info = XRRGetOutputInfo(display, resources, target_output); + if (output_info && output_info->crtc) { + XRRCrtcInfo *crtc_info = XRRGetCrtcInfo(display, resources, output_info->crtc); + if (crtc_info) { + width = static_cast(crtc_info->width); + height = static_cast(crtc_info->height); + XRRFreeCrtcInfo(crtc_info); + } + XRRFreeOutputInfo(output_info); + } + } + XRRFreeScreenResources(resources); } - return {1920, 1080}; // 默认值 + + XCloseDisplay(display); + return (width > 0 && height > 0) ? Robot::Point{width, height} : Robot::Point{1920, 1080}; #else // 其他平台 diff --git a/3rdparty/autogui-cpp/src/Autogui.h b/3rdparty/autogui-cpp/src/Autogui.h index bbd8b35..e43017b 100644 --- a/3rdparty/autogui-cpp/src/Autogui.h +++ b/3rdparty/autogui-cpp/src/Autogui.h @@ -125,6 +125,27 @@ inline bool isSpecialKey(const std::string& key) { return std::find(specialKeys.begin(), specialKeys.end(), lower) != specialKeys.end(); } +// 解决多屏幕移动鼠标问题 +struct ScreenInfo { + int id; // 屏幕ID + int x, y; // 相对于虚拟桌面原点的偏移 + int width, height;// 屏幕尺寸 + bool isPrimary; // 是否主屏 +}; + +// 获取所有屏幕信息 +std::vector getAllScreens(); + +// 获取当前鼠标所在的屏幕 +ScreenInfo getCurrentScreen(); + +// 相对于当前屏幕移动鼠标(以当前屏幕左上角为原点) +void moveToOnCurrentScreen(int x, int y, double duration = 0.0); + +// 获取指定屏幕的尺寸(替代原来的size(),支持多屏) +Robot::Point getScreenSize(int screenId = -1); // -1表示当前屏幕 + + // 主要 API 函数 /** * @brief 移动鼠标到指定位置 diff --git a/README.md b/README.md index f55ddf8..2da6032 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ make ``` 注意:本项目只是Yosuga的客户端部分,完整的还包括服务端 -- 服务端项目地址见: +- 服务端项目地址见:https://github.com/Misakityan/Yosuga_server 当前支持平台(已测试过的): - Windows: Windows 10 diff --git a/src/Core/Inc/AppCore.h b/src/Core/Inc/AppCore.h index cdaa186..a6e763b 100644 --- a/src/Core/Inc/AppCore.h +++ b/src/Core/Inc/AppCore.h @@ -27,7 +27,7 @@ private: static QMutex m_mutex; private slots: // 业务接收槽函数 - + void onRecordingFinished_Byte(const QByteArray &wavData); public: // 单例访问点 static AppCore *getInstance(); @@ -36,4 +36,7 @@ public: ~AppCore() override; +public: + // 单次对话 + void SingleExchange(); }; \ No newline at end of file diff --git a/src/Core/Src/AppCore.cpp b/src/Core/Src/AppCore.cpp index fe90184..6c74305 100644 --- a/src/Core/Src/AppCore.cpp +++ b/src/Core/Src/AppCore.cpp @@ -9,6 +9,9 @@ #include "AutoAgentHandle.h" #include "ScreenShotReqDataHandle.h" +#include "AudioInput.h" +#include "NetWorkDO.h" +#include "websocketmanager.h" // 初始化静态成员 QScopedPointer AppCore::m_instance; QMutex AppCore::m_mutex; @@ -40,6 +43,15 @@ AppCore::AppCore(QObject *parent) : QObject(parent) AudioDataHandle::getInstance(); AutoAgentHandle::getInstance(); ScreenShotReqDataHandle::getInstance(); + // 注入发送接口 + NetworkDO::getInstance()->registerSender([](const QString& type, const QJsonObject& data){ + WebSocketClient::getInstance()->sendJson(type, data); + }); + // TODO Test + AudioInput::getInstance()->setAudioPath(QDir::currentPath(), "/temp.wav"); + // 连接必要的信号 + connect(AudioInput::getInstance(), &AudioInput::recordingFinished_Byte, + this, &AppCore::onRecordingFinished_Byte); // 录音完成信号 } AppCore::~AppCore() @@ -52,3 +64,16 @@ AppCore::~AppCore() qDebug() << "AppCore destroyed"; } + +void AppCore::SingleExchange() { + // 开始录音,录音结束后会触发录音完成信号 + AudioInput::getInstance()->startAutoStopAudio(AudioInput::getInstance()->getSilenceThreshold(), 800); +} + +void AppCore::onRecordingFinished_Byte(const QByteArray &wavData) { + // 将录音数据发送给服务端 + AudioDataTransferObject packet; + packet.setData("isStream", false).setData("data", wavData.toBase64().data()); + NetworkDO::getInstance()->sendPacket(packet); +} + diff --git a/src/DAO/Inc/ScreenShotDataTransferObject.h b/src/DAO/Inc/ScreenShotDataTransferObject.h index 875395b..b3a97e8 100644 --- a/src/DAO/Inc/ScreenShotDataTransferObject.h +++ b/src/DAO/Inc/ScreenShotDataTransferObject.h @@ -28,7 +28,7 @@ public: // 静态工厂方法 static ScreenShotDataTransferObject fromJson(const QJsonObject& json); - [[nodiscard]] QString type() const override { return "screenshot_req"; } + [[nodiscard]] QString type() const override { return "screenshot_data"; } // 序列化 [[nodiscard]] QJsonObject toJson() const override; // 通过多态即可统一调用方式 diff --git a/src/DAO/Src/NetWorkDO.cpp b/src/DAO/Src/NetWorkDO.cpp index 31274f7..526f695 100644 --- a/src/DAO/Src/NetWorkDO.cpp +++ b/src/DAO/Src/NetWorkDO.cpp @@ -72,7 +72,7 @@ void NetworkDO::onDataReceived(const QString& type, const QJsonObject& data) else if (type == "auto_agent") { emit autoAgentPacketReceived(AutoAgentDataObject::fromJson(data)); } - else if (type == "screenshot_req") { + else if (type == "screenshot_data") { emit screenShotPacketReceived(ScreenShotDataTransferObject::fromJson(data)); } else { diff --git a/src/DAO/Src/ScreenShotDataTransferObject.cpp b/src/DAO/Src/ScreenShotDataTransferObject.cpp index 07059c0..12f03e6 100644 --- a/src/DAO/Src/ScreenShotDataTransferObject.cpp +++ b/src/DAO/Src/ScreenShotDataTransferObject.cpp @@ -34,7 +34,7 @@ ScreenShotDataTransferObject ScreenShotDataTransferObject::fromJson(const QJsonO // 调用构造函数创建对象 return ScreenShotDataTransferObject(owner, isSuccess, realtimeScreenShot, - width, height, describeInfo); + width, height, describeInfo, LLMResponse); } // 序列化为 JSON diff --git a/src/Handle/AudioHandle/Inc/AudioInput.h b/src/Handle/AudioHandle/Inc/AudioInput.h index f8ec6a1..b740328 100644 --- a/src/Handle/AudioHandle/Inc/AudioInput.h +++ b/src/Handle/AudioHandle/Inc/AudioInput.h @@ -148,5 +148,7 @@ private: qreal m_silenceThreshold = 1200; /// 静音阈值 int m_silenceDuration = 1500; /// 静音持续时间 qreal m_smoothRms = 0.0; /// 平滑RMS值(用于防止低频杂波突然打断静音检测) + + bool m_hasVoiceDetected = false; /// 是否已检测到人声 }; diff --git a/src/Handle/AudioHandle/Src/AudioInput.cpp b/src/Handle/AudioHandle/Src/AudioInput.cpp index b257208..b4ee43e 100644 --- a/src/Handle/AudioHandle/Src/AudioInput.cpp +++ b/src/Handle/AudioHandle/Src/AudioInput.cpp @@ -148,7 +148,7 @@ void AudioInput::onReadyRead() const qreal currentRms = calculateRMS(data); m_rmsValue = currentRms; // 计算平滑RMS (用于防止低频杂波突然打断静音检测) - constexpr qreal alpha = 0.3; // 70% 历史权重, 30% 当前权重 + constexpr qreal alpha = 0.15; // 85% 历史权重, 15% 当前权重 if (qFuzzyIsNull(m_smoothRms)) { // 如果是第一帧数据,直接赋值,避免从0开始慢慢爬升 m_smoothRms = currentRms; @@ -163,13 +163,30 @@ void AudioInput::onReadyRead() qDebug() << "Raw:" << currentRms << " Smooth:" << m_smoothRms; if (m_smoothRms < m_silenceThreshold) { - // 静音状态 - if (!m_silenceTimer->isActive()) { - m_silenceTimer->start(m_silenceDuration); + // [当前是静音] + + // 如果之前已经检测到过人声(说明是话说完了,或者是句间停顿) + if (m_hasVoiceDetected) { + // 启动/保持“短时”静音检测 (由 AppCore 传入,例如 500ms 或 1500ms) + if (!m_silenceTimer->isActive()) { + m_silenceTimer->start(m_silenceDuration); + } + // 如果 Timer 正在运行,就让它继续倒计时,超时会自动触发 stopAudio + } + else { + // [还没有检测到过人声] (起始静音) + // 这里不需要做额外操作,startAutoStopAudio 里设置的 5000ms 长定时器在跑 + // 允许用户深呼吸或准备 } } else { - // 有声音,重置定时器 + // [当前有声音] + m_hasVoiceDetected = true; // 标记:已经有人说话了 + + // 重置静音定时器 + // 只要有人说话,就不断重置定时器,防止断录 m_silenceTimer->stop(); + // 这里可以预设启动,也可以不启动,只要有声音就会一直 stop + // 为了安全,设为 silenceDuration m_silenceTimer->start(m_silenceDuration); } } @@ -246,11 +263,20 @@ void AudioInput::startAutoStopAudio(const qreal silenceThreshold, const int sile m_silenceThreshold = silenceThreshold; m_silenceDuration = silenceDuration; + // 重置状态 + m_hasVoiceDetected = false; + m_smoothRms = 0.0; startAudio(); + // 延迟200ms是为了避开硬件启动时的爆音,但不需要立即启动短时倒计时 // 延迟启动静音检测,给一点缓冲时间 QTimer::singleShot(200, this, [this](){ - m_silenceTimer->start(m_silenceDuration); + if(isAutoRecording) { // 确保还在录音状态 + // 如果还没检测到声音,给5秒的等待时间;如果检测到了,逻辑由onReadyRead接管 + if(!m_hasVoiceDetected) { + m_silenceTimer->start(5000); // 5秒没声音就停止 + } + } }); } @@ -285,8 +311,12 @@ void AudioInput::thresholdTimeout() // 防止浮点误差导致负数 if (variance < 0) variance = 0; const double stdDev = std::sqrt(variance); + + // 增加一个固定的偏移量 offset + // 确保即使环境有轻微波动,也不会触发录音 + constexpr double offset = 80.0; // 阈值 = 均值 + 2 * 标准差 - const double bestThreshold = mean + 3 * stdDev; + const double bestThreshold = mean + (3 * stdDev) + offset; m_silenceThreshold = std::max(bestThreshold, 150.0); m_silenceThreshold = std::min(m_silenceThreshold, 30000.0); qDebug() << "Auto Threshold Calc -> Mean:" << mean diff --git a/src/Handle/DataObjectHandle/Src/AutoAgentHandle.cpp b/src/Handle/DataObjectHandle/Src/AutoAgentHandle.cpp index ea2b1a7..b843a04 100644 --- a/src/Handle/DataObjectHandle/Src/AutoAgentHandle.cpp +++ b/src/Handle/DataObjectHandle/Src/AutoAgentHandle.cpp @@ -42,24 +42,40 @@ AutoAgentHandle::~AutoAgentHandle() } void AutoAgentHandle::onAutoAgentPacketReceived(const AutoAgentDataObject &packet) { + qDebug() << "Received AutoAgent packet: " << packet.getAction(); if (packet.getAction() == "click") { // 单击 qDebug() << "Click: " << packet.getX1() << ", " << packet.getY1(); - AutoGUI::moveTo(packet.getX1(), packet.getY1(), 0.6); + AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.6); AutoGUI::click(packet.getX1(), packet.getY1()); } if (packet.getAction() == "left_double") { // 双击 qDebug() << "Double click: " << packet.getX1() << ", " << packet.getY1(); - AutoGUI::moveTo(packet.getX1(), packet.getY1(), 0.6); + AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.6); AutoGUI::leftDouble(packet.getX1(), packet.getY1()); } if (packet.getAction() == "right_single") { // 右键单击 qDebug() << "Right click: " << packet.getX1() << ", " << packet.getY1(); - AutoGUI::moveTo(packet.getX1(), packet.getY1(), 0.6); + AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.6); AutoGUI::rightSingle(packet.getX1(), packet.getY1()); } if (packet.getAction() == "drag") { // 拖拽 qDebug() << "Drag: " << packet.getX1() << ", " << packet.getY1() << " to " << packet.getX2() << ", " << packet.getY2(); - AutoGUI::drag(packet.getX1(), packet.getY1(), packet.getX2(), packet.getY2(), 0.8); + AutoGUI::drag(packet.getX1(), packet.getY1(), packet.getX2(), packet.getY2(), 1.2); } // TODO: 快捷键,输入文本,滚动待实现 -} + if (packet.getAction() == "type") { // 输入文本 + qDebug() << "Type: " << packet.getContent(); + AutoGUI::type(packet.getContent().toStdString(), 0.08); + } + if (packet.getAction() == "scroll") { // 滚动 + qDebug() << "Scroll: " << packet.getX1() << ", " << packet.getY1() << packet.getDirection(); + if (packet.getDirection() == "up") { + AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.1); + AutoGUI::scroll(40, 1); + } + if (packet.getDirection() == "down") { + AutoGUI::moveToOnCurrentScreen(packet.getX1(), packet.getY1(), 0.1); + AutoGUI::scroll(40, -1); + } + } +} \ No newline at end of file diff --git a/src/Handle/DataObjectHandle/Src/ScreenShotReqDataHandle.cpp b/src/Handle/DataObjectHandle/Src/ScreenShotReqDataHandle.cpp index 856e40d..7800a0b 100644 --- a/src/Handle/DataObjectHandle/Src/ScreenShotReqDataHandle.cpp +++ b/src/Handle/DataObjectHandle/Src/ScreenShotReqDataHandle.cpp @@ -42,6 +42,7 @@ ScreenShotReqDataHandle::ScreenShotReqDataHandle(QObject *parent) : QObject(pare const QString sysText = QString("System: %1 OS Version: %2 Display Server: %3") .arg(sysInfo.osType, sysInfo.osVersion, sysInfo.displayServer); this->m_systemInfo = sysText; + qDebug() << "当前平台信息为: " << sysText; } ScreenShotReqDataHandle::~ScreenShotReqDataHandle() @@ -55,12 +56,14 @@ void ScreenShotReqDataHandle::onScreenShotPacketReceived(const ScreenShotDataTra const ScreenHelper::ScreenshotResult result = ScreenHelper::captureFocusedScreen(); // 获取当前屏幕截图 if (!result.success) { // 如果截图失败 // TODO: 考虑失败时候构造一个错误DTO给服务端 + qDebug() << "截图失败: " << result.errorMsg; return; } ScreenShotDataTransferObject reback; // 构造返回的DTO reback.setData("isSuccess", true).setData("RealTimeScreenShot", result.base64Data) .setData("Width", result.width).setData("Height", result.height) - .setData("DescribeInfo", this->m_systemInfo); + .setData("DescribeInfo", this->m_systemInfo).setData("LLMResponse", packet.LLMResponse()); // 发送DTO NetworkDO::getInstance()->sendPacket(reback); + qDebug() << "ScreenShot packet sent to:" << packet.owner(); } diff --git a/src/UI/Menu/Inc/menu.h b/src/UI/Menu/Inc/menu.h index 61d44d8..f90616e 100644 --- a/src/UI/Menu/Inc/menu.h +++ b/src/UI/Menu/Inc/menu.h @@ -7,23 +7,22 @@ * 基于Ela UI的菜单控件 */ -#include "ElaMenu.h" +#include #include #include #include #include // 智能指针 - #include "Setting.h" #include "networkmanager.h" #include "socketmanager.h" -class Menu : public ElaMenu +class Menu final : public ElaMenu { Q_OBJECT public: explicit Menu(QWidget *parent = nullptr); - ~Menu(); + ~Menu() override; void showMenu(const QPoint &pos); signals: @@ -33,10 +32,11 @@ signals: private: void createMenu(); - QAction *toggleThe; /// 切换主题(全局) - QAction *startExchangeAction; /// 开启对话 - QAction *settingsAction; /// 设置 - QAction *closeAction; /// 关闭 + QAction *toggleThe; /// 切换主题(全局) + QAction *startSingleExchangeAction; /// 开启单次对话 + QAction *startContinueExchangeAction; /// 开启连续对话 + QAction *settingsAction; /// 设置 + QAction *closeAction; /// 关闭 QScopedPointer settingWindow; // 使用智能指针管理 Setting 窗口 diff --git a/src/UI/Menu/Src/menu.cpp b/src/UI/Menu/Src/menu.cpp index d3f62c1..aab21c6 100644 --- a/src/UI/Menu/Src/menu.cpp +++ b/src/UI/Menu/Src/menu.cpp @@ -5,29 +5,29 @@ #include #include "TextRenderer.h" -// #include "AudioInput.h" -// #include "AudioOutput.h" +#include "AppCore.h" Menu::Menu(QWidget *parent) : ElaMenu(parent) { // 设置默认主题 eTheme->setThemeMode(ElaThemeType::Dark); - createMenu(); } -Menu::~Menu() -{ - +Menu::~Menu() { + AppCore::destroy(); // 显式销毁 AppCore } void Menu::createMenu() { toggleThe = addAction("切换主题"); + // 单次对话功能按钮 + startSingleExchangeAction = addAction("单次对话(测试)"); + // 连续对话功能按钮 - startExchangeAction = addAction("连续对话(测试)"); + startContinueExchangeAction = addAction("连续对话(测试)"); // 添加设置按钮 settingsAction = addAction("设置"); @@ -42,10 +42,14 @@ void Menu::createMenu() toggleTheme(); }); - // TODO 连续对话功能,需要优化实现 - connect(startExchangeAction, &QAction::triggered, this, [this]() { - // startExchange(); - qDebug() << "Start Exchange triggered"; + // 单次对话功能 + connect(startSingleExchangeAction, &QAction::triggered, this, []() { + qDebug() << "Start SingleExchange triggered"; + AppCore::getInstance()->SingleExchange(); + }); + // 连续对话功能 TODO: 待开发 + connect(startContinueExchangeAction, &QAction::triggered, this, []() { + qDebug() << "Start ContinueExchange triggered"; });