Skip to content

Commit 74b86d2

Browse files
author
Recogerous
committed
feat: add gui and fix bugs
1 parent 3284655 commit 74b86d2

File tree

3 files changed

+491
-15
lines changed

3 files changed

+491
-15
lines changed

README.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Modtime Pecker - 文件夹最新修改时间检查工具
2+
3+
## Description
4+
5+
Modtime Pecker is a Python script designed to check the latest modification time of all folders and files in a specified folder. It recursively searches all subfolders to find the latest modification time of direct subfolders.
6+
7+
Modtime Pecker 是一个Python脚本,旨在帮助用户高效地查看指定文件夹下所有直接子文件夹和文件的最后修改时间。
8+
9+
In general, when using a file explorer to view the modification time of all files and folders in a folder, there is a problem that the modification time of the folder will not be updated with the modification of the files in the folder. This leads to the inability to intuitively view the latest modification time of the folder. This tool aims to solve this problem.
10+
11+
在通常情况下,使用文件资源管理器查看某一文件夹下的所有文件和文件夹的修改时间时,会出现一个问题,即文件夹修改时间不会随着文件夹内文件的修改而更新。这就导致了无法直观地查看文件夹的最新修改时间。本工具旨在解决这一问题。
12+
13+
## Features
14+
15+
- Recursively search all subfolders to find the latest modification time of direct subfolders.
16+
递归搜索所有子文件夹,找到直接子文件夹的最新修改时间。
17+
- Support multiple target folders.
18+
支持一次性查看多个目标文件夹。
19+
- Support custom output format including JSON and TXT.
20+
支持自定义输出格式,包括JSON和TXT。
21+
- 支持将结果复制到剪切板中。
22+
Support copying the result to the clipboard.
23+
24+
## Environment
25+
26+
- Only support Windows currently. 目前仅支持Windows。
27+
- Python 3.6 or higher
28+
- pyperclip~=1.8.2
29+
- tqdm~=4.66.2
30+
31+
## Usage
32+
33+
```bash
34+
python modtime_pecker.py [-h] [-p PATH [PATH ...]] [-i IMPORT_TXT [IMPORT_TXT ...]] [-c] [-g] [-sc] [-st] [-sj]
35+
```
36+
### Arguments
37+
38+
- `-p, --path`: Path(s) of the folder(s) to be checked.
39+
指定要检查的文件夹路径,可指定多个。
40+
- `-i, --import_txt`: Read path from the specified txt file(s).
41+
从TXT文件导入路径列表。
42+
- `-c, --current`: Check the modification time of the current folder.
43+
检查脚本所在当前文件夹。
44+
- `-g, --gui`: Use GUI interface (not available in modtime_pecker_nogui.py).
45+
启动图形用户界面(modtime_pecker_nogui.py中无此功能)。
46+
- `-sc, --save_clipboard`: Save (copy) the result to the clipboard.
47+
将结果复制到剪贴板。
48+
- `-st, --save_txt`: Save the result as a txt file.
49+
将结果保存为TXT文件。
50+
- `-sj, --save_json`: Save the result as a json file.
51+
将结果保存为JSON文件。
52+
53+
In `modtime_pecker_nogui.py`:
54+
55+
Default behavior: If no arguments are specified, the script will check the current folder and save the result as a TXT file.
56+
默认行为:如果未指定任何参数,脚本将检查当前文件夹并保存结果为TXT文件。
57+
58+
In `modtime_pecker.py`:
59+
60+
Default behavior: If no arguments are specified, the script will start the GUI.
61+
默认行为:如果未指定任何参数,脚本将启动图形用户界面。
62+
63+
### Examples
64+
65+
1. Check the modification time of the current folder and save the result as a text file:
66+
67+
```bash
68+
python modtime_pecker.py -c -st
69+
```
70+
71+
2. Check the modification time of specified folders and save the result as a json file:
72+
73+
```bash
74+
python modtime_pecker.py -p /path/to/folder1 /path/to/folder2 -sj
75+
```
76+
77+
3. Check the modification time of folders listed in a text file and copy the result to clipboard:
78+
79+
```bash
80+
python modtime_pecker.py -i folders.txt -sc
81+
```
82+
83+
### Output
84+
85+
The script will output the latest modification time of each folder and file in the specified folder(s) in the following format:
86+
87+
```
88+
In /path/to/folder1:
89+
2023-12-31 18:30:00 - file1.txt
90+
2023-12-30 12:00:00 - subdir1
91+
92+
In /path/to/folder2:
93+
2024-01-01 08:00:00 - file2.txt
94+
2023-12-28 09:45:00 - subdir2
95+
96+
```
97+
98+
JSON output example:
99+
100+
```json
101+
{
102+
"/path/to/folder1": [
103+
{
104+
"2023-12-31 18:30:00": "file1.txt"
105+
},
106+
{
107+
"2023-12-30 12:00:00": "subdir1"
108+
}
109+
],
110+
"/path/to/folder2": [
111+
{
112+
"2024-01-01 08:00:00": "file2.txt"
113+
},
114+
{
115+
"2023-12-28 09:45:00": "subdir2"
116+
}
117+
]
118+
}
119+
```
120+
121+
## License
122+
123+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

