Python實(shí)戰(zhàn)之手把手教你寫一個(gè)帶界面的照片按日期歸檔與清理工具
前言
為硬盤減負(fù)你是否也有這樣的煩惱:手機(jī)或相機(jī)的存儲卡滿了,里面的照片和視頻成千上萬,堆在一個(gè)文件夾里雜亂無章?想把它們備份到移動(dòng)硬盤,卻發(fā)現(xiàn)手動(dòng)按日期分類太累?備份完了又不敢輕易刪除源文件,怕萬一沒拷過去怎么辦?
今天,我們將利用 Python 和 wxPython 圖形界面庫,編寫一個(gè)自動(dòng)化的工具。它不僅能按拍攝日期自動(dòng)歸檔媒體文件,還能在校驗(yàn)成功后安全地將源文件移入回收站。
核心功能與技術(shù)棧
我們要實(shí)現(xiàn)的功能非常明確:
- GUI 界面:通過日歷選擇日期,通過文件選擇器選擇源目錄和目標(biāo)目錄。
- 按日期篩選:自動(dòng)掃描源文件夾(包括子目錄),找出指定日期創(chuàng)建的照片/視頻。
- 自動(dòng)歸檔:在目標(biāo)盤建立 YYYYMMDD 格式的文件夾,并復(fù)制文件。
- 安全清理:復(fù)制成功且校驗(yàn)無誤后,將源文件移入回收站(而不是直接永久刪除)。
- 防假死:使用多線程處理文件操作,防止界面卡頓。
使用的庫:
- wxPython: 繪制原生風(fēng)格的圖形界面。
- shutil: 負(fù)責(zé)文件的高級復(fù)制(保留元數(shù)據(jù))。
- os & datetime: 處理文件路徑和時(shí)間。
- send2trash: 關(guān)鍵庫,用于將文件發(fā)送到回收站。
- threading: 實(shí)現(xiàn)后臺任務(wù)處理。
代碼深度剖析
界面布局 (GUI Layout)
我們繼承了 wx.Frame 來創(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)換
在編寫過程中,我們遇到了一個(gè)典型的報(bào)錯(cuò):
TypeError: unsupported operand type(s) for +: ‘sip.enumtype’ and ‘int’
原因分析:wxPython 的較新版本(Phoenix)中,wx_date.Month 屬性返回的是一個(gè)枚舉對象(如 wx.DateTime.Month.Jan),而不是整數(shù)。且 wxPython 的月份是從 0 開始(0-11),而 Python 標(biāo)準(zhǔn)庫 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()。它可以遞歸地遍歷目錄樹。
MEDIA_EXTENSIONS = {‘.jpg', ‘.mp4', …} # 定義白名單
for root, dirs, files in os.walk(src_dir):
for file in files:
# 1. 擴(kuò)展名過濾
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 是為了給用戶一劑“后悔藥”。萬一你選錯(cuò)了日期或者程序邏輯有誤,文件只是在回收站里,隨時(shí)可以還原,數(shù)據(jù)安全大于一切。
用戶體驗(yàn):多線程防假死
如果直接在按鈕點(diǎn)擊事件里運(yùn)行上述循環(huán),處理幾百個(gè)視頻時(shí),界面會(huì)直接卡死(顯示“未響應(yīng)”)。為了解決這個(gè)問題,我們引入了 threading。
def OnStartBackup(self, event):
# 禁用按鈕
self.btn_start.Disable()
# 開啟子線程
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)擊“開始”,你會(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Django使用視圖動(dòng)態(tài)輸出CSV以及PDF的操作詳解
這篇文章主要介紹了Django 如何使用視圖動(dòng)態(tài)輸出 CSV 以及 PDF,我們需要用到 python 的 csv 和 reportLab 庫,通過django視圖來定義輸出我們需要的 csv 或者 pdf 文件,需要的朋友可以參考下2024-06-06
Python實(shí)現(xiàn)對一個(gè)函數(shù)應(yīng)用多個(gè)裝飾器的方法示例
這篇文章主要介紹了Python實(shí)現(xiàn)對一個(gè)函數(shù)應(yīng)用多個(gè)裝飾器的方法,結(jié)合實(shí)例形式分析了Python編程中一個(gè)函數(shù)使用多個(gè)裝飾器的簡單操作技巧,需要的朋友可以參考下2018-02-02
在python中實(shí)現(xiàn)發(fā)送短信功能
工作中我們經(jīng)常會(huì)用到發(fā)短信的需求,那么如何在python代碼中實(shí)現(xiàn)發(fā)短息你的需求呢,本文我們就一起深入探討下,文中有詳細(xì)的代碼示例供大家參考,具有一定的參考價(jià)值,需要的朋友可以參考下2024-04-04
Python使用pytest-playwright的原因分析
pytest-playwright 是一個(gè) Python 包,它允許您使用 Microsoft 的 Playwright 庫在 Python 項(xiàng)目中進(jìn)行端到端測試,這篇文章主要介紹了Python為什么使用pytest-playwright,需要的朋友可以參考下2023-03-03
Win10下配置tensorflow-gpu的詳細(xì)教程(無VS2015/2017)
這篇文章主要介紹了Win10下配置tensorflow-gpu(無VS2015/2017),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
解決python遞歸函數(shù)及遞歸次數(shù)受到限制的問題
這篇文章主要介紹了解決python遞歸函數(shù)及遞歸次數(shù)受到限制的問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
django使用多個(gè)數(shù)據(jù)庫的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于django使用多個(gè)數(shù)據(jù)庫的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

