# 开发个人电子书阅读时长统计工具:分析你的阅读习惯


在信息爆炸的时代,高效阅读成为提升学习与工作效率的关键。通过分析电子书阅读时长,我们可以优化阅读计划、平衡不同书籍的投入时间。本文将带你开发一个个人电子书阅读时长统计工具,支持文件/手动输入阅读记录,自动计算日/周/月阅读时长,并通过可视化图表直观展示趋势。

技术思路分析

该工具分为5大核心模块:
1. 数据输入:支持从txt文件读取或手动输入阅读记录(格式:日期,开始时间,结束时间,书籍名)。
2. 时间处理:解析字符串格式的时间,计算单次阅读时长(转换为分钟)。
3. 统计分析:按日/周/月分组统计总时长,处理跨周/月的边界情况。
4. 数据可视化:用柱状图展示每日时长,折线图展示周/月趋势。
5. GUI交互:通过tkinter实现文件选择、文本输入、结果展示的友好界面。

代码实现(Python)

我们使用tkinter构建GUI,matplotlib实现可视化,datetime处理时间,collections.defaultdict简化统计。

1. 核心库导入与辅助函数

import tkinter as tk
from tkinter import filedialog, scrolledtext
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from datetime import datetime
from collections import defaultdict
import matplotlib.pyplot as plt
import calendar

2. 时间与记录解析

def parse_datetime(date, time_str):
    """将日期和时间字符串组合为datetime对象"""
    time_obj = datetime.strptime(time_str, '%H:%M').time()
    return datetime.combine(date, time_obj)

def calculate_duration(start_dt, end_dt):
    """计算阅读时长(分钟)"""
    return (end_dt - start_dt).total_seconds() / 60

def parse_record(record):
    """解析单条阅读记录:日期,开始时间,结束时间,书籍名"""
    parts = [p.strip() for p in record.split(',')]
    if len(parts) != 4:
        return None
    date_str, start_str, end_str, book = parts
    try:
        date = datetime.strptime(date_str, '%Y-%m-%d').date()
        start_dt = parse_datetime(date, start_str)
        end_dt = parse_datetime(date, end_str)
        duration = calculate_duration(start_dt, end_dt)
        if duration < 0:  # 结束时间早于开始时间,无效
            return None
        return (date, book, duration)
    except Exception as e:
        print(f"解析失败:{record},错误:{e}")
        return None

3. 统计分析函数

def daily_stats(records):
    """按日期统计:{日期: {书籍: 总时长}, 日期: 总时长}"""
    daily_books = defaultdict(lambda: defaultdict(float))  # 日期→书籍→时长
    daily_total = defaultdict(float)  # 日期→总时长
    for date, book, dur in records:
        daily_books[date][book] += dur
        daily_total[date] += dur
    return daily_books, daily_total

def weekly_stats(daily_total):
    """按周统计总时长(周数由isocalendar计算)"""
    weekly = defaultdict(float)
    for date, total in daily_total.items():
        year, week, _ = date.isocalendar()
        weekly[f"{year} 第{week}周"] += total
    return weekly

4. 可视化函数

def plot_daily(daily_total):
    """绘制每日时长柱状图"""
    dates = sorted(daily_total.keys())
    totals = [daily_total[date] for date in dates]
    date_labels = [d.strftime('%Y-%m-%d') for d in dates]
    fig, ax = plt.subplots(figsize=(8, 4))
    ax.bar(date_labels, totals, color='skyblue')
    ax.set_xlabel('日期')
    ax.set_ylabel('时长(分钟)')
    ax.set_title('每日阅读时长统计')
    plt.xticks(rotation=45)
    plt.tight_layout()
    return fig

def plot_weekly(weekly):
    """绘制每周时长折线图"""
    weeks = sorted(weekly.keys())
    totals = [weekly[week] for week in weeks]
    fig, ax = plt.subplots(figsize=(8, 4))
    ax.plot(weeks, totals, marker='o', color='orange')
    ax.set_xlabel('周')
    ax.set_ylabel('总时长(分钟)')
    ax.set_title('每周阅读趋势')
    plt.xticks(rotation=45)
    plt.tight_layout()
    return fig

5. GUI界面实现

