首页 > 其他分享 >语音播报,套件多少异常的问题。(含源代码)

语音播报,套件多少异常的问题。(含源代码)

时间:2025-01-21 11:42:48浏览次数:1  
标签:播报 sheet self results standard 套件 tk 源代码 data

在工作中遇到一家工厂老板的需求:因为产品是有多个配件组成,在生产的时候,经常会多生产,少生产,在组装时,也会出现配件多少的问题,现就此问题设计一款程序。多出,少的,异常的,正常好,会开语音播报。现将全部代码给出以备。

 

import inspect
import os
import threading
import time
import tkinter as tk
import uuid
from collections import Counter
from datetime import datetime
from tkinter import filedialog, messagebox
import pandas as pd
import pyttsx3
from openpyxl import Workbook, load_workbook
# from pyttsx3 import engine
# 创建语音引擎
engine = pyttsx3.init()


class DataComparatorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("配件对比工具")
        self.root.geometry("800x820")

        # 初始化数据
        self.input_data = None
        self.standard_data = None
        self.original_standard_data = None
        self.comparison_results = []
        self.scan_data = {}  # 初始化扫描数据字典
        self.is_processing = False  # 防止递归触发
        self.save_path = os.path.join(os.getcwd(), "result.xlsx")  # 默认保存路径
        self.standard_data_written = False  # 标志位,确保标准数据只写入一次
        self.comparison_results_written = False  # 标志位,确保对比结果只写入一次

        # 创建界面控件
        self.create_widgets()

    def create_widgets(self):
        # 主布局框架
        main_frame = tk.Frame(self.root)
        main_frame.pack(padx=20, pady=20, expand=True, fill="both")

        # 导入标准数据按钮放到最上方
        import_standard_frame = tk.Frame(main_frame)
        import_standard_frame.pack(padx=10, pady=10, fill="x")

        self.import_standard_button = tk.Button(import_standard_frame, text="导入标准数据", command=self.import_standard_data, bg="#AAA8E6", fg="black", font=15)  # 淡蓝色按钮
        # 字体设置成幼圆
        self.import_standard_button.config(font=("幼圆", 15))
        self.import_standard_button.pack(side=tk.RIGHT, padx=10, pady=5)  # 修改为使用 pack 和 side=tk.RIGHT
        self.data_format_label = tk.Label(import_standard_frame, text="数据格式: Excel 格式(配件名称、数量)", fg="grey")
        self.data_format_label.pack(side=tk.RIGHT, padx=10, pady=5)  # 修改为使用 pack 和 side=tk.RIGHT

        # 新增标准数据导入状态提示标签
        self.standard_data_status_label = tk.Label(import_standard_frame, text="没有导入标准数据", fg="red")
        self.standard_data_status_label.pack(side=tk.RIGHT, padx=10, pady=5)

        # 导入数据区域
        import_frame = tk.LabelFrame(main_frame, text="导入数据", padx=20, pady=20)
        import_frame.pack(padx=10, pady=10, fill="x")

        # 调整手动导入数据按钮的高度和宽度
        self.import_input_button = tk.Button(import_frame, text="手\n动\n导\n入\n对\n比\n数\n据", command=self.import_input_data, bg="#AAA8E6", fg="black", height=14, width=8)  # 修改高度和宽度
        self.import_input_button.config(font=("幼圆", 15))
        self.import_input_button.pack(side=tk.RIGHT, padx=10, pady=5)  # 修改为使用 pack 和 side=tk.RIGHT

        # 调整扫描数据展示框的大小,铺满整个区域,并确保能够输入
        self.scan_data_display = tk.Text(import_frame, width=80, height=10, relief="sunken", wrap=tk.WORD, font=("Arial", 12))  # 修改宽度和高度,并设置字体大小
        self.scan_data_display.pack(pady=10, fill="both", expand=True)
        self.scan_data_display.focus()  # 使扫描框获取焦点,以便扫描时能够输入

        # 监听文本框的变化,自动添加逗号和换行符
        self.scan_data_display.bind("<KeyRelease>", self.detect_singsong)  # 监听输入框变化

        # 对比数据区域:增加高度
        compare_frame = tk.LabelFrame(main_frame, text="对比数据", padx=20, pady=20)
        compare_frame.pack(padx=10, pady=10, fill="x")

        # 使用文本框显示对比进度
        self.compare_progress_text = tk.Text(compare_frame, height=20, width=100, state=tk.DISABLED, font=("Arial", 12))  # 增大高度,并设置字体大小
        self.compare_progress_text.pack(pady=10, fill="both", expand=True)

        # 操作区域
        action_frame = tk.Frame(main_frame)
        action_frame.pack(padx=10, pady=10, fill="x")

        self.save_button = tk.Button(action_frame, text="保存对比结果", height=20, width=100, command=self.save_comparison_results, bg="#ADD8E6", fg="black")
        self.save_button.pack(side=tk.RIGHT, padx=10)

        # 新增的保存状态标签
        self.save_status_label = tk.Label(action_frame, text="", fg="red")  # 显示文件保存状态
        self.save_status_label.pack(side=tk.RIGHT, padx=10)

    def detect_singsong(self, event=None):
        """检测是否输入了 SINGSONG 并触发对比"""
        if self.is_processing:
            return

        # 获取扫描框的内容,并去掉前后空格
        scan_data = self.scan_data_display.get("1.0", tk.END).strip().lower()

        # 去掉所有空格
        scan_data = scan_data.replace(" ", "")  # 去掉所有空格

        # 如果扫描到"SINGSONG",进行对比
        if "singsong" in scan_data:
            self.is_processing = True
            try:
                self.process_scan_data(scan_data)  # 处理扫描数据并触发对比
            finally:
                self.is_processing = False  # 确保 is_processing 被重置

    def process_scan_data(self, scan_data):
        """处理扫描数据并与标准数据对比"""
        # 处理扫描数据,移除空格后每个配件加上逗号
        items = scan_data.split(",")
        items = [item.strip().replace(" ", "") for item in items if item.strip()]  # 去除多余的空格

        # 排除掉"SINGSONG"
        items = [item for item in items if item.lower() != "singsong"]

        # 统计每个配件的数量
        scan_dict = dict(Counter(items))
        self.scan_data = scan_dict

        print(f"处理后的扫描数据:{self.scan_data}")  # 打印扫描处理后的数据
        self.compare_data()  # 进行对比

    def import_input_data(self):
        """导入输入源数据"""
        file_path = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx"), ("CSV Files", "*.csv")])
        if file_path:
            try:
                if file_path.endswith('.csv'):
                    self.input_data = pd.read_csv(file_path, encoding='utf-8')
                else:
                    self.input_data = pd.read_excel(file_path)
                # 更新已导入产品信息
                product_count = len(self.input_data)
                self.imported_label.config(text=f"已导入 {product_count} 个产品")
                messagebox.showinfo("提示", f"输入源数据导入成功!共 {product_count} 个产品")
                self.check_buttons()
            except Exception as e:
                messagebox.showerror("错误", f"导入输入源数据失败:{e}")

    def import_standard_data(self):
        """导入标准数据"""
        file_path = filedialog.askopenfilename(filetypes=[("Excel Files", "*.xlsx")])  # 只支持Excel文件
        if file_path:
            try:
                # 使用pandas读取Excel文件
                df = pd.read_excel(file_path)

                # 检查列名是否存在
                if 'PIEZA' not in df.columns or 'CANTIDAD' not in df.columns:
                    messagebox.showerror("错误", "Excel 文件中缺少 'PIEZA' 或 'CANTIDAD' 列")
                    return

                # 处理 A 列和 C 列,创建标准数据字典
                self.standard_data = dict(zip(df['PIEZA'].str.lower(), df['CANTIDAD']))  # 只读取A、C列进行对比
                self.original_standard_data = df  # 保存原始数据,以便在保存时使用

                print(f"标准数据:{self.standard_data}")  # 打印标准数据

                messagebox.showinfo("提示", "标准数据导入成功!")
                self.check_buttons()
                self.save_standard_data()  # 在导入标准数据后,保存标准数据

                # 更新标准数据导入状态提示标签
                self.standard_data_status_label.config(text="标准数据已导入", fg="green")
            except Exception as e:
                messagebox.showerror("错误", f"导入标准数据失败:{e}")
                # 更新标准数据导入状态提示标签
                self.standard_data_status_label.config(text="没有导入标准数据", fg="red")
        else:
            # 更新标准数据导入状态提示标签
            self.standard_data_status_label.config(text="没有导入标准数据", fg="red")

    def check_buttons(self):
        """检查按钮状态"""
        if self.input_data is not None and self.standard_data is not None:
            self.save_button.config(state=tk.NORMAL)

    # def compare_data(self):
    #     """进行数据对比"""
    #     self.compare_progress_text.config(state=tk.NORMAL)
    #     self.compare_progress_text.delete(1.0, tk.END)  # 清空对比结果
    #
    #     comparison_results = []
    #     total_parts = len(self.scan_data)
    #     processed_parts = 0
    #
    #     # 遍历扫描数据与标准数据对比
    #     for part, quantity in self.scan_data.items():
    #         standard_quantity = self.standard_data.get(part.lower(), 0)
    #
    #         if standard_quantity == 0:
    #             comparison_results.append(f"未在标准数据中找到配件:{part}")
    #         elif quantity > standard_quantity:
    #             comparison_results.append(f"多了配件:{part},多{quantity - standard_quantity}个")
    #         elif quantity < standard_quantity:
    #             comparison_results.append(f"少了配件:{part},少{standard_quantity - quantity}个")
    #         else:
    #             comparison_results.append(f"配件完整:{part}")
    #
    #         # 更新进度
    #         processed_parts += 1
    #         progress = int((processed_parts / total_parts) * 100)
    #         self.compare_progress_text.insert(tk.END, f"正在处理:{part} - {progress}% 完成\n")
    #         self.compare_progress_text.yview(tk.END)
    #
    #     # 对比标准数据中缺少的配件
    #     for part, standard_quantity in self.standard_data.items():
    #         if part not in self.scan_data:
    #             comparison_results.append(f"缺少配件:{part},标准数量为 {standard_quantity}个")
    #
    #     self.comparison_results = comparison_results
    #     self.display_results()
    #     self.voice_result()
    #
    #     # 保存对比结果
    #     if not self.comparison_results_written:
    #         self.save_comparison_results()  # 只有在第一次保存对比结果时调用
    #         self.comparison_results_written = True
    #
    #     # 清空扫描数据,准备下一轮输入
    #     self.scan_data_display.delete(1.0, tk.END)  # 清空文本框
    #     self.scan_data = {}
    #     self.is_processing = False  # 允许再次处理

    def compare_data(self):
        """进行数据对比"""
        self.compare_progress_text.config(state=tk.NORMAL)
        self.compare_progress_text.delete(1.0, tk.END)  # 清空对比结果

        comparison_results = []
        total_parts = len(self.scan_data)
        processed_parts = 0

        # 重置标志位,确保每次对比结果都被保存
        self.comparison_results_written = False

        # 遍历扫描数据与标准数据对比
        for part, quantity in self.scan_data.items():
            standard_quantity = self.standard_data.get(part.lower(), 0)

            if standard_quantity == 0:
                comparison_results.append(f"异常配件:{part}")
            elif quantity > standard_quantity:
                comparison_results.append(f"多了配件:{part},多{quantity - standard_quantity}个")
            elif quantity < standard_quantity:
                comparison_results.append(f"少了配件:{part},少{standard_quantity - quantity}个")

            # 更新进度
            processed_parts += 1
            progress = int((processed_parts / total_parts) * 100)
            self.compare_progress_text.insert(tk.END, f"正在处理:{part} - {progress}% 完成\n")
            self.compare_progress_text.yview(tk.END)

        # 对比标准数据中缺少的配件
        for part, standard_quantity in self.standard_data.items():
            if part not in self.scan_data:
                comparison_results.append(f"缺少配件:{part},标准数量为 {standard_quantity}个")

        # 检查是否所有配件都完全匹配
        if not comparison_results:
            comparison_results.append("全部配件完整")

        self.comparison_results = comparison_results
        self.display_results()

        # 修改语音播报逻辑
        self.voice_result()

        # 保存对比结果
        if not self.comparison_results_written:
            self.save_comparison_results()  # 只有在第一次保存对比结果时调用
            self.comparison_results_written = True
            # 写入分隔线
            self.compare_progress_text.insert(tk.END, "\n" * 20)
        # 清空扫描数据,准备下一轮输入
        self.scan_data_display.delete(1.0, tk.END)  # 清空文本框
        self.scan_data = {}
        self.is_processing = False  # 允许再次处理

    def display_results(self):
        """显示对比结果"""
        scan_data_text = "\n".join([f"{key}: {value}" for key, value in self.scan_data.items()])
        self.compare_progress_text.config(state=tk.NORMAL)
        self.compare_progress_text.delete(1.0, tk.END)
        self.compare_progress_text.insert(tk.END, f"扫描数据:\n{scan_data_text}\n\n对比结果:\n")
        result_text = "\n".join(self.comparison_results)
        self.compare_progress_text.insert(tk.END, result_text + '\n')
        self.compare_progress_text.config(state=tk.DISABLED)

    def _voice_result(self):
        """语音播报对比结果"""
        if self.comparison_results:
            def speak():
                try:
                    # 直接运行语音播报,而不停止任何当前的语音
                    for result in self.comparison_results:
                        engine.say(result)
                    engine.runAndWait()  # 确保在语音播报完后再继续执行
                    time.sleep(1)
                    self.scan_data_display.delete(1.0, tk.END)  # 扫描框清空
                except Exception as e:
                    print(f"语音播报出错:{e}")

            thread = threading.Thread(target=speak)
            thread.start()

    def voice_result(self):
        """语音播报对比结果"""
        if self.comparison_results:
            engine.stop()  # 停止之前的语音播报
            for result in self.comparison_results:
                engine.say(result)
            engine.runAndWait()  # 确保在语音播报完后再继续执行

            # 停留1秒后清空输入框
            time.sleep(1)
            self.scan_data_display.delete(1.0, tk.END)

    def save_standard_data(self):
        """保存标准数据"""
        if self.standard_data_written:
            return  # 如果已经保存过标准数据,则直接返回,不再保存

        # 获取MAC地址
        mac_address = ':'.join(
            ['{:02x}'.format((uuid.getnode() >> elements) & 0xff) for elements in range(0, 2 * 6, 2)])

        # 获取当前时间
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # 获取标准数据的完整内容
        standard_data_content = self.original_standard_data

        # 如果文件路径不存在,创建文件夹
        file_dir = os.path.dirname(self.save_path)
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)

        try:
            # 检查文件是否存在
            if os.path.exists(self.save_path):
                workbook = load_workbook(self.save_path)
            else:
                workbook = Workbook()

            # 删除其他sheet,只保留对比数据和标准数据
            sheets_to_delete = [sheet for sheet in workbook.sheetnames if sheet not in ["对比数据", "标准数据"]]
            for sheet in sheets_to_delete:
                del workbook[sheet]

            # 处理“对比数据” sheet
            if "对比数据" not in workbook.sheetnames:
                sheet = workbook.create_sheet("对比数据", 0)  # 将其放在第一个位置
                sheet.append(["配件", "数量", "类型"])  # 写入列名

            # 写入扫描数据和对比结果
            for part, quantity in self.scan_data.items():
                sheet.append([part, quantity, "扫描数据"])
            for result in self.comparison_results:
                sheet.append([result, "", "对比结果"])

            # 处理“标准数据” sheet
            if "标准数据" not in workbook.sheetnames:
                sheet = workbook.create_sheet("标准数据")
                # 写入原始数据的表头
                sheet.append(standard_data_content.columns.tolist())
                # 写入原始数据
                for index, row in standard_data_content.iterrows():
                    sheet.append(row.tolist())
                # 追加 MAC地址、时间、文件路径
                last_row = sheet.max_row + 1
                sheet.cell(row=last_row, column=1, value="MAC地址")
                sheet.cell(row=last_row, column=2, value=mac_address)
                sheet.cell(row=last_row + 1, column=1, value="时间")
                sheet.cell(row=last_row + 1, column=2, value=timestamp)
                sheet.cell(row=last_row + 2, column=1, value="文件路径")
                sheet.cell(row=last_row + 2, column=2, value=self.save_path)

            workbook.save(self.save_path)
            self.standard_data_written = True
        except Exception as e:
            messagebox.showerror("错误", f"保存标准数据失败:{e}")

    def save_comparison_results(self):
        """保存对比结果"""
        # 如果文件路径不存在,创建文件夹
        file_dir = os.path.dirname(self.save_path)
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)

        try:
            # 检查文件是否存在
            if os.path.exists(self.save_path):
                workbook = load_workbook(self.save_path)
            else:
                workbook = Workbook()

            # 删除其他sheet,只保留对比数据和标准数据
            sheets_to_delete = [sheet for sheet in workbook.sheetnames if sheet not in ["对比数据", "标准数据"]]
            for sheet in sheets_to_delete:
                del workbook[sheet]

            # 处理“对比数据” sheet
            if "对比数据" not in workbook.sheetnames:
                sheet = workbook.create_sheet("对比数据", 0)  # 将其放在第一个位置
                sheet.append(["配件", "数量", "类型"])  # 写入列名
            else:
                sheet = workbook["对比数据"]
            # 如果有结果,插入分隔符行
            if sheet.max_row > 1:  # 确保已经写入过内容
                sheet.append(["*************", "*************", "*************"])  # 添加分隔符行
            # 写入扫描数据和对比结果
            for part, quantity in self.scan_data.items():
                sheet.append([part, quantity, "扫描数据"])
            for result in self.comparison_results:
                sheet.append([result, "", "对比结果"])

            workbook.save(self.save_path)

            # 更新界面显示文件已保存
            self.save_status_label.config(text="文件已保存", fg="green")
        except Exception as e:
            messagebox.showerror("错误", f"保存对比结果失败:{e}")

    def _save_comparison_results(self):
        """保存对比结果"""
        # 如果文件路径不存在,创建文件夹
        file_dir = os.path.dirname(self.save_path)
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)

        try:
            # 检查文件是否存在
            if os.path.exists(self.save_path):
                workbook = load_workbook(self.save_path)
            else:
                workbook = Workbook()

            # 删除其他sheet,只保留对比数据和标准数据
            sheets_to_delete = [sheet for sheet in workbook.sheetnames if sheet not in ["对比数据", "标准数据"]]
            for sheet in sheets_to_delete:
                del workbook[sheet]

            # 处理“对比数据” sheet
            if "对比数据" not in workbook.sheetnames:
                sheet = workbook.create_sheet("对比数据", 0)  # 将其放在第一个位置
                sheet.append(["配件", "数量", "类型"])  # 写入列名
            else:
                sheet = workbook["对比数据"]

            # 如果有结果,插入分隔符行
            if sheet.max_row > 1:  # 确保已经写入过内容
                sheet.append(["*************", "*************", "*************"])  # 添加分隔符行

            # 写入扫描数据和对比结果
            for part, quantity in self.scan_data.items():
                sheet.append([part, quantity, "扫描数据"])
            for result in self.comparison_results:
                sheet.append([result, "", "对比结果"])

            # 保存工作簿
            workbook.save(self.save_path)

            # 更新界面显示文件已保存
            self.save_status_label.config(text="文件已保存", fg="green")
        except Exception as e:
            messagebox.showerror("错误", f"保存对比结果失败:{e}")


