Python實(shí)戰(zhàn)之手把手教你寫一個(gè)帶界面的照片按日期歸檔與清理工具
前言
為硬盤減負(fù)你是否也有這樣的煩惱:手機(jī)或相機(jī)的存儲(chǔ)卡滿了,里面的照片和視頻成千上萬(wàn),堆在一個(gè)文件夾里雜亂無(wú)章?想把它們備份到移動(dòng)硬盤,卻發(fā)現(xiàn)手動(dòng)按日期分類太累?備份完了又不敢輕易刪除源文件,怕萬(wàn)一沒(méi)拷過(guò)去怎么辦?
今天,我們將利用 Python 和 wxPython 圖形界面庫(kù),編寫一個(gè)自動(dòng)化的工具。它不僅能按拍攝日期自動(dòng)歸檔媒體文件,還能在校驗(yàn)成功后安全地將源文件移入回收站。
核心功能與技術(shù)棧
我們要實(shí)現(xiàn)的功能非常明確:
- GUI 界面:通過(guò)日歷選擇日期,通過(guò)文件選擇器選擇源目錄和目標(biāo)目錄。
- 按日期篩選:自動(dòng)掃描源文件夾(包括子目錄),找出指定日期創(chuàng)建的照片/視頻。
- 自動(dòng)歸檔:在目標(biāo)盤建立 YYYYMMDD 格式的文件夾,并復(fù)制文件。
- 安全清理:復(fù)制成功且校驗(yàn)無(wú)誤后,將源文件移入回收站(而不是直接永久刪除)。
- 防假死:使用多線程處理文件操作,防止界面卡頓。
使用的庫(kù):
- wxPython: 繪制原生風(fēng)格的圖形界面。
- shutil: 負(fù)責(zé)文件的高級(jí)復(fù)制(保留元數(shù)據(jù))。
- os & datetime: 處理文件路徑和時(shí)間。
- send2trash: 關(guān)鍵庫(kù),用于將文件發(fā)送到回收站。
- threading: 實(shí)現(xiàn)后臺(tái)任務(wù)處理。
代碼深度剖析
界面布局 (GUI Layout)
我們繼承了 wx.Frame 來(lái)創(chuàng)建主窗口。布局上使用了 wx.BoxSizer,這是一種彈性盒子布局,能夠讓界面元素隨窗口大小自動(dòng)調(diào)整。
核心控件初始化
self.date_picker = wx.adv.DatePickerCtrl(panel, style=wx.adv.DP_DROPDOWN | wx.adv.DP_SHOWCENTURY) self.src_picker = wx.DirPickerCtrl(panel, message=“選擇源文件夾”) self.dst_picker = wx.DirPickerCtrl(panel, path=default_path, message=“選擇目標(biāo)文件夾”)
亮點(diǎn):使用了 DatePickerCtrl,讓用戶點(diǎn)選日歷,而不是手動(dòng)輸入“2023-11-25”,減少了格式錯(cuò)誤的可能。
遇到的“坑”:wx.DateTime 類型轉(zhuǎn)換
在編寫過(guò)程中,我們遇到了一個(gè)典型的報(bào)錯(cuò):
TypeError: unsupported operand type(s) for +: ‘sip.enumtype’ and ‘int’
原因分析:wxPython 的較新版本(Phoenix)中,wx_date.Month 屬性返回的是一個(gè)枚舉對(duì)象(如 wx.DateTime.Month.Jan),而不是整數(shù)。且 wxPython 的月份是從 0 開(kāi)始(0-11),而 Python 標(biāo)準(zhǔn)庫(kù) datetime 需要的是 1-12。
解決方案:我們必須使用 .GetMonth() 方法獲取整數(shù)值,并手動(dòng) +1。
修正后的代碼
wx_date = self.date_picker.GetValue()
GetMonth() 返回 0-11,所以需要 +1
target_date = datetime.date(wx_date.GetYear(), wx_date.GetMonth() + 1, wx_date.GetDay())
核心邏輯:遍歷與篩選
為了找到深藏在子文件夾里的照片,我們使用了 os.walk()。它可以遞歸地遍歷目錄樹(shù)。
MEDIA_EXTENSIONS = {‘.jpg', ‘.mp4', …} # 定義白名單
for root, dirs, files in os.walk(src_dir):
for file in files:
# 1. 擴(kuò)展名過(guò)濾
if ext.lower() not in MEDIA_EXTENSIONS: continue
# 2. 日期匹配
ctime = os.path.getctime(file_path)
file_date = datetime.date.fromtimestamp(ctime)
if file_date == target_date:
self.ProcessFile(...)
這里的邏輯非常嚴(yán)謹(jǐn):只處理白名單內(nèi)的媒體文件,避免誤移動(dòng)系統(tǒng)文件或文本文件。
數(shù)據(jù)安全:復(fù)制與“后悔藥”機(jī)制
這是本工具最核心的價(jià)值所在。普通腳本用 shutil.move,一旦出錯(cuò)文件可能丟失。我們采用了“復(fù)制+校驗(yàn)+放入回收站”的三步走策略。
def ProcessFile(self, src_path, dst_folder, filename):
# … 省略重名處理 …
# 第一步:復(fù)制 (copy2 保留拍攝時(shí)間等元數(shù)據(jù))
shutil.copy2(src_path, dst_path)
# 第二步:校驗(yàn)
# 確保目標(biāo)文件存在,且大小與源文件一致
if os.path.exists(dst_path) and os.path.getsize(dst_path) == os.path.getsize(src_path):
# 第三步:安全刪除 (放入回收站)
send2trash(src_path)
else:
self.Log("錯(cuò)誤:復(fù)制校驗(yàn)失敗,未刪除源文件")
使用 send2trash 是為了給用戶一劑“后悔藥”。萬(wàn)一你選錯(cuò)了日期或者程序邏輯有誤,文件只是在回收站里,隨時(shí)可以還原,數(shù)據(jù)安全大于一切。
用戶體驗(yàn):多線程防假死
如果直接在按鈕點(diǎn)擊事件里運(yùn)行上述循環(huán),處理幾百個(gè)視頻時(shí),界面會(huì)直接卡死(顯示“未響應(yīng)”)。為了解決這個(gè)問(wèn)題,我們引入了 threading。
def OnStartBackup(self, event):
# 禁用按鈕
self.btn_start.Disable()
# 開(kāi)啟子線程
thread = threading.Thread(target=self.RunBackupLogic, args=(…))
thread.start()
def Log(self, message):
# 使用 wx.CallAfter 確保在主線程更新 UI,防止崩潰
wx.CallAfter(self.log_ctrl.AppendText, f"…{message}\n")注意:子線程不能直接操作界面控件(如 TextCtrl),必須使用 wx.CallAfter 將更新指令發(fā)送回主UI線程,這是 GUI 編程的金科玉律。
運(yùn)行效果
程序啟動(dòng)后,只需三步:
- 選擇你要整理的那一天的日期(例如某次旅游的日期)。
- 選擇存放混亂照片的源文件夾。
- 選擇移動(dòng)硬盤作為目標(biāo)文件夾。
點(diǎn)擊“開(kāi)始”,你會(huì)在日志框看到一行行滾動(dòng)的記錄:
[10:00:01] 成功備份并清理: IMG_2023.JPG
[10:00:02] 發(fā)現(xiàn)重名,重命名為: VIDEO_001_1.MP4
完成后,程序目錄還會(huì)生成一個(gè) backup_history.txt,記錄你的備份流水。
運(yùn)行結(jié)果



