主界面介绍:在这里插入图片描述
操作:删除无效物料在这里插入图片描述合并相同规格物料在这里插入图片描述

一、项目介绍
BOM整理工具是一个基于Python和Tkinter开发的桌面应用程序,专门用于处理电子行业的物料清单(Bill of Materials),提供了一系列功能来简化BOM的整理、分析和导出过程。

二、功能实现、介绍

2.1 核心功能需求列表

  • Excel导入导出:支持导入外部Excel文件,导出整理后的Excel文件
  • 数据显示:在界面中清晰显示Excel表格内容
  • 料号检索:支持搜索"厂家料号"或"Part Number"列
  • 空行删除:删除"厂家料号"或"Part Number"列中无值或空的行
  • 料号合并:合并相同料号的行,汇总数量和位号
  • 序号重排:保持原始顺序的同时更新序号
  • 空贴物料删除:删除备注列以"_NC"结尾的行
  • BOM排版:统一列名并调整列顺序

2.2 界面与用户体验

  • 美观的界面:合理的窗口大小和布局
  • 操作详情:显示删除、合并等操作的详细内容
  • 导出格式:支持特定的Excel导出格式要求
  • 单页显示:导出时确保BOM在一页内显示

三、技术栈

  •  Python	核心编程语言	3.6+
    
  •  Tkinter	GUI开发	内置
    
  •  pandas	数据处理	1.0+
    
  •  openpyxl	Excel操作	3.0+
    

四、 核心功能实现(代码实例)

4.1 Excel导入功能:

def import_excel(self):
    filetypes = [('Excel files', '*.xlsx *.xls'), ('All files', '*.*')]
    filename = filedialog.askopenfilename(title="选择Excel文件", filetypes=filetypes)
    if not filename:
        return
    
    try:
        self.df = pd.read_excel(filename)
        # 保存原始数据状态
        self.original_df = self.df.copy()
        self.filename = filename
        self.display_data()
        self.status_var.set(f"已导入: {os.path.basename(filename)}")
        messagebox.showinfo("成功", "Excel文件导入成功!")
    except Exception as e:
        messagebox.showerror("错误", f"导入失败: {str(e)}")
        self.status_var.set("导入失败")

4.2 料号合并功能:

def merge_same_pn(self):
    if self.df is None:
        messagebox.showinfo("提示", "请先导入Excel文件")
        return
    
    # 查找料号列
    pn_column = None
    for col in self.df.columns:
        col_lower = str(col).lower()
        if '厂家料号' in str(col) or 'part number' in col_lower or 'pn' in col_lower:
            pn_column = col
            break
    
    if not pn_column:
        messagebox.showinfo("提示", "未找到料号列")
        return
    
    # 查找数量列和位号列
    qty_column = None
    pos_column = None
    for col in self.df.columns:
        col_lower = str(col).lower()
        if '数量' in str(col) or 'qty' in col_lower:
            qty_column = col
        elif '位号' in str(col) or 'position' in col_lower or 'pos' in col_lower:
            pos_column = col
    
    # 记录原始料号的出现顺序
    seen_pns = []
    for pn in self.df[pn_column]:
        if pn not in seen_pns and pd.notna(pn) and str(pn).strip() != '' and str(pn).strip() != 'nan':
            seen_pns.append(pn)
    
    # 合并相同料号的行
    grouped = self.df.groupby(pn_column)
    merged_data = []
    merge_details = []
    
    # 按照原始顺序处理料号
    for pn in seen_pns:
        if pn in grouped.groups:
            group = grouped.get_group(pn)
            # 只处理需要合并的料号(行数大于1)
            if len(group) > 1:
                row = group.iloc[0].to_dict()
                
                # 合并数量
                if qty_column:
                    row[qty_column] = group[qty_column].sum()
                
                # 合并位号
                if pos_column:
                    positions = group[pos_column].dropna().astype(str).tolist()
                    row[pos_column] = ','.join(positions)
                
                merged_data.append(row)
                # 保存合并前的每一行数据
                merge_details.append((pn, group, len(group), 
                                   group[qty_column].sum() if qty_column else 0, 
                                   ','.join(group[pos_column].dropna().astype(str).tolist()) if pos_column else '', 
                                   row[qty_column] if qty_column else 0, 
                                   row[pos_column] if pos_column else ''))
            else:
                # 不需要合并的料号,直接添加
                merged_data.append(group.iloc[0].to_dict())
    
    self.df = pd.DataFrame(merged_data)
    self.display_data()
    self.status_var.set(f"已合并 {len(merged_data)} 个料号")

4.3 空贴物料删除功能:

