import
os
import
json
from
PIL
import
Image
from
tkinter
import
Tk, Button, Label, Entry, filedialog, messagebox, Text, Scrollbar, END
from
tkinter.ttk
import
Progressbar
CONFIG_FILE
=
"config.json"
def
select_folder(entry_widget):
folder_path
=
filedialog.askdirectory()
if
folder_path:
entry_widget.delete(
0
,
"end"
)
entry_widget.insert(
0
, folder_path)
def
load_images_from_folder(folder):
images
=
[]
valid_extensions
=
[
'.jpg'
,
'.jpeg'
,
'.png'
,
'.bmp'
,
'.gif'
]
for
filename
in
os.listdir(folder):
ext
=
os.path.splitext(filename)[
1
].lower()
if
ext
in
valid_extensions:
img_path
=
os.path.join(folder, filename)
try
:
img
=
Image.
open
(img_path)
images.append(img)
except
Exception as e:
log_message(f
"无法加载图片 {filename}: {e}"
)
return
images
def
resize_images_to_max_height(images):
max_height
=
max
(img.height
for
img
in
images)
resized_images
=
[]
for
img
in
images:
if
img.height < max_height:
ratio
=
max_height
/
img.height
new_width
=
int
(img.width
*
ratio)
resized_img
=
img.resize((new_width, max_height), Image.Resampling.LANCZOS)
resized_images.append(resized_img)
else
:
resized_images.append(img)
return
resized_images
def
merge_images_horizontally(images):
total_width
=
sum
(img.width
for
img
in
images)
max_height
=
max
(img.height
for
img
in
images)
merged_image
=
Image.new(
'RGB'
, (total_width, max_height))
x_offset
=
0
for
img
in
images:
merged_image.paste(img, (x_offset,
0
))
x_offset
+
=
img.width
return
merged_image
def
save_image(image, folder, index):
output_path
=
os.path.join(folder, f
'merged_{index}.jpg'
)
image.save(output_path)
log_message(f
"图片已保存到 {output_path}"
)
def
process_images(input_folder, output_folder, start_index):
images
=
load_images_from_folder(input_folder)
total_images
=
len
(images)
if
total_images
=
=
0
:
log_message(
"输入文件夹中没有图片!"
)
return
index
=
start_index
processed_count
=
0
while
images:
if
len
(images) >
=
3
:
three_images
=
images[:
3
]
images
=
images[
3
:]
if
any
(img.width > img.height
for
img
in
three_images):
for
img
in
three_images:
if
img.width > img.height:
save_image(img, output_folder, index)
index
+
=
1
processed_count
+
=
1
update_progress(processed_count, total_images)
else
:
resized_images
=
resize_images_to_max_height(three_images)
merged_image
=
merge_images_horizontally(resized_images)
save_image(merged_image, output_folder, index)
index
+
=
1
processed_count
+
=
3
update_progress(processed_count, total_images)
else
:
for
img
in
images:
save_image(img, output_folder, index)
index
+
=
1
processed_count
+
=
1
update_progress(processed_count, total_images)
break
update_progress(total_images, total_images)
log_message(
"图片合并完成!"
)
messagebox.showinfo(
"完成"
,
"图片合并完成!"
)
save_config(input_folder, output_folder, index)
def
log_message(message):
log_text.insert(END, message
+
"\n"
)
log_text.see(END)
log_text.update()
def
update_progress(processed, total):
progress
=
int
((processed
/
total)
*
100
)
progress_bar[
'value'
]
=
progress
progress_label.config(text
=
f
"进度: {progress}%"
)
root.update_idletasks()
def
save_config(input_folder, output_folder, last_index):
config
=
{
"input_folder"
: input_folder,
"output_folder"
: output_folder,
"last_index"
: last_index
}
with
open
(CONFIG_FILE,
"w"
) as f:
json.dump(config, f)
def
load_config():
if
os.path.exists(CONFIG_FILE):
with
open
(CONFIG_FILE,
"r"
) as f:
config
=
json.load(f)
return
(
config.get(
"input_folder"
, ""),
config.get(
"output_folder"
, ""),
config.get(
"last_index"
,
0
)
)
return
"
", "
",
0
def
start_processing():
input_folder
=
input_entry.get()
output_folder
=
output_entry.get()
try
:
start_index
=
int
(start_index_entry.get())
except
ValueError:
messagebox.showerror(
"错误"
,
"开始序号必须是整数!"
)
return
save_config(input_folder, output_folder, start_index)
if
not
input_folder
or
not
output_folder:
messagebox.showerror(
"错误"
,
"请选择输入和输出文件夹!"
)
return
if
not
os.path.exists(input_folder):
messagebox.showerror(
"错误"
,
"输入文件夹不存在!"
)
return
if
not
os.path.exists(output_folder):
os.makedirs(output_folder)
log_message(
"开始处理图片..."
)
progress_bar[
'value'
]
=
0
progress_label.config(text
=
"进度: 0%"
)
process_images(input_folder, output_folder, start_index)
root
=
Tk()
root.title(
"图片合并工具 by DeepSeek"
)
window_width
=
550
window_height
=
450
root.geometry(f
"{window_width}x{window_height}"
)
screen_width
=
root.winfo_screenwidth()
screen_height
=
root.winfo_screenheight()
x_position
=
(screen_width
-
window_width)
/
/
2
y_position
=
(screen_height
-
window_height)
/
/
2
root.geometry(f
"+{x_position}+{y_position}"
)
input_folder, output_folder, last_index
=
load_config()
input_label
=
Label(root, text
=
"输入文件夹:"
)
input_label.grid(row
=
0
, column
=
0
, padx
=
10
, pady
=
10
)
input_entry
=
Entry(root, width
=
30
)
input_entry.grid(row
=
0
, column
=
1
, padx
=
10
, pady
=
10
)
input_entry.insert(
0
, input_folder)
input_button
=
Button(root, text
=
"选择"
, command
=
lambda
: select_folder(input_entry))
input_button.grid(row
=
0
, column
=
2
, padx
=
10
, pady
=
10
)
output_label
=
Label(root, text
=
"输出文件夹:"
)
output_label.grid(row
=
1
, column
=
0
, padx
=
10
, pady
=
10
)
output_entry
=
Entry(root, width
=
30
)
output_entry.grid(row
=
1
, column
=
1
, padx
=
10
, pady
=
10
)
output_entry.insert(
0
, output_folder)
output_button
=
Button(root, text
=
"选择"
, command
=
lambda
: select_folder(output_entry))
output_button.grid(row
=
1
, column
=
2
, padx
=
10
, pady
=
10
)
start_index_label
=
Label(root, text
=
"开始序号:"
)
start_index_label.grid(row
=
2
, column
=
0
, padx
=
10
, pady
=
10
)
start_index_entry
=
Entry(root, width
=
10
)
start_index_entry.grid(row
=
2
, column
=
1
, padx
=
10
, pady
=
10
, sticky
=
"w"
)
start_index_entry.insert(
0
,
str
(last_index
+
1
))
start_button
=
Button(root, text
=
"开始合并"
, command
=
start_processing)
start_button.grid(row
=
2
, column
=
2
, padx
=
10
, pady
=
10
)
progress_label
=
Label(root, text
=
"进度: 0%"
)
progress_label.grid(row
=
3
, column
=
0
, padx
=
10
, pady
=
10
, sticky
=
"w"
)
progress_bar
=
Progressbar(root, orient
=
"horizontal"
, length
=
400
, mode
=
"determinate"
)
progress_bar.grid(row
=
3
, column
=
1
, columnspan
=
2
, padx
=
10
, pady
=
10
)
log_label
=
Label(root, text
=
"合并日志:"
)
log_label.grid(row
=
4
, column
=
0
, padx
=
10
, pady
=
10
, sticky
=
"w"
)
log_text
=
Text(root, height
=
10
, width
=
50
)
log_text.grid(row
=
5
, column
=
0
, columnspan
=
3
, padx
=
10
, pady
=
10
)
scrollbar
=
Scrollbar(root, command
=
log_text.yview)
scrollbar.grid(row
=
5
, column
=
3
, sticky
=
"ns"
)
log_text.config(yscrollcommand
=
scrollbar.
set
)
root.mainloop()