到此這篇關(guān)于Python實(shí)戰(zhàn)之手把手教你寫一個(gè)帶界面的照片按日期歸檔與清理工具的文章就介紹到這了,更多相關(guān)Python文件按日期歸檔內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Python的Twisted框架編寫簡(jiǎn)單的網(wǎng)絡(luò)客戶端
這篇文章主要介紹了使用Python的Twisted框架編寫簡(jiǎn)單的網(wǎng)絡(luò)客戶端,翻譯自Twisted文檔,包括一個(gè)簡(jiǎn)單的IRC客戶端的實(shí)現(xiàn),需要的朋友可以參考下2015-04-04
python二維碼操作:對(duì)QRCode和MyQR入門詳解
今天小編就為大家分享一篇python二維碼操作:對(duì)QRCode和MyQR入門詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-06-06
解決Pytorch中的神坑:關(guān)于model.eval的問(wèn)題
這篇文章主要介紹了解決Pytorch中的神坑:關(guān)于model.eval的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-05-05
使用Python構(gòu)建MCP服務(wù)器的詳細(xì)配置步驟
模型控制協(xié)議(MCP)是一種專為實(shí)現(xiàn)AI代理與工具解耦而設(shè)計(jì)的通信協(xié)議,為AI驅(qū)動(dòng)應(yīng)用程序的開(kāi)發(fā)提供了高度的靈活性和模塊化架構(gòu),本文將詳細(xì)介紹MCP服務(wù)器的構(gòu)建過(guò)程,包括工具函數(shù)的注冊(cè)、配置以及與Claude Desktop等AI代理的集成,需要的朋友可以參考下2025-06-06
pytest實(shí)戰(zhàn)技巧之參數(shù)化基本用法和多種方式
本文介紹了pytest參數(shù)化的基本用法和多種方式,幫助讀者更好地使用這個(gè)功能,同時(shí),還介紹了一些高級(jí)技巧,如動(dòng)態(tài)生成參數(shù)名稱、參數(shù)化的組合和動(dòng)態(tài)生成參數(shù)化裝飾器,幫助讀者更靈活地使用參數(shù)化,感興趣的朋友參考下吧2023-12-12
Python動(dòng)態(tài)導(dǎo)入模塊的方法實(shí)例分析
這篇文章主要介紹了Python動(dòng)態(tài)導(dǎo)入模塊的方法,結(jié)合實(shí)例形式較為詳細(xì)的分析了Python動(dòng)態(tài)導(dǎo)入系統(tǒng)模塊、自定義模塊以及模塊列表的相關(guān)操作技巧,需要的朋友可以參考下2018-06-06