# 创建主窗口
root = tk.Tk()

# 创建应用
app = DataComparatorApp(root)

# 运行主事件循环
root.mainloop()

 

标签:播报,sheet,self,results,standard,套件,tk,源代码,data
From: https://www.cnblogs.com/lyt263/p/18683346

相关文章

  • 《String类的equals()的作用和源代码解读》
    一、equals()方法的由来equals()最开始是定义在Java.lang包下的Object中的一个经行比较的方法,根据Object类的核心代码可以看出来,在Object类中equals()方法比较时使用“==”运算符来比较两者地址,但实际应用情况下,人们往往想比较两者的值是否相同,当两个相同的值存进不同内存地址时......
  • String类的equals()的作用和源代码解读
    1. 了解equals()方法equals方法是用于比较两个对象是否相等的方法,定义在Object类中。其默认实现仅比较对象的引用地址,但可以通过重写方法实现对对象内容的比较。只有引用数据类型才可以使用equals方法,我们点进equals方法的源码:我们看代码前几行,观察到当传入进来的参数之间......
  • 如何在网站中安全有效地修改源代码,确保不影响网站的正常运行?
    修改网站源代码是一项需要谨慎操作的任务,以确保不会影响网站的稳定性和功能。以下是详细的步骤和建议:确定修改需求:明确具体的修改需求,包括功能改进、界面优化等方面的要求。制定详细的修改计划,确保每个改动都有明确的目标。备份现有文件:在进行任何更改之前,请确保对当......
  • 单片机毕业设计之stm32单片机物联网远程心率血氧MAX30102健康监控系统,老人健康监测+行
    一、设计简介        本项目旨在利用STM32F103C8T6微控制器为核心,构建一个实时人体健康监测系统。该系统集成了多种传感器和模块,能够全面、准确地监测并显示人体的关键健康数据,同时提供异常报警功能,还通过蓝牙通信功能实现了数据的远程传输和记录,方便用户随时了解自己......
  • 基于STM32单片机自动售货机扫码支付无人超市语音播报无线蓝牙APP/WIFI-APP控制/WIFI视
    STM32-S147语音播报+二维码付+4种商品+4路电机出货+选货+手付+库存+缺货+找零+声光+按键+TFT屏+(无线方式选择)产品功能描述:本系统由STM32F103C8T6单片机核心板、1.44寸TFT彩屏、(无线蓝牙/无线WIFI/无线视频监控模块-可选)、步进电机控制电路、语音播报模块接口、蜂鸣器报警电......
  • 【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-
    【02】做一个精美的打飞机小游戏,python开发小游戏-鹰击长空—优雅草央千澈-持续更新-分享源代码和游戏包供游玩-记录完整开发过程-用做好的素材来完善鹰击长空1.0.1版本背景之前优雅草央千澈在AE特效制作处博文已经完整介绍了本款游戏的素材开发,本文开始把素材利用起来放进去......
  • No Magic - 复杂产品系统架构开发套件
    概述      NoMagic系列产品被达索收购后融入3DExperience产品协同研发管理平台中,是一款强大的建模工具,专为软件分析和设计、系统建模、项目管理等领域设计。该软件提供对SysML/UML/UAF语言的完整支持,提供MagicGrid方法论,涵盖:业务分析、干系人需求分析、系统需求定义......
  • 云平台运维监控套件:确保业务稳定运行的关键工具
    云平台运维监控套件:确保业务稳定运行的关键工具随着云计算技术的不断发展和普及,越来越多的企业选择将业务迁移到云端,以提高灵活性和可扩展性。然而,这也带来了新的挑战,特别是在运维管理方面。云平台运维监控套件作为一种全面、高效的解决方案,能够实时监控云主机的各项性能指......
  • 【MATLAB代码】CV和CA模型组成的IMM(滤波方式为UKF),可复制粘贴源代码
    该代码实现了一维无迹卡尔曼滤波器(UKF)与交互式多模型(IMM)结合的状态估计。代码分为多个部分,主要功能包括参数定义、观测数据生成、状态估计、模型更新以及结果可视化。文章目录运行结果程序代码主要功能代码结构应用场景注意事项运行结果程序代码下方......
  • 如何安全地修改网站源代码的时间戳?
    要安全地修改网站源代码的时间戳,请遵循以下最佳实践:备份现有文件:在任何实质性操作之前,务必先备份当前文件。可以选择物理备份或逻辑备份,视具体情况而定。理解影响范围:仔细评估即将做出的更改会对哪些应用程序产生影响。特别是那些依赖于特定时间戳的应用,比如缓存机制、版本控制......