def delete_nc_materials(self):
    if self.df is None:
        messagebox.showinfo("提示", "请先导入Excel文件")
        return
    
    # 查找备注列
    note_column = None
    for col in self.df.columns:
        col_lower = str(col).lower()
        if '备注' in str(col) or 'note' in col_lower:
            note_column = col
            break
    
    if not note_column:
        messagebox.showinfo("提示", "未找到备注列")
        return
    
    # 保存原始数据
    original_df = self.df.copy()
    
    # 查找备注列最后三个字符为"_NC"的行
    nc_rows = []
    for i, row in self.df.iterrows():
        note_value = row[note_column]
        if pd.notna(note_value):
            note_str = str(note_value)
            if len(note_str) >= 3 and note_str[-3:] == "_NC":
                nc_rows.append(i)
    
    # 删除这些行
    self.df = self.df.drop(nc_rows)
    
    deleted_rows = len(nc_rows)
    self.display_data()
    self.status_var.set(f"已删除 {deleted_rows} 个空贴物料")

4.4 BOM排版功能:

def format_bom(self):
    if self.df is None:
        messagebox.showinfo("提示", "请先导入Excel文件")
        return
    
    # 列名映射
    column_mappings = {
        '数量': '数量',
        'QTY': '数量',
        '项目': '序号',
        'ITEM': '序号',
        '参考编号': '位号',
        'REF-DES': '位号',
        '厂家料号': '厂家料号',
        'Part Number': '厂家料号',
        '制造商': '制造商',
        'Manufacturer': '制造商',
        '说明': '说明',
        'Description': '说明',
        '数值': '备注',
        '备注': '备注',
        'Value': '备注'
    }
    
    # 修改列名
    new_columns = []
    for col in self.df.columns:
        col_lower = str(col).lower()
        # 查找映射
        mapped = False
        for key, value in column_mappings.items():
            if key.lower() in col_lower:
                new_columns.append(value)
                mapped = True
                break
        if not mapped:
            new_columns.append(col)
    
    # 重命名列
    self.df.columns = new_columns
    
    # 检查是否有"料号"列
    if '料号' not in self.df.columns:
        # 新增"料号"列,所有行为空值
        self.df['料号'] = ''
    
    # 调整列顺序
    desired_order = ['序号', '料号', '说明', '制造商', '数量', '位号', '厂家料号', '备注']
    
    # 确保所有列都存在
    for col in desired_order:
        if col not in self.df.columns:
            self.df[col] = ''
    
    # 重新排列列
    self.df = self.df[desired_order]
    
    # 显示数据
    self.display_data()
    self.status_var.set("BOM排版完成")

4.5 Excel导出功能

