背景
日常办公中,工作日志往往具有明显的重复性:例如每天需要记录“考勤”“订饭”,每周一需要记录“周会”“收周报”等固定事项。但实际记录时,又经常夹杂临时任务、请假、补充说明等内容。
如果完全手写,每天都要重复输入固定内容;如果只用 Excel,又不够便捷,查询、复制、统计都比较麻烦。因此,我使用 Python 和 wxPython 编写了一个桌面端工作日志程序,用来快速记录、查询、复制和导出工作日志。
项目核心文件为work_log_app.py,数据保存在同目录下的work_log_data.json中。
C:\Users\86182\Desktop\工作日志
目标
本程序的目标是做一个轻量、离线、方便使用的工作日志工具,主要功能包括:
- 按日期记录工作日志,自动显示星期。
- 支持每天固定事项,例如“考勤”“订饭”。
- 支持每周固定事项,例如周一“周会”“收周报”。
- 支持请假状态,并且请假时仍可填写临时事项。
- 支持常用词维护、模糊搜索和快速加入。
- 支持按日期查询、按事项关键词查询。
- 支持按年度、月度以日历形式统计记录情况。
- 支持复制全部日志或复制指定起止日期范围内的日志。
- 支持导出 Excel 文件。
- 支持 Python 运行和 exe 打包运行时的数据路径兼容。
方法
程序采用 wxPython 构建桌面界面,使用 JSON 保存本地数据,使用 Python 标准库生成 Excel 文件。
整体结构可以分为四层:
- 数据层:负责读取和保存
work_log_data.json。 - 工具函数层:负责日期、星期、格式化、排序、查询等通用逻辑。
- 导出层:负责将日志内容导出为
.xlsx文件。 - 界面层:使用
wx.Frame、wx.Notebook、wx.Panel等控件构建功能界面。
程序入口如下:
defmain():app=wx.App(False)frame=WorkLogFrame()frame.Show()app.MainLoop()其中WorkLogFrame是主窗口类,所有界面和事件逻辑都集中在这个类中。
过程
1. 数据文件路径处理
程序首先处理数据文件路径:
APP_DIR=Path(sys.executable).resolve().parentifgetattr(sys,"frozen",False)elsePath(__file__).resolve().parent DATA_FILE=APP_DIR/"work_log_data.json"这段代码解决了一个很常见的问题:Python 直接运行时,数据文件应放在.py文件同目录;打包成 exe 后,数据文件应放在 exe 同目录。如果继续使用__file__,打包后可能会指向临时解包目录,导致程序读不到原有记录。
2. 数据结构设计
日志数据使用 JSON 保存,结构清晰,便于维护:
{"daily_tasks":["考勤。","订饭。"],"weekly_tasks":{"0":["周会。","收周报。"]},"common_words":["考勤。","订饭。","请假"],"entries":[{"date":"2026-06-03","tasks":["考勤。","订饭。","编写工作日志程序。"]}]}其中:
daily_tasks表示每天默认事项。weekly_tasks表示每周固定事项,0代表周一。common_words表示常用词。entries表示实际日志记录。
读取数据时,程序会自动补齐缺失字段:
data.setdefault("daily_tasks",DEFAULT_DAILY_TASKS[:])data.setdefault("weekly_tasks",dict(DEFAULT_WEEKLY_TASKS))data.setdefault("entries",[])data.setdefault("common_words",unique_items(generated_words))这样即使后续程序增加了新字段,旧数据文件也可以继续兼容。
3. 日志格式化
日志复制时,需要生成类似下面的文本:
2026-06-03 周三 考勤。 订饭。 编写工作日志程序。对应函数为:
defformat_day_block(entry):tasks=entry.get("tasks",[])header=f"{entry['date']}{weekday_name(entry['date'])}"ifnottasks:returnheaderreturnf"{header}\n"+"\n\n".join(tasks)这里通过weekday_name()自动计算星期,通过空行分隔事项,方便直接复制到微信、文档或邮件中。
4. 主界面设计
程序使用wx.Notebook创建四个页签:
self.notebook.AddPage(self.record_panel,"记录")self.notebook.AddPage(self.query_panel,"查询")self.notebook.AddPage(self.stats_panel,"统计")self.notebook.AddPage(self.words_panel,"常用词")四个页面分别负责:
- 记录:日常录入、保存、删除、复制、导出。
- 查询:按日期或事项关键词查询。
- 统计:按年度、月度生成日历视图。
- 常用词:编辑每天事项、周一事项和常用词。
5. 记录功能实现
记录页提供日期选择、前一天、后一天、请假、每日事项、每周事项、临时事项等控件。
切换日期时,会自动加载当天记录:
defset_selected_date(self,iso_date):self.selected_date=iso_date dt=datetime.strptime(iso_date,"%Y-%m-%d")wx_dt=wx.DateTime(dt.day,dt.month-1,dt.year)self.date_picker.SetValue(wx_dt)self.query_date_picker.SetValue(wx_dt)self.copy_start_picker.SetValue(wx_dt)self.copy_end_picker.SetValue(wx_dt)self.load_entry_for_date(iso_date)点击“前一天”“后一天”时,本质上是对日期做加减:
defmove_selected_date(self,days):new_date=date.fromisoformat(self.selected_date)+timedelta(days=days)self.set_selected_date(new_date.isoformat())保存日志时,程序会将勾选事项和临时事项合并:
defcollect_tasks(self):extras=normalize_lines(self.extra_text.GetValue())ifself.leave_check.GetValue():returnunique_items(["请假"]+extras)tasks=[]...tasks.extend(extras)returnunique_items(tasks)这里有一个细节:请假并不是完全排他的状态。即使勾选“请假”,仍然可以继续填写临时事项,例如“请假”“处理邮件”“参加线上会议”等。
6. 常用词模糊搜索
记录页右侧有常用词列表,支持输入关键词过滤:
defrefresh_common_words_ui(self):all_words=self.data.get("common_words",[])words=all_words keyword=self.quick_word_filter.GetValue().strip()ifkeyword:words=[wordforwordinwordsifkeywordinword]self.quick_words_list.Set(words)双击常用词后,会加入到临时事项中:
defon_quick_word_double_click(self,event):index=self.quick_words_list.GetSelection()ifindex!=wx.NOT_FOUND:self.add_quick_task(self.quick_words_list.GetString(index))这种设计适合高频输入场景,减少重复打字。
7. 查询功能实现
查询页支持两种方式。
第一种是按日期查询:
defon_query_day(self,event):iso_date=self.get_picker_iso(self.query_date_picker)entry=entry_by_date(self.data["entries"],iso_date)第二种是按事项关键词查询:
defon_query_task(self,event):keyword=self.task_query_combo.GetValue().strip()forentryinsorted_entries(self.data["entries"]):hit_tasks=[taskfortaskinentry.get("tasks",[])ifkeywordintask]这可以快速回答“某件事在哪几天做过”这个问题。
8. 指定日期范围复制
除了复制全部日志,程序还支持复制某个起止日期范围内的内容:
defentries_between(entries,start_iso,end_iso):start=min(start_iso,end_iso)end=max(start_iso,end_iso)returnsorted_entries([entryforentryinentriesifstart<=entry["date"]<=end])即使用户把结束日期选在开始日期前面,程序也会自动纠正范围。
9. 日历统计功能
统计页使用wx.grid.Grid构建月度日历视图:
self.calendar_grid=wx.grid.Grid(self.stats_panel)self.calendar_grid.CreateGrid(6,7)程序通过calendar.Calendar().monthdatescalendar()获取月历数据,然后根据日志状态设置单元格颜色:
- 红色:请假。
- 绿色:已记录。
- 白色:当月未记录。
- 灰色:非当前月份日期。
核心逻辑如下:
if"请假"intasks:value+="\n请假"bg=wx.Colour(255,205,205)else:value+=f"\n{len(tasks)}项"bg=wx.Colour(220,242,220)双击日历中的某一天,还可以直接跳转到记录页进行查看或修改。
10. Excel 导出
程序没有依赖openpyxl,而是使用标准库zipfile手动生成.xlsx文件。
Excel 本质上是一个 ZIP 包,里面包含若干 XML 文件。程序构造了:
[Content_Types].xml_rels/.relsxl/workbook.xmlxl/_rels/workbook.xml.relsxl/worksheets/sheet1.xml
最终写入压缩包:
withzipfile.ZipFile(path,"w",zipfile.ZIP_DEFLATED)aszf:forname,contentinfiles.items():zf.writestr(name,content)这种方式的优点是不需要额外安装第三方 Excel 库,适合轻量级工具。
结果
最终完成的程序具备较完整的工作日志管理能力:
- 可以快速记录每天事项。
- 可以自动填充每日、每周固定事项。
- 可以处理请假和临时事项。
- 可以维护常用词,并支持模糊搜索。
- 可以查询某一天的日志。
- 可以查询某件事在哪些日期做过。
- 可以按月用日历方式查看记录情况。
- 可以复制全部日志或指定日期范围内的日志。
- 可以导出 Excel 文件。
- 可以兼容 Python 运行和 exe 打包运行。
程序数据保存在本地 JSON 文件中,不依赖网络,也不依赖数据库,适合个人办公使用。
总结
这个项目虽然规模不大,但涵盖了桌面应用开发中的多个典型问题:界面设计、事件绑定、本地数据持久化、日期处理、文本格式化、查询统计、Excel 导出以及 exe 打包后的路径兼容。
从实现角度看,wxPython 适合开发这类本地办公小工具;JSON 适合保存轻量级结构化数据;标准库生成 Excel 虽然代码量略多,但避免了额外依赖。
后续还可以继续扩展,例如增加多用户配置、日志分类、导入历史 Excel、按关键词统计频次、生成月报周报等功能。对于日常办公自动化来说,这类“小而实用”的工具往往最能节省时间。