modtime_pecker.py

Lines changed: 166 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import sys
1010
import tkinter as tk
1111
from datetime import datetime
12-
from tkinter import messagebox
12+
from tkinter import ttk, filedialog, messagebox
1313

1414
import pyperclip
1515
from tqdm import tqdm
@@ -26,7 +26,7 @@ def get_latest_modification_time(path):
2626
# print(rst_path, end="")
2727
rst = rst_path
2828

29-
time_path_dict = {}
29+
time_path_list = []
3030
# 遍历目标文件夹下的所有直接的文件和文件夹
3131
for entry in os.scandir(path):
3232
modtime = get_timestamp(entry)
@@ -42,18 +42,19 @@ def get_latest_modification_time(path):
4242
# 比较子文件夹中最新的一个条目的修改时间和子文件夹的修改时间
4343
if top_latest_modtime_in_subdir > modtime:
4444
modtime = top_latest_modtime_in_subdir
45-
time_path_dict[modtime] = entry_name
45+
time_path_list.append((modtime, entry_name))
4646

4747
# 递归结束后,获取到目标文件夹下直接的文件和文件夹的修改时间
48-
if time_path_dict:
49-
# 将字典按修改时间排序,得到最终所需结果
50-
time_path_dict = dict(sorted(time_path_dict.items(), reverse=True))
51-
for modtime, entry_name in time_path_dict.items():
48+
if time_path_list:
49+
# 将列表按修改时间(列表每一项元组中的第一个元素)排序,得到最终所需结果
50+
time_path_list.sort(key=lambda x: x[0], reverse=True)
51+
# print(time_path_list)
52+
for modtime, entry_name in time_path_list:
5253
rst_entry = f"{modtime} - {entry_name}{os.linesep}"
5354
# print(rst_entry, end="")
5455
rst += rst_entry
5556
# 获取子文件夹中最新修改的一个条目的修改时间,用于返回给上一级文件夹
56-
top_latest_modtime = list(time_path_dict.keys())[0]
57+
top_latest_modtime = time_path_list[0][0]
5758
# print(f"{top_latest_modtime}")
5859
else:
5960
# 空文件夹,返回空字符串
@@ -79,7 +80,10 @@ def multi_check(path_list):
7980
for target_path in pbar:
8081
if not isinstance(pbar, list):
8182
desc = f"Checking the latest modification time in {target_path}"
82-
pbar.set_description(desc)
83+
try:
84+
pbar.set_description(desc)
85+
except AttributeError:
86+
pass
8387
rsts += get_latest_modification_time(target_path)[0]
8488
rsts += os.linesep
8589
return rsts
@@ -115,11 +119,11 @@ def save2json(rst):
115119
json_dict = {}
116120
for paragraph in paragraphs:
117121
lines = paragraph.split(os.linesep)
118-
target_path = lines[0][3:-1]
119-
json_dict[target_path] = {}
122+
target_path = lines[0][3:-2]
123+
json_dict[target_path] = []
120124
for line in lines[1:]:
121125
mtime, entry_name = line.split(' - ')
122-
json_dict[target_path][mtime] = entry_name
126+
json_dict[target_path].append({mtime: entry_name})
123127
# 保存为json文件
124128
with open(filename, 'w', encoding='utf-8') as f:
125129
json.dump(json_dict, f, ensure_ascii=False, indent=4)
@@ -129,10 +133,156 @@ def save2json(rst):
129133
return msg
130134

