feat: 添加GPU频率/功耗/风扇监控、温度告警、趋势图、右键菜单、进程列表、GPU切换

第二、三阶段功能:

数据增强(第一阶段):
- GPU频率: NVIDIA nvidia-smi clocks.gr, Intel gt/gt0/rps_act_freq_mhz
- 显存频率: NVIDIA nvidia-smi clocks.mem
- 功耗: NVIDIA nvidia-smi power.draw, AMD hwmon power1_average
- 风扇转速: NVIDIA nvidia-smi fan.speed, AMD hwmon fan1_input
- 任务栏图标根据最高温度变色(≥80°C红/≥60°C橙)
- readGpuStats 每次刷新前重置统计字段,避免残留旧值

交互提升(第二阶段):
- 温度告警: GPU温度≥80°C时通过D-Bus发送桌面通知,30秒冷却
- 右键菜单: 使用Qt.labs.platform原生菜单+MenuHelper,支持刷新和GPU切换
- 迷你趋势图: Canvas绘制近60秒温度走势折线
- 英文翻译: 新增en.ts,23条全部翻译

高级功能(第三阶段):
- NVIDIA进程列表: nvidia-smi --query-compute-apps查询,弹框中显示进程名和显存占用
- GPU切换: pkexec prime-select set <mode>,弹框按钮+右键菜单,仅PRIME模式显示

