完整提交;
This commit is contained in:
408
index.html
408
index.html
@@ -1,125 +1,291 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>性能监控</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
font-family: Arial, sans-serif;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.drag-bar {
|
||||
height: 30px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 5px;
|
||||
-webkit-app-region: drag;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
button {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.metrics {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin-top: 15px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="drag-bar">
|
||||
<div>性能监控</div>
|
||||
<div class="controls">
|
||||
<button id="minimize">最小化</button>
|
||||
<button id="close">关闭</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric-item">CPU: <span id="cpu">0%</span></div>
|
||||
<div class="metric-item">内存: <span id="memory">0%</span></div>
|
||||
<div class="metric-item">网络: <span id="network">0 KB/s</span></div>
|
||||
</div>
|
||||
|
||||
<div class="settings">
|
||||
<label>
|
||||
<input type="checkbox" id="auto-launch"> 开机启动
|
||||
</label>
|
||||
</div>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>性能监控</title>
|
||||
<style>
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
color: black;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s ease;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
<script>
|
||||
// 引入Electron的remote模块(需要在主进程中启用nodeIntegration)
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
// 获取元素
|
||||
const minimizeBtn = document.getElementById('minimize');
|
||||
const closeBtn = document.getElementById('close');
|
||||
const cpuEl = document.getElementById('cpu');
|
||||
const memoryEl = document.getElementById('memory');
|
||||
const networkEl = document.getElementById('network');
|
||||
const autoLaunchCheckbox = document.getElementById('auto-launch');
|
||||
|
||||
// 最小化按钮事件
|
||||
minimizeBtn.addEventListener('click', () => {
|
||||
// 发送最小化事件到主进程
|
||||
ipcRenderer.send('window-minimize');
|
||||
});
|
||||
|
||||
// 关闭按钮事件
|
||||
closeBtn.addEventListener('click', () => {
|
||||
// 发送关闭事件到主进程
|
||||
ipcRenderer.send('window-close');
|
||||
});
|
||||
|
||||
// 开机启动设置事件
|
||||
autoLaunchCheckbox.addEventListener('change', () => {
|
||||
// 发送开机启动设置事件到主进程
|
||||
ipcRenderer.send('set-auto-launch', autoLaunchCheckbox.checked);
|
||||
});
|
||||
|
||||
// 模拟性能数据更新
|
||||
setInterval(() => {
|
||||
// 生成随机数据
|
||||
const cpu = Math.floor(Math.random() * 100);
|
||||
const memory = Math.floor(Math.random() * 100);
|
||||
const network = Math.floor(Math.random() * 1000);
|
||||
.drag-bar {
|
||||
height: 32px;
|
||||
background: rgba(255, 255, 255, 0.5);
|
||||
border-radius: 4px;
|
||||
-webkit-app-region: drag;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 10px;
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.controls {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
button {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
color: black;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
backdrop-filter: blur(5px);
|
||||
transition: all 0.2s ease;
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
button:active {
|
||||
background: rgba(240, 240, 240, 1);
|
||||
border: 1px solid rgba(0, 0, 0, 0.25);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) inset;
|
||||
}
|
||||
|
||||
.metrics {
|
||||
font-size: 14px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.metric-item {
|
||||
margin: 0;
|
||||
padding: 4px 0;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
padding: 10px 14px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.metric-item:hover {
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-weight: 600;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.settings {
|
||||
margin-top: 15px;
|
||||
padding-top: 10px;
|
||||
border-top: 1px solid rgba(200, 200, 200, 0.1);
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
accent-color: #0078d4;
|
||||
}
|
||||
|
||||
/* 动画类 */
|
||||
.window-container {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.minimized {
|
||||
transform: scale(0.1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 350px) {
|
||||
.metrics {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
.metric-value {
|
||||
font-weight: 600;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 150px) {
|
||||
.metrics {
|
||||
grid-template-columns: 1fr;
|
||||
font-size: 10px;
|
||||
gap: 3px;
|
||||
}
|
||||
body {
|
||||
padding: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="window-container" id="window-container">
|
||||
<!-- 拖动区域 -->
|
||||
<div style="-webkit-app-region: drag; position: absolute; top: 0; left: 0; right: 0; height: 30px; z-index: -1"></div>
|
||||
|
||||
<div class="metrics">
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">CPU</span>
|
||||
<span class="metric-value" id="cpu">0%</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">内存</span>
|
||||
<span class="metric-value" id="memory">0%</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">网络</span>
|
||||
<span class="metric-value" id="network">0 KB/s</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">GPU</span>
|
||||
<span class="metric-value" id="gpu">0%</span>
|
||||
</div>
|
||||
<div class="metric-item">
|
||||
<span class="metric-label">显存</span>
|
||||
<span class="metric-value" id="gpu-memory">0 MB</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 引入性能监控库
|
||||
const si = require('systeminformation')
|
||||
const pidusage = require('pidusage')
|
||||
const process = require('process')
|
||||
|
||||
// 获取元素
|
||||
const cpuEl = document.getElementById('cpu')
|
||||
const memoryEl = document.getElementById('memory')
|
||||
const networkEl = document.getElementById('network')
|
||||
const gpuEl = document.getElementById('gpu')
|
||||
const gpuMemoryEl = document.getElementById('gpu-memory')
|
||||
|
||||
// 跟踪窗口聚焦状态
|
||||
let isWindowFocused = true;
|
||||
let lastUpdateTime = 0;
|
||||
|
||||
// 更新UI
|
||||
cpuEl.textContent = cpu + '%';
|
||||
memoryEl.textContent = memory + '%';
|
||||
networkEl.textContent = network + ' KB/s';
|
||||
}, 1000);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
// 监听窗口聚焦/失焦事件
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
ipcRenderer.on('window-focused', () => {
|
||||
isWindowFocused = true;
|
||||
});
|
||||
|
||||
ipcRenderer.on('window-blurred', () => {
|
||||
isWindowFocused = false;
|
||||
});
|
||||
|
||||
// 监听主进程发送的GPU信息
|
||||
ipcRenderer.on('gpu-info', (event, gpuInfo) => {
|
||||
// 过滤掉任何剩余的虚拟GPU信息
|
||||
const physicalGpus = gpuInfo.filter(gpu => {
|
||||
// 检查是否为虚拟GPU
|
||||
const isVirtual = gpu.name.includes('Oray') || gpu.name.includes('Virtual') || gpu.name.includes('Software')
|
||||
return !isVirtual && (gpu.memoryUsed !== undefined || gpu.vram !== undefined)
|
||||
})
|
||||
|
||||
if (physicalGpus && physicalGpus.length > 0) {
|
||||
// 显示第一个物理GPU的显存使用情况
|
||||
const gpu = physicalGpus[0]
|
||||
// 优先使用memoryUsed和memoryTotal,如果没有则使用vram
|
||||
if (gpu.memoryUsed !== undefined && gpu.memoryTotal !== undefined) {
|
||||
// 转换为MB单位
|
||||
const usedMB = Math.round(gpu.memoryUsed)
|
||||
const totalMB = Math.round(gpu.memoryTotal)
|
||||
gpuMemoryEl.textContent = usedMB + ' / ' + totalMB + ' MB'
|
||||
} else if (gpu.vram !== undefined) {
|
||||
// 如果只有vram信息,显示总显存
|
||||
const totalMB = Math.round(gpu.vram)
|
||||
gpuMemoryEl.textContent = '总共: ' + totalMB + ' MB'
|
||||
} else {
|
||||
gpuMemoryEl.textContent = 'N/A'
|
||||
}
|
||||
} else {
|
||||
gpuMemoryEl.textContent = 'N/A'
|
||||
}
|
||||
})
|
||||
|
||||
// 使用requestAnimationFrame实现性能数据更新
|
||||
let lastTime = 0;
|
||||
const focusedUpdateInterval = 200; // 聚焦时200ms更新一次
|
||||
const blurredUpdateInterval = 2000; // 失焦时2秒更新一次
|
||||
|
||||
async function updatePerformanceData(timestamp) {
|
||||
// 根据窗口聚焦状态确定更新间隔
|
||||
const updateInterval = isWindowFocused ? focusedUpdateInterval : blurredUpdateInterval;
|
||||
|
||||
if (timestamp - lastTime >= updateInterval) {
|
||||
try {
|
||||
// 获取CPU使用率
|
||||
const cpuData = await si.currentLoad();
|
||||
const cpuUsage = Math.round(cpuData.currentLoad);
|
||||
|
||||
// 获取内存使用率
|
||||
const memData = await si.mem();
|
||||
const memoryUsage = Math.round((memData.active / memData.total) * 100);
|
||||
|
||||
// 获取进程内存使用率
|
||||
const pid = process.pid;
|
||||
const pidData = await pidusage(pid);
|
||||
const processMemory = Math.round(pidData.memory / 1024 / 1024); // 转换为MB
|
||||
|
||||
// 获取网络使用情况
|
||||
const networkData = await si.networkStats();
|
||||
const networkUsage = networkData[0] ? Math.round((networkData[0].rx_sec + networkData[0].tx_sec) / 1024) : 0; // KB/s
|
||||
|
||||
// 获取GPU使用率
|
||||
const gpuData = await si.graphics();
|
||||
let gpuUsage = 0;
|
||||
if (gpuData.controllers && gpuData.controllers.length > 0) {
|
||||
// 尝试获取GPU使用率,如果没有则使用默认值
|
||||
gpuUsage = gpuData.controllers[0].utilizationMemory || gpuData.controllers[0].fanSpeed || 0;
|
||||
}
|
||||
|
||||
// 更新UI
|
||||
cpuEl.textContent = cpuUsage + '%';
|
||||
memoryEl.textContent = memoryUsage + '%';
|
||||
networkEl.textContent = networkUsage + ' KB/s';
|
||||
gpuEl.textContent = gpuUsage + '%';
|
||||
} catch (error) {
|
||||
console.error('获取性能数据时出错:', error);
|
||||
}
|
||||
|
||||
lastTime = timestamp;
|
||||
}
|
||||
|
||||
requestAnimationFrame(updatePerformanceData);
|
||||
}
|
||||
|
||||
// 启动动画循环
|
||||
requestAnimationFrame(updatePerformanceData)
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
283
main.js
283
main.js
@@ -1,125 +1,296 @@
|
||||
const { app, BrowserWindow, Tray, Menu, nativeImage, ipcMain } = require('electron');
|
||||
const path = require('path');
|
||||
const AutoLaunch = require('auto-launch');
|
||||
const { app, BrowserWindow, Tray, Menu, nativeImage, ipcMain } = require('electron')
|
||||
const path = require('path')
|
||||
const AutoLaunch = require('auto-launch')
|
||||
const si = require('systeminformation')
|
||||
|
||||
// 保持对窗口对象的全局引用,如果不这样做,窗口将会在JavaScript垃圾回收时自动关闭
|
||||
let mainWindow;
|
||||
let tray = null;
|
||||
let mainWindow
|
||||
let tray = null
|
||||
|
||||
// 创建开机启动管理器
|
||||
let autoLauncher = new AutoLaunch({
|
||||
name: 'Motioner',
|
||||
path: process.execPath,
|
||||
});
|
||||
})
|
||||
|
||||
// 跟踪开机启动状态
|
||||
let isAutoLaunchEnabled = false
|
||||
|
||||
// 存储GPU监控定时器
|
||||
let gpuMonitorInterval = null
|
||||
|
||||
// 创建窗口的函数
|
||||
function createWindow() {
|
||||
// 创建浏览器窗口
|
||||
mainWindow = new BrowserWindow({
|
||||
width: 300,
|
||||
height: 200,
|
||||
width: 350,
|
||||
height: 180,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false
|
||||
contextIsolation: false,
|
||||
},
|
||||
resizable: false,
|
||||
// 创建无边框窗口
|
||||
frame: false,
|
||||
// 保持窗口始终在最前面
|
||||
alwaysOnTop: true,
|
||||
// 窗口透明
|
||||
transparent: true
|
||||
});
|
||||
transparent: false,
|
||||
backgroundMaterial: 'mica',
|
||||
// 隐藏任务栏图标
|
||||
skipTaskbar: true,
|
||||
// 隐藏窗口切换时的显示
|
||||
show: false,
|
||||
// 隐藏窗口标题栏和任务栏显示
|
||||
titleBarStyle: 'hidden',
|
||||
// 隐藏窗口在Alt+Tab切换中显示
|
||||
hiddenInMissionControl: true
|
||||
})
|
||||
|
||||
// 加载应用的index.html
|
||||
mainWindow.loadFile('index.html');
|
||||
mainWindow.loadFile('index.html')
|
||||
|
||||
// 处理最小化事件
|
||||
ipcMain.on('window-minimize', () => {
|
||||
mainWindow.hide();
|
||||
});
|
||||
mainWindow.hide()
|
||||
})
|
||||
|
||||
// 处理关闭事件
|
||||
ipcMain.on('window-close', () => {
|
||||
app.quit();
|
||||
});
|
||||
|
||||
app.quit()
|
||||
})
|
||||
|
||||
// 处理开机启动设置事件
|
||||
ipcMain.on('set-auto-launch', (event, enable) => {
|
||||
if (enable) {
|
||||
autoLauncher.enable().then(() => {
|
||||
console.log('开机启动已启用');
|
||||
}).catch(err => {
|
||||
console.error('启用开机启动失败:', err);
|
||||
});
|
||||
autoLauncher
|
||||
.enable()
|
||||
.then(() => {
|
||||
console.log('开机启动已启用')
|
||||
isAutoLaunchEnabled = true
|
||||
// 重新创建托盘菜单以更新状态
|
||||
createTray()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('启用开机启动失败:', err)
|
||||
})
|
||||
} else {
|
||||
autoLauncher.disable().then(() => {
|
||||
console.log('开机启动已禁用');
|
||||
}).catch(err => {
|
||||
console.error('禁用开机启动失败:', err);
|
||||
});
|
||||
autoLauncher
|
||||
.disable()
|
||||
.then(() => {
|
||||
console.log('开机启动已禁用')
|
||||
isAutoLaunchEnabled = false
|
||||
// 重新创建托盘菜单以更新状态
|
||||
createTray()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('禁用开机启动失败:', err)
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
// 当窗口关闭时触发
|
||||
mainWindow.on('closed', function () {
|
||||
// 取消对窗口对象的引用,通常会存储窗口在数组中,这是删除相应元素的时候
|
||||
mainWindow = null;
|
||||
});
|
||||
mainWindow = null
|
||||
// 清除GPU监控定时器
|
||||
if (gpuMonitorInterval) {
|
||||
clearInterval(gpuMonitorInterval)
|
||||
gpuMonitorInterval = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 保存窗口原始位置和大小
|
||||
let originalBounds = { width: 350, height: 150 }
|
||||
|
||||
// 当Electron完成初始化并准备创建浏览器窗口时调用此方法
|
||||
app.whenReady().then(() => {
|
||||
createWindow();
|
||||
createWindow()
|
||||
|
||||
// 创建系统托盘
|
||||
createTray();
|
||||
createTray()
|
||||
|
||||
// 启动GPU监控
|
||||
startGpuMonitoring()
|
||||
|
||||
// 显示窗口
|
||||
mainWindow.show()
|
||||
|
||||
// 监听窗口失焦事件
|
||||
mainWindow.on('blur', () => {
|
||||
// 2秒后缩小窗口
|
||||
setTimeout(() => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
// 保存当前窗口位置和大小
|
||||
const bounds = mainWindow.getBounds()
|
||||
originalBounds = { ...bounds }
|
||||
|
||||
// 缩小窗口到小球大小,以顶部中心点为基准
|
||||
const newX = Math.round(bounds.x + bounds.width / 2 - 20)
|
||||
const newY = bounds.y
|
||||
|
||||
try {
|
||||
mainWindow.setBounds({
|
||||
x: newX,
|
||||
y: newY,
|
||||
width: 150,
|
||||
height: 210,
|
||||
})
|
||||
// 移除backgroundMaterial带来的阴影
|
||||
mainWindow.setBackgroundMaterial('none')
|
||||
mainWindow.setOpacity(0.3)
|
||||
} catch (error) {
|
||||
console.error('设置窗口边界时出错:', error)
|
||||
}
|
||||
}
|
||||
}, 2000)
|
||||
|
||||
// 通知渲染进程窗口已失焦
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('window-blurred')
|
||||
}
|
||||
})
|
||||
|
||||
// 监听窗口聚焦事件
|
||||
mainWindow.on('focus', () => {
|
||||
// 恢复窗口到原始大小和位置
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
try {
|
||||
mainWindow.setBounds({
|
||||
x: originalBounds.x,
|
||||
y: originalBounds.y,
|
||||
width: originalBounds.width,
|
||||
height: originalBounds.height,
|
||||
})
|
||||
// 恢复backgroundMaterial带来的阴影
|
||||
mainWindow.setBackgroundMaterial('mica')
|
||||
mainWindow.setOpacity(1)
|
||||
} catch (error) {
|
||||
console.error('设置窗口边界时出错:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 通知渲染进程窗口已聚焦
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('window-focused')
|
||||
}
|
||||
})
|
||||
|
||||
app.on('activate', function () {
|
||||
// 在macOS上,当单击dock图标并且没有其他窗口打开时,通常在应用程序中重新创建一个窗口
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow();
|
||||
});
|
||||
});
|
||||
if (BrowserWindow.getAllWindows().length === 0) createWindow()
|
||||
})
|
||||
})
|
||||
|
||||
// 当所有窗口都关闭时退出应用
|
||||
app.on('window-all-closed', function () {
|
||||
// 在macOS上,应用程序及其菜单栏通常会保持活动状态,直到用户明确退出
|
||||
if (process.platform !== 'darwin') app.quit();
|
||||
});
|
||||
if (process.platform !== 'darwin') app.quit()
|
||||
})
|
||||
|
||||
// 创建系统托盘
|
||||
function createTray() {
|
||||
// 创建托盘图标
|
||||
const iconPath = path.join(__dirname, 'assets/icon.png');
|
||||
let icon;
|
||||
const iconPath = path.join(__dirname, 'assets/icon.png')
|
||||
let icon
|
||||
try {
|
||||
icon = nativeImage.createFromPath(iconPath);
|
||||
icon = nativeImage.createFromPath(iconPath)
|
||||
} catch (error) {
|
||||
// 如果找不到图标文件,使用默认图标
|
||||
icon = nativeImage.createEmpty();
|
||||
icon = nativeImage.createEmpty()
|
||||
}
|
||||
|
||||
tray = new Tray(icon);
|
||||
|
||||
|
||||
tray = new Tray(icon)
|
||||
|
||||
// 创建上下文菜单
|
||||
const contextMenu = Menu.buildFromTemplate([
|
||||
{
|
||||
label: '显示',
|
||||
click: () => {
|
||||
mainWindow.show();
|
||||
}
|
||||
mainWindow.show()
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '开机启动',
|
||||
type: 'checkbox',
|
||||
checked: isAutoLaunchEnabled,
|
||||
click: () => {
|
||||
if (isAutoLaunchEnabled) {
|
||||
autoLauncher
|
||||
.disable()
|
||||
.then(() => {
|
||||
console.log('开机启动已禁用')
|
||||
isAutoLaunchEnabled = false
|
||||
// 重新创建托盘菜单以更新状态
|
||||
createTray()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('禁用开机启动失败:', err)
|
||||
})
|
||||
} else {
|
||||
autoLauncher
|
||||
.enable()
|
||||
.then(() => {
|
||||
console.log('开机启动已启用')
|
||||
isAutoLaunchEnabled = true
|
||||
// 重新创建托盘菜单以更新状态
|
||||
createTray()
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('启用开机启动失败:', err)
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '退出',
|
||||
click: () => {
|
||||
app.quit();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
tray.setContextMenu(contextMenu);
|
||||
|
||||
app.quit()
|
||||
},
|
||||
},
|
||||
])
|
||||
|
||||
tray.setContextMenu(contextMenu)
|
||||
|
||||
// 点击托盘图标显示窗口
|
||||
tray.on('click', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
}
|
||||
mainWindow.show()
|
||||
})
|
||||
}
|
||||
|
||||
// 启动GPU监控
|
||||
function startGpuMonitoring() {
|
||||
// 每秒获取一次GPU信息
|
||||
gpuMonitorInterval = setInterval(async () => {
|
||||
try {
|
||||
// 获取GPU信息
|
||||
const graphicsData = await si.graphics()
|
||||
|
||||
// 过滤掉虚拟GPU设备(如OrayIddDriver等)
|
||||
const physicalGpus = graphicsData.controllers.filter(controller => {
|
||||
// 忽略虚拟GPU和软件渲染器
|
||||
const isVirtual = controller.model.includes('Oray') || controller.model.includes('Virtual') || controller.model.includes('Software') || controller.vendor.includes('Oray') || (controller.vram === 0 && !controller.memoryTotal)
|
||||
return !isVirtual
|
||||
})
|
||||
|
||||
// 提取显存信息
|
||||
const gpuInfo = physicalGpus.map(controller => ({
|
||||
name: controller.model,
|
||||
memoryUsed: controller.memoryUsed,
|
||||
memoryTotal: controller.memoryTotal,
|
||||
memoryFree: controller.memoryFree,
|
||||
utilizationGpu: controller.utilizationGpu,
|
||||
temperature: controller.temperatureGpu,
|
||||
vram: controller.vram,
|
||||
}))
|
||||
|
||||
// 发送GPU信息到渲染进程
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
mainWindow.webContents.send('gpu-info', gpuInfo)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取GPU信息时出错:', error)
|
||||
}
|
||||
}, 1000) // 每秒更新一次
|
||||
}
|
||||
|
||||
206
npminstall-debug.log
Normal file
206
npminstall-debug.log
Normal file
@@ -0,0 +1,206 @@
|
||||
{
|
||||
root: 'E:\\yuantao\\motioner',
|
||||
registry: 'https://registry.npmmirror.com',
|
||||
pkgs: [],
|
||||
production: false,
|
||||
cacheStrict: false,
|
||||
cacheDir: 'C:\\Users\\MSI\\.npminstall_tarball',
|
||||
env: {
|
||||
npm_config_registry: 'https://registry.npmmirror.com',
|
||||
npm_config_argv: '{"remain":[],"cooked":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\MSI\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com"],"original":["--fix-bug-versions","--china","--userconfig=C:\\\\Users\\\\MSI\\\\.cnpmrc","--disturl=https://cdn.npmmirror.com/binaries/node","--registry=https://registry.npmmirror.com"]}',
|
||||
npm_config_user_agent: 'npminstall/7.12.0 npm/? node/v24.0.1 win32 x64',
|
||||
npm_config_cache: 'C:\\Users\\MSI\\.npminstall_tarball',
|
||||
NODE: 'C:\\Users\\MSI\\.nvmd\\versions\\24.0.1\\node.exe',
|
||||
npm_node_execpath: 'C:\\Users\\MSI\\.nvmd\\versions\\24.0.1\\node.exe',
|
||||
npm_execpath: 'C:\\Users\\MSI\\.nvmd\\versions\\24.0.1\\node_modules\\cnpm\\node_modules\\npminstall\\bin\\install.js',
|
||||
npm_config_userconfig: 'C:\\Users\\MSI\\.cnpmrc',
|
||||
npm_config_disturl: 'https://cdn.npmmirror.com/binaries/node',
|
||||
npm_config_r: 'https://registry.npmmirror.com',
|
||||
COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com',
|
||||
EDGEDRIVER_CDNURL: 'https://npmmirror.com/mirrors/edgedriver',
|
||||
NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
|
||||
NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
|
||||
PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs',
|
||||
CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver',
|
||||
OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver',
|
||||
CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip',
|
||||
ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/',
|
||||
ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/',
|
||||
SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass',
|
||||
SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc',
|
||||
NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v',
|
||||
PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_CHROME_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_CHROME_HEADLESS_SHELL_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright',
|
||||
SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli',
|
||||
SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl',
|
||||
RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2',
|
||||
RE2_DOWNLOAD_SKIP_PATH: 'true',
|
||||
PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma',
|
||||
npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3',
|
||||
npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar',
|
||||
npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp',
|
||||
npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips',
|
||||
npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs',
|
||||
npm_config_gl_binary_host: 'https://cdn.npmmirror.com/binaries/gl',
|
||||
npm_rootpath: 'E:\\yuantao\\motioner',
|
||||
INIT_CWD: 'E:\\yuantao\\motioner'
|
||||
},
|
||||
binaryMirrors: {
|
||||
ENVS: {
|
||||
COREPACK_NPM_REGISTRY: 'https://registry.npmmirror.com',
|
||||
EDGEDRIVER_CDNURL: 'https://npmmirror.com/mirrors/edgedriver',
|
||||
NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
|
||||
NVM_NODEJS_ORG_MIRROR: 'https://cdn.npmmirror.com/binaries/node',
|
||||
PHANTOMJS_CDNURL: 'https://cdn.npmmirror.com/binaries/phantomjs',
|
||||
CHROMEDRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/chromedriver',
|
||||
OPERADRIVER_CDNURL: 'https://cdn.npmmirror.com/binaries/operadriver',
|
||||
CYPRESS_DOWNLOAD_PATH_TEMPLATE: 'https://cdn.npmmirror.com/binaries/cypress/${version}/${platform}-${arch}/cypress.zip',
|
||||
ELECTRON_MIRROR: 'https://cdn.npmmirror.com/binaries/electron/',
|
||||
ELECTRON_BUILDER_BINARIES_MIRROR: 'https://cdn.npmmirror.com/binaries/electron-builder-binaries/',
|
||||
SASS_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-sass',
|
||||
SWC_BINARY_SITE: 'https://cdn.npmmirror.com/binaries/node-swc',
|
||||
NWJS_URLBASE: 'https://cdn.npmmirror.com/binaries/nwjs/v',
|
||||
PUPPETEER_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_CHROME_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PUPPETEER_CHROME_HEADLESS_SHELL_DOWNLOAD_BASE_URL: 'https://cdn.npmmirror.com/binaries/chrome-for-testing',
|
||||
PLAYWRIGHT_DOWNLOAD_HOST: 'https://cdn.npmmirror.com/binaries/playwright',
|
||||
SENTRYCLI_CDNURL: 'https://cdn.npmmirror.com/binaries/sentry-cli',
|
||||
SAUCECTL_INSTALL_BINARY_MIRROR: 'https://cdn.npmmirror.com/binaries/saucectl',
|
||||
RE2_DOWNLOAD_MIRROR: 'https://cdn.npmmirror.com/binaries/node-re2',
|
||||
RE2_DOWNLOAD_SKIP_PATH: 'true',
|
||||
PRISMA_ENGINES_MIRROR: 'https://cdn.npmmirror.com/binaries/prisma',
|
||||
npm_config_better_sqlite3_binary_host: 'https://cdn.npmmirror.com/binaries/better-sqlite3',
|
||||
npm_config_keytar_binary_host: 'https://cdn.npmmirror.com/binaries/keytar',
|
||||
npm_config_sharp_binary_host: 'https://cdn.npmmirror.com/binaries/sharp',
|
||||
npm_config_sharp_libvips_binary_host: 'https://cdn.npmmirror.com/binaries/sharp-libvips',
|
||||
npm_config_robotjs_binary_host: 'https://cdn.npmmirror.com/binaries/robotjs',
|
||||
npm_config_gl_binary_host: 'https://cdn.npmmirror.com/binaries/gl'
|
||||
},
|
||||
'@ali/s2': { host: 'https://cdn.npmmirror.com/binaries/looksgood-s2' },
|
||||
sharp: { replaceHostFiles: [Array], replaceHostMap: [Object] },
|
||||
'@tensorflow/tfjs-node': {
|
||||
replaceHostFiles: [Array],
|
||||
replaceHostRegExpMap: [Object],
|
||||
replaceHostMap: [Object]
|
||||
},
|
||||
cypress: {
|
||||
host: 'https://cdn.npmmirror.com/binaries/cypress',
|
||||
newPlatforms: [Object]
|
||||
},
|
||||
'utf-8-validate': {
|
||||
host: 'https://cdn.npmmirror.com/binaries/utf-8-validate/v{version}'
|
||||
},
|
||||
xprofiler: {
|
||||
remote_path: './xprofiler/v{version}/',
|
||||
host: 'https://cdn.npmmirror.com/binaries'
|
||||
},
|
||||
leveldown: { host: 'https://cdn.npmmirror.com/binaries/leveldown/v{version}' },
|
||||
couchbase: { host: 'https://cdn.npmmirror.com/binaries/couchbase/v{version}' },
|
||||
gl: { host: 'https://cdn.npmmirror.com/binaries/gl/v{version}' },
|
||||
sqlite3: {
|
||||
host: 'https://cdn.npmmirror.com/binaries/sqlite3',
|
||||
remote_path: 'v{version}'
|
||||
},
|
||||
'@journeyapps/sqlcipher': { host: 'https://cdn.npmmirror.com/binaries' },
|
||||
grpc: {
|
||||
host: 'https://cdn.npmmirror.com/binaries',
|
||||
remote_path: '{name}/v{version}'
|
||||
},
|
||||
'grpc-tools': { host: 'https://cdn.npmmirror.com/binaries' },
|
||||
wrtc: {
|
||||
host: 'https://cdn.npmmirror.com/binaries',
|
||||
remote_path: '{name}/v{version}'
|
||||
},
|
||||
fsevents: { host: 'https://cdn.npmmirror.com/binaries/fsevents' },
|
||||
nodejieba: { host: 'https://cdn.npmmirror.com/binaries/nodejieba' },
|
||||
canvas: {
|
||||
host: 'https://cdn.npmmirror.com/binaries/canvas',
|
||||
remote_path: 'v{version}'
|
||||
},
|
||||
'skia-canvas': { host: 'https://cdn.npmmirror.com/binaries/skia-canvas' },
|
||||
'flow-bin': {
|
||||
replaceHost: 'https://github.com/facebook/flow/releases/download/v',
|
||||
host: 'https://cdn.npmmirror.com/binaries/flow/v'
|
||||
},
|
||||
'jpegtran-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/jpegtran-bin'
|
||||
},
|
||||
'cwebp-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/cwebp-bin'
|
||||
},
|
||||
'zopflipng-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/zopflipng-bin'
|
||||
},
|
||||
'optipng-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/optipng-bin'
|
||||
},
|
||||
mozjpeg: {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/mozjpeg-bin'
|
||||
},
|
||||
gifsicle: {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/gifsicle-bin'
|
||||
},
|
||||
'pngquant-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/pngquant-bin',
|
||||
replaceHostMap: [Object]
|
||||
},
|
||||
'pngcrush-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/pngcrush-bin'
|
||||
},
|
||||
'jpeg-recompress-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/jpeg-recompress-bin'
|
||||
},
|
||||
'advpng-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/advpng-bin'
|
||||
},
|
||||
'pngout-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/pngout-bin'
|
||||
},
|
||||
'jpegoptim-bin': {
|
||||
replaceHost: [Array],
|
||||
host: 'https://cdn.npmmirror.com/binaries/jpegoptim-bin'
|
||||
},
|
||||
argon2: { host: 'https://cdn.npmmirror.com/binaries/argon2' },
|
||||
'ali-zeromq': { host: 'https://cdn.npmmirror.com/binaries/ali-zeromq' },
|
||||
'ali-usb_ctl': { host: 'https://cdn.npmmirror.com/binaries/ali-usb_ctl' },
|
||||
'gdal-async': { host: 'https://cdn.npmmirror.com/binaries/node-gdal-async' },
|
||||
'libpg-query': { host: 'https://cdn.npmmirror.com/binaries' }
|
||||
},
|
||||
forbiddenLicenses: null,
|
||||
flatten: false,
|
||||
proxy: undefined,
|
||||
prune: false,
|
||||
disableFallbackStore: false,
|
||||
workspacesMap: Map(0) {},
|
||||
enableWorkspace: false,
|
||||
workspaceRoot: 'E:\\yuantao\\motioner',
|
||||
isWorkspaceRoot: true,
|
||||
isWorkspacePackage: false,
|
||||
offline: false,
|
||||
strictSSL: true,
|
||||
ignoreScripts: false,
|
||||
foregroundScripts: false,
|
||||
ignoreOptionalDependencies: false,
|
||||
detail: false,
|
||||
forceLinkLatest: false,
|
||||
trace: false,
|
||||
engineStrict: false,
|
||||
registryOnly: false,
|
||||
client: false,
|
||||
autoFixVersion: [Function: autoFixVersion]
|
||||
}
|
||||
@@ -18,7 +18,9 @@
|
||||
"license": "ISC",
|
||||
"keywords": [],
|
||||
"dependencies": {
|
||||
"auto-launch": "*"
|
||||
"auto-launch": "*",
|
||||
"pidusage": "*",
|
||||
"systeminformation": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "*",
|
||||
|
||||
7
test-gpu.js
Normal file
7
test-gpu.js
Normal file
@@ -0,0 +1,7 @@
|
||||
const si = require('systeminformation');
|
||||
|
||||
si.graphics().then(data => {
|
||||
console.log(JSON.stringify(data, null, 2));
|
||||
}).catch(error => {
|
||||
console.error('Error:', error);
|
||||
});
|
||||
Reference in New Issue
Block a user