Python+Qt實(shí)現(xiàn)智能應(yīng)用時(shí)長(zhǎng)統(tǒng)計(jì)工具
概述:數(shù)字時(shí)代的時(shí)間管理者
在數(shù)字化生活日益普及的今天,我們每天與各種應(yīng)用程序的交互時(shí)間越來越長(zhǎng)。但你是否真正了解自己的數(shù)字生活習(xí)慣?哪些應(yīng)用占用了你最多的時(shí)間?時(shí)間都花在了哪里?
今天我將介紹一款自主研發(fā)的應(yīng)用時(shí)長(zhǎng)統(tǒng)計(jì)工具,它能夠:
- 實(shí)時(shí)監(jiān)控應(yīng)用使用情況
- 可視化展示時(shí)間分配
- 分析每周使用趨勢(shì)
- 最小化到系統(tǒng)托盤后臺(tái)運(yùn)行
這款工具采用Python+Qt開發(fā),結(jié)合matplotlib實(shí)現(xiàn)專業(yè)級(jí)數(shù)據(jù)可視化,是程序員和普通用戶都能輕松使用的效率工具。

功能全景圖
核心功能矩陣
| 功能模塊 | 技術(shù)實(shí)現(xiàn) | 數(shù)據(jù)維度 |
|---|---|---|
| 實(shí)時(shí)監(jiān)控 | psutil+win32api跨平臺(tái)采集 | 秒級(jí)精度 |
| 數(shù)據(jù)持久化 | JSON序列化+按日期存儲(chǔ) | 歷史數(shù)據(jù)可追溯 |
| 可視化分析 | Matplotlib+Qt5嵌入式圖表 | 多維數(shù)據(jù)呈現(xiàn) |
| 系統(tǒng)托盤 | QSystemTrayIcon | 無感后臺(tái)運(yùn)行 |
| 周趨勢(shì)分析 | 時(shí)間序列聚合+堆疊柱狀圖 | 七日對(duì)比 |
技術(shù)棧亮點(diǎn)
- 應(yīng)用識(shí)別:Windows/MacOS/Linux三平臺(tái)兼容
- 性能優(yōu)化:定時(shí)器精準(zhǔn)控制1秒采集間隔
- 數(shù)據(jù)安全:雙重備份機(jī)制(實(shí)時(shí)+每日)
- 交互設(shè)計(jì):標(biāo)簽頁自動(dòng)刷新+表格高亮交互
效果展示
1. 實(shí)時(shí)數(shù)據(jù)看板

特點(diǎn)說明:
- 當(dāng)前運(yùn)行應(yīng)用藍(lán)色高亮
- 使用時(shí)長(zhǎng)TOP3金色標(biāo)識(shí)
- 實(shí)時(shí)刷新排名變化
2. 時(shí)長(zhǎng)占比分析

智能處理:
- 自動(dòng)合并<5%的小項(xiàng)為"其他"
- 中心顯示總時(shí)長(zhǎng)
- 響應(yīng)式標(biāo)簽防重疊
3. 周趨勢(shì)圖譜

