// 图形界面版:悬浮字幕窗。 #include #include #include #include #include #include #include "appcli.h" #include "captionwindow.h" #include "livetranscriber.h" #include "qwen3asr.h" #include "streamingasr.h" #include "transcript.h" namespace { int runGui(lc::Qwen3Asr & asr, const lc::StreamOptions & sop, const QString & screenshotPath, int screenshotDelaySec, int opacityPercent) { lc::CaptionWindow window; window.setPanelOpacity(opacityPercent); lc::LiveTranscriber live(asr); QObject::connect(&live, &lc::LiveTranscriber::caption, &window, [&window](const QString & committed, const QString & tail, const QString & lang) { window.setLanguage( QString::fromStdString(lc::languageDisplayName(lang.toStdString()))); window.setCaption(committed, tail); }); QObject::connect(&live, &lc::LiveTranscriber::failed, &window, [](const QString & m) { qWarning("采集失败: %s", qUtf8Printable(m)); }); QObject::connect(&window, &lc::CaptionWindow::closed, qApp, &QCoreApplication::quit); QObject::connect(&window, &lc::CaptionWindow::languageClicked, &window, [] { qInfo() << "语言标签被点击(设置界面将在 Phase 4 接入)"; }); QString err; if (!live.start(sop, &err)) { qCritical("启动采集失败: %s", qUtf8Printable(err)); return 1; } qInfo("正在采集: %s", qUtf8Printable(live.deviceName())); window.show(); if (!screenshotPath.isEmpty()) { // 自截图:用于验证渲染(不依赖外部截图工具) QTimer::singleShot(screenshotDelaySec * 1000, &window, [&window, screenshotPath] { const bool ok1 = window.grab().save(screenshotPath); window.setCollapsed(false); QString expandedPath = screenshotPath; expandedPath.replace(QStringLiteral(".png"), QStringLiteral("-expanded.png")); const bool ok2 = window.grab().save(expandedPath); std::fprintf(stderr, "截图: 折叠态 %s / 展开态 %s\n", ok1 ? "成功" : "失败", ok2 ? "成功" : "失败"); QCoreApplication::quit(); }); } return QCoreApplication::exec(); } } // namespace int main(int argc, char ** argv) { // 说明意图:DPI 取整策略必须在 QGuiApplication 之前定。 // (Deepin 的会话环境已经设了 QT_SCALE_FACTOR_ROUNDING_POLICY, // 所以本机仍会看到 Qt 的那条提醒,那不是本程序的问题。) QGuiApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication app(argc, argv); QCoreApplication::setApplicationName(QStringLiteral("live-caption")); QCommandLineParser parser; parser.setApplicationDescription(QStringLiteral("实时字幕(悬浮窗)")); parser.addHelpOption(); lc::app::addModelOptions(parser); lc::app::addStreamOptions(parser); parser.addOption(QCommandLineOption("opacity", "字幕背景不透明度(20-100,百分比)", "n", "84")); parser.addOption(QCommandLineOption("screenshot", "启动后自动截图并退出(验证渲染用)", "path")); parser.addOption(QCommandLineOption("screenshot-delay", "截图前等待秒数", "sec", "6")); parser.process(app); const lc::AsrOptions asrOpt = lc::app::asrOptions(parser); const lc::StreamOptions sop = lc::app::streamOptions(parser); lc::Qwen3Asr asr; if (!lc::app::loadModel(asr, asrOpt)) return 1; return runGui(asr, sop, parser.value("screenshot"), parser.value("screenshot-delay").toInt(), parser.value("opacity").toInt()); }