其他改进:
- 自定义深色主题tooltip替代系统默认白色tooltip
- 第一张卡片tooltip显示在下方避免被裁剪
- CMakeLists.txt lupdate添加C++源文件提取tr()翻译
- gpuStats格式扩展为9字段: temp|gpuUsage|memUsage|memUsed|memTotal|gpuClock|memClock|powerDraw|fanSpeed
This commit is contained in:
2026-07-14 17:32:46 +08:00
parent cda61d9d72
commit 2570a028ea
7 changed files with 819 additions and 45 deletions
+4 -1
View File
@@ -24,25 +24,28 @@ set(TRANSLATIONS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/translations)
set(TS_FILES set(TS_FILES
${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts
${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts
${TRANSLATIONS_DIR}/${PLUGIN_ID}_en.ts
) )
# 生成 .qm 编译翻译文件 # 生成 .qm 编译翻译文件
set(QM_FILES set(QM_FILES
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm
${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_en.qm
) )
add_custom_command( add_custom_command(
OUTPUT ${QM_FILES} OUTPUT ${QM_FILES}
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_CN.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_CN.qm
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_zh_TW.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_zh_TW.qm
COMMAND /usr/lib/qt6/bin/lrelease ${TRANSLATIONS_DIR}/${PLUGIN_ID}_en.ts -qm ${CMAKE_CURRENT_BINARY_DIR}/${PLUGIN_ID}_en.qm
DEPENDS ${TS_FILES} DEPENDS ${TS_FILES}
COMMENT "Compiling translations to .qm files" COMMENT "Compiling translations to .qm files"
) )
# 手动更新翻译源文件(不加入 ALL,仅在需要时手动 make update_translations # 手动更新翻译源文件(不加入 ALL,仅在需要时手动 make update_translations
add_custom_target(update_translations add_custom_target(update_translations
COMMAND /usr/lib/qt6/bin/lupdate ${CMAKE_CURRENT_SOURCE_DIR}/package -ts ${TS_FILES} COMMAND /usr/lib/qt6/bin/lupdate ${CMAKE_CURRENT_SOURCE_DIR}/package ${CMAKE_CURRENT_SOURCE_DIR}/graphicsdriverapplet.cpp -ts ${TS_FILES}
COMMENT "Updating translation source files" COMMENT "Updating translation source files"
) )
+178 -6
View File
@@ -12,6 +12,10 @@
#include <QTextStream> #include <QTextStream>
#include <QRegularExpression> #include <QRegularExpression>
#include <QFileInfo> #include <QFileInfo>
#include <QDateTime>
#include <QDBusInterface>
#include <QDBusMessage>
#include <QVariantList>
DCORE_USE_NAMESPACE DCORE_USE_NAMESPACE
@@ -20,6 +24,7 @@ DS_BEGIN_NAMESPACE
GraphicsDriverApplet::GraphicsDriverApplet(QObject *parent) GraphicsDriverApplet::GraphicsDriverApplet(QObject *parent)
: DApplet(parent) : DApplet(parent)
, m_ready(false) , m_ready(false)
, m_lastAlertTime(0)
{ {
} }
@@ -73,6 +78,24 @@ QStringList GraphicsDriverApplet::gpuPrimary() const
return m_gpuPrimary; return m_gpuPrimary;
} }
QVariantList GraphicsDriverApplet::tempHistory() const
{
QVariantList result;
for (const QList<int> &history : m_tempHistory) {
QVariantList gpuHistory;
for (int temp : history) {
gpuHistory << temp;
}
result << QVariant(gpuHistory);
}
return result;
}
QStringList GraphicsDriverApplet::gpuProcesses() const
{
return m_gpuProcesses;
}
void GraphicsDriverApplet::refreshDeviceInfo() void GraphicsDriverApplet::refreshDeviceInfo()
{ {
detectGpus(); detectGpus();
@@ -86,6 +109,7 @@ void GraphicsDriverApplet::refreshStats()
void GraphicsDriverApplet::detectGpus() void GraphicsDriverApplet::detectGpus()
{ {
m_gpus.clear(); m_gpus.clear();
m_tempHistory.clear();
// 通过 lspci 检测显卡 // 通过 lspci 检测显卡
QStringList lines = runCommand("lspci", {"-mm"}); QStringList lines = runCommand("lspci", {"-mm"});
@@ -103,6 +127,10 @@ void GraphicsDriverApplet::detectGpus()
gpu.memUsage = -1; gpu.memUsage = -1;
gpu.memTotal = -1; gpu.memTotal = -1;
gpu.memUsed = -1; gpu.memUsed = -1;
gpu.gpuClock = -1;
gpu.memClock = -1;
gpu.powerDraw = -1;
gpu.fanSpeed = -1;
// lspci -mm 格式: "Class" "Vendor" "Device" ... // lspci -mm 格式: "Class" "Vendor" "Device" ...
// 提取 PCI ID(行首的 01:00.0 等) // 提取 PCI ID(行首的 01:00.0 等)
@@ -326,10 +354,11 @@ int GraphicsDriverApplet::findDrmCard(const QString &pciId)
void GraphicsDriverApplet::readGpuStats() void GraphicsDriverApplet::readGpuStats()
{ {
// 先处理 NVIDIA GPU(单次 nvidia-smi 调用获取所有 NVIDIA GPU 数据) // 先处理 NVIDIA GPU(单次 nvidia-smi 调用获取所有 NVIDIA GPU 数据)
// nvidia-smi 输出格式: pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used // nvidia-smi 输出格式: pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,
// memory.total,memory.used,clocks.gr,clocks.mem,power.draw,fan.speed
QMap<QString, QStringList> nvidiaData; // pciId -> stats QMap<QString, QStringList> nvidiaData; // pciId -> stats
QStringList nvLines = runCommand("nvidia-smi", { QStringList nvLines = runCommand("nvidia-smi", {
"--query-gpu=pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used", "--query-gpu=pci.bus_id,temperature.gpu,utilization.gpu,utilization.memory,memory.total,memory.used,clocks.gr,clocks.mem,power.draw,fan.speed",
"--format=csv,noheader,nounits" "--format=csv,noheader,nounits"
}); });
for (const QString &line : nvLines) { for (const QString &line : nvLines) {
@@ -344,6 +373,17 @@ void GraphicsDriverApplet::readGpuStats()
// 逐个 GPU 读取统计 // 逐个 GPU 读取统计
for (GpuInfo &gpu : m_gpus) { for (GpuInfo &gpu : m_gpus) {
// 每次刷新前重置实时统计字段,确保数据源不可用时不会残留旧值
gpu.temperature = -1;
gpu.gpuUsage = -1;
gpu.memUsage = -1;
gpu.memTotal = -1;
gpu.memUsed = -1;
gpu.gpuClock = -1;
gpu.memClock = -1;
gpu.powerDraw = -1;
gpu.fanSpeed = -1;
if (gpu.driver == "nvidia" && nvidiaData.contains(gpu.pciId)) { if (gpu.driver == "nvidia" && nvidiaData.contains(gpu.pciId)) {
// NVIDIA: 使用 nvidia-smi 数据 // NVIDIA: 使用 nvidia-smi 数据
QStringList &parts = nvidiaData[gpu.pciId]; QStringList &parts = nvidiaData[gpu.pciId];
@@ -352,6 +392,19 @@ void GraphicsDriverApplet::readGpuStats()
gpu.memUsage = parts[3].trimmed().toInt(); gpu.memUsage = parts[3].trimmed().toInt();
gpu.memTotal = parts[4].trimmed().toInt(); gpu.memTotal = parts[4].trimmed().toInt();
gpu.memUsed = parts[5].trimmed().toInt(); gpu.memUsed = parts[5].trimmed().toInt();
// 频率 (parts[6]=GPU, parts[7]=显存)
if (parts.size() > 6) gpu.gpuClock = parts[6].trimmed().toInt();
if (parts.size() > 7) gpu.memClock = parts[7].trimmed().toInt();
// 功耗 (parts[8])[N/A] 时保持 -1
if (parts.size() > 8) {
QString pwr = parts[8].trimmed();
if (pwr != "[N/A]" && !pwr.isEmpty()) gpu.powerDraw = pwr.toDouble();
}
// 风扇转速 (parts[9])[N/A] 时保持 -1
if (parts.size() > 9) {
QString fan = parts[9].trimmed();
if (fan != "[N/A]" && !fan.isEmpty()) gpu.fanSpeed = fan.toInt();
}
} else if (gpu.drmCard >= 0) { } else if (gpu.drmCard >= 0) {
// AMD / Intel / 其他: 使用 sysfs // AMD / Intel / 其他: 使用 sysfs
QString cardPath = QString("/sys/class/drm/card%1/device").arg(gpu.drmCard); QString cardPath = QString("/sys/class/drm/card%1/device").arg(gpu.drmCard);
@@ -384,16 +437,36 @@ void GraphicsDriverApplet::readGpuStats()
if (nameFile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (nameFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QString name = QTextStream(&nameFile).readAll().trimmed(); QString name = QTextStream(&nameFile).readAll().trimmed();
if (name == gpu.driver || name == "amdgpu" || name == "radeon") { if (name == gpu.driver || name == "amdgpu" || name == "radeon") {
QFile tempFile(hwmonDir.absoluteFilePath(hwmon + "/temp1_input")); QString hwmonPath = hwmonDir.absoluteFilePath(hwmon);
// 温度
QFile tempFile(hwmonPath + "/temp1_input");
if (tempFile.open(QIODevice::ReadOnly | QIODevice::Text)) { if (tempFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
int tempMilli = QTextStream(&tempFile).readAll().trimmed().toInt(); int tempMilli = QTextStream(&tempFile).readAll().trimmed().toInt();
gpu.temperature = tempMilli / 1000; gpu.temperature = tempMilli / 1000;
} }
// 功耗 (µW -> W)
QFile powerFile(hwmonPath + "/power1_average");
if (powerFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
qint64 microWatts = QTextStream(&powerFile).readAll().trimmed().toLongLong();
gpu.powerDraw = microWatts / 1000000.0;
}
// 风扇转速 (RPM)
QFile fanFile(hwmonPath + "/fan1_input");
if (fanFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
gpu.fanSpeed = QTextStream(&fanFile).readAll().trimmed().toInt();
}
break; break;
} }
} }
} }
} }
// GPU 频率: Intel i915 使用 gt/gt0/rps_act_freq_mhzAMD 使用 hwmon freq1_input
QString gtPath = QString("/sys/class/drm/card%1/gt/gt0/rps_act_freq_mhz").arg(gpu.drmCard);
QFile gtFreqFile(gtPath);
if (gtFreqFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
gpu.gpuClock = QTextStream(&gtFreqFile).readAll().trimmed().toInt();
}
} }
// --- 以下为 Intel 核显等无 hwmon/vram sysfs 接口时的回退逻辑 --- // --- 以下为 Intel 核显等无 hwmon/vram sysfs 接口时的回退逻辑 ---
@@ -443,18 +516,117 @@ void GraphicsDriverApplet::readGpuStats()
} }
} }
// 生成统计数据 (格式: temp|gpuUsage|memUsage|memUsed|memTotal) // 生成统计数据 (格式: temp|gpuUsage|memUsage|memUsed|memTotal|gpuClock|memClock|powerDraw|fanSpeed)
m_gpuStats.clear(); m_gpuStats.clear();
for (const GpuInfo &gpu : m_gpus) { for (const GpuInfo &gpu : m_gpus) {
m_gpuStats << QString("%1|%2|%3|%4|%5") m_gpuStats << QString("%1|%2|%3|%4|%5|%6|%7|%8|%9")
.arg(gpu.temperature) .arg(gpu.temperature)
.arg(gpu.gpuUsage) .arg(gpu.gpuUsage)
.arg(gpu.memUsage) .arg(gpu.memUsage)
.arg(gpu.memUsed) .arg(gpu.memUsed)
.arg(gpu.memTotal); .arg(gpu.memTotal)
.arg(gpu.gpuClock)
.arg(gpu.memClock)
.arg(gpu.powerDraw, 0, 'f', 1)
.arg(gpu.fanSpeed);
} }
emit gpuStatsChanged(); emit gpuStatsChanged();
// 更新温度历史(保留最近 60 个采样点)
if (m_tempHistory.size() != m_gpus.size()) {
m_tempHistory.resize(m_gpus.size());
}
for (int i = 0; i < m_gpus.size(); ++i) {
m_tempHistory[i].append(m_gpus[i].temperature);
if (m_tempHistory[i].size() > 60) {
m_tempHistory[i].removeFirst();
}
}
emit tempHistoryChanged();
// 温度告警检测
checkTempAlert();
// 读取 NVIDIA 进程列表
readGpuProcesses();
}
// 温度告警:检测是否有 GPU 温度超过 80°C,30 秒内不重复告警
void GraphicsDriverApplet::checkTempAlert()
{
qint64 now = QDateTime::currentSecsSinceEpoch();
if (now - m_lastAlertTime < 30) return;
for (const GpuInfo &gpu : m_gpus) {
if (gpu.temperature >= 80) {
sendNotification(
tr("GPU Temperature Alert"),
tr("%1 temperature reached %2°C, please check cooling")
.arg(gpu.name)
.arg(gpu.temperature)
);
m_lastAlertTime = now;
break;
}
}
}
// 通过 D-Bus 发送桌面通知
void GraphicsDriverApplet::sendNotification(const QString &summary, const QString &body)
{
QDBusInterface notify("org.freedesktop.Notifications",
"/org/freedesktop/Notifications",
"org.freedesktop.Notifications",
QDBusConnection::sessionBus());
if (notify.isValid()) {
notify.call("Notify", "dde-shell-graphics-driver", 0u,
"dialog-warning", summary, body,
QStringList(), QVariantMap(), 5000);
}
}
// 读取 NVIDIA GPU 上运行的进程列表
void GraphicsDriverApplet::readGpuProcesses()
{
m_gpuProcesses.clear();
// 仅当存在 NVIDIA GPU 时查询
bool hasNvidia = false;
for (const GpuInfo &gpu : m_gpus) {
if (gpu.driver == "nvidia") { hasNvidia = true; break; }
}
if (!hasNvidia) {
emit gpuProcessesChanged();
return;
}
// nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv,noheader,nounits
QStringList lines = runCommand("nvidia-smi", {
"--query-compute-apps=pid,process_name,used_memory",
"--format=csv,noheader,nounits"
});
for (const QString &line : lines) {
QStringList parts = line.split(",");
if (parts.size() >= 3) {
m_gpuProcesses << QString("%1|%2|%3")
.arg(parts[0].trimmed())
.arg(parts[1].trimmed())
.arg(parts[2].trimmed());
}
}
emit gpuProcessesChanged();
}
// GPU 切换:调用 prime-select 切换 PRIME 模式(需要 polkit 提权)
bool GraphicsDriverApplet::switchGpu(const QString &mode)
{
// mode: "nvidia" / "intel" / "on-demand"
QProcess process;
process.start("pkexec", {"prime-select", "set", mode});
process.waitForFinished(30000);
return process.exitCode() == 0;
} }
D_APPLET_CLASS(GraphicsDriverApplet) D_APPLET_CLASS(GraphicsDriverApplet)
+21
View File
@@ -9,6 +9,7 @@
#include <QString> #include <QString>
#include <QStringList> #include <QStringList>
#include <QTimer> #include <QTimer>
#include <QVariantList>
DS_BEGIN_NAMESPACE DS_BEGIN_NAMESPACE
@@ -26,6 +27,10 @@ struct GpuInfo {
int memUsage; // 显存使用率 (%),-1 表示不可用 int memUsage; // 显存使用率 (%),-1 表示不可用
int memTotal; // 显存总量 (MB),-1 表示不可用 int memTotal; // 显存总量 (MB),-1 表示不可用
int memUsed; // 显存已用 (MB),-1 表示不可用 int memUsed; // 显存已用 (MB),-1 表示不可用
int gpuClock; // GPU 频率 (MHz)-1 表示不可用
int memClock; // 显存频率 (MHz),-1 表示不可用
double powerDraw; // 功耗 (W)-1 表示不可用
int fanSpeed; // 风扇转速 (%),-1 表示不可用
}; };
class GraphicsDriverApplet : public DApplet class GraphicsDriverApplet : public DApplet
@@ -39,6 +44,8 @@ class GraphicsDriverApplet : public DApplet
Q_PROPERTY(QString gpuMode READ gpuMode NOTIFY gpuModeChanged) Q_PROPERTY(QString gpuMode READ gpuMode NOTIFY gpuModeChanged)
Q_PROPERTY(QStringList gpuPrimary READ gpuPrimary NOTIFY gpuPrimaryChanged) Q_PROPERTY(QStringList gpuPrimary READ gpuPrimary NOTIFY gpuPrimaryChanged)
Q_PROPERTY(bool ready READ ready NOTIFY readyChanged) Q_PROPERTY(bool ready READ ready NOTIFY readyChanged)
Q_PROPERTY(QVariantList tempHistory READ tempHistory NOTIFY tempHistoryChanged)
Q_PROPERTY(QStringList gpuProcesses READ gpuProcesses NOTIFY gpuProcessesChanged)
public: public:
explicit GraphicsDriverApplet(QObject *parent = nullptr); explicit GraphicsDriverApplet(QObject *parent = nullptr);
@@ -54,9 +61,12 @@ public:
QString gpuMode() const; QString gpuMode() const;
QStringList gpuPrimary() const; QStringList gpuPrimary() const;
bool ready() const; bool ready() const;
QVariantList tempHistory() const;
QStringList gpuProcesses() const;
Q_INVOKABLE void refreshDeviceInfo(); Q_INVOKABLE void refreshDeviceInfo();
Q_INVOKABLE void refreshStats(); Q_INVOKABLE void refreshStats();
Q_INVOKABLE bool switchGpu(const QString &mode);
signals: signals:
void deviceInfoChanged(); void deviceInfoChanged();
@@ -66,11 +76,16 @@ signals:
void gpuModeChanged(); void gpuModeChanged();
void gpuPrimaryChanged(); void gpuPrimaryChanged();
void readyChanged(); void readyChanged();
void tempHistoryChanged();
void gpuProcessesChanged();
private: private:
void detectGpus(); void detectGpus();
void readGpuStats(); void readGpuStats();
void readGpuProcesses();
void detectGpuMode(); void detectGpuMode();
void checkTempAlert();
void sendNotification(const QString &summary, const QString &body);
static int findDrmCard(const QString &pciId); static int findDrmCard(const QString &pciId);
static QStringList runCommand(const QString &cmd, const QStringList &args); static QStringList runCommand(const QString &cmd, const QStringList &args);
static QString parseGpuName(const QString &line); static QString parseGpuName(const QString &line);
@@ -84,6 +99,12 @@ private:
QString m_gpuMode; QString m_gpuMode;
QStringList m_gpuPrimary; QStringList m_gpuPrimary;
bool m_ready; bool m_ready;
// 温度历史:每个 GPU 保留最近 60 个温度采样
QVector<QList<int>> m_tempHistory;
// NVIDIA 进程列表:每行 "pid|name|usedMemory"
QStringList m_gpuProcesses;
// 温度告警冷却时间戳
qint64 m_lastAlertTime;
}; };
DS_END_NAMESPACE DS_END_NAMESPACE
+338 -12
View File
@@ -5,6 +5,7 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import Qt.labs.platform 1.1 as LP
import org.deepin.ds 1.0 import org.deepin.ds 1.0
import org.deepin.ds.dock 1.0 import org.deepin.ds.dock 1.0
import org.deepin.dtk 1.0 import org.deepin.dtk 1.0
@@ -25,6 +26,24 @@ AppletItem {
readonly property string currentDriver: applet ? (applet.currentDriver || qsTr("Unknown")) : qsTr("Unknown") readonly property string currentDriver: applet ? (applet.currentDriver || qsTr("Unknown")) : qsTr("Unknown")
readonly property string gpuMode: applet ? (applet.gpuMode || "") : "" readonly property string gpuMode: applet ? (applet.gpuMode || "") : ""
// 所有 GPU 中的最高温度,用于任务栏图标变色
readonly property int maxTemp: {
if (!applet || !applet.gpuStats) return -1
var max = -1
for (var i = 0; i < applet.gpuStats.length; i++) {
var parts = applet.gpuStats[i].split("|")
var temp = parseInt(parts[0])
if (!isNaN(temp) && temp > max) max = temp
}
return max
}
// 图标颜色:温度 >=80 红色,>=60 橙色,否则正常
readonly property color iconColor: {
if (maxTemp >= 80) return Qt.rgba(220/255, 38/255, 38/255, 1)
if (maxTemp >= 60) return Qt.rgba(245/255, 158/255, 11/255, 1)
return root.primaryText
}
property Palette basePalette: DockPalette.iconTextPalette property Palette basePalette: DockPalette.iconTextPalette
readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9) readonly property color primaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.9)
readonly property color secondaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.65) readonly property color secondaryText: Qt.rgba(basePalette.r, basePalette.g, basePalette.b, 0.65)
@@ -34,7 +53,7 @@ AppletItem {
readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1) readonly property color accentBlue: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 1)
readonly property color accentBlueLight: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 0.12) readonly property color accentBlueLight: Qt.rgba(20 / 255, 80 / 255, 160 / 255, 0.12)
// 图标区域 // 图标区域(根据最高温度变色)
Rectangle { Rectangle {
anchors.centerIn: parent anchors.centerIn: parent
width: dockSize * 0.7 width: dockSize * 0.7
@@ -42,14 +61,14 @@ AppletItem {
color: "transparent" color: "transparent"
radius: width * 0.45 radius: width * 0.45
border.width: 1 border.width: 1
border.color: root.secondaryText border.color: root.iconColor
Text { Text {
anchors.centerIn: parent anchors.centerIn: parent
text: "GPU" text: "GPU"
font.pixelSize: root.dockSize * 0.22 font.pixelSize: root.dockSize * 0.22
font.bold: true font.bold: true
color: root.primaryText color: root.iconColor
} }
} }
@@ -272,8 +291,8 @@ AppletItem {
delegate: Rectangle { delegate: Rectangle {
id: gpuCard id: gpuCard
width: gpuColumn.width width: gpuColumn.width
height: 98 height: 120
color: root.cardBackground color: root.cardBackground
radius: 10 radius: 10
border.width: 1 border.width: 1
@@ -320,12 +339,65 @@ AppletItem {
if (temp >= 60) return Qt.rgba(245/255, 158/255, 11/255, 1) if (temp >= 60) return Qt.rgba(245/255, 158/255, 11/255, 1)
return Qt.rgba(22/255, 163/255, 74/255, 1) return Qt.rgba(22/255, 163/255, 74/255, 1)
} }
property string gpuClockStr: {
var parts = statsLine.split("|")
var v = parts[5]
return v === "-1" || v === "" ? "-" : v
}
property string memClockStr: {
var parts = statsLine.split("|")
var v = parts[6]
return v === "-1" || v === "" ? "-" : v
}
property string powerStr: {
var parts = statsLine.split("|")
var v = parts[7]
return v === "-1.0" || v === "" ? "-" : v
}
property string fanStr: {
var parts = statsLine.split("|")
var v = parts[8]
return v === "-1" || v === "" ? "-" : v
}
ToolTip.text: qsTr("GPU: ") + parseGpuName(modelData) + "\n" // 自定义深色主题悬浮提示(替代系统默认白色 tooltip
+ qsTr("Driver: ") + parseDriverName(modelData) + "\n" // 第一张卡片 tooltip 显示在下方,避免被弹框顶部裁剪
+ qsTr("Version: ") + (parseDriverVersion(modelData) || qsTr("Unknown")) Rectangle {
ToolTip.visible: cardHover.hovered visible: cardHover.hovered
ToolTip.delay: 300 anchors.top: index === 0 ? parent.bottom : undefined
anchors.topMargin: index === 0 ? 6 : 0
anchors.bottom: index === 0 ? undefined : parent.top
anchors.bottomMargin: index === 0 ? 0 : 6
anchors.horizontalCenter: parent.horizontalCenter
width: tooltipContent.implicitWidth + 24
height: tooltipContent.implicitHeight + 16
color: Qt.rgba(0, 0, 0, 0.85)
radius: 8
z: 100
ColumnLayout {
id: tooltipContent
anchors.centerIn: parent
spacing: 3
Text {
text: qsTr("GPU: ") + parseGpuName(modelData)
color: "white"
font.pixelSize: 11
font.bold: true
}
Text {
text: qsTr("Driver: ") + parseDriverName(modelData)
color: Qt.rgba(1, 1, 1, 0.65)
font.pixelSize: 10
}
Text {
text: qsTr("Version: ") + (parseDriverVersion(modelData) || qsTr("Unknown"))
color: Qt.rgba(1, 1, 1, 0.65)
font.pixelSize: 10
}
}
}
HoverHandler { HoverHandler {
id: cardHover id: cardHover
@@ -456,10 +528,96 @@ AppletItem {
} }
Text { Text {
text: gpuUsageStr !== "" ? gpuUsageStr + "%" : "" text: gpuUsageStr !== "-" ? gpuUsageStr + "%" : "-"
font.pixelSize: 11 font.pixelSize: 11
color: root.secondaryText color: root.secondaryText
font.bold: gpuUsageStr !== "" font.bold: gpuUsageStr !== "-"
}
}
// 频率/功耗/风扇信息行
RowLayout {
Layout.fillWidth: true
spacing: 8
// GPU 频率
Text {
text: gpuClockStr !== "-" ? gpuClockStr + " MHz" : "-"
font.pixelSize: 10
color: root.tertiaryText
visible: gpuClockStr !== "-"
}
// 功耗
Text {
text: powerStr !== "-" ? powerStr + " W" : ""
font.pixelSize: 10
color: root.tertiaryText
visible: powerStr !== "-"
}
// 风扇转速
Text {
text: fanStr !== "-" ? qsTr("Fan") + " " + fanStr + "%" : ""
font.pixelSize: 10
color: root.tertiaryText
visible: fanStr !== "-"
}
Item { Layout.fillWidth: true }
// 显存频率
Text {
text: memClockStr !== "-" ? memClockStr + " MHz" : ""
font.pixelSize: 10
color: root.tertiaryText
visible: memClockStr !== "-"
}
}
// 温度趋势迷你图
Canvas {
id: tempCanvas
Layout.fillWidth: true
Layout.preferredHeight: 24
visible: {
if (!root.applet || !root.applet.tempHistory) return false
var h = root.applet.tempHistory[index]
return h && h.length > 1
}
onPaint: {
var ctx = getContext("2d")
ctx.reset()
var w = width
var h = height
if (!root.applet || !root.applet.tempHistory) return
var history = root.applet.tempHistory[index]
if (!history || history.length < 2) return
// 有效温度范围 20-100°C
var minT = 20, maxT = 100
var len = history.length
ctx.strokeStyle = gpuCard.tempColor
ctx.lineWidth = 1.5
ctx.beginPath()
for (var i = 0; i < len; i++) {
var x = (i / (len - 1)) * w
var t = history[i]
if (t < 0) t = minT
var y = h - ((t - minT) / (maxT - minT)) * h
if (y < 0) y = 0
if (y > h) y = h
if (i === 0) ctx.moveTo(x, y)
else ctx.lineTo(x, y)
}
ctx.stroke()
}
// 温度历史变化时重绘
Connections {
target: root.applet
function onTempHistoryChanged() { tempCanvas.requestPaint() }
} }
} }
} }
@@ -497,6 +655,124 @@ AppletItem {
} }
} }
// NVIDIA 进程列表(仅有进程时显示)
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: processList.implicitHeight + 24
color: root.cardBackground
radius: 10
border.width: 1
border.color: root.cardBorder
visible: root.applet && root.applet.gpuProcesses && root.applet.gpuProcesses.length > 0
ColumnLayout {
id: processList
anchors.fill: parent
anchors.margins: 10
spacing: 4
Text {
text: qsTr("GPU Processes")
font.pixelSize: 11
font.bold: true
color: root.secondaryText
}
Repeater {
model: root.applet ? (root.applet.gpuProcesses || []) : []
RowLayout {
Layout.fillWidth: true
spacing: 8
Text {
text: modelData.split("|")[1] || "?"
font.pixelSize: 10
color: root.primaryText
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
text: (modelData.split("|")[2] || "?") + " MB"
font.pixelSize: 10
color: root.tertiaryText
}
}
}
}
}
// GPU 切换按钮(仅 PRIME 模式下显示)
RowLayout {
Layout.fillWidth: true
spacing: 8
visible: root.gpuMode === "PRIME"
Button {
Layout.fillWidth: true
Layout.preferredHeight: 30
font.pixelSize: 11
text: qsTr("NVIDIA")
background: Rectangle {
color: parent.hovered ? root.accentBlueLight : root.cardBackground
radius: 6
border.width: 1
border.color: root.cardBorder
}
contentItem: Text {
text: parent.text
color: root.accentBlue
font.pixelSize: 11
horizontalAlignment: Text.AlignHCenter
}
onClicked: if (root.applet) root.applet.switchGpu("nvidia")
}
Button {
Layout.fillWidth: true
Layout.preferredHeight: 30
font.pixelSize: 11
text: qsTr("Intel")
background: Rectangle {
color: parent.hovered ? root.accentBlueLight : root.cardBackground
radius: 6
border.width: 1
border.color: root.cardBorder
}
contentItem: Text {
text: parent.text
color: root.accentBlue
font.pixelSize: 11
horizontalAlignment: Text.AlignHCenter
}
onClicked: if (root.applet) root.applet.switchGpu("intel")
}
Button {
Layout.fillWidth: true
Layout.preferredHeight: 30
font.pixelSize: 11
text: qsTr("On-Demand")
background: Rectangle {
color: parent.hovered ? root.accentBlueLight : root.cardBackground
radius: 6
border.width: 1
border.color: root.cardBorder
}
contentItem: Text {
text: parent.text
color: root.accentBlue
font.pixelSize: 11
horizontalAlignment: Text.AlignHCenter
}
onClicked: if (root.applet) root.applet.switchGpu("on-demand")
}
}
// 刷新按钮 // 刷新按钮
Button { Button {
id: refreshBtn id: refreshBtn
@@ -626,4 +902,54 @@ AppletItem {
toolTip.close() toolTip.close()
} }
} }
// 右键菜单(使用 Qt.labs.platform 原生菜单,通过 dock 的 MenuHelper 弹出)
TapHandler {
acceptedButtons: Qt.RightButton
gesturePolicy: TapHandler.ReleaseWithinBounds
onTapped: {
contextMenuLoader.active = true
if (contextMenuLoader.item) {
MenuHelper.openMenu(contextMenuLoader.item)
}
}
}
Loader {
id: contextMenuLoader
active: false
sourceComponent: LP.Menu {
LP.MenuItem {
text: qsTr("Refresh")
onTriggered: {
if (root.applet) root.applet.refreshDeviceInfo()
}
}
LP.MenuItem {
text: qsTr("Switch to NVIDIA")
visible: root.gpuMode === "PRIME"
onTriggered: {
if (root.applet) root.applet.switchGpu("nvidia")
}
}
LP.MenuItem {
text: qsTr("Switch to Intel")
visible: root.gpuMode === "PRIME"
onTriggered: {
if (root.applet) root.applet.switchGpu("intel")
}
}
LP.MenuItem {
text: qsTr("Switch to On-Demand")
visible: root.gpuMode === "PRIME"
onTriggered: {
if (root.applet) root.applet.switchGpu("on-demand")
}
}
}
}
} }
@@ -0,0 +1,132 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.1" language="en_US">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU Temperature Alert</translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 temperature reached %2°C, please check cooling</translation>
</message>
</context>
<context>
<name>driverview</name>
<message>
<location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source>
<translation>No GPU detected</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source>
<translation>Unknown</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source>
<translation>Graphics Driver Manager</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source>
<translation>Current Driver:</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="259"/>
<source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source>
<translation>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source>
<translation>Solo mode: only one GPU detected.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid mode: dual GPU detected, but no known switching solution found.</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source>
<translation>GPU: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source>
<translation>Driver: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="364"/>
<source>Version: </source>
<translation>Version: </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="426"/>
<source>Primary</source>
<translation>Primary</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation>Fan</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU Processes</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation>On-Demand</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source>
<translation>Refresh</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation>Switch to NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation>Switch to Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation>Switch to On-Demand</translation>
</message>
</context>
</TS>
@@ -1,72 +1,132 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="zh_CN"> <TS version="2.1" language="zh_CN">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 %2°C</translation>
</message>
</context>
<context> <context>
<name>driverview</name> <name>driverview</name>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source> <source>Unknown</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source> <source>No GPU detected</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source> <source>Graphics Driver Manager</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source> <source>Current Driver:</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source> <source>Refresh</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation> NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation> Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source> <source>GPU: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source> <source>Driver: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="364"/>
<source>Version: </source> <source>Version: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="426"/>
<source>Primary</source> <source>Primary</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="259"/>
<source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source> <source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source>
<translation>PRIME 3D PRIME Render Offload </translation> <translation>PRIME 3D PRIME Render Offload </translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source> <source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee GPU </translation> <translation>Bumblebee GPU </translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source> <source>Solo mode: only one GPU detected.</source>
<translation>Solo GPU</translation> <translation>Solo GPU</translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source> <source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid GPU</translation> <translation>Hybrid GPU</translation>
</message> </message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation></translation>
</message>
</context> </context>
</TS> </TS>
@@ -1,72 +1,132 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS> <!DOCTYPE TS>
<TS version="2.1" language="zh_TW"> <TS version="2.1" language="zh_TW">
<context>
<name>GraphicsDriverApplet</name>
<message>
<location filename="../graphicsdriverapplet.cpp" line="564"/>
<source>GPU Temperature Alert</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../graphicsdriverapplet.cpp" line="565"/>
<source>%1 temperature reached %2°C, please check cooling</source>
<translation>%1 %2°C</translation>
</message>
</context>
<context> <context>
<name>driverview</name> <name>driverview</name>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="24"/>
<location filename="../package/driverview.qml" line="25"/>
<location filename="../package/driverview.qml" line="364"/>
<location filename="../package/driverview.qml" line="802"/>
<source>Unknown</source> <source>Unknown</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="23"/>
<location filename="../package/driverview.qml" line="77"/>
<location filename="../package/driverview.qml" line="615"/>
<location filename="../package/driverview.qml" line="830"/>
<source>No GPU detected</source> <source>No GPU detected</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="175"/>
<source>Graphics Driver Manager</source> <source>Graphics Driver Manager</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="198"/>
<source>Current Driver:</source> <source>Current Driver:</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="640"/>
<source>GPU Processes</source>
<translation>GPU </translation>
</message>
<message>
<location filename="../package/driverview.qml" line="681"/>
<source>NVIDIA</source>
<translation>NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="702"/>
<source>Intel</source>
<translation>Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="723"/>
<source>On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="744"/>
<location filename="../package/driverview.qml" line="885"/>
<source>Refresh</source> <source>Refresh</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="892"/>
<source>Switch to NVIDIA</source>
<translation> NVIDIA</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="900"/>
<source>Switch to Intel</source>
<translation> Intel</translation>
</message>
<message>
<location filename="../package/driverview.qml" line="908"/>
<source>Switch to On-Demand</source>
<translation></translation>
</message>
<message>
<location filename="../package/driverview.qml" line="362"/>
<source>GPU: </source> <source>GPU: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="363"/>
<source>Driver: </source> <source>Driver: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="364"/>
<source>Version: </source> <source>Version: </source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="426"/>
<source>Primary</source> <source>Primary</source>
<translation></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="259"/>
<source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source> <source>PRIME mode: the integrated GPU handles display, the discrete GPU renders on demand. 3D apps use PRIME Render Offload to invoke the discrete GPU.</source>
<translation>PRIME 3D PRIME Render Offload 調</translation> <translation>PRIME 3D PRIME Render Offload 調</translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="261"/>
<source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source> <source>Bumblebee mode: a legacy dual-GPU solution, now deprecated.</source>
<translation>Bumblebee GPU </translation> <translation>Bumblebee GPU </translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="263"/>
<source>Solo mode: only one GPU detected.</source> <source>Solo mode: only one GPU detected.</source>
<translation>Solo GPU</translation> <translation>Solo GPU</translation>
</message> </message>
<message> <message>
<location filename="../package/driverview.qml"/> <location filename="../package/driverview.qml" line="265"/>
<source>Hybrid mode: dual GPU detected, but no known switching solution found.</source> <source>Hybrid mode: dual GPU detected, but no known switching solution found.</source>
<translation>Hybrid GPU</translation> <translation>Hybrid GPU</translation>
</message> </message>
<message>
<location filename="../package/driverview.qml" line="527"/>
<source>Fan</source>
<translation></translation>
</message>
</context> </context>
</TS> </TS>