fix: 彻底修复 Windows 托盘图标不显示 + 添加版本编译时间日志

Windows 托盘修复:
- 设置 setQuitOnLastWindowClosed(false),关闭主窗口时不退出应用
- 关闭主窗口改为隐藏 (event->ignore()),托盘继续运行
- 托盘菜单"退出"调用 qApp->quit() 真正退出
- 图标简化为 16x16 QImage ARGB32 + 纯色圆,确保 Windows 兼容
- 先设置图标再 trayIcon_->show()
- 添加托盘气泡通知

日志增强:
- 启动日志显示版本号 (v0.1.0)
- 打印编译时间 (__DATE__ __TIME__),方便确认二进制是否更新

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
impressionyang 2026-06-11 15:39:00 +08:00
parent dc2f8e5c4f
commit 6bf22041f8
3 changed files with 40 additions and 55 deletions

View File

@ -11,7 +11,8 @@ namespace impress {
Application::Application(int& argc, char** argv)
: QApplication(argc, argv)
{
LOG_INFO(kTag, "Impress Voice Input 启动");
LOG_INFO(kTag, QString("Impress Voice Input v%1 启动").arg(applicationVersion()));
LOG_INFO(kTag, QString("编译时间: %1 %2").arg(__DATE__).arg(__TIME__));
configManager_ = std::make_unique<ConfigManager>(this);
configManager_->loadDefaults();

View File

@ -18,6 +18,8 @@
#include <QSystemTrayIcon>
#include <QPainter>
#include <QIcon>
#include <QApplication>
#include <QCloseEvent>
static const char* const kTag = "MainWindow";
@ -109,6 +111,9 @@ void MainWindow::setupTrayIcon() {
return;
}
// 防止关闭主窗口时应用退出,保持托盘图标运行
qApp->setQuitOnLastWindowClosed(false);
trayMenu_ = new QMenu(this);
auto* showAction = trayMenu_->addAction("显示主窗口");
connect(showAction, &QAction::triggered, this, [this]() {
@ -118,13 +123,19 @@ void MainWindow::setupTrayIcon() {
});
trayMenu_->addSeparator();
auto* exitAction = trayMenu_->addAction("退出");
connect(exitAction, &QAction::triggered, this, &MainWindow::close);
connect(exitAction, &QAction::triggered, this, [this]() {
if (voiceInputService_) {
voiceInputService_->stop();
}
qApp->quit();
});
trayIcon_ = new QSystemTrayIcon(this);
trayIcon_->setContextMenu(trayMenu_);
// 初始状态
// 先设置图标,再显示托盘
updateTrayIcon("语音输入就绪");
trayIcon_->show();
// 双击托盘显示窗口
connect(trayIcon_, &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) {
@ -135,8 +146,6 @@ void MainWindow::setupTrayIcon() {
}
});
trayIcon_->show();
// 发送通知气泡,让用户注意到托盘图标
trayIcon_->showMessage(
"Impress Voice Input",
@ -145,37 +154,31 @@ void MainWindow::setupTrayIcon() {
3000
);
LOG_INFO(kTag, QString("系统托盘图标已创建 (可用: %1)").arg(QSystemTrayIcon::isSystemTrayAvailable()));
LOG_INFO(kTag, QString("系统托盘图标已创建 (可用: %1, 已设置 quitOnLastWindowClosed=false)")
.arg(QSystemTrayIcon::isSystemTrayAvailable()));
}
void MainWindow::updateTrayIcon(const QString& status) {
if (!trayIcon_) return;
QColor color;
QString symbol;
// 根据状态文字匹配图标
// 根据状态文字匹配图标颜色
if (status.contains("正在录音")) {
color = QColor("#e74c3c"); // 红色 - 录音中
symbol = "";
} else if (status.contains("正在识别")) {
color = QColor("#f39c12"); // 橙色 - 识别中
symbol = "";
} else if (status.contains("等待长按") || status.contains("PreRecording")) {
color = QColor("#f1c40f"); // 黄色 - 预录音
symbol = "";
} else if (status.contains("已启动") || status.contains("就绪")) {
color = QColor("#27ae60"); // 绿色 - 就绪
symbol = "";
} else if (status.contains("已关闭") || status.contains("停止")) {
color = QColor("#95a5a6"); // 灰色 - 停止
symbol = "";
} else {
color = QColor("#3498db"); // 蓝色 - 其他
symbol = "";
}
QPixmap pm = createTrayIcon(color, symbol);
QPixmap pm = createTrayIcon(color);
QIcon icon(pm);
if (icon.isNull()) {
LOG_ERROR(kTag, "托盘图标创建失败");
@ -184,51 +187,24 @@ void MainWindow::updateTrayIcon(const QString& status) {
trayIcon_->setIcon(icon);
trayIcon_->setToolTip(QString("Impress Voice Input - %1").arg(status));
// 确保托盘图标可见
if (!trayIcon_->isVisible()) {
trayIcon_->show();
}
}
QPixmap MainWindow::createTrayIcon(const QColor& color, const QString& symbol) {
const int size = 32;
QPixmap pixmap(size, size);
// 使用不透明背景Windows 托盘对透明图标支持有限
pixmap.fill(QColor(40, 40, 45));
QPixmap MainWindow::createTrayIcon(const QColor& color) {
// 使用 16x16 标准托盘图标尺寸
const int size = 16;
QImage image(size, size, QImage::Format_ARGB32);
image.fill(Qt::transparent);
QPainter painter(&pixmap);
QPainter painter(&image);
painter.setRenderHint(QPainter::Antialiasing);
//
// 绘制实心彩色
painter.setBrush(color);
painter.setPen(Qt::NoPen);
int margin = 4;
int margin = 1;
painter.drawEllipse(margin, margin, size - 2 * margin, size - 2 * margin);
// 内部符号
if (symbol == "") {
// 录音中:内部白色实心圆
painter.setBrush(QColor("#ffffff"));
int innerSize = 8;
int offset = (size - innerSize) / 2;
painter.drawEllipse(offset, offset, innerSize, innerSize);
} else if (symbol == "") {
// 识别中:白色环形
painter.setPen(QPen(QColor("#ffffff"), 3));
painter.setBrush(Qt::NoBrush);
int innerSize = 12;
int offset = (size - innerSize) / 2;
painter.drawEllipse(offset, offset, innerSize, innerSize);
} else {
// 空心:小白点
painter.setBrush(QColor("#ffffff"));
int dotSize = 6;
int offset = (size - dotSize) / 2;
painter.drawEllipse(offset, offset, dotSize, dotSize);
}
return pixmap;
return QPixmap::fromImage(image);
}
void MainWindow::setupMenuBar() {
@ -242,7 +218,15 @@ void MainWindow::setupMenuBar() {
auto* exitAction = fileMenu->addAction("退出");
exitAction->setShortcut(QKeySequence("Ctrl+Q"));
connect(exitAction, &QAction::triggered, this, &MainWindow::close);
connect(exitAction, &QAction::triggered, this, [this]() {
if (trayIcon_) {
trayIcon_->hide();
}
if (voiceInputService_) {
voiceInputService_->stop();
}
qApp->quit();
});
// 帮助菜单
auto* helpMenu = menuBar()->addMenu("帮助");
@ -271,8 +255,8 @@ void MainWindow::closeEvent(QCloseEvent* event) {
if (voiceInputService_) {
voiceInputService_->stop();
}
LOG_INFO(kTag, "主窗口关闭");
QMainWindow::closeEvent(event);
LOG_INFO(kTag, "主窗口关闭 (应用保持运行,托盘图标可见)");
event->ignore(); // 不关闭应用,只隐藏窗口
}
void MainWindow::updateModelStatus() {

View File

@ -40,7 +40,7 @@ private:
void setupStatusBar(SenseVoiceEngine* sttEngine);
void setupTrayIcon();
void updateTrayIcon(const QString& status);
QPixmap createTrayIcon(const QColor& color, const QString& symbol);
QPixmap createTrayIcon(const QColor& color);
void loadStyleSheet();
void onVoiceInputConfigChanged();
void updateModelStatus();