class ReadingStatsApp:
    def __init__(self, root):
        self.root = root
        self.root.title("电子书阅读时长统计工具")
        self.records = []  # 存储解析后的记录 (date, book, duration)
        self.init_ui()

    def init_ui(self):
        # 文件输入区域
        file_frame = tk.Frame(self.root)
        file_frame.pack(pady=5)
        tk.Button(file_frame, text="选择阅读记录文件", command=self.load_file).pack(side=tk.LEFT, padx=5)

        # 手动输入区域
        input_frame = tk.Frame(self.root)
        input_frame.pack(pady=5)
        tk.Label(input_frame, text="手动输入记录(每行:日期,开始时间,结束时间,书籍名):").pack(anchor=tk.W, padx=5)
        self.text_input = scrolledtext.ScrolledText(input_frame, width=50, height=5)
        self.text_input.pack(pady=5, padx=5)
        tk.Button(input_frame, text="提交", command=self.submit_manual).pack(padx=5)

        # 统计结果区域
        result_frame = tk.Frame(self.root)
        result_frame.pack(pady=10)
        tk.Label(result_frame, text="统计结果:").pack(anchor=tk.W, padx=5)
        self.result_text = scrolledtext.ScrolledText(result_frame, width=60, height=6)
        self.result_text.pack(pady=5, padx=5)

        # 图表区域
        self.fig_frame = tk.Frame(self.root)
        self.fig_frame.pack(pady=10)

    def load_file(self):
        file_path = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt")])
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    self.parse_and_add(f.readlines())
            except Exception as e:
                self.result_text.insert(tk.END, f"文件错误:{e}\n")

    def submit_manual(self):
        text = self.text_input.get('1.0', tk.END).strip()
        if text:
            self.parse_and_add(text.split('\n'))

    def parse_and_add(self, lines):
        new_records = []
        for line in lines:
            line = line.strip()
            if line:
                record = parse_record(line)
                if record:
                    new_records.append(record)
                else:
                    self.result_text.insert(tk.END, f"解析错误:{line}\n")
        self.records.extend(new_records)
        self.update_stats()

    def update_stats(self):
        if not self.records:
            self.result_text.insert(tk.END, "无有效记录!\n")
            return

        daily_books, daily_total = daily_stats(self.records)
        weekly = weekly_stats(daily_total)

        # 生成统计文本
        stats = "每日统计:\n"
        for date in sorted(daily_total.keys()):
            total = daily_total[date]
            books = daily_books[date]
            details = " + ".join([f"{b}{d:.0f}分" for b, d in books.items()])
            h, m = int(total // 60), int(total % 60)
            stats += f"{date.strftime('%Y-%m-%d')}:{h}小时{m}分({details})\n"

        stats += "\n每周趋势:\n"
        for week in sorted(weekly.keys()):
            total = weekly[week]
            h, m = int(total // 60), int(total % 60)
            avg = total / 7
            avg_h, avg_m = int(avg // 60), int(avg % 60)
            stats += f"{week}:总时长{h}小时{m}分,日均{avg_h}小时{avg_m}分\n"

        # 绘制图表(清空旧图表)
        for widget in self.fig_frame.winfo_children():
            widget.destroy()
        daily_fig = plot_daily(daily_total)
        weekly_fig = plot_weekly(weekly)
        FigureCanvasTkAgg(daily_fig, self.fig_frame).draw().get_tk_widget().pack(side=tk.LEFT, padx=5)
        FigureCanvasTkAgg(weekly_fig, self.fig_frame).draw().get_tk_widget().pack(side=tk.LEFT, padx=5)

        self.result_text.delete('1.0', tk.END)
        self.result_text.insert(tk.END, stats)

6. 主函数(程序入口)

if __name__ == "__main__":
    plt.switch_backend('TkAgg')  # 适配tkinter GUI
    root = tk.Tk()
    app = ReadingStatsApp(root)
    root.mainloop()

功能测试与优化

  1. 输入测试:用示例输入(文件或手动)验证时间解析和时长计算是否正确。
  2. 统计验证:检查日/周总时长是否与预期一致(如示例中5月1日总时长120分钟)。
  3. 可视化检查:柱状图的X轴日期、Y轴数值是否正确;折线图的周趋势是否匹配。

优化方向
– 支持更多时间格式(如12小时制)。
– 增加“月统计”和多书籍对比图。
– 导出统计结果到文件。

总结

通过本项目,我们实践了文件处理、时间计算、数据统计、可视化与GUI开发的全流程。工具不仅能帮助分析阅读习惯,更能锻炼Python综合编程能力。你可以基于此扩展功能,如多设备同步记录、阅读效率分析等,让工具更贴合实际需求!

运行程序后,你可以:
– 点击“选择阅读记录文件”导入txt格式的阅读记录(每行格式:日期,开始时间,结束时间,书籍名)。
– 或在文本框手动输入记录,点击“提交”。
– 工具会自动统计并可视化你的阅读时长,帮助你优化阅读计划!