前言
交毕设居然要求要把代码拷贝到word文档上???
什么神奇操作,而我的毕设代码量似乎六七千行,这不得累死…
还是写个脚本吧,说干就干!
本来计划很美好,结果执行起来后写出却是阉割版…
(treeview监听事件后不知道怎么只让子类处理…菜鸡菜鸡,要是像vue那么方便就好了, messagebox居然在mac上老是warning…)
该脚本只需要python环境即可,安装的见我另一篇博文Python环境配置与入门建议(面向新手文)
界面
选了一个文件夹当例子
导出后:
by the way, 若你想保留缩进的话,可以做以下更改(只是我自己有些代码层数多,缩进多了之后行的空间不太够,所以直接就去掉缩进了)
拷贝到word后,只需要搜索 【代码文件】就能找到每一个拷贝的文件名了,然后就可以设置一下 标题,我想了想,还是要把这个选择留给用户去做,因为我不太清楚你想把这个文件定义为第几级标题。
宽度不够中间那一条线可用鼠标拖动过去
用户自定义区
代码
"""
Author: Andy Dennis
Date: 2022.5.14, SAT
Last Alter Time: 16:36, 2022.5.14
"""
import tkinter as tk
from tkinter import filedialog
from tkinter import ttk
import os
import tkinter.font as tf
from threading import Thread
# ---> 自定义区 start, 供用户自定义修改
file_decoration_prefix = '代码文件' # 文件名前缀
count_lines_with_null_string = False # 计算代码行数时是否考虑空行
put_null_string_to_txt = False # 是否把空行也复制进文档
# 不参与计算的文件类型
ignore_file_type = ['pt',
'png', 'jpg', 'jpeg', 'pdf', 'ico',
'doc', 'docx', 'ppt', 'pptx', 'csv', 'xls', 'xlsx']
# 代码注释样式, {}代表注释内容, 默认 //, 该注释会放在文件开头,作为文件开始的标识
annotation_map = {'html': '<!-- {} -->',
'css': '/* {} */',
'py': '# {}',
'vue': '<!-- {} -->'}
# 忽略的文件
ignore_name_lt = ['.DS_Store', '__pycache__']
# <--- 自定义区 end
# 全局变量
dir_path = '' # 工作目录
code_lt = []
code_file_nums = 0
code_file_lines = 0
# txt文本读取出来后,返回列表
def read_txt_lines(fp_):
# print('!!! ', fp_)
with open(fp_, "r", encoding='utf-8') as f: # 打开文件
data = f.readlines()
data = [item.strip() for item in data]
return data
# 列表转txt文本
def write_lt2txt(lt, fp_):
lt = [i + '\n' for i in lt]
with open(fp_, 'w', encoding='utf-8') as f:
f.writelines(lt)
print('写入到 {} 文件成功'.format(fp_))
def insert_item(tree_root, tree_p, path_p):
"""
递归在树状目录中插入显示文件结构, 深度优先
tree_root: 树状表格根节点,
tree_p: 父类根节点,
path_p: 目录名字
"""
dir_lt = os.listdir(path_p)
for item_index, item_name in enumerate(dir_lt):
if item_name in ignore_name_lt: # 跳过一些文件
continue
item_path = '{}/{}'.format(path_p, item_name)
if os.path.isdir(item_path):
tree_new = tree_root.insert(
tree_p, item_index, text="{}".format(item_name))
insert_item(tree_root, tree_new, item_path)
else:
tree_new = tree_root.insert(
tree_p, item_index, text="{}".format(item_name))
# 清空树状目录
def clearTreeView(tree_node):
x = tree_node.get_children()
for item in x:
tree_node.delete(item)
def copy_file_recurrent(abs_path_p, path_p):
"""
递归拷贝文件代码, 深度优先
tree_root: 树状表格根节点,
tree_p: 父类根节点,
path_p: 目录名字
"""
global code_lt
global code_file_nums
global code_file_lines
# global ignore_file_type
# global file_decoration_prefix # 文件名前缀
# global count_lines_with_null_string # 计算代码行数时是否考虑空行
# global put_null_string_to_txt # 是否把空行也复制进文档
dir_lt = os.listdir(abs_path_p)
for item_name in dir_lt:
item_abs_path = '{}/{}'.format(abs_path_p, item_name)
if len(path_p) > 0:
tmp_path = '{}/{}'.format(path_p, item_name)
else:
tmp_path = item_name
if os.path.isdir(item_abs_path):
copy_file_recurrent(item_abs_path, tmp_path)
else:
if item_name in ignore_name_lt: # 跳过忽略的文件
continue
item_type = item_name.split('.')[-1]
# print('@@@ item_type', item_type, item_type in ignore_file_type)
# print(ignore_file_type)
if item_type not in ignore_file_type:
code_file_nums += 1
code_lt.append('{}'.format(item_name))
anaotation_template = annotation_map.get(item_type, '// {}')
code_lt.append(anaotation_template.format(file_decoration_prefix + ' ' + tmp_path))
tmp_lt = read_txt_lines(item_abs_path)
for line_str in tmp_lt:
if len(line_str) > 0:
code_lt.append(line_str)
code_file_lines += 1
else:
if put_null_string_to_txt:
code_lt.append(line_str)
if count_lines_with_null_string:
code_file_lines += 1
# 选择文件线程操作
def select_work_dir_worker():
global dir_path
clearTreeView(tree_root)
fp = filedialog.askdirectory(title=u'选择目标目录')
if fp is not None and len(fp) > 0:
dir_path = fp
insert_item(tree_root, "", dir_path)
tmp_path = fp
if tmp_path[0] == '/':
tmp_path = tmp_path[1:] # mac下默认/开头
tmp_path = tmp_path.replace('\\', '/').split('/')
if len(tmp_path) > 4:
tmp_path = tmp_path[-4:]
tmp_path.insert(0, '...')
else:
tmp_path.insert(0, '')
tmp_path = '/'.join(tmp_path)
label1['text'] = 'work dir: {}'.format(dir_path)
label2['text'] = 'tips: 选择了 {}'.format(tmp_path)
label3['text'] = ''
else:
label2['text'] = 'tips: 您取消了选择 工作目录 操作.'
# 拷贝代码线程工作
def copy_code_op_worker():
global code_lt
global code_file_nums
global code_file_lines
if len(dir_path) == 0:
label2['text'] = 'tips: 请您先选择工作目录.'
return
# 清零工作
code_lt.clear()
code_file_nums = 0
code_file_lines = 0
copy_file_recurrent(dir_path, '')
# print(code_lt)
print('代码文件: {}, 代码行数: {}'.format(code_file_nums, code_file_lines))
# print('copy_code_op')
fp = filedialog.asksaveasfilename(defaultextension='.txt')
if fp is not None and len(fp) > 0:
write_lt2txt(code_lt, fp)
tmp_path = fp.replace('\\', '/')
if tmp_path[0] == '/':
tmp_path = tmp_path[1:] # mac下默认/开头
tmp_path = tmp_path.split('/')
if len(tmp_path) > 4:
tmp_path = tmp_path[-4:]
tmp_path.insert(0, '...')
else:
tmp_path.insert(0, '')
tmp_path = '/'.join(tmp_path)
label2['text'] = '导出到 {}'.format(tmp_path)
label3['text'] = '代码文件个数: {}, 代码行数: {}'.format(
code_file_nums, code_file_lines
)
else:
label2['text'] = 'tips: 您取消了 代码拷贝 操作.'
code_lt.clear()
code_file_nums = 0
code_file_lines = 0
# 线程类
class MyThread(Thread):
def __init__(self, func):
"""
:param func: 可调用的对象
"""
Thread.__init__(self)
self.func = func
def run(self):
self.func()
# 线程工厂
def work_factory(func):
t = MyThread(func)
t.start()
# t.join()
# 选择工作目录操作
def select_work_dir():
work_factory(select_work_dir_worker)
# 拷贝代码操作
def copy_code_op():
work_factory(copy_code_op_worker)
if __name__ == '__main__':
window = tk.Tk()
window.title("Code Copy v1") # #窗口标题
window.geometry("600x468") # #窗口位置500后面是字母x
bt1 = tk.Button(window, text='选择目录', width=16, height=1,
font=tf.Font(size=14), command=select_work_dir)
bt1.place(x=100, y=30)
bt2 = tk.Button(window, text='拷贝代码', width=16, height=1,
font=tf.Font(size=14), command=copy_code_op)
bt2.place(x=350, y=30)
label1 = tk.Label(window, text="请选择工作目录".format(dir_path))
label1.place(x=40, y=64)
label2 = tk.Label(window, text="")
label2.place(x=40, y=420)
label3 = tk.Label(window, text="")
label3.place(x=40, y=440)
'''
树状结构
'''
tree_root = ttk.Treeview(window, height=16, columns='code')
tree_root.place(x=100, y=100)
# 不允许重新设置大小
window.resizable('false', 'false')
window.mainloop() # #窗口持久化