diff --git a/src/app/application.cpp b/src/app/application.cpp index 162d6d7..4f8f1a4 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -20,8 +20,9 @@ Application::Application(int& argc, char** argv) sttEngine_ = new SenseVoiceEngine(this); connect(sttEngine_, &SenseVoiceEngine::modelLoaded, this, [this](const QString& path) { modelLoaded_ = true; + modelPath_ = path; LOG_INFO(kTag, QString("全局模型已加载: %1").arg(path)); - emit modelLoaded(); + emit modelLoaded(path); }); connect(sttEngine_, &SenseVoiceEngine::modelLoadError, this, [this](const QString&, const QString& err) { modelLoaded_ = false; @@ -54,9 +55,12 @@ void Application::loadGlobalModel() { QString modelPath = configManager_->get("stt.model_path").toString(); if (modelPath.isEmpty()) { LOG_WARNING(kTag, "模型路径为空,请在配置中设置后重启"); + emit modelLoadError("模型路径未设置"); return; } + modelPath_ = modelPath; + QString tokensPath = configManager_->get("stt.tokens_path").toString(); QString device = configManager_->get("stt.device").toString(); int numThreads = configManager_->get("stt.num_threads").toInt(); @@ -65,6 +69,7 @@ void Application::loadGlobalModel() { sttEngine_->setDebugSaveAudio(debugSave); LOG_INFO(kTag, QString("正在异步加载全局模型: %1").arg(modelPath)); + emit modelLoading(modelPath); sttEngine_->loadModelAsync(modelPath, tokensPath, device, numThreads); } diff --git a/src/app/application.h b/src/app/application.h index ad4b410..a8ac9ae 100644 --- a/src/app/application.h +++ b/src/app/application.h @@ -26,12 +26,18 @@ public: /** @brief 获取全局 STT 引擎(共享实例) */ SenseVoiceEngine* sttEngine() const; + /** @brief 获取当前模型路径 */ + QString modelPath() const { return modelPath_; } + /** @brief 获取全局 STT 引擎加载状态 */ bool isModelLoaded() const; signals: - /** @brief 模型加载完成 */ - void modelLoaded(); + /** @brief 模型加载中(带路径) */ + void modelLoading(const QString& modelPath); + + /** @brief 模型加载完成(带路径) */ + void modelLoaded(const QString& modelPath); /** @brief 模型加载失败 */ void modelLoadError(const QString& error); @@ -41,6 +47,7 @@ private: std::unique_ptr configManager_; SenseVoiceEngine* sttEngine_ = nullptr; + QString modelPath_; bool modelLoaded_ = false; }; diff --git a/src/ui/main_window.cpp b/src/ui/main_window.cpp index 1d0baf9..fadc3b9 100644 --- a/src/ui/main_window.cpp +++ b/src/ui/main_window.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include static const char* const kTag = "MainWindow"; @@ -28,6 +31,7 @@ MainWindow::MainWindow(ConfigManager* configManager, setupUI(sttEngine); setupMenuBar(); + setupStatusBar(sttEngine); loadStyleSheet(); // 初始化语音输入服务(共享全局引擎) @@ -71,6 +75,29 @@ void MainWindow::setupUI(SenseVoiceEngine* sttEngine) { setCentralWidget(tabWidget_); } +void MainWindow::setupStatusBar(SenseVoiceEngine* sttEngine) { + sttEngine_ = sttEngine; + + auto* bar = statusBar(); + bar->setSizeGripEnabled(false); + + modelStatusLabel_ = new QLabel(this); + modelStatusLabel_->setStyleSheet( + "QLabel { padding: 2px 8px; font-size: 13px; }"); + bar->addPermanentWidget(modelStatusLabel_); + + // 连接全局引擎信号 + connect(sttEngine_, &SenseVoiceEngine::modelLoaded, + this, &MainWindow::updateModelStatus); + connect(sttEngine_, &SenseVoiceEngine::modelLoadError, + this, &MainWindow::updateModelStatus); + connect(sttEngine_, &SenseVoiceEngine::modelUnloaded, + this, &MainWindow::updateModelStatus); + + // 初始化显示 + updateModelStatus(); +} + void MainWindow::setupMenuBar() { // 文件菜单 auto* fileMenu = menuBar()->addMenu("文件"); @@ -112,9 +139,35 @@ void MainWindow::closeEvent(QCloseEvent* event) { QMainWindow::closeEvent(event); } +void MainWindow::updateModelStatus() { + if (!sttEngine_ || !modelStatusLabel_) return; + + QString modelPath = configManager_->get("stt.model_path").toString(); + QString modelName = modelPath.isEmpty() ? QString() : QFileInfo(modelPath).fileName(); + + if (sttEngine_->isLoaded()) { + QString statusText = modelName.isEmpty() ? "模型已就绪" : QString("模型已就绪: %1").arg(modelName); + modelStatusLabel_->setText(statusText); + modelStatusLabel_->setStyleSheet( + "QLabel { padding: 2px 8px; font-size: 13px; color: #27ae60; font-weight: bold; }"); + } else if (modelPath.isEmpty()) { + modelStatusLabel_->setText("⚠️ 模型路径未设置"); + modelStatusLabel_->setStyleSheet( + "QLabel { padding: 2px 8px; font-size: 13px; color: #e74c3c; }"); + } else { + modelStatusLabel_->setText( + QString("⚠️ 模型加载失败: %1").arg(modelName)); + modelStatusLabel_->setStyleSheet( + "QLabel { padding: 2px 8px; font-size: 13px; color: #e67e22; }"); + } +} + void MainWindow::onVoiceInputConfigChanged() { if (!voiceInputService_) return; + // 更新模型状态显示 + updateModelStatus(); + bool enabled = configManager_->get("stt.capslock_voice_enabled").toBool(); if (enabled && !voiceInputService_->isRunning()) { voiceInputService_->start(); diff --git a/src/ui/main_window.h b/src/ui/main_window.h index f5d6b70..e10e63d 100644 --- a/src/ui/main_window.h +++ b/src/ui/main_window.h @@ -4,6 +4,8 @@ #include #include +class QLabel; + namespace impress { class ConfigManager; @@ -17,6 +19,7 @@ class VoiceInputService; * @brief 主窗口 * * 使用 Tab 页导航管理三个功能页面,共享全局 STT 引擎。 + * 状态栏显示模型名称和加载状态。 */ class MainWindow : public QMainWindow { Q_OBJECT @@ -32,15 +35,19 @@ protected: private: void setupUI(SenseVoiceEngine* sttEngine); void setupMenuBar(); + void setupStatusBar(SenseVoiceEngine* sttEngine); void loadStyleSheet(); void onVoiceInputConfigChanged(); + void updateModelStatus(); ConfigManager* configManager_; + SenseVoiceEngine* sttEngine_; VoiceInputService* voiceInputService_; STTTestPage* sttPage_; FileTranscribePage* transcribePage_; SettingsPage* settingsPage_; QTabWidget* tabWidget_; + QLabel* modelStatusLabel_; }; } // namespace impress