def export_excel(self):
    if self.df is None:
        messagebox.showinfo("提示", "请先导入Excel文件")
        return
    
    filetypes = [('Excel files', '*.xlsx'), ('All files', '*.*')]
    filename = filedialog.asksaveasfilename(title="保存Excel文件", filetypes=filetypes, defaultextension=".xlsx")
    if not filename:
        return
    
    try:
        # 导出Excel文件
        self.df.to_excel(filename, index=False)
        
        # 使用openpyxl进行格式设置
        from openpyxl import load_workbook
        from openpyxl.styles import Alignment, Border, Side
        
        wb = load_workbook(filename)
        ws = wb.active
        
        # 获取列名和列索引的映射
        col_name_to_index = {}
        for col in range(1, ws.max_column + 1):
            col_name = ws.cell(row=1, column=col).value
            col_name_to_index[col_name] = col
        
        # 制造商、数量、厂家料号、备注、序号列内容水平居中处理
        center_columns = []
        for col_name, col_index in col_name_to_index.items():
            col_lower = str(col_name).lower()
            if any(keyword in col_lower for keyword in ['制造商', 'manufacturer', '数量', 'qty', '厂家料号', 'part number', 'pn', '备注', 'note', '序号', 'serial', 'no']):
                center_columns.append(col_index)
        
        # 说明、位号列适当调整宽度,自动换行
        wrap_columns = []
        for col_name, col_index in col_name_to_index.items():
            col_lower = str(col_name).lower()
            if any(keyword in col_lower for keyword in ['说明', 'description', '位号', 'position', 'pos']):
                wrap_columns.append(col_index)
        
        # 处理所有单元格
        for row in range(1, ws.max_row + 1):
            for col in range(1, ws.max_column + 1):
                cell = ws.cell(row=row, column=col)
                
                # 第一行特殊处理:水平居中+垂直居中
                if row == 1:
                    cell.alignment = Alignment(horizontal='center', vertical='center')
                else:
                    # 所有数据垂直居中
                    alignment = Alignment(vertical='center')
                    
                    # 水平居中处理
                    if col in center_columns:
                        alignment = Alignment(horizontal='center', vertical='center')
                    # 自动换行处理
                    elif col in wrap_columns:
                        alignment = Alignment(vertical='center', wrap_text=True)
                    
                    cell.alignment = alignment
        
        # 根据列名设置特定的列宽(使用字符单位)
        for col_name, col_index in col_name_to_index.items():
            column_letter = ws.cell(row=1, column=col_index).column_letter
            col_lower = str(col_name).lower()
            
            if any(keyword in col_lower for keyword in ['位号', 'position', 'pos']):
                # 位号的列宽 25字符
                ws.column_dimensions[column_letter].width = 25
            elif any(keyword in col_lower for keyword in ['厂家料号', 'part number', 'pn']):
                # 厂家料号的列宽 21字符
                ws.column_dimensions[column_letter].width = 21
            elif any(keyword in col_lower for keyword in ['制造商', 'manufacturer']):
                # 制造商的列宽 15字符
                ws.column_dimensions[column_letter].width = 15
            elif any(keyword in col_lower for keyword in ['说明', 'description']):
                # 说明的列宽 60字符
                ws.column_dimensions[column_letter].width = 60
            elif any(keyword in col_lower for keyword in ['数量', 'qty']):
                # 数量的列宽 10字符
                ws.column_dimensions[column_letter].width = 10
            else:
                # 其余列的宽度 16字符
                ws.column_dimensions[column_letter].width = 16
        
        # 添加全部框线
        thin_border = Border(left=Side(style='thin'), 
                           right=Side(style='thin'), 
                           top=Side(style='thin'), 
                           bottom=Side(style='thin'))
        
        # 为有效区域添加框线
        for row in range(1, ws.max_row + 1):
            for col in range(1, ws.max_column + 1):
                cell = ws.cell(row=row, column=col)
                cell.border = thin_border
        
        # 设置单页显示
        ws.page_setup.orientation = ws.ORIENTATION_LANDSCAPE  # 横向
        ws.page_setup.fitToWidth = 1  # 宽度适应一页
        ws.page_setup.fitToHeight = False  # 高度不限制
        ws.page_setup.paperSize = ws.PAPERSIZE_A4  # 使用A4纸
        
        # 设置打印区域为有效数据区域
        from openpyxl.utils import get_column_letter
        max_row = ws.max_row
        max_col = ws.max_column
        print_area = f'A1:{get_column_letter(max_col)}{max_row}'
        ws.print_area = print_area
        
        # 设置缩放比例,确保在一页内显示
        ws.sheet_view.zoomScale = 80  # 适当缩小以适应一页
        
        # 保存修改
        wb.save(filename)
        
        self.status_var.set(f"已导出: {os.path.basename(filename)}")
        messagebox.showinfo("成功", "Excel文件导出成功!")
    except Exception as e:
        messagebox.showerror("错误", f"导出失败: {str(e)}")
        self.status_var.set("导出失败")

五、界面展示

5.1 主界面
在这里插入图片描述

  • 菜单栏:文件、操作
  • 工具栏:导入、导出、删除空行、删除空贴物料、合并相同料号、重新排序号、排版功能
  • 搜索框:用于搜索料号
  • 数据显示区域:Treeview控件显示Excel数据
  • 状态栏:显示操作状态

5.2 操作详情窗口

  • 操作类型选择:删除空行、删除空贴物料、合并相同料号
  • 预览按钮:显示操作详情
  • 导出按钮:导出操作详情
  • 数据显示区域:显示详细操作内容

六、总结与展望

6.1 总结

BOM整理工具通过提供一系列自动化功能,大大简化了BOM的整理过程,提高了工作效率。工具支持多种BOM处理需求,包括数据清理、合并、排序和导出等,同时提供了友好的用户界面和详细的操作反馈。

  • 功能全面:覆盖了BOM整理的主要需求
  • 操作简单:直观的GUI界面
  • 灵活定制:支持多种导出格式要求
  • 数据安全:保留原始数据状态

6.2 未来展望

  • 支持更多Excel格式:扩展对不同版本Excel文件的支持
  • 增强分析功能:添加BOM分析、统计等功能
  • 批量处理:支持批量处理多个BOM文件
  • 系统集成:与其他系统(如ERP、PLM)的集成
  • 性能优化:处理更大规模的BOM文件

七、工具价值

  • 减少人工操作,提高工作效率
  • 确保BOM数据的准确性和一致性
  • 标准化BOM格式,便于系统集成
  • 降低出错率,减少生产风险

八、总结

BOM整理工具是一个实用、高效的解决方案,为电子行业的BOM管理提供了有力支持。通过不断的功能扩展和优化,工具可以更好地满足用户的需求,提高BOM处理的准确性和效率,为企业的生产和采购决策提供可靠的数据基础。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