代码:
import tkinter as tk
from tkinter import ttk
import math
from tkinter.font import Font
class StorageCalculator:
def __init__(self, root):
self.root = root
self.root.title("监控视频存储计算器")
self.root.geometry("600x800")
self.root.configure(bg='#f0f0f0')
# 设置主题和样式
self.style = ttk.Style()
self.style.theme_use('clam') # 使用clam主题作为基础
# 定义颜色方案
self.colors = {
'primary': '#2196F3', # 主色调
'secondary': '#64B5F6', # 次要色调
'bg': '#f0f0f0', # 背景色
'text': '#212121', # 文字颜色
'success': '#4CAF50' # 成功色
}
# 设置自定义字体
self.title_font = Font(family="微软雅黑", size=12, weight="bold")
self.normal_font = Font(family="微软雅黑", size=10)
self.result_font = Font(family="微软雅黑", size=11, weight="bold")
# 配置样式
self.style.configure('Title.TLabel',
font=self.title_font,
background=self.colors['bg'],
foreground=self.colors['primary'])
self.style.configure('Normal.TLabel',
font=self.normal_font,
background=self.colors['bg'])
self.style.configure('Custom.TButton',
font=self.normal_font,
background=self.colors['primary'],
padding=(20, 10))
self.style.configure('Tab.TNotebook',
background=self.colors['bg'])
self.style.configure('Result.TLabelframe',
background=self.colors['bg'])
# 创建主标题
self.create_header()
# 创建notebook
self.notebook = ttk.Notebook(root, style='Tab.TNotebook')
self.notebook.pack(fill='both', expand=True, padx=20, pady=(0, 20))
# 创建计算页面
self.create_forward_page()
self.create_reverse_page()
# 创建页脚
self.create_footer()
def create_header(self):
"""创建头部标题区域"""
header_frame = ttk.Frame(self.root)
header_frame.pack(fill='x', padx=20, pady=20)
title = ttk.Label(header_frame,
text="监控视频存储计算器",
style='Title.TLabel')
title.pack()
subtitle = ttk.Label(header_frame,
text="专业的存储空间评估工具",
style='Normal.TLabel')
subtitle.pack()
def create_forward_page(self):
"""创建正向计算页面"""
self.forward_frame = ttk.Frame(self.notebook, padding="20")
self.notebook.add(self.forward_frame, text=" 计算存储需求 ")
# 创建输入区域
input_frame = ttk.LabelFrame(self.forward_frame,
text="参数输入",
padding="15")
input_frame.pack(fill='x', padx=10, pady=5)
# 添加输入控件
self.create_input_field(input_frame, "摄像头数量:", 0, self.normal_font)
self.cameras = self.entry
self.create_input_field(input_frame, "每天录像时间(小时):", 1, self.normal_font)
self.hours = self.entry
self.hours.insert(0, "24") # 默认24小时
self.create_input_field(input_frame, "需要保存的天数:", 2, self.normal_font)
self.days = self.entry
# 摄像头类型选择
ttk.Label(input_frame, text="摄像头类型:",
font=self.normal_font).grid(row=3, column=0,
sticky=tk.W, pady=5)
self.camera_type = ttk.Combobox(input_frame, width=25,
font=self.normal_font)
self.camera_type['values'] = ['200万像素', '300万像素', '400万像素', '500万像素']
self.camera_type.current(0)
self.camera_type.grid(row=3, column=1, sticky=tk.W, pady=5)
# 编码方式选择
ttk.Label(input_frame, text="编码方式:",
font=self.normal_font).grid(row=4, column=0,
sticky=tk.W, pady=5)
self.encoding = ttk.Combobox(input_frame, width=25,
font=self.normal_font)
self.encoding['values'] = ['H.264', 'H.265']
self.encoding.current(1) # 默认H.265
self.encoding.grid(row=4, column=1, sticky=tk.W, pady=5)
# 计算按钮
btn_frame = ttk.Frame(self.forward_frame)
btn_frame.pack(fill='x', pady=20)
calc_btn = ttk.Button(btn_frame,
text="计算存储需求",
style='Custom.TButton',
command=self.calculate_forward)
calc_btn.pack(expand=True)
# 结果显示区域
self.forward_result_frame = ttk.LabelFrame(self.forward_frame,
text="计算结果",
padding="15",
style='Result.TLabelframe')
self.forward_result_frame.pack(fill='x', padx=10, pady=5)
self.storage_label = ttk.Label(self.forward_result_frame,
text="",
font=self.result_font)
self.storage_label.pack(anchor=tk.W)
self.recommendation_label = ttk.Label(self.forward_result_frame,
text="",
font=self.result_font)
self.recommendation_label.pack(anchor=tk.W)
def create_reverse_page(self):
"""创建反向计算页面"""
self.reverse_frame = ttk.Frame(self.notebook, padding="20")
self.notebook.add(self.reverse_frame, text=" 计算存储时间 ")
# 创建输入区域
input_frame = ttk.LabelFrame(self.reverse_frame,
text="参数输入",
padding="15")
input_frame.pack(fill='x', padx=10, pady=5)
# 添加输入控件
self.create_input_field(input_frame, "硬盘容量(TB):", 0, self.normal_font)
self.storage_size = self.entry
self.create_input_field(input_frame, "摄像头数量:", 1, self.normal_font)
self.rev_cameras = self.entry
self.create_input_field(input_frame, "每天录像时间(小时):", 2, self.normal_font)
self.rev_hours = self.entry
# 摄像头类型选择
ttk.Label(input_frame, text="摄像头类型:",
font=self.normal_font).grid(row=3, column=0,
sticky=tk.W, pady=5)
self.rev_camera_type = ttk.Combobox(input_frame, width=25,
font=self.normal_font)
self.rev_camera_type['values'] = ['200万像素', '300万像素', '400万像素', '500万像素']
self.rev_camera_type.current(0)
self.rev_camera_type.grid(row=3, column=1, sticky=tk.W, pady=5)
# 编码方式选择
ttk.Label(input_frame, text="编码方式:",
font=self.normal_font).grid(row=4, column=0,
sticky=tk.W, pady=5)
self.rev_encoding = ttk.Combobox(input_frame, width=25,
font=self.normal_font)
self.rev_encoding['values'] = ['H.264', 'H.265']
self.rev_encoding.current(1) # 默认H.265
self.rev_encoding.grid(row=4, column=1, sticky=tk.W, pady=5)
# 计算按钮
btn_frame = ttk.Frame(self.reverse_frame)
btn_frame.pack(fill='x', pady=20)
calc_btn = ttk.Button(btn_frame,
text="计算可存储天数",
style='Custom.TButton',
command=self.calculate_reverse)
calc_btn.pack(expand=True)
# 结果显示区域
self.reverse_result_frame = ttk.LabelFrame(self.reverse_frame,
text="计算结果",
padding="15",
style='Result.TLabelframe')
self.reverse_result_frame.pack(fill='x', padx=10, pady=5)
self.days_label = ttk.Label(self.reverse_result_frame,
text="",
font=self.result_font)
self.days_label.pack(anchor=tk.W)
def create_footer(self):
"""创建页脚"""
footer = ttk.Label(self.root,
text="© 2024 专业视频监控存储解决方案",
style='Normal.TLabel')
footer.pack(pady=10)
def create_input_field(self, parent, label_text, row, font):
"""创建统一的输入字段"""
ttk.Label(parent, text=label_text,
font=font).grid(row=row, column=0,
sticky=tk.W, pady=5)
self.entry = ttk.Entry(parent, width=25,
font=font)
self.entry.grid(row=row, column=1, sticky=tk.W, pady=5)
return self.entry
def calculate_storage(self, cameras, hours_per_day, days, camera_type, encoding):
"""
计算存储需求
camera_type: 摄像头类型 (200万/300万/400万/500万)
encoding: 编码方式 (H.264/H.265)
"""
# 每天存储空间(GB)
daily_storage = {
'200万像素': {
'H.264': 42.19, # 4096kbps
'H.265': 21.09 # 2048kbps
},
'300万像素': {
'H.264': 42.19, # 4096kbps
'H.265': 21.09 # 2048kbps
},
'400万像素': {
'H.264': 42.19, # 4096kbps
'H.265': 21.09 # 2048kbps
},
'500万像素': {
'H.264': 63.28, # 6144kbps
'H.265': 31.64 # 3072kbps
}
}
# 计算单个摄像头每天实际存储量
daily_per_camera = daily_storage[camera_type][encoding] * (hours_per_day / 24)
# 计算总存储量
total_storage_gb = daily_per_camera * cameras * days
# 转换为TB并返回
return round(total_storage_gb / 1024, 2)
def calculate_days(self, storage_tb, cameras, hours_per_day, camera_type, encoding):
"""
计算可存储天数
"""
daily_storage = {
'200万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'300万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'400万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'500万像素': {
'H.264': 63.28,
'H.265': 31.64
}
}
# 计算单个摄像头每天实际存储量
daily_per_camera = daily_storage[camera_type][encoding] * (hours_per_day / 24)
# 计算可存储天数
total_gb = storage_tb * 1024
days = total_gb / (daily_per_camera * cameras)
return round(days, 1)
def calculate_forward(self):
try:
cameras = int(self.cameras.get())
hours = float(self.hours.get())
days = int(self.days.get())
camera_type = self.camera_type.get()
encoding = self.encoding.get()
if cameras <= 0 or hours <= 0 or days <= 0:
raise ValueError("请输入大于0的数值")
# 获取单个摄像头每天的存储量
daily_storage = {
'200万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'300万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'400万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'500万像素': {
'H.264': 63.28,
'H.265': 31.64
}
}
daily_per_camera = daily_storage[camera_type][encoding] * (hours / 24)
daily_total = daily_per_camera * cameras
storage = self.calculate_storage(cameras, hours, days, camera_type, encoding)
self.storage_label.config(
text=f"每天存储空间: {round(daily_total, 2)} GB/天\n"
f"总存储容量: {storage} TB",
foreground=self.colors['success'])
self.recommendation_label.config(
text=f"建议配置: {math.ceil(storage)} TB 硬盘\n"
f"(基于{camera_type}摄像头,{encoding}编码)\n"
f"单个摄像头: {round(daily_per_camera, 2)} GB/天",
foreground=self.colors['success'])
except ValueError as e:
self.storage_label.config(
text="输入错误!",
foreground='red')
self.recommendation_label.config(
text="请检查输入的数值是否正确",
foreground='red')
def calculate_reverse(self):
try:
storage = float(self.storage_size.get())
cameras = int(self.rev_cameras.get())
hours = float(self.rev_hours.get())
camera_type = self.rev_camera_type.get()
encoding = self.rev_encoding.get()
if storage <= 0 or cameras <= 0 or hours <= 0:
raise ValueError("请输入大于0的数值")
# 获取单个摄像头每天的存储量
daily_storage = {
'200万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'300万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'400万像素': {
'H.264': 42.19,
'H.265': 21.09
},
'500万像素': {
'H.264': 63.28,
'H.265': 31.64
}
}
daily_per_camera = daily_storage[camera_type][encoding] * (hours / 24)
daily_total = daily_per_camera * cameras
days = self.calculate_days(storage, cameras, hours, camera_type, encoding)
self.days_label.config(
text=f"每天存储空间: {round(daily_total, 2)} GB/天\n"
f"单个摄像头: {round(daily_per_camera, 2)} GB/天\n"
f"可存储天数: {days} 天\n"
f"约等于 {round(days/30, 1)} 个月 或 {round(days/365, 1)} 年\n"
f"(基于{camera_type}摄像头,{encoding}编码)",
foreground=self.colors['success'])
except ValueError as e:
self.days_label.config(
text="输入错误!\n请检查输入的数值是否正确",
foreground='red')
def main():
root = tk.Tk()
app = StorageCalculator(root)
root.mainloop()
if __name__ == "__main__":
main()