上篇文章用bat 脚本处理照片不太丝滑 ;
用python ;
效果
用GUI组件、logging组件、python基础
废话不多说了; 直接上代码:
GUI页面:
"""
本代码由[Tkinter布局助手]生成
官网:https://www.pytk.net/tkinter-helper
QQ交流群:788392508
"""
from tkinter import *
from tkinter import filedialog
from tkinter.ttk import *
from tkinter import messagebox
from CopyApp import CopyApp
class WinGUI(Tk, CopyApp):
def __init__(self):
super().__init__()
self.__win()
self.tk_button_lmr64ezr = self.__tk_button_lmr64ezr(self)
self.tk_input_source = self.__tk_input_lmr6jxgx(self)
self.tk_input_tag = self.__tk_input_lmr6k60x(self)
self.tk_button_lmr6kbq5 = self.__tk_button_lmr6kbq5(self)
self.tk_input_idnumS = self.__tk_input_lmrbgynm(self)
self.tk_label_lmrbh7to = self.__tk_label_lmrbh7to(self)
self.tk_select_box_xy = self.__tk_select_box_lmrbhgtc(self)
self.tk_label_lmrbjnrs = self.__tk_label_lmrbjnrs(self)
self.tk_input_idnumE = self.__tk_input_lmrbkcai(self)
self.tk_button_lmrbktwv = self.__tk_button_lmrbktwv(self)
self.tk_frame_print = self.__tk_frame_lmrbm48o(self)
self.tk_input_numlimit = self.__tk_input_lmrbn84q(self)
self.tk_label_lmrbnd7t = self.__tk_label_lmrbnd7t(self)
self.tk_label_lmrcg7e7 = self.__tk_label_lmrcg7e7(self)
self.tk_input_exclude = self.__tk_input_lmrcgxkq(self)
# exclude
def __win(self):
self.title("文件复制助手")
# 设置窗口大小、居中
width = 600
height = 500
screenwidth = self.winfo_screenwidth()
screenheight = self.winfo_screenheight()
geometry = '%dx%d+%d+%d' % (width, height, (screenwidth - width) / 2, (screenheight - height) / 2)
self.geometry(geometry)
self.resizable(width=False, height=False)
# 自动隐藏滚动条
def scrollbar_autohide(self, bar, widget):
self.__scrollbar_hide(bar, widget)
widget.bind("<Enter>", lambda e: self.__scrollbar_show(bar, widget))
bar.bind("<Enter>", lambda e: self.__scrollbar_show(bar, widget))
widget.bind("<Leave>", lambda e: self.__scrollbar_hide(bar, widget))
bar.bind("<Leave>", lambda e: self.__scrollbar_hide(bar, widget))
def __scrollbar_show(self, bar, widget):
bar.lift(widget)
def __scrollbar_hide(self, bar, widget):
bar.lower(widget)
def vbar(self, ele, x, y, w, h, parent):
sw = 15 # Scrollbar 宽度
x = x + w - sw
vbar = Scrollbar(parent)
ele.configure(yscrollcommand=vbar.set)
vbar.config(command=ele.yview)
vbar.place(x=x, y=y, width=sw, height=h)
self.scrollbar_autohide(vbar, ele)
def __tk_button_lmr64ezr(self, parent):
btn = Button(parent, text="选择源文件夹", takefocus=False, )
btn.place(x=420, y=40, width=100, height=30)
return btn
def __tk_input_lmr6jxgx(self, parent):
ipt = Entry(parent, )
ipt.place(x=60, y=40, width=315, height=30)
return ipt
def __tk_input_lmr6k60x(self, parent):
ipt = Entry(parent, )
ipt.place(x=60, y=100, width=315, height=30)
return ipt
def __tk_button_lmr6kbq5(self, parent):
btn = Button(parent, text="目标文件夹", takefocus=False, )
btn.place(x=420, y=100, width=100, height=30)
return btn
# 起始学号
def __tk_input_lmrbgynm(self, parent):
ipt = Entry(parent, )
ipt.place(x=220, y=240, width=179, height=30)
# ipt.configure(validate="focusout", validatecommand=self.rule, invalidcommand=self.msg(ipt))
return ipt
def __tk_label_lmrbh7to(self, parent):
label = Label(parent, text="起始学号", anchor="center", )
label.place(x=60, y=240, width=136, height=30)
return label
def __tk_select_box_lmrbhgtc(self, parent):
cb = Combobox(parent, state="readonly", )
# cb['values'] = CopyApp.xy_dict
# cb['values'] = ("学院选择", "Python", "Tkinter Helper", "sd")
# 学院简称下拉框
cb['values'] = (
"other",
"cy",
"cj",
"dk",
"dy",
"gg",
"jc",
"lx",
"nx",
"ng",
"nj",
"rj",
"sk",
"sp",
"xk",
"yy",
"zb",
"zh"
)
cb.place(x=60, y=170, width=150, height=30)
return cb
# 学号开始
def __tk_label_lmrbjnrs(self, parent):
label = Label(parent, text="结束学号", anchor="center", )
label.place(x=60, y=300, width=141, height=30)
return label
# 学号末尾
def __tk_input_lmrbkcai(self, parent):
ipt = Entry(parent, )
ipt.place(x=220, y=300, width=177, height=30)
# ipt.configure(validate="focusout", validatecommand=self.rule, invalidcommand=self.msg(ipt))
return ipt
def __tk_button_lmrbktwv(self, parent):
btn = Button(parent, text="开始复制", takefocus=False, )
btn.place(x=430, y=240, width=113, height=83)
return btn
def __tk_frame_lmrbm48o(self, parent):
frame = Label(parent, )
frame.place(x=50, y=452, width=529, height=48)
return frame
# 复制个数限制
def __tk_input_lmrbn84q(self, parent):
ipt = Entry(parent, )
ipt.place(x=420, y=170, width=99, height=30)
ipt.insert(END, '')
# 校验是否是数字
# ipt.configure(validate="focusout", validatecommand=self.rule, invalidcommand=self.msg(ipt))
return ipt
def __tk_label_lmrbnd7t(self, parent):
label = Label(parent, text="复制个数限制", anchor="center", )
label.place(x=300, y=170, width=100, height=30)
return label
def __tk_label_lmrcg7e7(self, parent):
label = Label(parent, text="排除文件", anchor="center", )
label.place(x=60, y=350, width=142, height=30)
return label
# 排除文件
def __tk_input_lmrcgxkq(self, parent):
ipt = Entry(parent, )
ipt.place(x=220, y=350, width=375, height=30)
return ipt
'''
GUI 窗口初始化 类 函数
'''
class Win(WinGUI):
def __init__(self):
super().__init__()
self.__event_bind()
self.config(menu=self.create_menu())
# tab页签
def create_menu(self):
menu = Menu(self, tearoff=False)
menu.add_command(label="文件复制助手", command=self.d)
return menu
#
def d(self):
print("点击了菜单")
'''
源文件夹 选择 打开函数
:param evt:
:return:
'''
def source_dir_open(self, evt):
# 文件夹路径回显 清空
self.tk_input_source.delete(0, 10000)
# 一般这个直接选择文件,会比较符合人们的使用习惯和软件的用户体验
selected_folder = filedialog.askdirectory()
# 文件夹路径回显到 文本框
self.tk_input_source.insert(0, str(selected_folder))
# logging.info("源文件夹 选择 打开函数",selected_folder)
print("<Button-1>事件未处理:", evt)
def sourcewenj(self, evt):
print("<Button>事件未处理:", evt)
def tag_file(self, evt):
self.tk_input_tag.delete(0, 10000)
selected_folder = filedialog.askdirectory() # 使用askdirectory函数选择文件夹
print(selected_folder)
self.tk_frame_print.config(text=selected_folder)
self.tk_input_tag.insert(0, str(selected_folder))
print("<Button-1>事件未处理:", evt)
def start_num(self, evt):
print("<Leave>事件未处理:", evt)
def choose_xy(self, evt):
print("<<ComboboxSelected>>事件未处理:", evt)
def end_num(self, evt):
print("<Leave>事件未处理:", evt)
'''
复制提交按钮 函数
:param evt:
:return:
'''
def sbmint(self, evt):
# 学院
xy_index = self.tk_select_box_xy.get()
# 目标路径
tag_path = self.tk_input_tag.get()
# 复制文件个数限制
limit_num = self.tk_input_numlimit.get()
# 源文件夹地址
source_path = self.tk_input_source.get()
# 起始学号
start_num = self.tk_input_idnumS.get()
# 最大学号
end_num = self.tk_input_idnumE.get()
# 排除学号:
exclude_num = self.tk_input_exclude.get()
# 源地址 判空
if len(source_path) < 1:
messagebox.showerror("提示", message="请选择源地址")
return
# 目的地址 判空
if len(tag_path) < 1:
messagebox.showerror("提示", message="请选择目的地址")
return
# 学院集合
id_num_list = []
# 复制学院字典表初始化
result_dict = CopyApp.init(self, start_num, end_num, xy_index)
if len(result_dict) > 0:
# 学院对象获取
if len(xy_index) > 1:
xy_obj = result_dict[xy_index]
# 学院 拼接路径
tag_path = tag_path + xy_obj["xy_pyth"]
# 学号集合 文件名集合
id_num_list = xy_obj["xy_list"]
# 排除文件
exclude_list = []
print(exclude_num)
if len(exclude_num) > 0:
exclude_list = exclude_num.split(",")
print(exclude_list)
# 复制函数
CopyApp.copy_file(self, source_path, tag_path, id_num_list, limit_num, exclude_list)
# 限制个数
def mun_limit_fun(self, evt):
# print("<Leave>事件未处理:",evt)
# self.tk_input_source.delete(0, 10000)
# selected_folder = filedialog.askdirectory() # 使用askdirectory函数选择文件夹
# print(selected_folder)
# self.tk_frame_print.config(text=selected_folder)
# self.tk_input_tag.insert(0, str(selected_folder))
print(self.tk_input_numlimit.get())
def exc_num(self, evt):
print("<Leave>事件未处理:", evt)
def __event_bind(self):
self.tk_button_lmr64ezr.bind('<Button-1>', self.source_dir_open)
self.tk_button_lmr6kbq5.bind('<Button>', self.sourcewenj)
self.tk_button_lmr6kbq5.bind('<Button-1>', self.tag_file)
# self.tk_input_lmrbgynm.bind('<Leave>',self.start_num)
self.tk_select_box_xy.bind('<<ComboboxSelected>>', self.choose_xy)
# self.tk_input_lmrbkcai.bind('<Leave>',self.end_num)
self.tk_button_lmrbktwv.bind('<Button-1>', self.sbmint)
self.tk_input_numlimit.bind('<Leave>', self.mun_limit_fun)
self.tk_input_exclude.bind('<Leave>', self.exc_num)
pass
def msg(self, obj):
messagebox.showerror("提示", message="请输入小于500的整数")
obj.delete(0, 10000)
def rule(self):
print(self.tk_input_numlimit.get())
# return self.tk_input_numlimit.get()== "123456"
# return self.tk_input_numlimit.get().isdigit() and int(self.tk_input_numlimit.get()) < 500
return self.tk_input_numlimit.get().isdigit()
if __name__ == "__main__":
win = Win()
win.mainloop()
逻辑实现:
# 起始编号
import os
import re
import shutil
from mylogging import mylogging
class CopyApp:
"""
复制助手
"""
def __init__(self, xy_dict):
super.__init__()
# def lookDir(rootdir, min, max, xy_list, new_path):
'''
:param rootdir: 源文件夹路径
:param new_path: 目标文件存放路径
:param xy_list: 要复制的文件名称集合 不含后缀名
:param limit_num: 单个文件夹存放的文件个数限制
:return:
'''
def copy_file(self, rootdir, new_path, xy_list, limit_num, exclude_list):
logging = mylogging.get_logger("GUI_copy")
logging.info(("开始执行复制操作者…… ", "rootdir", rootdir, "new_path", new_path, xy_list, str(limit_num)))
copy_file_count = 0
new_dir_count = 1
for parent, dirname, filenames in os.walk(rootdir):
for filename in filenames:
filename_num = filename[:filename.rfind(".")]
# 要打印的集合不为空; and 且 源文件不在集合中时,结束本次循环;
if len(xy_list) > 0 and filename_num not in xy_list:
# print("# 集合不为空; and 且 源文件不在集合中时,结束本次循环;")
logging.info(("# 复制集合不为空; 且 源文件不在集合中时,结束本次循环;", filename_num))
continue
# 排除文件集合不为空
if len(exclude_list) > 0 and filename_num in exclude_list:
logging.info(("# 排除文件集合 %s 不为空; 且 源文件%s 在排除集合中时,结束本次循环;" % (exclude_list, filename_num)))
continue
print(filename_num)
# 文件提取数量超 limit_num==400 建立新文件夹 存储
if len(limit_num) > 0 and copy_file_count % int(limit_num) == 0:
# 正则
logging.warning(("文件提取数量%s 超 limit_num==%s 建立新文件夹 存储" % (filename_num, limit_num)))
# 新文件夹名称 命名
if new_dir_count == 1:
new_path += str(new_dir_count)
else:
eliminate = re.compile('\d+')
# 新文件夹命名
new_path = eliminate.sub(str(new_dir_count), new_path)
# 创建文件计数器
new_dir_count += 1
# 目标路径判断 不存在就建一个
if not os.path.exists(new_path):
os.mkdir(new_path)
try:
# 源文件名在 提交集合计数器
copy_file_count += 1
logging.info("开始复制文件:%s" % filename)
# 目标文件路径+ 文件名
new_file_name = new_path + "\\" + filename
# 源文件路径 +文件名
old_file_name = rootdir + "\\" + filename
# 复制一个文件到一个文件或一个目录
shutil.copy(old_file_name, new_file_name)
except OSError as e:
logging.error("复制文件出错了 %s" % filename)
pass
def init(self, start_num, end_num, xy_index):
# 返回结果集合
result_dict = {}
xy_name_dict = {
"cy": "草业学院",
"cj": "城建学院",
"dk": "动物科学学院",
"dy": "动物医学学院",
"gg": "公共管理学院",
"jc": "基础部",
"lx": "林学",
"nx": "农学",
"ng": "农业工程",
"nj": "农业经济",
"rj": "软件",
"sk": "生命科学",
"sp": "食品",
"xk": "信息科学学院",
"yy": "园艺",
"zb": "植物保护学院",
"zh": "资环"
}
start_num_dict = {
"cy": "20232117801",
"cj": "20231715201",
"dk": "20230201401",
"dy": "20232016901",
"gg": "20231009501",
"jc": "20230908801",
"lx": "20230303101",
"nx": "20230100101",
"ng": "20230605801",
"nj": "20230807801",
"rj": "20231612301",
"sk": "20231210101",
"sp": "20230707001",
"xk": "20231210101",
"yy": "20230504401",
"zb": "20231916001",
"zh": "20230403901"
}
end_num_dict = {
"cy": "20232118028",
"cj": "20231715935",
"dk": "20230202040",
"dy": "20232017745",
"gg": "20231010035",
"jc": "20230909433",
"lx": "20230302430",
"nx": "20230100930",
"ng": "20230606928",
"nj": "20230808740",
"rj": "20231615150",
"sk": "20231312245",
"sp": "20230707740",
"xk": "20231210951",
"yy": "20230505725",
"zb": "20231916840",
"zh": "20230404340"
}
if len(xy_index) == 0:
return result_dict
# 遍历学院集合
# for index in xy_name_dict:
# 文件名称集合
id_num_list = []
# 起始文件名和 最大文件名为空 用原来的文件
if len(start_num) == 0 or len(end_num) == 0:
start_num = start_num_dict[xy_index]
end_num = end_num_dict[xy_index]
# 起始文件名和 最大文件名 中间文件名
for num in range(int(start_num), int(end_num) + 1):
id_num_list.append(str(num))
# 返回结果集合
result_dict[xy_index] = dict(
[
("name", xy_index),
("fullname", xy_name_dict[xy_index]),
("start_num", start_num),
("end_num", end_num),
("xy_pyth", "\\" + xy_index),
("xy_list", id_num_list)
]
)
return result_dict
if __name__ == '__main__':
# sour_path = input("请输入源文件路径:")
# start_num = input("请输入起始学号:")
# end_num = input("请输入终止学号:")
# new_path = input("请输入文件存放路径:")
name = input("请输入学院的简称:::")
# lookDir("F:\\aaaa", int(20230808101), int(20230808430), "F:\\b")
# lookDir(sour_path, int(20230808101), int(20230808430), "F:\\b")
result_dict = init()
# print(result_dict)
xy_dict = result_dict[name]
print(xy_dict)
print(len(xy_dict["xy_list"]))
# lookDir("F:\\aaaa", xy_dict["xy_list"], "F:\\b" + xy_dict["xy_pyth"])
日志逻辑:
# coding:utf-8
import os
import logging
from logging import handlers
import sys
# dirname, filename = os.path.split(os.path.abspath(sys.argv[0]))
# LOG_ROOT = dirname
LOG_ROOT = "F:\logs"
class mylogging:
def get_logger(log_filename, level=logging.INFO, when='D', back_count=0):
"""
:param LOG_ROOT:
:brief 日志记录
:param log_filename: 日志名称
:param level: 日志等级
:param when: 间隔时间:
S:秒
M:分
H:小时
D:天
W:每星期(interval==0时代表星期一)
midnight: 每天凌晨
:param back_count: 备份文件的个数,若超过该值,就会自动删除
:return: logger
"""
# 创建一个日志器。提供了应用程序接口
logger = logging.getLogger(log_filename)
# 设置日志输出的最低等级,低于当前等级则会被忽略
logger.setLevel(level)
# 创建日志输出路径
log_path = os.path.join(LOG_ROOT, "logs")
if not os.path.exists(log_path):
os.mkdir(log_path)
log_file_path = os.path.join(log_path, log_filename)
# 创建格式器
formatter = logging.Formatter('%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s')
# 创建处理器:ch为控制台处理器,fh为文件处理器
ch = logging.StreamHandler()
ch.setLevel(level)
# 输出到文件
fh = logging.handlers.TimedRotatingFileHandler(
filename=log_file_path,
when=when,
backupCount=back_count,
encoding='utf-8')
fh.setLevel(level)
# 设置日志输出格式
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# 将处理器,添加至日志器中
logger.addHandler(fh)
logger.addHandler(ch)
return logger
if __name__ == '__main__':
log = get_logger("1")
log.info("这是一条INFO级别的信息,来自 【WeiyiGeek.top】 触发。")
log.debug('debug message')
log.info('info message')
log.warning('warn message')
log.error('error message')
log.critical('critical message')
1、实现批量照片按照 文件名称提取;
2、有文件个数的限制;
大家批评指正。