131135
def gui():
136+
def browse_folder():
137+
folder_path = filedialog.askdirectory()
138+
if folder_path:
139+
path_entry.delete(0, tk.END)
140+
path_entry.insert(0, folder_path)
141+
142+
def check_modification_time():
143+
target_path = path_entry.get()
144+
if not target_path:
145+
messagebox.showerror("Error", "Please select a folder.")
146+
return
147+
148+
try:
149+
rst = get_latest_modification_time(target_path)[0]
150+
result_text.delete("1.0", tk.END)
151+
result_text.insert(tk.END, rst)
152+
status_label.config(text="Status: Check completed.")
153+
except Exception as e:
154+
messagebox.showerror("Error", str(e))
155+
status_label.config(text="Status: Error occurred.")
156+
157+
def check_script_folder_time():
158+
script_path = os.path.dirname(os.path.realpath(sys.executable))
159+
try:
160+
rst = get_latest_modification_time(script_path)[0]
161+
result_text.delete("1.0", tk.END)
162+
result_text.insert(tk.END, rst)
163+
status_label.config(text="Status: Check completed.")
164+
except Exception as e:
165+
messagebox.showerror("Error", str(e))
166+
status_label.config(text="Status: Error occurred.")
167+
168+
def copy_to_clipboard():
169+
result = result_text.get("1.0", tk.END)
170+
if result.strip():
171+
pyperclip.copy(result)
172+
messagebox.showinfo("Info", "Result copied to clipboard.")
173+
else:
174+
messagebox.showwarning("Warning", "Nothing to copy.")
175+
176+
def save_as_txt():
177+
result = result_text.get("1.0", tk.END)
178+
if result.strip():
179+
filename = filedialog.asksaveasfilename(
180+
defaultextension=".txt",
181+
filetypes=[("Text files", "*.txt")]
182+
)
183+
if filename:
184+
with open(filename, 'w', encoding='utf-8') as f:
185+
f.write(result)
186+
messagebox.showinfo("Info", f"Result saved as {filename}.")
187+
else:
188+
messagebox.showwarning("Warning", "Nothing to save.")
189+
190+
def save_as_json():
191+
result = result_text.get("1.0", tk.END)
192+
if result.strip():
193+
filename = filedialog.asksaveasfilename(
194+
defaultextension=".json",
195+
filetypes=[("JSON files", "*.json")]
196+
)
197+
if filename:
198+
paragraphs = result.split('\n\n')
199+
# 去除空段落
200+
for paragraph in paragraphs:
201+
if not paragraph:
202+
paragraphs.remove(paragraph)
203+
json_dict = {}
204+
for paragraph in paragraphs:
205+
lines = paragraph.split('\n')
206+
target_path = lines[0][3:-3]
207+
json_dict[target_path] = []
208+
for line in lines[1:]:
209+
try:
210+
mtime, entry_name = line.strip().split(' - ')
211+
except ValueError:
212+
# 提醒用户尽量避免修改结果文本之后再保存
213+
error_msg = (f"Invalid text: \n'{line}'\n"
214+
f"Please avoid modifying the "
215+
f"result text before saving.")
216+
messagebox.showerror("Error", error_msg)
217+
return
218+
json_dict[target_path].append({mtime: entry_name})
219+
with open(filename, 'w', encoding='utf-8') as f:
220+
json.dump(json_dict, f, ensure_ascii=False, indent=4)
221+
messagebox.showinfo("Info", f"Result saved as {filename}.")
222+
else:
223+
messagebox.showwarning("Warning", "Nothing to save.")
224+
132225
root = tk.Tk()
133-
root.title("Modtime Pecker - Latest Modification Time Checke")
226+
root.title("Modtime Pecker - Latest Modification Time Checker")
134227
root.geometry('800x600')
135228

229+
# 文件夹路径
230+
folder_frame = ttk.Frame(root)
231+
folder_frame.pack(pady=10)
232+
233+
ttk.Label(folder_frame, text="Folder Path:").grid(
234+
row=0, column=0, padx=5, pady=5)
235+
path_entry = ttk.Entry(folder_frame, width=50)
236+
path_entry.grid(row=0, column=1, padx=5, pady=5)
237+
238+
browse_button = ttk.Button(folder_frame,
239+
text="Browse",
240+
command=browse_folder)
241+
browse_button.grid(row=0, column=2, padx=5, pady=5)
242+
243+
# 功能按钮
244+
action_frame = ttk.Frame(root)
245+
action_frame.pack(pady=10)
246+
247+
check_button = ttk.Button(action_frame,
248+
text="Check Modification Time",
249+
command=check_modification_time)
250+
check_button.grid(row=0, column=0, padx=5, pady=5)
251+
252+
check_script_button = ttk.Button(action_frame,
253+
text="Check Script Folder Time",
254+
command=check_script_folder_time)
255+
check_script_button.grid(row=0, column=1, padx=5, pady=5)
256+
257+
copy_button = ttk.Button(action_frame,
258+
text="Copy Result",
259+
command=copy_to_clipboard)
260+
copy_button.grid(row=0, column=2, padx=5, pady=5)
261+
262+
save_txt_button = ttk.Button(action_frame,
263+
text="Save as TXT",
264+
command=save_as_txt)
265+
save_txt_button.grid(row=0, column=3, padx=5, pady=5)
266+
267+
save_json_button = ttk.Button(action_frame,
268+
text="Save as JSON",
269+
command=save_as_json)
270+
save_json_button.grid(row=0, column=4, padx=5, pady=5)
271+
272+
# 结果显示框
273+
result_frame = ttk.Frame(root)
274+
result_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
275+
276+
ttk.Label(result_frame, text="Result:").pack(anchor=tk.W, padx=5, pady=5)
277+
result_text = tk.Text(result_frame, wrap=tk.WORD, width=80, height=20)
278+
result_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
279+
280+
# 状态栏
281+
status_label = ttk.Label(root, text="Status: Ready")
282+
status_label.pack(side=tk.BOTTOM, padx=10, pady=5)
283+
284+
root.mainloop()
285+
136286
def argparser():
137287
parser = argparse.ArgumentParser(
138288
description='Check the latest modification time of all '
@@ -207,5 +357,6 @@ def cli():
207357

208358
if __name__ == '__main__':
209359
if len(sys.argv) == 1:
210-
sys.argv.extend(['-c', '-st'])
211-
cli()
360+
gui()
361+
else:
362+
cli()

0 commit comments

Comments
 (0)