分析維度:
- Top5應(yīng)用每日對(duì)比
- 小時(shí)為單位直觀顯示
- 顏色編碼區(qū)分應(yīng)用
部署與使用指南
環(huán)境準(zhǔn)備
# 基礎(chǔ)依賴 pip install pyqt5 psutil matplotlib # 平臺(tái)特定依賴 # Windows pip install pywin32
使用教程
python app_usage_tracker.py
最小化到托盤:關(guān)閉窗口自動(dòng)后臺(tái)運(yùn)行
數(shù)據(jù)查看:
- 實(shí)時(shí)數(shù)據(jù)頁:查看當(dāng)前會(huì)話統(tǒng)計(jì)
- 圖表頁:點(diǎn)擊標(biāo)簽自動(dòng)刷新
數(shù)據(jù)存儲(chǔ):
- app_usage_data/目錄下查看歷史數(shù)據(jù)
- 每日數(shù)據(jù)自動(dòng)歸檔
自定義配置
# 修改監(jiān)控頻率(毫秒) self.timer.start(1000) # 默認(rèn)1秒 # 修改數(shù)據(jù)存儲(chǔ)路徑 self.data_dir = "custom_data_path"
核心代碼解析
1. 應(yīng)用進(jìn)程監(jiān)控引擎
def get_current_app(self):
"""跨平臺(tái)應(yīng)用識(shí)別核心方法"""
try:
if platform.system() == "Windows":
import win32gui
window = win32gui.GetForegroundWindow()
_, pid = win32gui.GetWindowThreadProcessId(window)
return psutil.Process(pid).name()
# 其他平臺(tái)處理...
except Exception as e:
print(f"應(yīng)用識(shí)別異常: {e}")
return "Unknown"
技術(shù)要點(diǎn):
Windows使用win32gui獲取前臺(tái)窗口
Linux依賴xdotool工具鏈
MacOS通過AppKit接口
2. 數(shù)據(jù)可視化引擎
def update_weekly_chart(self):
"""周趨勢(shì)圖生成邏輯"""
# 1. 數(shù)據(jù)準(zhǔn)備
dates = [datetime.now().date() - timedelta(days=i) for i in range(6, -1, -1)]
# 2. Top5應(yīng)用篩選
app_total_time = defaultdict(float)
for date in dates:
daily_data = self.load_daily_data(date.strftime("%Y-%m-%d"))
for app, secs in (daily_data or {}).items():
app_total_time[app] += secs
# 3. 柱狀圖繪制
fig, ax = plt.subplots(figsize=(12,7))
for i, (app, _) in enumerate(sorted(app_total_time.items(), key=lambda x: x[1], reverse=True)[:5]):
daily_usage = [
self.load_daily_data(d.strftime("%Y-%m-%d")).get(app, 0)/3600
for d in dates
]
ax.bar(x + i*width, daily_usage, width, label=app)
# 4. 圖表美化...設(shè)計(jì)亮點(diǎn):
動(dòng)態(tài)數(shù)據(jù)聚合
自動(dòng)響應(yīng)式布局
視覺層次分明
3. 系統(tǒng)托盤集成
def init_system_tray(self):
"""托盤圖標(biāo)管理系統(tǒng)"""
self.tray_icon = QSystemTrayIcon(self)
menu = QMenu()
menu.addAction("顯示主界面", self.show)
menu.addAction("退出", self.close_app)
self.tray_icon.setContextMenu(menu)
self.tray_icon.show()
交互設(shè)計(jì):
右鍵菜單快速操作
雙擊恢復(fù)窗口
氣泡消息提示
源碼下載
import sys
import time
from datetime import datetime, timedelta
import json
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QWidget,
QTableWidget, QTableWidgetItem, QSystemTrayIcon,
QMenu, QAction, QMessageBox, QTabWidget, QHeaderView,
QLabel)
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtGui import QIcon, QFont, QColor
import psutil
import platform
import os
import numpy as np
from matplotlib import cm
from matplotlib.font_manager import FontProperties
# 設(shè)置matplotlib支持中文顯示
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 使用微軟雅黑
plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示負(fù)號(hào)
class AppUsageTracker(QMainWindow):
def __init__(self):
super().__init__()
# 初始化數(shù)據(jù)
self.app_data = {}
self.current_app = None
self.last_update_time = time.time()
self.data_dir = "app_usage_data"
self.data_file = os.path.join(self.data_dir, "current_usage.json")
# 創(chuàng)建數(shù)據(jù)目錄
os.makedirs(self.data_dir, exist_ok=True)
# 加載歷史數(shù)據(jù)
self.load_data()
# 初始化UI
self.init_ui()
# 設(shè)置系統(tǒng)托盤
self.init_system_tray()
# 設(shè)置定時(shí)器
self.timer = QTimer()
self.timer.timeout.connect(self.update_app_usage)
self.timer.start(1000) # 每秒更新一次
# 每周數(shù)據(jù)定時(shí)保存
self.weekly_save_timer = QTimer()
self.weekly_save_timer.timeout.connect(self.save_weekly_data)
self.weekly_save_timer.start(3600000) # 每小時(shí)檢查一次
def init_ui(self):
"""初始化用戶界面"""
self.setWindowTitle("?? 應(yīng)用使用時(shí)長(zhǎng)統(tǒng)計(jì)")
self.setWindowIcon(QIcon(self.get_emoji_icon("??")))
self.setGeometry(100, 100, 1200, 800) # 增大窗口尺寸
# 主布局
main_widget = QWidget()
self.setCentralWidget(main_widget)
layout = QVBoxLayout(main_widget)
# 創(chuàng)建標(biāo)簽頁
self.tabs = QTabWidget()
self.tabs.currentChanged.connect(self.on_tab_changed)
layout.addWidget(self.tabs)
# 初始化各標(biāo)簽頁
self.init_realtime_tab()
self.init_bar_chart_tab()
self.init_pie_chart_tab()
self.init_weekly_chart_tab() # 修改為周統(tǒng)計(jì)圖表頁
# 添加工具欄
self.init_toolbar()
def on_tab_changed(self, index):
"""標(biāo)簽頁切換時(shí)自動(dòng)刷新圖表"""
if index == 1: # 使用時(shí)長(zhǎng)排行標(biāo)簽頁
self.update_bar_chart()
elif index == 2: # 使用時(shí)長(zhǎng)占比標(biāo)簽頁
self.update_pie_chart()
elif index == 3: # 周統(tǒng)計(jì)數(shù)據(jù)標(biāo)簽頁
self.update_weekly_chart()
def init_realtime_tab(self):
"""初始化實(shí)時(shí)數(shù)據(jù)標(biāo)簽頁"""
self.realtime_tab = QWidget()
self.tabs.addTab(self.realtime_tab, "?? 實(shí)時(shí)數(shù)據(jù)")
layout = QVBoxLayout(self.realtime_tab)
# 添加標(biāo)題
title_label = QLabel("應(yīng)用使用時(shí)長(zhǎng)實(shí)時(shí)統(tǒng)計(jì)")
title_label.setFont(QFont("Microsoft YaHei", 12, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("padding: 10px;")
layout.addWidget(title_label)
# 創(chuàng)建表格
self.table = QTableWidget()
self.table.setColumnCount(3)
self.table.setHorizontalHeaderLabels(["排名", "應(yīng)用名稱", "使用時(shí)長(zhǎng)"])
# 設(shè)置表格樣式
self.table.setStyleSheet(
"QTableWidget { border: 1px solid #e0e0e0; }"
"QTableWidget::item { padding: 5px; }"
"QHeaderView::section { background-color: #f0f0f0; padding: 5px; }"
)
self.table.setColumnWidth(0, 60)
self.table.setColumnWidth(1, 300)
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
self.table.setEditTriggers(QTableWidget.NoEditTriggers)
self.table.setSortingEnabled(False)
self.table.setAlternatingRowColors(True)
layout.addWidget(self.table)
def init_bar_chart_tab(self):
"""初始化條形圖標(biāo)簽頁"""
self.bar_chart_tab = QWidget()
self.tabs.addTab(self.bar_chart_tab, "?? 使用時(shí)長(zhǎng)排行")
layout = QVBoxLayout(self.bar_chart_tab)
# 添加標(biāo)題
title_label = QLabel("應(yīng)用使用時(shí)長(zhǎng)排行榜")
title_label.setFont(QFont("Microsoft YaHei", 12, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("padding: 10px;")
layout.addWidget(title_label)
# 創(chuàng)建圖表
self.bar_figure = plt.figure(figsize=(10, 6), dpi=100, facecolor='white')
self.bar_canvas = FigureCanvas(self.bar_figure)
layout.addWidget(self.bar_canvas)
def init_pie_chart_tab(self):
"""初始化餅圖標(biāo)簽頁"""
self.pie_chart_tab = QWidget()
self.tabs.addTab(self.pie_chart_tab, "?? 使用時(shí)長(zhǎng)占比")
layout = QVBoxLayout(self.pie_chart_tab)
# 添加標(biāo)題
title_label = QLabel("應(yīng)用使用時(shí)長(zhǎng)占比")
title_label.setFont(QFont("Microsoft YaHei", 12, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("padding: 10px;")
layout.addWidget(title_label)
# 創(chuàng)建圖表
self.pie_figure = plt.figure(figsize=(10, 6), dpi=100, facecolor='white')
self.pie_canvas = FigureCanvas(self.pie_figure)
layout.addWidget(self.pie_canvas)
def init_weekly_chart_tab(self):
"""初始化周統(tǒng)計(jì)圖表標(biāo)簽頁"""
self.weekly_chart_tab = QWidget()
self.tabs.addTab(self.weekly_chart_tab, "?? 周使用趨勢(shì)")
layout = QVBoxLayout(self.weekly_chart_tab)
# 添加標(biāo)題
title_label = QLabel("近七日應(yīng)用使用時(shí)長(zhǎng)趨勢(shì)")
title_label.setFont(QFont("Microsoft YaHei", 12, QFont.Bold))
title_label.setAlignment(Qt.AlignCenter)
title_label.setStyleSheet("padding: 10px;")
layout.addWidget(title_label)
# 創(chuàng)建圖表
self.weekly_figure = plt.figure(figsize=(12, 7), dpi=100, facecolor='white')
self.weekly_canvas = FigureCanvas(self.weekly_figure)
layout.addWidget(self.weekly_canvas)
def init_toolbar(self):
"""初始化工具欄"""
self.refresh_button = QAction("?? 刷新圖表", self)
self.refresh_button.triggered.connect(self.update_all_charts)
self.toolbar = self.addToolBar("工具欄")
self.toolbar.addAction(self.refresh_button)
def init_system_tray(self):
"""初始化系統(tǒng)托盤"""
self.tray_icon = QSystemTrayIcon(self)
self.tray_icon.setIcon(QIcon(self.get_emoji_icon("??")))
tray_menu = QMenu()
show_action = QAction("?? 顯示窗口", self)
show_action.triggered.connect(self.show)
tray_menu.addAction(show_action)
exit_action = QAction("?? 退出", self)
exit_action.triggered.connect(self.close_app)
tray_menu.addAction(exit_action)
self.tray_icon.setContextMenu(tray_menu)
self.tray_icon.show()
self.tray_icon.activated.connect(self.tray_icon_clicked)
def tray_icon_clicked(self, reason):
"""托盤圖標(biāo)點(diǎn)擊事件處理"""
if reason == QSystemTrayIcon.DoubleClick:
self.show()
self.activateWindow()
def close_app(self):
"""退出應(yīng)用程序"""
self.save_data()
self.tray_icon.hide()
QApplication.quit()
def closeEvent(self, event):
"""重寫關(guān)閉事件,最小化到托盤"""
event.ignore()
self.hide()
self.tray_icon.showMessage(
"應(yīng)用使用時(shí)長(zhǎng)統(tǒng)計(jì)",
"程序已最小化到系統(tǒng)托盤",
QSystemTrayIcon.Information,
2000
)
def get_emoji_icon(self, emoji):
"""將emoji轉(zhuǎn)換為圖標(biāo)"""
return emoji
def get_current_app(self):
"""獲取當(dāng)前活動(dòng)窗口的應(yīng)用"""
try:
current_app = "Unknown"
if platform.system() == "Windows":
import win32gui
import win32process
window = win32gui.GetForegroundWindow()
_, pid = win32process.GetWindowThreadProcessId(window)
try:
process = psutil.Process(pid)
current_app = process.name()
current_app = os.path.splitext(current_app)[0]
except (psutil.NoSuchProcess, psutil.AccessDenied):
current_app = "Unknown"
elif platform.system() == "Darwin":
from AppKit import NSWorkspace
current_app = NSWorkspace.sharedWorkspace().frontmostApplication().localizedName()
else: # Linux
import subprocess
try:
window_id = subprocess.check_output(["xdotool", "getactivewindow"]).decode().strip()
pid = subprocess.check_output(["xdotool", "getwindowpid", window_id]).decode().strip()
process = psutil.Process(int(pid))
current_app = process.name()
current_app = os.path.splitext(current_app)[0]
except:
current_app = "Unknown"
# 清理應(yīng)用名稱
for suffix in ['.exe', '.bin', '.app']:
if current_app.endswith(suffix):
current_app = current_app[:-len(suffix)]
return current_app
except Exception as e:
print(f"獲取應(yīng)用名稱出錯(cuò): {e}")
return "Unknown"
def update_app_usage(self):
"""更新應(yīng)用使用時(shí)長(zhǎng)"""
current_app = self.get_current_app()
current_time = time.time()
time_elapsed = current_time - self.last_update_time
if self.current_app is not None:
if self.current_app in self.app_data:
self.app_data[self.current_app] += time_elapsed
else:
self.app_data[self.current_app] = time_elapsed
self.current_app = current_app
self.last_update_time = current_time
self.update_table()
if current_time % 3600 < 1: # 大約每小時(shí)保存一次
self.save_data()
def update_table(self):
"""更新表格數(shù)據(jù)"""
sorted_data = sorted(self.app_data.items(), key=lambda x: x[1], reverse=True)
self.table.setRowCount(len(sorted_data))
for row, (app, seconds) in enumerate(sorted_data):
# 排名
rank_item = QTableWidgetItem(str(row + 1))
rank_item.setTextAlignment(Qt.AlignCenter)
# 應(yīng)用名稱
app_item = QTableWidgetItem(app)
# 使用時(shí)長(zhǎng)
time_item = QTableWidgetItem(self.format_time(seconds))
time_item.setTextAlignment(Qt.AlignRight)
# 設(shè)置數(shù)據(jù)
for item in [rank_item, app_item, time_item]:
item.setData(Qt.UserRole, seconds)
self.table.setItem(row, 0, rank_item)
self.table.setItem(row, 1, app_item)
self.table.setItem(row, 2, time_item)
# 高亮顯示當(dāng)前運(yùn)行應(yīng)用
if app == self.current_app:
for col in range(3):
item = self.table.item(row, col)
item.setBackground(QColor(220, 240, 255))
item.setFont(QFont("Microsoft YaHei", 9, QFont.Bold))
# 設(shè)置前三名樣式
if row < 3:
for col in range(3):
item = self.table.item(row, col)
item.setBackground(QColor(255, 240, 200))
item.setFont(QFont("Microsoft YaHei", 9, QFont.Bold))
def update_all_charts(self):
"""更新所有圖表"""
self.update_bar_chart()
self.update_pie_chart()
self.update_weekly_chart()
def update_bar_chart(self):
"""更新條形圖"""
self.bar_figure.clear()
ax = self.bar_figure.add_subplot(111)
if not self.app_data:
ax.text(0.5, 0.5, "暫無數(shù)據(jù)", ha='center', va='center', fontsize=12)
self.bar_canvas.draw()
return
# 準(zhǔn)備數(shù)據(jù)
sorted_data = sorted(self.app_data.items(), key=lambda x: x[1], reverse=True)
apps = [app[:15] + '...' if len(app) > 15 else app for app, _ in sorted_data]
times = [secs / 3600 for _, secs in sorted_data]
# 創(chuàng)建條形圖
colors = cm.viridis(np.linspace(0.2, 0.8, len(apps)))
bars = ax.barh(np.arange(len(apps)), times, color=colors, edgecolor='none')
# 設(shè)置標(biāo)簽
ax.set_yticks(np.arange(len(apps)))
ax.set_yticklabels(apps)
# 添加數(shù)據(jù)標(biāo)簽
for bar in bars:
width = bar.get_width()
ax.text(width, bar.get_y() + bar.get_height()/2,
f' {width:.1f}h', va='center', ha='left', fontsize=9)
# 美化圖表
ax.set_xlabel('使用時(shí)長(zhǎng) (小時(shí))', fontsize=11)
ax.set_title('應(yīng)用使用時(shí)長(zhǎng)統(tǒng)計(jì)', fontsize=13, pad=15)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.grid(True, axis='x', color='#e0e0e0', linestyle='--', alpha=0.7)
ax.invert_yaxis()
self.bar_figure.tight_layout()
self.bar_canvas.draw()
def update_pie_chart(self):
"""更新餅圖"""
self.pie_figure.clear()
ax = self.pie_figure.add_subplot(111)
if not self.app_data:
ax.text(0.5, 0.5, "暫無數(shù)據(jù)", ha='center', va='center', fontsize=12)
self.pie_canvas.draw()
return
# 準(zhǔn)備數(shù)據(jù)
sorted_data = sorted(self.app_data.items(), key=lambda x: x[1], reverse=True)
total_time = sum(secs for _, secs in sorted_data)
app_percentages = [(app, (secs/total_time)*100) for app, secs in sorted_data]
# 分離主要應(yīng)用和其他應(yīng)用
main_apps = [item for item in app_percentages if item[1] >= 5]
other_apps = [item for item in app_percentages if item[1] < 5]
other_time = sum(secs for _, secs in sorted_data if (secs/total_time)*100 < 5)
if other_apps:
labels = [app for app, _ in main_apps] + ["其他"]
sizes = [secs for _, secs in sorted_data if (secs/total_time)*100 >= 5] + [other_time]
else:
labels = [app for app, _ in main_apps]
sizes = [secs for _, secs in sorted_data]
# 限制標(biāo)簽長(zhǎng)度
labels = [label[:12] + '...' if len(label) > 12 else label for label in labels]
percentages = [(size/total_time)*100 for size in sizes]
# 創(chuàng)建餅圖
colors = cm.plasma(np.linspace(0.2, 0.8, len(labels)))
font = FontProperties(size=9)
wedges, texts, autotexts = ax.pie(
sizes,
labels=labels,
colors=colors,
autopct=lambda p: f'{p:.1f}%' if p >= 5 else '',
startangle=90,
wedgeprops={'linewidth': 1, 'edgecolor': 'white'},
textprops={'fontproperties': font},
pctdistance=0.85,
labeldistance=1.05
)
# 添加中心總時(shí)長(zhǎng)
ax.text(0, 0, f"總時(shí)長(zhǎng)\n{self.format_time(total_time)}",
ha='center', va='center', fontsize=11)
# 添加圖例和標(biāo)題
ax.set_title('應(yīng)用使用時(shí)長(zhǎng)占比 (≥5%顯示)', fontsize=13, pad=15)
legend_labels = [f"{label} ({p:.1f}%)" for label, p in zip(labels, percentages)]
ax.legend(wedges, legend_labels,
title="應(yīng)用列表",
loc="center left",
bbox_to_anchor=(1, 0, 0.5, 1),
frameon=False,
prop=font)
self.pie_figure.tight_layout()
self.pie_canvas.draw()
def update_weekly_chart(self):
"""更新周統(tǒng)計(jì)柱狀圖"""
self.weekly_figure.clear()
ax = self.weekly_figure.add_subplot(111)
# 獲取最近7天的日期
today = datetime.now().date()
dates = [(today - timedelta(days=i)).strftime("%m-%d") for i in range(6, -1, -1)]
full_dates = [(today - timedelta(days=i)).strftime("%Y-%m-%d") for i in range(6, -1, -1)]
# 收集所有應(yīng)用名稱
all_apps = set()
weekly_data = {}
for date in full_dates:
daily_data = self.load_daily_data(date)
if daily_data:
weekly_data[date] = daily_data
all_apps.update(daily_data.keys())
if not all_apps:
ax.text(0.5, 0.5, "暫無周數(shù)據(jù)", ha='center', va='center', fontsize=12)
self.weekly_canvas.draw()
return
# 選擇前5個(gè)最常用的應(yīng)用
app_total_time = {app: 0 for app in all_apps}
for date in full_dates:
if date in weekly_data:
for app, secs in weekly_data[date].items():
app_total_time[app] += secs
top_apps = sorted(app_total_time.items(), key=lambda x: x[1], reverse=True)[:5]
top_apps = [app for app, _ in top_apps]
# 準(zhǔn)備柱狀圖數(shù)據(jù)
bar_width = 0.15
x = np.arange(len(dates))
# 創(chuàng)建顏色映射
colors = cm.viridis(np.linspace(0.2, 0.8, len(top_apps)))
# 繪制每個(gè)應(yīng)用的柱狀圖
for i, app in enumerate(top_apps):
app_times = []
for date in full_dates:
if date in weekly_data and app in weekly_data[date]:
app_times.append(weekly_data[date][app] / 3600) # 轉(zhuǎn)換為小時(shí)
else:
app_times.append(0)
ax.bar(x + i*bar_width, app_times, bar_width,
label=app[:12] + '...' if len(app) > 12 else app,
color=colors[i])
# 設(shè)置x軸標(biāo)簽
ax.set_xticks(x + bar_width * (len(top_apps)-1)/2)
ax.set_xticklabels(dates)
# 美化圖表
ax.set_xlabel('日期', fontsize=11)
ax.set_ylabel('使用時(shí)長(zhǎng) (小時(shí))', fontsize=11)
ax.set_title('近七日應(yīng)用使用時(shí)長(zhǎng)趨勢(shì) (Top 5應(yīng)用)', fontsize=13, pad=15)
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.grid(True, axis='y', color='#e0e0e0', linestyle='--', alpha=0.7)
self.weekly_figure.tight_layout()
self.weekly_canvas.draw()
def format_time(self, seconds):
"""將秒數(shù)格式化為 HH:MM:SS"""
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
seconds = int(seconds % 60)
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
def load_data(self):
"""加載保存的數(shù)據(jù)"""
try:
if os.path.exists(self.data_file):
with open(self.data_file, "r", encoding='utf-8') as f:
self.app_data = {k: float(v) for k, v in json.load(f).items()}
except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
print(f"加載數(shù)據(jù)出錯(cuò): {e}")
self.app_data = {}
def save_data(self):
"""保存當(dāng)前數(shù)據(jù)"""
try:
with open(self.data_file, "w", encoding='utf-8') as f:
json.dump(self.app_data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存數(shù)據(jù)出錯(cuò): {e}")
def load_daily_data(self, date):
"""加載指定日期的數(shù)據(jù)"""
filename = os.path.join(self.data_dir, f"app_usage_{date}.json")
try:
if os.path.exists(filename):
with open(filename, "r", encoding='utf-8') as f:
return json.load(f)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"加載每日數(shù)據(jù)出錯(cuò): {e}")
return None
def save_weekly_data(self):
"""每周數(shù)據(jù)保存"""
today = datetime.now().date().strftime("%Y-%m-%d")
filename = os.path.join(self.data_dir, f"app_usage_{today}.json")
try:
with open(filename, "w", encoding='utf-8') as f:
json.dump(self.app_data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"保存每周數(shù)據(jù)出錯(cuò): {e}")
self.app_data = {}
self.current_app = self.get_current_app()
self.last_update_time = time.time()
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setApplicationName("應(yīng)用使用時(shí)長(zhǎng)統(tǒng)計(jì)")
app.setApplicationDisplayName("?? 應(yīng)用使用時(shí)長(zhǎng)統(tǒng)計(jì)")
tracker = AppUsageTracker()
tracker.show()
sys.exit(app.exec_())
深度優(yōu)化建議
性能提升方向
1. **數(shù)據(jù)壓縮**:對(duì)長(zhǎng)期存儲(chǔ)的JSON數(shù)據(jù)進(jìn)行g(shù)zip壓縮
2. **增量更新**:改用SQLite替代JSON文件存儲(chǔ)
3. **采樣優(yōu)化**:動(dòng)態(tài)調(diào)整監(jiān)控頻率(活躍時(shí)高頻,閑置時(shí)低頻)
功能擴(kuò)展
用戶體驗(yàn)改進(jìn)
- 添加主題切換功能
- 支持導(dǎo)出CSV/PDF報(bào)告
- 增加數(shù)據(jù)篩選功能
總結(jié)與展望
這款應(yīng)用時(shí)長(zhǎng)統(tǒng)計(jì)工具通過:
1. **精準(zhǔn)監(jiān)控**:秒級(jí)采集精度
2. **直觀可視化**:專業(yè)級(jí)圖表呈現(xiàn)
3. **無感運(yùn)行**:完善的托盤管理
4. **數(shù)據(jù)持久化**:雙重備份機(jī)制
實(shí)現(xiàn)了對(duì)數(shù)字生活習(xí)慣的全方位分析。特別適合:
- 自由職業(yè)者時(shí)間管理
- 家長(zhǎng)監(jiān)控兒童設(shè)備使用
- 程序員分析開發(fā)效率
**未來演進(jìn)路線**:
- 移動(dòng)端配套應(yīng)用
- 瀏覽器插件集成
- AI使用建議功能
以上就是Python+Qt實(shí)現(xiàn)智能應(yīng)用時(shí)長(zhǎng)統(tǒng)計(jì)工具的詳細(xì)內(nèi)容,更多關(guān)于Python統(tǒng)計(jì)應(yīng)用時(shí)長(zhǎng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python Excel實(shí)現(xiàn)自動(dòng)添加編號(hào)
這篇文章主要為大家詳細(xì)介紹了如何使用Python在Excel中實(shí)現(xiàn)自動(dòng)添加編號(hào)效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03
一文帶你精通Python中*args和**kwargs的應(yīng)用技巧
如果能在Python中創(chuàng)建適應(yīng)不同場(chǎng)景的函數(shù),而無需每次都重寫它們,會(huì)使得操作簡(jiǎn)潔方便,這就是*args和**kwargs的魔力所在,下面我們就來看看它們的具體一些應(yīng)用技巧吧2024-03-03
關(guān)于python3的ThreadPoolExecutor線程池大小設(shè)置
這篇文章主要介紹了關(guān)于python3的ThreadPoolExecutor線程池大小設(shè)置,線程池的理想大小取決于被提交任務(wù)的類型以及所部署系統(tǒng)的特性,需要的朋友可以參考下2023-04-04

