import
tkinter as tk
from
tkinter
import
ttk, filedialog, messagebox, simpledialog, colorchooser
import
base64, hashlib, os, qrcode, webbrowser, time, random, json, requests, threading, subprocess, socket, string, re, zipfile
from
PIL
import
Image, ImageTk, ImageDraw, ImageGrab
from
datetime
import
datetime, timezone, timedelta
from
tkinter
import
scrolledtext
import
pyperclip
import
zhconv
class
ToolBox:
def
__init__(
self
, root):
self
.root
=
root
self
.root.title(
"超级工具箱 v2.4"
)
self
.root.geometry(
"1600x1000"
)
self
.root.configure(bg
=
"#f0f0f0"
)
self
.style
=
ttk.Style()
self
.style.theme_use(
'clam'
)
self
.style.configure(
"TLabel"
, font
=
(
"Segoe UI"
,
10
), background
=
"#f0f0f0"
)
self
.style.configure(
"TButton"
, font
=
(
"Segoe UI"
,
10
))
self
.style.configure(
"TEntry"
, font
=
(
"Segoe UI"
,
10
))
self
.style.configure(
"Treeview"
, font
=
(
"Segoe UI"
,
10
))
self
.create_menu_bar()
self
.status_var
=
tk.StringVar()
self
.status_var.
set
(
"就绪"
)
self
.status_bar
=
ttk.Label(root, textvariable
=
self
.status_var, relief
=
"sunken"
, anchor
=
"w"
)
self
.status_bar.pack(side
=
"bottom"
, fill
=
"x"
)
self
.notebook
=
ttk.Notebook(root)
self
.notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
self
.create_file_tools_tab()
self
.create_encoder_tab()
self
.create_qrcode_tab()
self
.create_hash_tools_tab()
self
.create_time_tools_tab()
self
.create_network_tools_tab()
self
.create_conversion_tools_tab()
self
.create_other_tools_tab()
self
.create_context_menu()
def
create_menu_bar(
self
):
menubar
=
tk.Menu(
self
.root)
file_menu
=
tk.Menu(menubar, tearoff
=
0
)
file_menu.add_command(label
=
"退出"
, command
=
self
.root.quit)
menubar.add_cascade(label
=
"文件"
, menu
=
file_menu)
tool_menu
=
tk.Menu(menubar, tearoff
=
0
)
tool_menu.add_command(label
=
"刷新界面"
, command
=
self
.refresh_ui)
menubar.add_cascade(label
=
"工具"
, menu
=
tool_menu)
help_menu
=
tk.Menu(menubar, tearoff
=
0
)
help_menu.add_command(label
=
"关于"
, command
=
self
.show_about)
menubar.add_cascade(label
=
"帮助"
, menu
=
help_menu)
self
.root.config(menu
=
menubar)
def
refresh_ui(
self
):
self
.status_var.
set
(
"正在刷新界面..."
)
self
.root.update_idletasks()
messagebox.showinfo(
"提示"
,
"界面已刷新(动态数据会自动更新)"
)
self
.status_var.
set
(
"就绪"
)
def
show_about(
self
):
messagebox.showinfo(
"关于"
,
"超级工具箱 v2.4\n作者: Suneryzgg123\n新增功能:远程桌面、远程文件管理、远程关机、局域网聊天及实时音视频\n界面更美观、布局更合理。"
)
def
create_context_menu(
self
):
self
.context_menu
=
tk.Menu(
self
.root, tearoff
=
0
)
self
.context_menu.add_command(label
=
"复制"
, command
=
self
.copy_text)
self
.context_menu.add_command(label
=
"粘贴"
, command
=
self
.paste_text)
self
.root.bind(
"<Button-3>"
,
self
.show_context_menu)
def
show_context_menu(
self
, event):
self
.context_menu.post(event.x_root, event.y_root)
def
copy_text(
self
):
widget
=
self
.root.focus_get()
if
isinstance
(widget, (tk.Entry, tk.Text, scrolledtext.ScrolledText)):
widget.event_generate(
"<<Copy>>"
)
def
paste_text(
self
):
widget
=
self
.root.focus_get()
if
isinstance
(widget, (tk.Entry, tk.Text, scrolledtext.ScrolledText)):
widget.event_generate(
"<<Paste>>"
)
def
create_file_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"文件工具"
)
tab.columnconfigure(
0
, weight
=
1
)
search_frame
=
ttk.LabelFrame(tab, text
=
"文件搜索"
)
search_frame.grid(row
=
0
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
search_frame.columnconfigure(
1
, weight
=
1
)
ttk.Label(search_frame, text
=
"搜索目录:"
).grid(row
=
0
, column
=
0
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
self
.path_entry
=
ttk.Entry(search_frame, width
=
50
)
self
.path_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(search_frame, text
=
"选择目录"
, command
=
self
.browse_directory).grid(row
=
0
, column
=
2
, padx
=
5
)
ttk.Label(search_frame, text
=
"关键词(支持正则):"
).grid(row
=
1
, column
=
0
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
self
.keyword_entry
=
ttk.Entry(search_frame, width
=
50
)
self
.keyword_entry.grid(row
=
1
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(search_frame, text
=
"扩展名(如 .txt):"
).grid(row
=
2
, column
=
0
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
self
.extension_entry
=
ttk.Entry(search_frame, width
=
20
)
self
.extension_entry.grid(row
=
2
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Label(search_frame, text
=
"最小大小(KB):"
).grid(row
=
3
, column
=
0
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
self
.min_size_entry
=
ttk.Entry(search_frame, width
=
15
)
self
.min_size_entry.grid(row
=
3
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Button(search_frame, text
=
"开始搜索"
, command
=
self
.search_files).grid(row
=
4
, column
=
1
, pady
=
5
)
columns
=
(
"文件名"
,
"大小(KB)"
,
"修改时间"
)
self
.file_tree
=
ttk.Treeview(search_frame, columns
=
columns, show
=
"headings"
, height
=
10
)
for
col
in
columns:
self
.file_tree.heading(col, text
=
col)
self
.file_tree.column(col, anchor
=
'w'
)
self
.file_tree.grid(row
=
5
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
, sticky
=
'nsew'
)
self
.file_tree.bind(
"<Double-Button-1>"
,
self
.open_selected_file)
scrollbar
=
ttk.Scrollbar(search_frame, orient
=
"vertical"
, command
=
self
.file_tree.yview)
scrollbar.grid(row
=
5
, column
=
3
, sticky
=
'ns'
)
self
.file_tree.config(yscrollcommand
=
scrollbar.
set
)
self
.search_progress
=
ttk.Progressbar(search_frame, orient
=
"horizontal"
, mode
=
"determinate"
)
self
.search_progress.grid(row
=
6
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
5
, sticky
=
'ew'
)
rename_frame
=
ttk.LabelFrame(tab, text
=
"批量重命名"
)
rename_frame.grid(row
=
1
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(rename_frame, text
=
"原文件名:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.original_name
=
ttk.Entry(rename_frame, width
=
25
)
self
.original_name.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(rename_frame, text
=
"新文件名:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.new_name
=
ttk.Entry(rename_frame, width
=
25
)
self
.new_name.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(rename_frame, text
=
"执行重命名"
, command
=
self
.batch_rename).grid(row
=
0
, column
=
4
, padx
=
10
, pady
=
2
)
compress_frame
=
ttk.LabelFrame(tab, text
=
"文件压缩"
)
compress_frame.grid(row
=
2
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Button(compress_frame, text
=
"选择文件压缩"
, command
=
self
.select_files_to_compress).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.compress_file_label
=
ttk.Label(compress_frame, text
=
"未选择文件"
)
self
.compress_file_label.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(compress_frame, text
=
"压缩为ZIP"
, command
=
self
.compress_files).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
def
browse_directory(
self
):
path
=
filedialog.askdirectory()
if
path:
self
.path_entry.delete(
0
, tk.END)
self
.path_entry.insert(
0
, path)
def
search_files(
self
):
directory
=
self
.path_entry.get()
keyword
=
self
.keyword_entry.get()
ext_filter
=
self
.extension_entry.get().strip().lower()
try
:
min_size
=
float
(
self
.min_size_entry.get())
*
1024
if
self
.min_size_entry.get()
else
0
except
:
min_size
=
0
if
not
directory:
messagebox.showwarning(
"提示"
,
"请先选择目录"
)
return
for
item
in
self
.file_tree.get_children():
self
.file_tree.delete(item)
self
.search_progress[
'value'
]
=
0
self
.status_var.
set
(
"正在搜索文件..."
)
total_files
=
sum
([
len
(files)
for
_, _, files
in
os.walk(directory)])
processed
=
0
def
do_search():
nonlocal processed
for
root_dir, _, files
in
os.walk(directory):
for
file
in
files:
processed
+
=
1
self
.search_progress[
'value'
]
=
int
(processed
/
total_files
*
100
)
self
.root.update_idletasks()
if
keyword:
if
not
re.search(keyword,
file
, re.IGNORECASE):
continue
if
ext_filter
and
not
file
.lower().endswith(ext_filter):
continue
filepath
=
os.path.join(root_dir,
file
)
try
:
stat
=
os.stat(filepath)
except
:
continue
size_kb
=
stat.st_size
/
1024
if
size_kb < min_size:
continue
mod_time
=
datetime.fromtimestamp(stat.st_mtime).strftime(
'%Y-%m-%d %H:%M:%S'
)
self
.file_tree.insert("
", tk.END, values=(filepath, f"
{size_kb:.
1f
}", mod_time))
self
.file_tree.insert("
", tk.END, values=("
", "
", f"
共找到 {
len
(
self
.file_tree.get_children())} 个匹配文件"))
self
.status_var.
set
(
"文件搜索完成"
)
threading.Thread(target
=
do_search).start()
def
open_selected_file(
self
, event):
item
=
self
.file_tree.focus()
if
item:
filepath
=
self
.file_tree.item(item,
"values"
)[
0
]
if
os.path.isfile(filepath):
try
:
if
os.name
=
=
'nt'
:
os.startfile(filepath)
else
:
webbrowser.
open
(filepath)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"无法打开文件:{e}"
)
def
batch_rename(
self
):
original
=
self
.original_name.get()
new
=
self
.new_name.get()
directory
=
self
.path_entry.get()
if
not
directory:
messagebox.showwarning(
"提示"
,
"请先选择目录"
)
return
count
=
0
for
filename
in
os.listdir(directory):
if
original
in
filename:
new_filename
=
filename.replace(original, new)
try
:
os.rename(os.path.join(directory, filename),
os.path.join(directory, new_filename))
count
+
=
1
except
Exception as e:
print
(f
"重命名失败: {e}"
)
messagebox.showinfo(
"完成"
, f
"成功重命名 {count} 个文件"
)
def
select_files_to_compress(
self
):
files
=
filedialog.askopenfilenames(title
=
"选择要压缩的文件"
)
if
files:
self
.files_to_compress
=
files
self
.compress_file_label.config(text
=
f
"{len(files)} 个文件已选"
)
else
:
self
.files_to_compress
=
[]
self
.compress_file_label.config(text
=
"未选择文件"
)
def
compress_files(
self
):
if
not
hasattr
(
self
,
"files_to_compress"
)
or
not
self
.files_to_compress:
messagebox.showwarning(
"提示"
,
"请先选择文件"
)
return
zip_filename
=
filedialog.asksaveasfilename(defaultextension
=
".zip"
,
filetypes
=
[(
"ZIP文件"
,
"*.zip"
), (
"所有文件"
,
"*.*"
)])
if
zip_filename:
try
:
with zipfile.ZipFile(zip_filename,
'w'
) as zipf:
for
f
in
self
.files_to_compress:
zipf.write(f, os.path.basename(f))
messagebox.showinfo(
"完成"
, f
"成功压缩为 {zip_filename}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"压缩失败:{e}"
)
def
create_encoder_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"编解码工具"
)
tab.columnconfigure(
1
, weight
=
1
)
encode_frame
=
ttk.LabelFrame(tab, text
=
"输入输出"
)
encode_frame.grid(row
=
0
, column
=
0
, columnspan
=
3
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(encode_frame, text
=
"内容:"
).grid(row
=
0
, column
=
0
, sticky
=
'nw'
, padx
=
5
, pady
=
2
)
self
.encode_input
=
scrolledtext.ScrolledText(encode_frame, height
=
6
, width
=
60
)
self
.encode_input.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(encode_frame, text
=
"结果:"
).grid(row
=
1
, column
=
0
, sticky
=
'nw'
, padx
=
5
, pady
=
2
)
self
.encode_output
=
scrolledtext.ScrolledText(encode_frame, height
=
6
, width
=
60
)
self
.encode_output.grid(row
=
1
, column
=
1
, padx
=
5
, pady
=
2
)
btn_frame
=
ttk.Frame(tab)
btn_frame.grid(row
=
1
, column
=
0
, columnspan
=
3
, pady
=
5
)
ttk.Button(btn_frame, text
=
"Base64编码/解码"
, command
=
self
.base64_convert).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"Base32编码/解码"
, command
=
lambda
:
self
.baseN_convert(
32
)).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"Base85编码/解码"
, command
=
lambda
:
self
.baseN_convert(
85
)).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"Hex编码/解码"
, command
=
self
.hex_convert).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"ROT13转换"
, command
=
self
.rot13).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"URL编码/解码"
, command
=
self
.url_convert).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"Unicode转/解码"
, command
=
self
.unicode_convert).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"清空"
, command
=
self
.clear_encode).pack(side
=
'left'
, padx
=
3
)
ttk.Checkbutton(btn_frame, text
=
"自动复制结果"
, variable
=
tk.BooleanVar(value
=
True
)).pack(side
=
'left'
, padx
=
3
)
def
base64_convert(
self
):
text
=
self
.encode_input.get(
"1.0"
, tk.END).strip()
try
:
if
all
(c
in
string.printable
for
c
in
text):
result
=
base64.b64encode(text.encode()).decode()
else
:
result
=
base64.b64decode(text).decode()
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"Base64操作失败:{e}"
)
def
baseN_convert(
self
, base):
text
=
self
.encode_input.get(
"1.0"
, tk.END).strip()
try
:
if
base
=
=
32
:
if
all
(c
in
string.printable
for
c
in
text):
result
=
base64.b32encode(text.encode()).decode()
else
:
result
=
base64.b32decode(text).decode()
elif
base
=
=
85
:
if
all
(c
in
string.printable
for
c
in
text):
result
=
base64.b85encode(text.encode()).decode()
else
:
result
=
base64.b85decode(text).decode()
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"Base{base}操作失败:{e}"
)
def
hex_convert(
self
):
text
=
self
.encode_input.get(
"1.0"
, tk.END).strip()
try
:
if
all
(c
in
string.hexdigits
for
c
in
text.strip()):
result
=
bytes.fromhex(text.strip()).decode()
else
:
result
=
text.encode().
hex
()
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"Hex操作失败:{e}"
)
def
rot13(
self
):
text
=
self
.encode_input.get(
"1.0"
, tk.END)
result
=
text.translate(
str
.maketrans(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
,
"NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm"
))
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
def
url_convert(
self
):
import
urllib.parse
text
=
self
.encode_input.get(
"1.0"
, tk.END).strip()
try
:
if
"%"
in
text:
result
=
urllib.parse.unquote(text)
else
:
result
=
urllib.parse.quote(text)
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"URL转换失败:{e}"
)
def
unicode_convert(
self
):
text
=
self
.encode_input.get(
"1.0"
, tk.END).strip()
try
:
if
"\\u"
in
text:
result
=
text.encode().decode(
'unicode_escape'
)
else
:
result
=
text.encode(
'unicode_escape'
).decode()
self
.encode_output.delete(
"1.0"
, tk.END)
self
.encode_output.insert(tk.END, result)
pyperclip.copy(result)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"Unicode转换失败:{e}"
)
def
clear_encode(
self
):
self
.encode_input.delete(
"1.0"
, tk.END)
self
.encode_output.delete(
"1.0"
, tk.END)
def
create_qrcode_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"二维码工具"
)
frame
=
ttk.LabelFrame(tab, text
=
"二维码生成器"
)
frame.pack(fill
=
'both'
, expand
=
True
, padx
=
10
, pady
=
10
)
ttk.Label(frame, text
=
"输入内容:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
, sticky
=
'w'
)
self
.qr_content
=
ttk.Entry(frame, width
=
60
)
self
.qr_content.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(frame, text
=
"选择前景色"
, command
=
self
.choose_fg_color).grid(row
=
1
, column
=
0
, padx
=
5
, pady
=
2
)
self
.fg_color
=
"#000000"
self
.fg_color_label
=
ttk.Label(frame, text
=
self
.fg_color)
self
.fg_color_label.grid(row
=
1
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Button(frame, text
=
"选择背景色"
, command
=
self
.choose_bg_color).grid(row
=
2
, column
=
0
, padx
=
5
, pady
=
2
)
self
.bg_color
=
"#ffffff"
self
.bg_color_label
=
ttk.Label(frame, text
=
self
.bg_color)
self
.bg_color_label.grid(row
=
2
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Label(frame, text
=
"二维码版本(1-40):"
).grid(row
=
3
, column
=
0
, padx
=
5
, pady
=
2
, sticky
=
'w'
)
self
.qr_version
=
ttk.Spinbox(frame, from_
=
1
, to
=
40
, width
=
5
)
self
.qr_version.
set
(
1
)
self
.qr_version.grid(row
=
3
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Label(frame, text
=
"容错级别:"
).grid(row
=
4
, column
=
0
, padx
=
5
, pady
=
2
, sticky
=
'w'
)
self
.error_level
=
ttk.Combobox(frame, values
=
[
"L"
,
"M"
,
"Q"
,
"H"
], state
=
"readonly"
, width
=
5
)
self
.error_level.
set
(
"H"
)
self
.error_level.grid(row
=
4
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Label(frame, text
=
"中心图大小比例(0.1-0.4):"
).grid(row
=
5
, column
=
0
, padx
=
5
, pady
=
2
, sticky
=
'w'
)
self
.center_ratio
=
ttk.Spinbox(frame, from_
=
0.1
, to
=
0.4
, increment
=
0.05
, width
=
5
)
self
.center_ratio.
set
(
0.25
)
self
.center_ratio.grid(row
=
5
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Button(frame, text
=
"选择中心图片"
, command
=
self
.choose_center_image).grid(row
=
6
, column
=
0
, padx
=
5
, pady
=
2
)
self
.center_img_path
=
None
self
.center_img_label
=
ttk.Label(frame, text
=
"无"
)
self
.center_img_label.grid(row
=
6
, column
=
1
, sticky
=
'w'
, padx
=
5
, pady
=
2
)
ttk.Button(frame, text
=
"生成二维码"
, command
=
self
.generate_qrcode).grid(row
=
7
, column
=
1
, pady
=
5
)
ttk.Button(frame, text
=
"保存二维码"
, command
=
self
.save_qrcode).grid(row
=
7
, column
=
0
, pady
=
5
)
self
.qr_image_label
=
ttk.Label(frame)
self
.qr_image_label.grid(row
=
8
, column
=
0
, columnspan
=
2
, pady
=
5
)
def
choose_fg_color(
self
):
color
=
colorchooser.askcolor()[
1
]
if
color:
self
.fg_color
=
color
self
.fg_color_label.config(text
=
color)
def
choose_bg_color(
self
):
color
=
colorchooser.askcolor()[
1
]
if
color:
self
.bg_color
=
color
self
.bg_color_label.config(text
=
color)
def
choose_center_image(
self
):
path
=
filedialog.askopenfilename(title
=
"选择中心图片"
, filetypes
=
[(
"图片文件"
,
"*.png;*.jpg;*.jpeg;*.bmp"
)])
if
path:
self
.center_img_path
=
path
self
.center_img_label.config(text
=
os.path.basename(path))
else
:
self
.center_img_path
=
None
self
.center_img_label.config(text
=
"无"
)
def
generate_qrcode(
self
):
data
=
self
.qr_content.get()
if
not
data:
messagebox.showwarning(
"提示"
,
"请输入内容"
)
return
try
:
version
=
int
(
self
.qr_version.get())
err_level
=
{
"L"
: qrcode.constants.ERROR_CORRECT_L,
"M"
: qrcode.constants.ERROR_CORRECT_M,
"Q"
: qrcode.constants.ERROR_CORRECT_Q,
"H"
: qrcode.constants.ERROR_CORRECT_H
}[
self
.error_level.get()]
qr
=
qrcode.QRCode(
version
=
version,
error_correction
=
err_level,
box_size
=
10
,
border
=
4
,
)
qr.add_data(data)
qr.make(fit
=
True
)
self
.qr_img
=
qr.make_image(fill_color
=
self
.fg_color, back_color
=
self
.bg_color).convert(
'RGB'
)
if
self
.center_img_path:
center_img
=
Image.
open
(
self
.center_img_path)
qr_size
=
self
.qr_img.size[
0
]
ratio
=
float
(
self
.center_ratio.get())
size
=
int
(qr_size
*
ratio)
center_img
=
center_img.resize((size, size), Image.ANTIALIAS)
pos
=
((qr_size
-
size)
/
/
2
, (qr_size
-
size)
/
/
2
)
self
.qr_img.paste(center_img, pos)
self
.display_qrcode()
except
Exception as e:
messagebox.showerror(
"错误"
, f
"生成二维码失败:{e}"
)
def
display_qrcode(
self
):
self
.qr_img.save(
"temp_qr.png"
)
image
=
Image.
open
(
"temp_qr.png"
)
image.thumbnail((
350
,
350
))
photo
=
ImageTk.PhotoImage(image)
self
.qr_image_label.config(image
=
photo)
self
.qr_image_label.image
=
photo
def
save_qrcode(
self
):
if
hasattr
(
self
,
'qr_img'
):
filename
=
filedialog.asksaveasfilename(
defaultextension
=
".png"
,
filetypes
=
[(
"PNG文件"
,
"*.png"
), (
"所有文件"
,
"*.*"
)]
)
if
filename:
self
.qr_img.save(filename)
messagebox.showinfo(
"保存成功"
,
"二维码已保存"
)
else
:
messagebox.showwarning(
"提示"
,
"请先生成二维码"
)
def
create_hash_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"哈希工具"
)
tab.columnconfigure(
0
, weight
=
1
)
str_frame
=
ttk.LabelFrame(tab, text
=
"字符串哈希"
)
str_frame.grid(row
=
0
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(str_frame, text
=
"输入字符串:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.hash_input
=
ttk.Entry(str_frame, width
=
50
)
self
.hash_input.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(str_frame, text
=
"计算哈希"
, command
=
self
.calculate_str_hash).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.hash_result
=
ttk.Entry(str_frame, width
=
80
)
self
.hash_result.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(str_frame, text
=
"复制结果"
, command
=
lambda
: pyperclip.copy(
self
.hash_result.get())).grid(row
=
1
, column
=
3
, padx
=
5
, pady
=
2
)
file_frame
=
ttk.LabelFrame(tab, text
=
"文件哈希"
)
file_frame.grid(row
=
1
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(file_frame, text
=
"选择文件:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.file_hash_entry
=
ttk.Entry(file_frame, width
=
50
)
self
.file_hash_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(file_frame, text
=
"浏览"
, command
=
self
.browse_hash_file).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
ttk.Label(file_frame, text
=
"算法:"
).grid(row
=
1
, column
=
0
, padx
=
5
, pady
=
2
)
self
.hash_algo_combo
=
ttk.Combobox(file_frame, values
=
[
"md5"
,
"sha1"
,
"sha256"
,
"sha512"
,
"sha3_256"
], state
=
"readonly"
)
self
.hash_algo_combo.
set
(
"md5"
)
self
.hash_algo_combo.grid(row
=
1
, column
=
1
, padx
=
5
, pady
=
2
, sticky
=
'w'
)
ttk.Button(file_frame, text
=
"计算文件哈希"
, command
=
self
.calculate_file_hash).grid(row
=
1
, column
=
2
, padx
=
5
, pady
=
2
)
def
calculate_str_hash(
self
):
text
=
self
.hash_input.get()
algorithms
=
[
'md5'
,
'sha1'
,
'sha256'
,
'sha512'
,
'sha3_256'
]
results
=
[]
for
algo
in
algorithms:
try
:
hash_obj
=
hashlib.new(algo)
hash_obj.update(text.encode())
results.append(f
"{algo.upper()}: {hash_obj.hexdigest()}"
)
except
Exception as e:
results.append(f
"{algo.upper()}: 错误"
)
self
.hash_result.delete(
0
, tk.END)
self
.hash_result.insert(
0
,
" | "
.join(results))
def
browse_hash_file(
self
):
filename
=
filedialog.askopenfilename()
if
filename:
self
.file_hash_entry.delete(
0
, tk.END)
self
.file_hash_entry.insert(
0
, filename)
def
calculate_file_hash(
self
):
filename
=
self
.file_hash_entry.get()
if
not
os.path.isfile(filename):
messagebox.showerror(
"错误"
,
"文件不存在"
)
return
algo
=
self
.hash_algo_combo.get()
try
:
hash_obj
=
hashlib.new(algo)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"不支持该算法:{algo}"
)
return
try
:
with
open
(filename,
"rb"
) as f:
for
chunk
in
iter
(
lambda
: f.read(
4096
), b""):
hash_obj.update(chunk)
self
.hash_result.delete(
0
, tk.END)
self
.hash_result.insert(
0
, f
"{algo.upper()}: {hash_obj.hexdigest()}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"计算失败:{e}"
)
def
create_time_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"时间工具"
)
ts_frame
=
ttk.LabelFrame(tab, text
=
"时间戳与日期转换"
)
ts_frame.pack(padx
=
10
, pady
=
5
, fill
=
'both'
, expand
=
True
)
ttk.Label(ts_frame, text
=
"时间戳:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.timestamp_entry
=
ttk.Entry(ts_frame)
self
.timestamp_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(ts_frame, text
=
"转换为日期"
, command
=
self
.timestamp_to_datetime).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
ttk.Label(ts_frame, text
=
"日期时间 (YYYY-MM-DD HH:MM:SS):"
).grid(row
=
1
, column
=
0
, padx
=
5
, pady
=
2
)
self
.datetime_entry
=
ttk.Entry(ts_frame)
self
.datetime_entry.grid(row
=
1
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(ts_frame, text
=
"转换为时间戳"
, command
=
self
.datetime_to_timestamp).grid(row
=
1
, column
=
2
, padx
=
5
, pady
=
2
)
ttk.Button(ts_frame, text
=
"当前时间"
, command
=
self
.get_current_time).grid(row
=
2
, column
=
1
, pady
=
5
)
tz_frame
=
ttk.LabelFrame(tab, text
=
"时区转换"
)
tz_frame.pack(padx
=
10
, pady
=
5
, fill
=
'both'
, expand
=
True
)
ttk.Label(tz_frame, text
=
"目标时区 (如 +8, -5):"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.tz_entry
=
ttk.Entry(tz_frame, width
=
10
)
self
.tz_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(tz_frame, text
=
"转换当前时间"
, command
=
self
.convert_timezone).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.tz_result
=
ttk.Label(tz_frame, text
=
"转换结果:"
)
self
.tz_result.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
def
timestamp_to_datetime(
self
):
try
:
ts
=
int
(
self
.timestamp_entry.get())
dt
=
datetime.fromtimestamp(ts).strftime(
'%Y-%m-%d %H:%M:%S'
)
self
.datetime_entry.delete(
0
, tk.END)
self
.datetime_entry.insert(
0
, dt)
except
:
messagebox.showerror(
"错误"
,
"无效的时间戳"
)
def
datetime_to_timestamp(
self
):
try
:
dt_str
=
self
.datetime_entry.get()
dt
=
datetime.strptime(dt_str,
'%Y-%m-%d %H:%M:%S'
)
ts
=
int
(dt.timestamp())
self
.timestamp_entry.delete(
0
, tk.END)
self
.timestamp_entry.insert(
0
, ts)
except
:
messagebox.showerror(
"错误"
,
"无效的日期格式"
)
def
get_current_time(
self
):
current_ts
=
int
(time.time())
current_dt
=
datetime.now().strftime(
'%Y-%m-%d %H:%M:%S'
)
self
.timestamp_entry.delete(
0
, tk.END)
self
.timestamp_entry.insert(
0
, current_ts)
self
.datetime_entry.delete(
0
, tk.END)
self
.datetime_entry.insert(
0
, current_dt)
def
convert_timezone(
self
):
try
:
offset
=
int
(
self
.tz_entry.get())
now
=
datetime.now(timezone.utc)
+
timedelta(hours
=
offset)
self
.tz_result.config(text
=
f
"目标时区时间: {now.strftime('%Y-%m-%d %H:%M:%S')}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"转换失败: {e}"
)
def
create_network_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"网络工具"
)
tab.columnconfigure(
0
, weight
=
1
)
net_notebook
=
ttk.Notebook(tab)
net_notebook.grid(row
=
0
, column
=
0
, sticky
=
'nsew'
, padx
=
10
, pady
=
5
)
ip_tab
=
ttk.Frame(net_notebook)
net_notebook.add(ip_tab, text
=
"IP查询"
)
ttk.Label(ip_tab, text
=
"输入IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.ip_entry
=
ttk.Entry(ip_tab, width
=
25
)
self
.ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(ip_tab, text
=
"查询"
, command
=
self
.ip_lookup).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.ip_result
=
scrolledtext.ScrolledText(ip_tab, height
=
8
, width
=
60
)
self
.ip_result.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
rate_tab
=
ttk.Frame(net_notebook)
net_notebook.add(rate_tab, text
=
"汇率转换"
)
ttk.Label(rate_tab, text
=
"金额:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.amount_entry
=
ttk.Entry(rate_tab, width
=
15
)
self
.amount_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(rate_tab, text
=
"从:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.from_currency
=
ttk.Combobox(rate_tab, values
=
[
"USD"
,
"CNY"
,
"EUR"
,
"JPY"
,
"GBP"
,
"AUD"
], width
=
5
, state
=
"readonly"
)
self
.from_currency.
set
(
"USD"
)
self
.from_currency.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
ttk.Label(rate_tab, text
=
"到:"
).grid(row
=
0
, column
=
4
, padx
=
5
, pady
=
2
)
self
.to_currency
=
ttk.Combobox(rate_tab, values
=
[
"USD"
,
"CNY"
,
"EUR"
,
"JPY"
,
"GBP"
,
"AUD"
], width
=
5
, state
=
"readonly"
)
self
.to_currency.
set
(
"CNY"
)
self
.to_currency.grid(row
=
0
, column
=
5
, padx
=
5
, pady
=
2
)
ttk.Button(rate_tab, text
=
"转换"
, command
=
self
.convert_currency).grid(row
=
0
, column
=
6
, padx
=
5
, pady
=
2
)
self
.rate_result
=
ttk.Label(rate_tab, text
=
"结果:"
)
self
.rate_result.grid(row
=
1
, column
=
0
, columnspan
=
7
, padx
=
5
, pady
=
2
)
scan_tab
=
ttk.Frame(net_notebook)
net_notebook.add(scan_tab, text
=
"子网扫描"
)
ttk.Label(scan_tab, text
=
"子网 (如 192.168.1.0/24):"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.subnet_entry
=
ttk.Entry(scan_tab, width
=
20
)
self
.subnet_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Button(scan_tab, text
=
"开始扫描"
, command
=
self
.scan_subnet).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
columns
=
(
"IP地址"
,
"延时(ms)"
)
self
.scan_tree
=
ttk.Treeview(scan_tab, columns
=
columns, show
=
"headings"
, height
=
8
)
for
col
in
columns:
self
.scan_tree.heading(col, text
=
col)
self
.scan_tree.column(col, anchor
=
'w'
)
self
.scan_tree.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
, sticky
=
'nsew'
)
self
.scan_progress
=
ttk.Progressbar(scan_tab, orient
=
"horizontal"
, mode
=
"determinate"
)
self
.scan_progress.grid(row
=
2
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
, sticky
=
'ew'
)
transfer_tab
=
ttk.Frame(net_notebook)
net_notebook.add(transfer_tab, text
=
"文件传输"
)
transfer_notebook
=
ttk.Notebook(transfer_tab)
transfer_notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
server_tab
=
ttk.Frame(transfer_notebook)
transfer_notebook.add(server_tab, text
=
"服务器"
)
ttk.Label(server_tab, text
=
"监听端口:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.server_port_entry
=
ttk.Entry(server_tab, width
=
10
)
self
.server_port_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.server_port_entry.insert(
0
,
"5001"
)
ttk.Button(server_tab, text
=
"启动服务器"
, command
=
self
.start_file_server).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.server_status
=
ttk.Label(server_tab, text
=
"未启动"
)
self
.server_status.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
client_tab
=
ttk.Frame(transfer_notebook)
transfer_notebook.add(client_tab, text
=
"客户端"
)
ttk.Label(client_tab, text
=
"目标IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.client_ip_entry
=
ttk.Entry(client_tab, width
=
15
)
self
.client_ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(client_tab, text
=
"端口:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.client_port_entry
=
ttk.Entry(client_tab, width
=
10
)
self
.client_port_entry.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.client_port_entry.insert(
0
,
"5001"
)
ttk.Button(client_tab, text
=
"选择文件"
, command
=
self
.select_transfer_file).grid(row
=
1
, column
=
0
, padx
=
5
, pady
=
2
)
self
.transfer_file_entry
=
ttk.Entry(client_tab, width
=
40
)
self
.transfer_file_entry.grid(row
=
1
, column
=
1
, columnspan
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(client_tab, text
=
"发送文件"
, command
=
self
.send_file).grid(row
=
2
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
remote_tab
=
ttk.Frame(net_notebook)
net_notebook.add(remote_tab, text
=
"远程命令"
)
remote_notebook
=
ttk.Notebook(remote_tab)
remote_notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
remote_server
=
ttk.Frame(remote_notebook)
remote_notebook.add(remote_server, text
=
"命令服务器"
)
ttk.Label(remote_server, text
=
"监听端口:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.remote_port_entry
=
ttk.Entry(remote_server, width
=
10
)
self
.remote_port_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.remote_port_entry.insert(
0
,
"6001"
)
ttk.Button(remote_server, text
=
"启动远程命令服务器"
, command
=
self
.start_remote_server).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.remote_status
=
ttk.Label(remote_server, text
=
"未启动"
)
self
.remote_status.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(remote_server, text
=
"远程关机"
, command
=
self
.remote_shutdown).grid(row
=
2
, column
=
0
, columnspan
=
3
, pady
=
2
)
remote_client
=
ttk.Frame(remote_notebook)
remote_notebook.add(remote_client, text
=
"命令客户端"
)
ttk.Label(remote_client, text
=
"目标IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.remote_ip_entry
=
ttk.Entry(remote_client, width
=
15
)
self
.remote_ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(remote_client, text
=
"端口:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.remote_client_port_entry
=
ttk.Entry(remote_client, width
=
10
)
self
.remote_client_port_entry.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.remote_client_port_entry.insert(
0
,
"6001"
)
ttk.Label(remote_client, text
=
"命令:"
).grid(row
=
1
, column
=
0
, padx
=
5
, pady
=
2
)
self
.remote_command_entry
=
ttk.Entry(remote_client, width
=
50
)
self
.remote_command_entry.grid(row
=
1
, column
=
1
, columnspan
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(remote_client, text
=
"发送命令"
, command
=
self
.send_remote_command).grid(row
=
2
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
self
.remote_output
=
scrolledtext.ScrolledText(remote_client, height
=
8
, width
=
60
)
self
.remote_output.grid(row
=
3
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
remote_desktop_tab
=
ttk.Frame(net_notebook)
net_notebook.add(remote_desktop_tab, text
=
"远程桌面"
)
rd_notebook
=
ttk.Notebook(remote_desktop_tab)
rd_notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
rd_server
=
ttk.Frame(rd_notebook)
rd_notebook.add(rd_server, text
=
"桌面服务器"
)
ttk.Label(rd_server, text
=
"监听端口:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.rd_server_port_entry
=
ttk.Entry(rd_server, width
=
10
)
self
.rd_server_port_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.rd_server_port_entry.insert(
0
,
"7001"
)
ttk.Button(rd_server, text
=
"启动桌面服务器"
, command
=
self
.start_remote_desktop_server).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.rd_server_status
=
ttk.Label(rd_server, text
=
"未启动"
)
self
.rd_server_status.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
rd_client
=
ttk.Frame(rd_notebook)
rd_notebook.add(rd_client, text
=
"桌面客户端"
)
ttk.Label(rd_client, text
=
"目标IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.rd_client_ip_entry
=
ttk.Entry(rd_client, width
=
15
)
self
.rd_client_ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(rd_client, text
=
"端口:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.rd_client_port_entry
=
ttk.Entry(rd_client, width
=
10
)
self
.rd_client_port_entry.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.rd_client_port_entry.insert(
0
,
"7001"
)
ttk.Button(rd_client, text
=
"连接桌面服务器"
, command
=
self
.start_remote_desktop_client).grid(row
=
1
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
self
.rd_display
=
ttk.Label(rd_client)
self
.rd_display.grid(row
=
2
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
remote_file_tab
=
ttk.Frame(net_notebook)
net_notebook.add(remote_file_tab, text
=
"远程文件管理"
)
rf_notebook
=
ttk.Notebook(remote_file_tab)
rf_notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
rf_server
=
ttk.Frame(rf_notebook)
rf_notebook.add(rf_server, text
=
"文件管理服务器"
)
ttk.Label(rf_server, text
=
"监听端口:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.rf_server_port_entry
=
ttk.Entry(rf_server, width
=
10
)
self
.rf_server_port_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.rf_server_port_entry.insert(
0
,
"8001"
)
ttk.Button(rf_server, text
=
"启动文件管理服务器"
, command
=
self
.start_file_mgmt_server).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.rf_server_status
=
ttk.Label(rf_server, text
=
"未启动"
)
self
.rf_server_status.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
rf_client
=
ttk.Frame(rf_notebook)
rf_notebook.add(rf_client, text
=
"文件管理客户端"
)
ttk.Label(rf_client, text
=
"目标IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.rf_client_ip_entry
=
ttk.Entry(rf_client, width
=
15
)
self
.rf_client_ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(rf_client, text
=
"端口:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.rf_client_port_entry
=
ttk.Entry(rf_client, width
=
10
)
self
.rf_client_port_entry.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.rf_client_port_entry.insert(
0
,
"8001"
)
ttk.Button(rf_client, text
=
"请求目录列表"
, command
=
self
.request_file_list).grid(row
=
1
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
self
.rf_file_listbox
=
tk.Listbox(rf_client, height
=
10
)
self
.rf_file_listbox.grid(row
=
2
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
, sticky
=
'nsew'
)
self
.rf_file_listbox.bind(
"<Double-Button-1>"
,
self
.download_remote_file)
chat_tab
=
ttk.Frame(net_notebook)
net_notebook.add(chat_tab, text
=
"局域网聊天"
)
chat_notebook
=
ttk.Notebook(chat_tab)
chat_notebook.pack(fill
=
'both'
, expand
=
True
, padx
=
5
, pady
=
5
)
chat_server
=
ttk.Frame(chat_notebook)
chat_notebook.add(chat_server, text
=
"聊天服务器"
)
ttk.Label(chat_server, text
=
"监听端口:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.chat_server_port_entry
=
ttk.Entry(chat_server, width
=
10
)
self
.chat_server_port_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.chat_server_port_entry.insert(
0
,
"9001"
)
ttk.Button(chat_server, text
=
"启动聊天服务器"
, command
=
self
.start_chat_server).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.chat_server_status
=
ttk.Label(chat_server, text
=
"未启动"
)
self
.chat_server_status.grid(row
=
1
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
chat_client
=
ttk.Frame(chat_notebook)
chat_notebook.add(chat_client, text
=
"聊天客户端"
)
ttk.Label(chat_client, text
=
"目标IP:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.chat_client_ip_entry
=
ttk.Entry(chat_client, width
=
15
)
self
.chat_client_ip_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(chat_client, text
=
"端口:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.chat_client_port_entry
=
ttk.Entry(chat_client, width
=
10
)
self
.chat_client_port_entry.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.chat_client_port_entry.insert(
0
,
"9001"
)
self
.chat_display
=
scrolledtext.ScrolledText(chat_client, height
=
10
, width
=
50
)
self
.chat_display.grid(row
=
1
, column
=
0
, columnspan
=
4
, padx
=
5
, pady
=
2
)
self
.chat_input
=
ttk.Entry(chat_client, width
=
40
)
self
.chat_input.grid(row
=
2
, column
=
0
, columnspan
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(chat_client, text
=
"发送"
, command
=
self
.send_chat_message).grid(row
=
2
, column
=
3
, padx
=
5
, pady
=
2
)
btn_frame_chat
=
ttk.Frame(chat_client)
btn_frame_chat.grid(row
=
3
, column
=
0
, columnspan
=
4
, pady
=
2
)
ttk.Button(btn_frame_chat, text
=
"开始视频"
, command
=
self
.start_video_chat).pack(side
=
'left'
, padx
=
5
)
ttk.Button(btn_frame_chat, text
=
"开始语音"
, command
=
self
.start_voice_chat).pack(side
=
'left'
, padx
=
5
)
def
ip_lookup(
self
):
ip
=
self
.ip_entry.get()
try
:
response
=
requests.get(f
"http://ip-api.com/json/{ip}"
)
data
=
response.json()
result
=
(f
"国家: {data.get('country', '未知')}\n"
f
"地区: {data.get('regionName', '未知')}\n"
f
"城市: {data.get('city', '未知')}\n"
f
"ISP: {data.get('isp', '未知')}\n"
f
"AS: {data.get('as', '未知')}"
)
self
.ip_result.delete(
1.0
, tk.END)
self
.ip_result.insert(tk.END, result)
except
:
messagebox.showerror(
"错误"
,
"查询失败"
)
def
convert_currency(
self
):
amount
=
self
.amount_entry.get()
from_cur
=
self
.from_currency.get()
to_cur
=
self
.to_currency.get()
try
:
amt
=
float
(amount)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的金额"
)
return
url
=
f
"https://api.exchangerate.host/convert?from={from_cur}&to={to_cur}&amount={amt}"
try
:
res
=
requests.get(url).json()
result
=
res.get(
"result"
,
"转换失败"
)
self
.rate_result.config(text
=
f
"结果: {result} {to_cur}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"汇率转换失败:{e}"
)
def
scan_subnet(
self
):
subnet
=
self
.subnet_entry.get().strip()
if
not
subnet:
messagebox.showwarning(
"提示"
,
"请输入子网"
)
return
for
item
in
self
.scan_tree.get_children():
self
.scan_tree.delete(item)
try
:
ip_part, mask
=
subnet.split(
'/'
)
mask
=
int
(mask)
except
:
messagebox.showerror(
"错误"
,
"子网格式错误"
)
return
parts
=
ip_part.split(
'.'
)
if
len
(parts) !
=
4
:
messagebox.showerror(
"错误"
,
"IP格式错误"
)
return
base_ip
=
'.'
.join(parts[:
3
])
+
'.'
total
=
256
self
.scan_progress[
'maximum'
]
=
total
def
ping_ip(ip, idx):
param
=
'-n'
if
os.name
=
=
'nt'
else
'-c'
try
:
start
=
time.time()
ret
=
subprocess.call([
"ping"
, param,
"1"
,
"-w"
,
"100"
, ip], stdout
=
subprocess.DEVNULL, stderr
=
subprocess.DEVNULL)
delay
=
int
((time.time()
-
start)
*
1000
)
if
ret
=
=
0
:
self
.scan_tree.insert("", tk.END, values
=
(ip, delay))
except
:
pass
self
.scan_progress[
'value'
]
=
idx
+
1
threads
=
[]
for
i
in
range
(
0
, total):
ip
=
base_ip
+
str
(i)
t
=
threading.Thread(target
=
ping_ip, args
=
(ip, i))
threads.append(t)
t.start()
def
start_file_server(
self
):
port_str
=
self
.server_port_entry.get()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
server_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(
1
)
self
.server_status.config(text
=
f
"监听中,端口 {port}"
)
conn, addr
=
s.accept()
self
.server_status.config(text
=
f
"连接自 {addr}"
)
fname_size
=
int
(conn.recv(
16
).decode())
fname
=
conn.recv(fname_size).decode()
filesize
=
int
(conn.recv(
32
).decode())
with
open
(
"received_"
+
fname,
"wb"
) as f:
received
=
0
while
received < filesize:
data
=
conn.recv(
4096
)
if
not
data:
break
f.write(data)
received
+
=
len
(data)
conn.close()
self
.server_status.config(text
=
"传输完成,等待下一次连接"
)
s.close()
threading.Thread(target
=
server_thread, daemon
=
True
).start()
def
select_transfer_file(
self
):
filename
=
filedialog.askopenfilename()
if
filename:
self
.transfer_file_entry.delete(
0
, tk.END)
self
.transfer_file_entry.insert(
0
, filename)
def
send_file(
self
):
target_ip
=
self
.client_ip_entry.get().strip()
port_str
=
self
.client_port_entry.get().strip()
filepath
=
self
.transfer_file_entry.get().strip()
if
not
target_ip
or
not
port_str
or
not
filepath
or
not
os.path.isfile(filepath):
messagebox.showerror(
"错误"
,
"请填写完整且正确的信息"
)
return
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"端口号错误"
)
return
def
client_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
except
Exception as e:
messagebox.showerror(
"错误"
, f
"连接失败:{e}"
)
return
fname
=
os.path.basename(filepath)
s.send(
str
(
len
(fname)).zfill(
16
).encode())
s.send(fname.encode())
filesize
=
os.path.getsize(filepath)
s.send(
str
(filesize).zfill(
32
).encode())
with
open
(filepath,
"rb"
) as f:
while
True
:
data
=
f.read(
4096
)
if
not
data:
break
s.send(data)
s.close()
messagebox.showinfo(
"提示"
,
"文件发送完成"
)
threading.Thread(target
=
client_thread).start()
def
start_remote_server(
self
):
port_str
=
self
.remote_port_entry.get()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
remote_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(
1
)
self
.remote_status.config(text
=
f
"远程命令服务器启动,端口 {port}"
)
while
True
:
conn, addr
=
s.accept()
self
.remote_status.config(text
=
f
"连接自 {addr}"
)
try
:
cmd_size
=
int
(conn.recv(
16
).decode())
cmd
=
conn.recv(cmd_size).decode()
if
cmd.strip().upper()
=
=
"SHUTDOWN"
:
output
=
"正在关机..."
if
os.name
=
=
'nt'
:
os.system(
"shutdown /s /t 1"
)
else
:
os.system(
"sudo shutdown -h now"
)
else
:
output
=
subprocess.getoutput(cmd)
output_bytes
=
output.encode()
conn.send(
str
(
len
(output_bytes)).zfill(
16
).encode())
conn.send(output_bytes)
except
Exception as e:
conn.send(
str
(
0
).zfill(
16
).encode())
finally
:
conn.close()
self
.remote_status.config(text
=
"等待远程命令连接..."
)
threading.Thread(target
=
remote_thread, daemon
=
True
).start()
def
send_remote_command(
self
):
target_ip
=
self
.remote_ip_entry.get().strip()
port_str
=
self
.remote_client_port_entry.get().strip()
command
=
self
.remote_command_entry.get().strip()
if
not
target_ip
or
not
port_str
or
not
command:
messagebox.showerror(
"错误"
,
"请填写完整的远程命令信息"
)
return
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"端口号错误"
)
return
def
remote_client_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
except
Exception as e:
messagebox.showerror(
"错误"
, f
"连接失败:{e}"
)
return
cmd_bytes
=
command.encode()
s.send(
str
(
len
(cmd_bytes)).zfill(
16
).encode())
s.send(cmd_bytes)
out_size
=
int
(s.recv(
16
).decode())
output
=
s.recv(out_size).decode()
self
.remote_output.delete(
"1.0"
, tk.END)
self
.remote_output.insert(tk.END, output)
s.close()
threading.Thread(target
=
remote_client_thread).start()
def
remote_shutdown(
self
):
self
.remote_command_entry.delete(
0
, tk.END)
self
.remote_command_entry.insert(
0
,
"SHUTDOWN"
)
self
.send_remote_command()
def
start_remote_desktop_server(
self
):
port_str
=
self
.rd_server_port_entry.get()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
rd_server_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(
1
)
self
.rd_server_status.config(text
=
f
"桌面服务器监听中,端口 {port}"
)
conn, addr
=
s.accept()
self
.rd_server_status.config(text
=
f
"已连接:{addr}"
)
try
:
while
True
:
img
=
ImageGrab.grab()
img
=
img.resize((
800
,
600
))
from
io
import
BytesIO
buf
=
BytesIO()
img.save(buf,
format
=
'JPEG'
, quality
=
50
)
img_data
=
buf.getvalue()
conn.send(
str
(
len
(img_data)).zfill(
16
).encode())
conn.sendall(img_data)
time.sleep(
0.5
)
except
Exception as e:
self
.rd_server_status.config(text
=
f
"错误:{e}"
)
finally
:
conn.close()
s.close()
threading.Thread(target
=
rd_server_thread, daemon
=
True
).start()
def
start_remote_desktop_client(
self
):
target_ip
=
self
.rd_client_ip_entry.get().strip()
port_str
=
self
.rd_client_port_entry.get().strip()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
rd_client_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
except
Exception as e:
messagebox.showerror(
"错误"
, f
"连接失败:{e}"
)
return
try
:
while
True
:
data_len
=
int
(s.recv(
16
).decode())
received
=
b""
while
len
(received) < data_len:
packet
=
s.recv(
4096
)
if
not
packet:
break
received
+
=
packet
from
io
import
BytesIO
buf
=
BytesIO(received)
img
=
Image.
open
(buf)
photo
=
ImageTk.PhotoImage(img)
self
.rd_display.config(image
=
photo)
self
.rd_display.image
=
photo
except
Exception as e:
messagebox.showerror(
"错误"
, f
"接收桌面数据失败:{e}"
)
finally
:
s.close()
threading.Thread(target
=
rd_client_thread, daemon
=
True
).start()
def
start_file_mgmt_server(
self
):
port_str
=
self
.rf_server_port_entry.get()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
fm_server_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(
1
)
self
.rf_server_status.config(text
=
f
"文件管理服务器启动,端口 {port}"
)
while
True
:
conn, addr
=
s.accept()
try
:
dir_size
=
int
(conn.recv(
16
).decode())
dir_path
=
conn.recv(dir_size).decode()
if
not
os.path.isdir(dir_path):
file_list
=
{
"error"
:
"目录不存在"
}
else
:
files
=
os.listdir(dir_path)
file_list
=
{
"files"
: files,
"dir"
: dir_path}
data
=
json.dumps(file_list).encode()
conn.send(
str
(
len
(data)).zfill(
16
).encode())
conn.sendall(data)
except
Exception as e:
pass
finally
:
conn.close()
threading.Thread(target
=
fm_server_thread, daemon
=
True
).start()
def
request_file_list(
self
):
target_ip
=
self
.rf_client_ip_entry.get().strip()
port_str
=
self
.rf_client_port_entry.get().strip()
dir_path
=
filedialog.askdirectory(title
=
"选择请求的目录"
)
if
not
dir_path:
return
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
fm_client_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
path_bytes
=
dir_path.encode()
s.send(
str
(
len
(path_bytes)).zfill(
16
).encode())
s.send(path_bytes)
data_len
=
int
(s.recv(
16
).decode())
data
=
s.recv(data_len).decode()
result
=
json.loads(data)
self
.rf_file_listbox.delete(
0
, tk.END)
if
"error"
in
result:
messagebox.showerror(
"错误"
, result[
"error"
])
else
:
files
=
result.get(
"files"
, [])
for
f
in
files:
self
.rf_file_listbox.insert(tk.END, os.path.join(result.get(
"dir"
, dir_path), f))
except
Exception as e:
messagebox.showerror(
"错误"
, f
"请求文件列表失败:{e}"
)
finally
:
s.close()
threading.Thread(target
=
fm_client_thread, daemon
=
True
).start()
def
download_remote_file(
self
, event):
selection
=
self
.rf_file_listbox.curselection()
if
not
selection:
return
file_path
=
self
.rf_file_listbox.get(selection[
0
])
target_ip
=
self
.rf_client_ip_entry.get().strip()
port_str
=
self
.rf_client_port_entry.get().strip()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
download_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
req
=
"DOWNLOAD "
+
file_path
req_bytes
=
req.encode()
s.send(
str
(
len
(req_bytes)).zfill(
16
).encode())
s.send(req_bytes)
file_size
=
int
(s.recv(
16
).decode())
if
file_size
=
=
0
:
messagebox.showerror(
"错误"
,
"服务器无法传输该文件"
)
return
save_path
=
filedialog.asksaveasfilename(initialfile
=
os.path.basename(file_path))
if
not
save_path:
return
with
open
(save_path,
"wb"
) as f:
received
=
0
while
received < file_size:
data
=
s.recv(
4096
)
if
not
data:
break
f.write(data)
received
+
=
len
(data)
messagebox.showinfo(
"完成"
, f
"文件已保存至 {save_path}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"下载失败:{e}"
)
finally
:
s.close()
threading.Thread(target
=
download_thread, daemon
=
True
).start()
def
create_conversion_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"转换工具"
)
tab.columnconfigure(
0
, weight
=
1
)
unit_frame
=
ttk.LabelFrame(tab, text
=
"基础单位转换"
)
unit_frame.grid(row
=
0
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(unit_frame, text
=
"数值:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.num_entry
=
ttk.Entry(unit_frame, width
=
15
)
self
.num_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(unit_frame, text
=
"从单位:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.unit_from
=
ttk.Combobox(unit_frame, values
=
[
"米"
,
"千米"
,
"英尺"
,
"公斤"
,
"克"
], state
=
"readonly"
, width
=
8
)
self
.unit_from.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
ttk.Label(unit_frame, text
=
"转换为:"
).grid(row
=
0
, column
=
4
, padx
=
5
, pady
=
2
)
self
.unit_to
=
ttk.Combobox(unit_frame, values
=
[
"米"
,
"千米"
,
"英尺"
,
"公斤"
,
"克"
], state
=
"readonly"
, width
=
8
)
self
.unit_to.grid(row
=
0
, column
=
5
, padx
=
5
, pady
=
2
)
ttk.Button(unit_frame, text
=
"转换"
, command
=
self
.unit_convert).grid(row
=
0
, column
=
6
, padx
=
5
, pady
=
2
)
self
.unit_result
=
ttk.Label(unit_frame, text
=
"结果:"
)
self
.unit_result.grid(row
=
1
, column
=
0
, columnspan
=
7
, padx
=
5
, pady
=
2
)
temp_frame
=
ttk.LabelFrame(tab, text
=
"温度转换"
)
temp_frame.grid(row
=
1
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(temp_frame, text
=
"温度数值:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.temp_entry
=
ttk.Entry(temp_frame, width
=
10
)
self
.temp_entry.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.temp_from
=
ttk.Combobox(temp_frame, values
=
[
"摄氏度"
,
"华氏度"
,
"开氏度"
], state
=
"readonly"
, width
=
10
)
self
.temp_from.grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
ttk.Label(temp_frame, text
=
"转换为:"
).grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
self
.temp_to
=
ttk.Combobox(temp_frame, values
=
[
"摄氏度"
,
"华氏度"
,
"开氏度"
], state
=
"readonly"
, width
=
10
)
self
.temp_to.grid(row
=
0
, column
=
4
, padx
=
5
, pady
=
2
)
ttk.Button(temp_frame, text
=
"转换"
, command
=
self
.temperature_convert).grid(row
=
0
, column
=
5
, padx
=
5
, pady
=
2
)
self
.temp_result
=
ttk.Label(temp_frame, text
=
"结果:"
)
self
.temp_result.grid(row
=
1
, column
=
0
, columnspan
=
6
, padx
=
5
, pady
=
2
)
adv_frame
=
ttk.LabelFrame(tab, text
=
"高级单位转换"
)
adv_frame.grid(row
=
2
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(adv_frame, text
=
"数值:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.adv_value
=
ttk.Entry(adv_frame, width
=
15
)
self
.adv_value.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
ttk.Label(adv_frame, text
=
"类别:"
).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.conv_category
=
ttk.Combobox(adv_frame, values
=
[
"面积"
,
"体积"
,
"速度"
,
"时间"
], state
=
"readonly"
, width
=
10
)
self
.conv_category.grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
ttk.Label(adv_frame, text
=
"从:"
).grid(row
=
0
, column
=
4
, padx
=
5
, pady
=
2
)
self
.adv_from
=
ttk.Entry(adv_frame, width
=
10
)
self
.adv_from.grid(row
=
0
, column
=
5
, padx
=
5
, pady
=
2
)
ttk.Label(adv_frame, text
=
"到:"
).grid(row
=
0
, column
=
6
, padx
=
5
, pady
=
2
)
self
.adv_to
=
ttk.Entry(adv_frame, width
=
10
)
self
.adv_to.grid(row
=
0
, column
=
7
, padx
=
5
, pady
=
2
)
ttk.Button(adv_frame, text
=
"转换"
, command
=
self
.advanced_convert).grid(row
=
0
, column
=
8
, padx
=
5
, pady
=
2
)
self
.adv_result
=
ttk.Label(adv_frame, text
=
"结果:"
)
self
.adv_result.grid(row
=
1
, column
=
0
, columnspan
=
9
, padx
=
5
, pady
=
2
)
zh_frame
=
ttk.LabelFrame(tab, text
=
"简繁转换"
)
zh_frame.grid(row
=
3
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(zh_frame, text
=
"输入文本:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.zh_input
=
scrolledtext.ScrolledText(zh_frame, height
=
3
, width
=
40
)
self
.zh_input.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
btn_frame
=
ttk.Frame(zh_frame)
btn_frame.grid(row
=
1
, column
=
1
, pady
=
2
)
ttk.Button(btn_frame, text
=
"转简体"
, command
=
lambda
:
self
.convert_zh(
'zh-cn'
)).pack(side
=
'left'
, padx
=
3
)
ttk.Button(btn_frame, text
=
"转繁体"
, command
=
lambda
:
self
.convert_zh(
'zh-tw'
)).pack(side
=
'left'
, padx
=
3
)
self
.zh_output
=
scrolledtext.ScrolledText(zh_frame, height
=
3
, width
=
40
)
self
.zh_output.grid(row
=
2
, column
=
1
, padx
=
5
, pady
=
2
)
def
unit_convert(
self
):
try
:
value
=
float
(
self
.num_entry.get())
unit_from
=
self
.unit_from.get()
unit_to
=
self
.unit_to.get()
conversion
=
{
(
"米"
,
"千米"
):
0.001
, (
"千米"
,
"米"
):
1000
,
(
"米"
,
"英尺"
):
3.28084
, (
"英尺"
,
"米"
):
0.3048
,
(
"公斤"
,
"克"
):
1000
, (
"克"
,
"公斤"
):
0.001
}
if
unit_from
=
=
unit_to:
result
=
value
elif
(unit_from, unit_to)
in
conversion:
result
=
value
*
conversion[(unit_from, unit_to)]
else
:
result
=
value
self
.unit_result.config(text
=
f
"结果: {result:.4f} {unit_to}"
)
except
:
messagebox.showerror(
"错误"
,
"无效的输入"
)
def
temperature_convert(
self
):
try
:
temp
=
float
(
self
.temp_entry.get())
frm
=
self
.temp_from.get()
to
=
self
.temp_to.get()
if
frm
=
=
"华氏度"
:
celsius
=
(temp
-
32
)
*
5
/
9
elif
frm
=
=
"开氏度"
:
celsius
=
temp
-
273.15
else
:
celsius
=
temp
if
to
=
=
"华氏度"
:
result
=
celsius
*
9
/
5
+
32
elif
to
=
=
"开氏度"
:
result
=
celsius
+
273.15
else
:
result
=
celsius
self
.temp_result.config(text
=
f
"结果: {result:.2f} {to}"
)
except
:
messagebox.showerror(
"错误"
,
"无效的温度输入"
)
def
advanced_convert(
self
):
try
:
value
=
float
(
self
.adv_value.get())
category
=
self
.conv_category.get()
unit_from
=
self
.adv_from.get().lower()
unit_to
=
self
.adv_to.get().lower()
result
=
value
if
category
=
=
"面积"
:
if
unit_from
=
=
"平方米"
and
unit_to
=
=
"平方英尺"
:
result
=
value
*
10.7639
elif
unit_from
=
=
"平方英尺"
and
unit_to
=
=
"平方米"
:
result
=
value
/
10.7639
elif
category
=
=
"体积"
:
if
unit_from
=
=
"立方米"
and
unit_to
=
=
"升"
:
result
=
value
*
1000
elif
unit_from
=
=
"升"
and
unit_to
=
=
"立方米"
:
result
=
value
/
1000
elif
category
=
=
"速度"
:
if
unit_from
in
[
"公里/小时"
,
"km/h"
]
and
unit_to
in
[
"英里/小时"
,
"mph"
]:
result
=
value
*
0.621371
elif
unit_from
in
[
"英里/小时"
,
"mph"
]
and
unit_to
in
[
"公里/小时"
,
"km/h"
]:
result
=
value
/
0.621371
elif
category
=
=
"时间"
:
if
unit_from
=
=
"小时"
and
unit_to
=
=
"分钟"
:
result
=
value
*
60
elif
unit_from
=
=
"分钟"
and
unit_to
=
=
"小时"
:
result
=
value
/
60
self
.adv_result.config(text
=
f
"结果: {result:.4f} {unit_to}"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"转换失败:{e}"
)
def
convert_zh(
self
, target):
text
=
self
.zh_input.get(
"1.0"
, tk.END)
converted
=
zhconv.convert(text, target)
self
.zh_output.delete(
"1.0"
, tk.END)
self
.zh_output.insert(tk.END, converted)
def
create_other_tools_tab(
self
):
tab
=
ttk.Frame(
self
.notebook)
self
.notebook.add(tab, text
=
"其他工具"
)
tab.columnconfigure(
0
, weight
=
1
)
pwd_frame
=
ttk.LabelFrame(tab, text
=
"随机密码生成"
)
pwd_frame.grid(row
=
0
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
ttk.Label(pwd_frame, text
=
"长度:"
).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
self
.pwd_length
=
ttk.Spinbox(pwd_frame, from_
=
6
, to
=
32
, width
=
5
)
self
.pwd_length.grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.use_digits
=
tk.BooleanVar(value
=
True
)
ttk.Checkbutton(pwd_frame, text
=
"包含数字"
, variable
=
self
.use_digits).grid(row
=
0
, column
=
2
, padx
=
5
, pady
=
2
)
self
.use_symbols
=
tk.BooleanVar(value
=
True
)
ttk.Checkbutton(pwd_frame, text
=
"包含符号"
, variable
=
self
.use_symbols).grid(row
=
0
, column
=
3
, padx
=
5
, pady
=
2
)
ttk.Button(pwd_frame, text
=
"生成密码"
, command
=
self
.generate_password).grid(row
=
0
, column
=
4
, padx
=
5
, pady
=
2
)
self
.password_entry
=
ttk.Entry(pwd_frame, width
=
30
)
self
.password_entry.grid(row
=
1
, column
=
0
, columnspan
=
5
, padx
=
5
, pady
=
2
)
self
.pwd_strength
=
ttk.Label(pwd_frame, text
=
"强度:"
)
self
.pwd_strength.grid(row
=
2
, column
=
0
, columnspan
=
5
, padx
=
5
, pady
=
2
)
clip_frame
=
ttk.LabelFrame(tab, text
=
"剪贴板历史"
)
clip_frame.grid(row
=
1
, column
=
0
, padx
=
10
, pady
=
5
, sticky
=
'nsew'
)
self
.clip_history
=
[]
ttk.Button(clip_frame, text
=
"记录当前剪贴板"
, command
=
self
.record_clipboard).grid(row
=
0
, column
=
0
, padx
=
5
, pady
=
2
)
ttk.Button(clip_frame, text
=
"显示历史"
, command
=
self
.show_clip_history).grid(row
=
0
, column
=
1
, padx
=
5
, pady
=
2
)
self
.clip_listbox
=
tk.Listbox(clip_frame, height
=
5
)
self
.clip_listbox.grid(row
=
1
, column
=
0
, columnspan
=
2
, padx
=
5
, pady
=
2
, sticky
=
'nsew'
)
self
.clip_listbox.bind(
"<Double-Button-1>"
,
self
.copy_clip_item)
ttk.Button(clip_frame, text
=
"删除选中"
, command
=
self
.delete_clip_item).grid(row
=
2
, column
=
0
, columnspan
=
2
, padx
=
5
, pady
=
2
)
def
generate_password(
self
):
length
=
int
(
self
.pwd_length.get())
chars
=
string.ascii_letters
if
self
.use_digits.get():
chars
+
=
string.digits
if
self
.use_symbols.get():
chars
+
=
'!@#$%^&*()_+-='
password
=
''.join(random.choice(chars)
for
_
in
range
(length))
self
.password_entry.delete(
0
, tk.END)
self
.password_entry.insert(
0
, password)
pyperclip.copy(password)
strength
=
self
.evaluate_password_strength(password)
self
.pwd_strength.config(text
=
f
"强度:{strength}"
)
messagebox.showinfo(
"提示"
,
"密码已复制到剪贴板"
)
def
evaluate_password_strength(
self
, pwd):
score
=
0
if
any
(c.islower()
for
c
in
pwd): score
+
=
1
if
any
(c.isupper()
for
c
in
pwd): score
+
=
1
if
any
(c.isdigit()
for
c
in
pwd): score
+
=
1
if
any
(c
in
'!@#$%^&*()_+-='
for
c
in
pwd): score
+
=
1
if
len
(pwd) >
=
12
: score
+
=
1
levels
=
{
1
:
"弱"
,
2
:
"较弱"
,
3
:
"中等"
,
4
:
"较强"
,
5
:
"强"
}
return
levels.get(score,
"未知"
)
def
record_clipboard(
self
):
try
:
clip
=
self
.root.clipboard_get()
if
clip
not
in
self
.clip_history:
self
.clip_history.append(clip)
self
.clip_listbox.insert(tk.END, clip[:
50
]
+
(
"..."
if
len
(clip) >
50
else
""))
messagebox.showinfo(
"提示"
,
"已记录当前剪贴板内容"
)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"无法读取剪贴板:{e}"
)
def
show_clip_history(
self
):
if
not
self
.clip_history:
messagebox.showinfo(
"剪贴板历史"
,
"无历史记录"
)
else
:
history
=
"\n-----------------\n"
.join(
self
.clip_history)
messagebox.showinfo(
"剪贴板历史"
, history)
def
copy_clip_item(
self
, event):
selection
=
self
.clip_listbox.curselection()
if
selection:
idx
=
selection[
0
]
content
=
self
.clip_history[idx]
pyperclip.copy(content)
messagebox.showinfo(
"提示"
,
"已复制到剪贴板"
)
def
delete_clip_item(
self
):
selection
=
self
.clip_listbox.curselection()
if
selection:
idx
=
selection[
0
]
self
.clip_listbox.delete(idx)
del
self
.clip_history[idx]
def
start_chat_server(
self
):
port_str
=
self
.chat_server_port_entry.get()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
self
.chat_clients
=
[]
def
chat_server_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(("", port))
s.listen(
5
)
self
.chat_server_status.config(text
=
f
"聊天服务器启动,端口 {port}"
)
while
True
:
client, addr
=
s.accept()
self
.chat_clients.append(client)
threading.Thread(target
=
self
.handle_chat_client, args
=
(client,), daemon
=
True
).start()
threading.Thread(target
=
chat_server_thread, daemon
=
True
).start()
def
handle_chat_client(
self
, client):
try
:
while
True
:
msg_len
=
int
(client.recv(
16
).decode())
msg
=
client.recv(msg_len).decode()
self
.broadcast_chat_message(msg)
except
:
self
.chat_clients.remove(client)
def
broadcast_chat_message(
self
, msg):
self
.chat_display.insert(tk.END, msg
+
"\n"
)
for
c
in
self
.chat_clients:
try
:
msg_bytes
=
msg.encode()
c.send(
str
(
len
(msg_bytes)).zfill(
16
).encode())
c.send(msg_bytes)
except
:
pass
def
send_chat_message(
self
):
msg
=
self
.chat_input.get()
if
not
msg:
return
self
.chat_display.insert(tk.END,
"我:"
+
msg
+
"\n"
)
target_ip
=
self
.chat_client_ip_entry.get().strip()
port_str
=
self
.chat_client_port_entry.get().strip()
try
:
port
=
int
(port_str)
except
:
messagebox.showerror(
"错误"
,
"请输入正确的端口号"
)
return
def
chat_client_thread():
s
=
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try
:
s.connect((target_ip, port))
msg_bytes
=
(
"客户端:"
+
msg).encode()
s.send(
str
(
len
(msg_bytes)).zfill(
16
).encode())
s.send(msg_bytes)
except
Exception as e:
messagebox.showerror(
"错误"
, f
"发送失败:{e}"
)
finally
:
s.close()
threading.Thread(target
=
chat_client_thread, daemon
=
True
).start()
self
.chat_input.delete(
0
, tk.END)
def
start_video_chat(
self
):
messagebox.showinfo(
"提示"
,
"视频聊天功能暂未实现,请等待后续更新"
)
def
start_voice_chat(
self
):
messagebox.showinfo(
"提示"
,
"语音聊天功能暂未实现,请等待后续更新"
)
if
__name__
=
=
"__main__"
:
root
=
tk.Tk()
app
=
ToolBox(root)
root.mainloop()