功能如下:
第一:选择照片所在的目录,子目录会自动遍历出照片的绝对路径并显示到列表框。
第二:点击开始重命名,然后耐心等待即可,为了省时间,重命名成功的提示没有写出到黑窗口,只输出因拍摄时间同样(即那种1秒拍了十几张照片的,exif中的时间信息一模一样)而导致重命名失败的提示,此时,会自动加上后缀数字1到50,循环50次重命名,当某次重命名成功,会跳出循环,继续下一个照片重命名(之所以循环50次,就是防止有些用户用设备1秒拍出了50张照片)。
第三:实际使用发现,有些照片的格式不支持读取exif信息,忘记哪些了,jpg和dng支持,其它自测。
上面说的是原版本,没有窗口,纯命令行,有一些小问题,懒得修复了,也就不放出来了,免得因bug,弄坏大家电脑(代码里有一行是当文件名存在有 . 符号,就替换为- 号),因为没加判断,自己的电脑c盘很多带.号的文件路径全换成-了,导致软件失效。 猜测是在调试时选择照片路径,取消了选择,系统默认返回C盘根目录,导致遍历,把C盘的文件夹的 . 号全换成 -号了。此版本就不放出来了,谅解一下,也不修复了。
加上了三种重命名方式:
第一种:优先判断exif信息是否存在,存在则用exif时间重命名
第二种:exif信息不存在,则进行判断文件名是否包含有时间信息,例如564-………….jpg ,此情况就通过正则提取,重命名为2012-06-09-16-15.jpg
第三种:如果前两种都不满足,则通过读取照片文件在电脑中的创建时间来重命名。
import
exifread
from
tkinter.filedialog
import
*
#别问我怎么不导入os模块就能 重命名,我也不知道,可能这个tkinter模块包含有os了吧,哈哈
Files_Names
=
{
#全局变量,存储照片绝对路径 和 目录路径(即不包含照片名的路径)
"all_path"
:[],
"ml_name"
:""
}
def
mainapp():
#创建窗体
app
=
Tk()
app.title(
"批量根据exif信息自动重命名照片"
)
Label(app, text
=
"批量根据exif信息自动重命名照片"
, font
=
(
"微软雅黑"
,
25
,
"bold"
)).pack()
# 画标签并设置显示的文字,字体,加粗
Listbox(app, name
=
'l_b'
, bg
=
"#f2f2f2"
, fg
=
"red"
).pack(fill
=
BOTH,expand
=
True
) # 画列表框并指定名字,方便后续根据名字调用获取Listbox实例,指定背景,前景,显示方式
Button(app, text
=
"选择照片路径"
, command
=
ui_update).pack(fill
=
BOTH)
# command指定了当按下按钮,会调用哪个方法
Button(app, text
=
"开始Exif命命名"
, command
=
rename).pack(fill
=
BOTH)
# left
app_width
=
500
# 设置窗口宽度
app_height
=
500
# 设置窗口高度
win_width
=
int
((
app.winfo_screenwidth()
-
app_width)
/
2
)
# 设置窗口距离左上角原点的 宽度 屏幕总宽度-软件窗口宽度 再除2,即软件距离左侧窗口的宽度 转为整数,因为下面geometry只接受字符串型整数
win_height
=
int
((
app.winfo_screenheight()
-
app_height)
/
2
)
# 设置窗口距离左上角原点的 高度 屏幕总宽度-软件窗口高度 再除2,即软件距离顶边的高度 转为整数,因为下面geometry只接受字符串型整数
app.geometry(
"%sx%s+%s+%s"
%
(app_width, app_height, win_width, win_height))
# 设置窗口显示的宽高度 及 距离原点的长宽度
return
app
def
ui_update():
#此方法用于把照片所有的路径读到 列表框,当点击按钮《选择照片路径》,就会调用此方法
list_box
=
a.children[
"l_b"
]
# 读取出列表框对象
list_box.delete(
0
, END)
# 删除全部列表内容,防止你点了几次《选择照片路径》,重复添加了几次照片。
Files_Names[
"ml_name"
]
=
askdirectory()
#选择照片所在的目录并存到变量里,后面重命名完成后,调用这个变量,显示文件夹出来
Files_Names[
"all_path"
]
=
[]
#把存了照片的绝对路径的变量清空,防止你批量重命名完一个文件夹后(此时没关闭软件),继续使用去选择下一个文件夹来重命名,如果不清空,将会出错,因为残留有上一次批量重命名的路径名。
if
Files_Names[
"ml_name"
]!
=
"":
#此判断为了防止你点了《选择照片路径》按钮,又不选择文件夹,直接点了取消,此时返回空,就不执行遍历照片功能。
Files_Names[
"ml_name"
]
+
=
"/"
list_name(Files_Names[
"ml_name"
])
# 把选择的目录传入到list_name方法中,进行递归遍历照片文件路径
def
list_name(ml_name):
file_names
=
os.listdir(ml_name)
# 根据选择的目录列出里面的照片及目录 返回列表
if
file_names !
=
"":
# 只要返回的照片和目录路径不为空,防止你犯二,选择了一个没有照片的空目录
list_box
=
a.children[
"l_b"
]
# 读取出列表框对象,下面在循环插入路径到列表框要用对象调用
for
i
in
range
(
0
,
len
(file_names)):
# 列表转换为循环数。
path
=
os.path.join(ml_name, file_names[i])
# 把目录名ml_name拼接上文件名file_names[i],获得完整路径。
if
os.path.isfile(path):
# 如果为真,则是文件
list_box.insert(END, path)
# 循环插入文件名到列表框
Files_Names[
"all_path"
].append(path)
# 完整的照片路径追加到 全局变量列表中
else
:
# 如果不是文件,那应该是目录,就加上/,再调用自身方法,进行递归列出下一层的文件,很多人在递归这里搞不明白。。。
try
:
list_name(path
+
"/"
)
except
:
(
"访问目录出错,可能不是一个目录,或者是奇奇怪怪的文件名"
)
def
rename():
for
index,j_file_name
in
enumerate
(Files_Names[
"all_path"
]):
#准备循环重命名
with
open
(j_file_name,
'rb'
) as f:
# 打开照片文件并读取到f中
tags
=
exifread.process_file(f)
# 从f中通过exifread.process_file获取exif信息(返回一个字典类型)
image_exif_time
=
""
#清空exif拍摄时间信息,为下一个照片重命名做准备,不残留上一个照片的拍照时间
image_Make_mode
=
""
#清空exif拍摄时间信息,为下一个照片重命名做准备,不残留上一个照片的手机型号
if
"EXIF DateTimeOriginal"
in
tags.keys():
#如果这个存着拍摄时间的键 存在 上面取出的exif字典里
image_exif_time
=
tags[
"EXIF DateTimeOriginal"
]
#把时间取出来,赋值给变量,准备拿它来当新文件名,下面两个同样道理,都是存着时间的,当判断这个键不存在时,就判断下面两个键有没有时间存着。
elif
"EXIF DateTimeDigitized"
in
tags.keys():
#实践发现,这也是一个拍摄时间
image_exif_time
=
tags[
"EXIF DateTimeDigitized"
]
elif
"Image DateTime"
in
tags.keys():
#实践发现,这也是一个拍摄时间
image_exif_time
=
tags[
"Image DateTime"
]
if
":"
in
str
(image_exif_time):
#取出来的拍摄时间是 2020:01:01 18:18 这种形式,难看
image_exif_time
=
str
(image_exif_time).replace(
":"
,
"-"
)
#换成2020-01-01 18-18 形式
if
"Image Make"
in
tags.keys()
and
"Image Model"
in
tags.keys() :
#如果存在品牌和 型号,就取出来
image_Make_mode
=
"-"
+
str
(tags[
"Image Make"
])
+
str
(tags[
"Image Model"
])
if
image_exif_time!
=
"":
#如果有拍摄时间,就进行 拆分目录 和 文件名
img_path
=
"/"
.join(j_file_name.split(
"/"
)[
0
:
-
1
])
#取出照片目录 D:/ZM/2019.05.11社团周年庆
img_name
=
j_file_name.split(
"/"
)[
-
1
]
#取出文件名。 656566.jpg
hz
=
img_name.split(
"."
)[
-
1
]
#取出后缀。 .jpg
else
:
#如果没有拍摄时间,直接跳到循环尾,进行下一个照片exif时间的提取,不进行重命名了。
continue
try
:
# try一下,防止出现未知错误 这里要注意,不能写在with里,因为会占用照片文件,导致重命名失败。
new_file_name
=
(
"%s/%s%s.%s"
)
%
(img_path, image_exif_time, image_Make_mode, hz)
os.renames(j_file_name, new_file_name)
except
:
# 当出现未知错误,或者是照片里的exif拍摄时间重复,比如1秒拍了10张,这类照片因为时间一模一样,重命名不了,只能在时间一样的照片后面按顺序加上 数字 来重命名。
for
o
in
range
(
50
):
# 防止你相机设备太牛逼,1秒拍了50张,导致时间一模一样的照片,所以设置循环50次,在文件名后面依次加上数字来重命名。
try
:
new_file_name
=
(
"%s/%s%s-%s.%s"
)
%
(img_path, image_exif_time, image_Make_mode,
str
(o
+
1
), hz)
(
"第%d张图片重命名中,疑似名字重复或照片文件损坏,重命名失败,将添加后缀%s继续重命名!50次内若重命名还是失败,将忽略些图片!"
%
(index,
str
(o
+
1
)))
(
"原文件名:%s"
%
j_file_name)
(
"预计新文件名:%s\n\n"
%
new_file_name)
os.renames(j_file_name, new_file_name)
break
# 当重命名成功后,立即跳出循环,不用笨笨的循环到50次。
except
:
pass
os.startfile(Files_Names[
"ml_name"
])
#所有文件重命名完成后,弹出照片所有的窗口
(
"重命名完成!"
)
a
=
mainapp()
a.mainloop()