Gpgg 发表于 2025-3-31 14:08

首次使用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

苏紫方璇 发表于 2025-3-31 14:17

代码插入可以参考这个帖子
【公告】发帖代码插入以及添加链接教程(有福利)
https://www.52pojie.cn/thread-713042-1-1.html
(出处: 吾爱破解论坛)

hua520 发表于 2025-3-31 22:11

界面挺简洁的,虽然用不到,感觉用Excel也能实现,但是还是觉得挺棒的,提供了打包好的可运行程序下载。

小白羊 发表于 2025-3-31 14:22

厉害,加油,仰望互相学习下,用的DEep吗

ymj 发表于 2025-3-31 14:24

厉害了老哥,学习中......

sqdon 发表于 2025-3-31 14:34

持续学习中。。。。。。

makaay 发表于 2025-3-31 15:31

很实用的小工具,界面简单。想试一下,效果。

Gpgg 发表于 2025-3-31 15:40

小白羊 发表于 2025-3-31 14:22
厉害,加油,仰望互相学习下,用的DEep吗

deepSeek

Gpgg 发表于 2025-3-31 15:41

苏紫方璇 发表于 2025-3-31 14:17
代码插入可以参考这个帖子
【公告】发帖代码插入以及添加链接教程(有福利)
https://www.52pojie.cn/thr ...

谢谢大佬指导&#129392;&#129392;&#129392;&#129392;

nameisadam 发表于 2025-3-31 15:42

强大的AOI.

13642841500 发表于 2025-3-31 16:26

希望大佬能给出个教程{:1_921:}
页: [1] 2 3
查看完整版本: 首次使用AI完成报价计算器程序,大佬勿喷!