首次使用AI完成报价计算器程序,大佬勿喷!
本帖最后由 苏紫方璇 于 2025-3-31 14:18 编辑老刷到大佬们用Ai编程,一时心痒,也试了一下,全AI编写的2小时就完成了,小白一个,全程没自己写过一个代码。
from typing import List, Tuple, Optional# 新增类型提示导入
import tkinter as tk
from tkinter import ttk, messagebox, filedialog
from datetime import datetime
from decimal import Decimal, ROUND_HALF_UP
import pyperclip
from PIL import ImageGrab, Image, ImageDraw
class QuoteCalculator:
def __init__(self, root: tk.Tk) -> None:
self.root = root
self._configure_window()
self._init_style()
self._create_widgets()
self._setup_input_references()
self.update_time()
def _configure_window(self) -> None:
"""配置窗口属性"""
self.root.title("广告报价系统 v2.1")
self.root.geometry("1100x750")
self.root.minsize(1000, 650)
self.root.configure(bg="#f5f5f5")
def _init_style(self) -> None:
"""初始化样式配置"""
self.style = ttk.Style()
self.style.theme_use('clam')
self._configure_base_styles()
self._configure_component_styles()
def _configure_base_styles(self) -> None:
"""基础样式配置"""
self.style.configure(".",
background="#f5f5f5",
foreground="#333",
font=("微软雅黑", 10))
def _configure_component_styles(self) -> None:
"""组件样式配置"""
# 标题样式
self.style.configure("Title.TLabelframe.Label",
font=("微软雅黑", 12, "bold"),
foreground="#2c3e50",
padding=(0, 5, 0, 5))
# 输入框样式
self.style.configure("TEntry",
fieldbackground="white",
bordercolor="#bdc3c7",
lightcolor="#ecf0f1",
darkcolor="#bdc3c7",
padding=5)
# 按钮样式
self.style.map("TButton",
foreground=[('active', 'white'), ('!disabled', 'white')],
background=[('active', '#3498db'), ('!disabled', '#2980b9')])
self.style.configure("TButton",
font=("微软雅黑", 10, "bold"),
padding=8,
borderwidth=0)
# 表格样式
self.style.configure("Treeview",
font=("微软雅黑", 10),
rowheight=30,
background="white",
fieldbackground="white")
self.style.configure("Treeview.Heading",
font=("微软雅黑", 11, "bold"),
background="#3498db",
foreground="white")
def _create_widgets(self) -> None:
"""创建界面组件"""
main_frame = ttk.Frame(self.root, padding=(20, 15))
main_frame.pack(fill="both", expand=True)
# 输入区域
self._create_input_sections(main_frame)
# 结果区域
self._create_results_section(main_frame)
# 控制按钮
self._create_control_buttons(main_frame)
# 时间显示
self._create_time_label(main_frame)
def _create_input_sections(self, parent: ttk.Frame) -> None:
"""创建输入区域"""
# 广告项目
self.ad_frame = self._create_input_section(
parent, "广告项目",
["项目名称", "长度(m)", "宽度(m)", "单价(元/㎡)", "数量"],
self.calc_ad
)
# 图文项目
self.graphic_frame = self._create_input_section(
parent, "图文项目",
["项目名称", "单价(元)", "数量", "份数"],
self.calc_graphic
)
def _create_input_section(self, parent: ttk.Frame, title: str,
fields: List, command: callable) -> ttk.LabelFrame:
"""创建单个输入区域"""
frame = ttk.LabelFrame(parent, text=title, style="Title.TLabelframe")
frame.pack(fill="x", pady=(0, 10), ipadx=10, ipady=10)
entries = self._create_input_form(frame, fields, command)
self._store_entries_reference(title, entries)
return frame
def _create_input_form(self, parent: ttk.LabelFrame, fields: List,
command: callable) -> List:
"""创建输入表单"""
entries = []
# 项目名称
ttk.Label(parent, text=f"{fields}:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
name_entry = ttk.Entry(parent, width=24)
name_entry.grid(row=0, column=1, padx=5, pady=5, sticky="w", columnspan=3)
entries.append(name_entry)
# 其他参数
for i, field in enumerate(fields):
ttk.Label(parent, text=f"{field}:").grid(row=1, column=i*2, padx=5, pady=5, sticky="e")
entry = ttk.Entry(parent, width=10)
entry.grid(row=1, column=i*2+1, padx=5, pady=5, sticky="w")
entries.append(entry)
# 计算按钮
ttk.Button(parent, text="计算", command=command, style="TButton"
).grid(row=1, column=len(fields)*2-1, padx=10, sticky="e")
return entries
def _store_entries_reference(self, title: str, entries: List) -> None:
"""保存输入框引用"""
if "广告" in title:
self.ad_entries = entries
else:
self.graphic_entries = entries
def _create_results_section(self, parent: ttk.Frame) -> None:
"""创建结果展示区域"""
tree_frame = ttk.Frame(parent)
tree_frame.pack(fill="both", expand=True, pady=(0, 10))
# 结果表格
self.tree = ttk.Treeview(tree_frame,
columns=("项目名称", "规格", "单价", "数量", "总价"),
show="headings")
self.tree.pack(side="left", fill="both", expand=True)
# 表格列配置
columns_config = [
("项目名称", 180, 'w'),
("规格", 250, 'w'),
("单价", 120, 'e'),
("数量", 100, 'e'),
("总价", 150, 'e')
]
for col, width, anchor in columns_config:
self.tree.column(col, width=width, anchor=anchor)
self.tree.heading(col, text=col)
# 滚动条
scrollbar = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
scrollbar.pack(side="right", fill="y")
self.tree.configure(yscrollcommand=scrollbar.set)
# 总计金额
self.total_label = ttk.Label(parent,
text="零元整 总计:0.00元",
font=("微软雅黑", 12, "bold"),
foreground="#e74c3c")
self.total_label.pack(pady=(5, 10))
def _create_control_buttons(self, parent: ttk.Frame) -> None:
"""创建控制按钮"""
btn_frame = ttk.Frame(parent)
btn_frame.pack(fill="x", pady=(5, 0))
buttons = [
("清空", self.clear_all, "Danger.TButton"),
("复制", self.copy_quote, "TButton"),
("预览", self.show_preview, "TButton"),
("导出", self.export_image, "Success.TButton")
]
for text, cmd, style in buttons:
btn = ttk.Button(btn_frame, text=text, command=cmd,
style=style, width=8)
btn.pack(side="left", padx=5)
def _create_time_label(self, parent: ttk.Frame) -> None:
"""创建时间标签"""
self.time_label = ttk.Label(parent,
text=datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
font=("微软雅黑", 9),
foreground="#7f8c8d")
self.time_label.pack(side="right", anchor="se")
def _setup_input_references(self) -> None:
"""设置输入框引用"""
# 广告项目
self.ad_name, self.ad_length, self.ad_width, self.ad_price, self.ad_count = self.ad_entries
# 图文项目
self.graphic_name, self.graphic_price, self.graphic_quantity, self.graphic_count = self.graphic_entries
def update_time(self) -> None:
"""更新时间显示"""
self.time_label.config(text=datetime.now().strftime("%Y-%m-%d %H:%M:%S"))
self.root.after(1000, self.update_time)
def calc_ad(self) -> None:
"""计算广告项目"""
try:
name = self.ad_name.get().strip()
length = float(self.ad_length.get())
width = float(self.ad_width.get())
price = float(self.ad_price.get())
count = int(self.ad_count.get())
area = length * width
total = area * price * count
self.tree.insert("", "end", values=(
name,
f"{area:.2f}㎡ ({length}m×{width}m)",
f"{price:.2f}",
count,
f"{total:,.2f}元"
))
self.update_total()
except ValueError as e:
messagebox.showerror("输入错误", f"广告项目参数错误:\n{str(e)}")
def calc_graphic(self) -> None:
"""计算图文项目"""
try:
name = self.graphic_name.get().strip()
price = float(self.graphic_price.get())
quantity = int(self.graphic_quantity.get())
count = int(self.graphic_count.get())
total = price * quantity * count
self.tree.insert("", "end", values=(
name,
"-",
f"{price:.2f}",
f"{quantity}×{count}",
f"{total:,.2f}元"
))
self.update_total()
except ValueError as e:
messagebox.showerror("输入错误", f"图文项目参数错误:\n{str(e)}")
def update_total(self) -> None:
"""更新总计金额"""
total = 0.0
for item in self.tree.get_children():
value = self.tree.item(item)['values'][-1]
total += float(value.replace("元", "").replace(",", ""))
self.total_label.config(
text=f"{self.to_chinese(total)} 总计:{total:,.2f}元",
foreground="#e74c3c"
)
def clear_all(self) -> None:
"""清空所有数据"""
# 清空表格
for item in self.tree.get_children():
self.tree.delete(item)
# 重置总计
self.total_label.config(text="零元整 总计:0.00元")
# 清空输入框
for entry in [*self.ad_entries, *self.graphic_entries]:
entry.delete(0, tk.END)
def to_chinese(self, number: float) -> str:
"""金额转中文大写"""
if number < 0:
raise ValueError("金额不能为负数")
units = ['', '万', '亿']
digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
decimal_units = ['角', '分']
# 使用Decimal处理精度
dec_number = Decimal(str(number)).quantize(Decimal('0.00'), rounding=ROUND_HALF_UP)
integer_part = int(dec_number)
decimal_part = int((dec_number - integer_part) * 100)
result = []
unit_index = 0
# 处理整数部分
while integer_part > 0:
part = integer_part % 10000
if part > 0:
result.append(self._convert_part(part, digits) + units)
else:
result.append('零')
integer_part = integer_part // 10000
unit_index += 1
chn_str = ''.join(reversed(result)) or '零'
# 处理小数部分
decimal_str = ''
for i in range(2):
val = (decimal_part // (10 ** (1 - i))) % 10
if val > 0:
decimal_str += digits + decimal_units
return f"{chn_str}元{decimal_str}" if decimal_str else f"{chn_str}元整"
def _convert_part(self, number: int, digits: List) -> str:
"""转换四位数字"""
units = ['', '拾', '佰', '仟']
result = []
zero_flag = False
for i in range(4):
num = number % 10
if num != 0:
if zero_flag:
result.append('零')
zero_flag = False
result.append(digits + units)
else:
if not zero_flag and result:
zero_flag = True
number = number // 10
return ''.join(reversed(result))
def copy_quote(self) -> None:
"""复制报价到剪贴板"""
if not self.tree.get_children():
messagebox.showwarning("提示", "没有可复制的数据")
return
content = []
for item in self.tree.get_children():
values = self.tree.item(item)['values']
content.append(f"{values} | {values} | {values}")
content.append("\n" + self.total_label.cget('text'))
pyperclip.copy('\n'.join(content))
messagebox.showinfo("复制成功", "报价已复制到剪贴板")
def show_preview(self) -> None:
"""显示打印预览"""
if not self.tree.get_children():
messagebox.showwarning("提示", "没有可预览的数据")
return
preview = tk.Toplevel(self.root)
preview.title("打印预览")
preview.geometry("800x500+200+200")
preview.configure(bg="#f5f5f5")
# 预览表格
tree = ttk.Treeview(preview, columns=("项目名称", "规格", "单价", "数量", "总价"),
show="headings")
tree.pack(fill="both", expand=True, padx=20, pady=20)
for col in tree["columns"]:
tree.heading(col, text=col)
tree.column(col, width=120, anchor="center")
for item in self.tree.get_children():
tree.insert("", "end", values=self.tree.item(item)['values'])
# 总计标签
ttk.Label(preview,
text=self.total_label.cget('text'),
font=("微软雅黑", 12, "bold"),
foreground="#e74c3c",
background="#f5f5f5").pack(pady=10)
def export_image(self) -> None:
"""导出为图片"""
try:
path = filedialog.asksaveasfilename(
defaultextension=".png",
filetypes=[("PNG图片", "*.png"), ("PDF文档", "*.pdf")]
)
if not path:
return
# 捕获区域
header = self.root.winfo_children()
x1 = self.tree.winfo_rootx() - 20
y1 = header.winfo_rooty() - 10
x2 = self.tree.winfo_rootx() + self.tree.winfo_width() + 20
y2 = self.total_label.winfo_rooty() + self.total_label.winfo_height() + 20
# 图像处理
img = ImageGrab.grab(bbox=(x1, y1, x2, y2))
img = self._auto_crop_image(img)
if path.endswith(".pdf"):
img.convert("RGB").save(path, "PDF", resolution=100)
else:
img.save(path, dpi=(300, 300))
messagebox.showinfo("导出成功", f"文件已保存:{path}")
except Exception as e:
messagebox.showerror("导出错误", f"操作失败:{str(e)}")
def _auto_crop_image(self, img: Image.Image) -> Image.Image:
"""自动裁剪图片空白边缘"""
bg = Image.new("RGB", img.size, (245, 245, 245))
diff = Image.blend(img.convert("RGB"), bg, 0).convert("L")
bbox = diff.getbbox()
padding = 15
x1 = max(0, bbox - padding) if bbox else 0
y1 = max(0, bbox - padding) if bbox else 0
x2 = min(img.width, bbox + padding) if bbox else img.width
y2 = min(img.height, bbox + padding) if bbox else img.height
return img.crop((x1, y1, x2, y2)) if bbox else img
if __name__ == "__main__":
root = tk.Tk()
app = QuoteCalculator(root)
root.mainloop()
通过网盘分享的文件:报价计算器.exe
链接: https://pan.baidu.com/s/1Q2UvDQ05cg6N8uthUyksww 提取码: 52pj 代码插入可以参考这个帖子
【公告】发帖代码插入以及添加链接教程(有福利)
https://www.52pojie.cn/thread-713042-1-1.html
(出处: 吾爱破解论坛)
界面挺简洁的,虽然用不到,感觉用Excel也能实现,但是还是觉得挺棒的,提供了打包好的可运行程序下载。 厉害,加油,仰望互相学习下,用的DEep吗 厉害了老哥,学习中...... 持续学习中。。。。。。 很实用的小工具,界面简单。想试一下,效果。 小白羊 发表于 2025-3-31 14:22
厉害,加油,仰望互相学习下,用的DEep吗
deepSeek 苏紫方璇 发表于 2025-3-31 14:17
代码插入可以参考这个帖子
【公告】发帖代码插入以及添加链接教程(有福利)
https://www.52pojie.cn/thr ...
谢谢大佬指导🥰🥰🥰🥰 强大的AOI. 希望大佬能给出个教程{:1_921:}