将不确定变为确定~.config文件,配置系统未能初始化

回到目录

在.config文件中,定义了一些节点,可能是connectionStrings,appSettings,configSections之后进行读取配置节点的信息,结果出现了异常,配置系统未能初始化

非常奇怪的问题,在网上找了一节文章,终于找到了答案,还是自己书写的问题,在MSDN上说的很清楚,你的configSections必须位于configuration节点的第一个元素,你不能将connectionStrings写在configSections上面

出错的代码:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="backgroundEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\sqlexpress;Initial Catalog=background;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>
  
  <configSections>
    <section name="WebConfigSection" type="MediaActionServer.WebConfigSection, MediaActionServer"/>
  </configSections>

  <WebConfigSection WebName="占占网站" DoMain="www.zhanzhan.com"  />
  <appSettings>
    <add key="site" value="www.zzl.com"/>

  </appSettings>
</configuration>

 

正确的代码:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="WebConfigSection" type="MediaActionServer.WebConfigSection, MediaActionServer"/>
  </configSections>
  <connectionStrings>
    <add name="backgroundEntities" connectionString="metadata=res://*/Model1.csdl|res://*/Model1.ssdl|res://*/Model1.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=.\sqlexpress;Initial Catalog=background;Integrated Security=True;MultipleActiveResultSets=True&quot;" providerName="System.Data.EntityClient" />
  </connectionStrings>

  <WebConfigSection WebName="占占网站" DoMain="www.zhanzhan.com"  />
  <appSettings>
    <add key="site" value="www.zzl.com"/>

  </appSettings>
</configuration>

测试代码:

   static void Main(string[] args)
    {
       Console.WriteLine(System.Configuration.ConfigurationManager.AppSettings["site"]);
       Console.WriteLine(WebConfigManager.Instance.DoMain);
       Console.WriteLine(WebConfigManager.Instance.WebName);
    }

 

建议:小微这方面作的不是很人性化,怎么读节点还和它的顺序有关呀,希望.net之后的版本可以解决这个问题。

 回到目录


<script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script>
import pandas as pd import tkinter as tk from tkinter import ttk, messagebox, filedialog import os import json import openpyxl from openpyxl.utils.dataframe import dataframe_to_rows from tkinter.font import Font import traceback import logging import sys import io import numpy as np from PIL import Image, ImageTk # 配置日志系统 logging.basicConfig( filename='app.log', level=logging.ERROR, format='%(asctime)s - %(levelname)s - %(message)s' ) logger = logging.getLogger() class ScrollableFrame(ttk.Frame): """自定义可滚动框架实现""" def __init__(self, parent, *args, **kwargs): super().__init__(parent, *args, **kwargs) # 创建Canvas和滚动条 self.canvas = tk.Canvas(self, highlightthickness=0) self.scrollbar = ttk.Scrollbar(self, orient="vertical", command=self.canvas.yview) self.scrollable_frame = ttk.Frame(self.canvas) # 配置Canvas self.canvas.configure(yscrollcommand=self.scrollbar.set) self.canvas_frame = self.canvas.create_window((0, 0), window=self.scrollable_frame, anchor="nw") # 布局 self.canvas.pack(side="left", fill="both", expand=True, padx=0, pady=0) self.scrollbar.pack(side="right", fill="y") # 绑定事件 self.scrollable_frame.bind("<Configure>", self.on_frame_configure) self.canvas.bind("<Configure>", self.on_canvas_configure) self.canvas.bind_all("<MouseWheel>", self.on_mousewheel) def on_frame_configure(self, event): """当内部框架大小改变时更新滚动区域""" self.canvas.configure(scrollregion=self.canvas.bbox("all")) def on_canvas_configure(self, event): """当Canvas大小改变时调整内部框架宽度""" self.canvas.itemconfig(self.canvas_frame, width=event.width) def on_mousewheel(self, event): """鼠标滚轮滚动支持""" self.canvas.yview_scroll(int(-1*(event.delta/120)), "units") class ToolTip: """自定义工具提示类""" def __init__(self, widget, text='', max_width=500): self.widget = widget self.text = text self.max_width = max_width self.tip_window = None self.id = None self.x = self.y = 0 def show_tip(self, text, x, y): """显示工具提示""" self.text = text if self.tip_window or not self.text: return # 计算屏幕位置 x = x + self.widget.winfo_rootx() + 20 y = y + self.widget.winfo_rooty() + 20 # 创建顶层窗口 self.tip_window = tk.Toplevel(self.widget) self.tip_window.wm_overrideredirect(True) self.tip_window.wm_geometry(f"+{x}+{y}") # 创建标签 label = tk.Label(self.tip_window, text=self.text, justify='left', background="#ffffe0", relief='solid', borderwidth=1, wraplength=self.max_width) label.pack(ipadx=1, ipady=1) def hide_tip(self): """隐藏工具提示""" if self.tip_window: self.tip_window.destroy() self.tip_window = None class ExcelControlPanel: def __init__(self, master): self.master = master master.title("功能点确认系统") master.geometry("1280x800") master.configure(bg="#f0f2f5") # 设置全局样式 self.set_styles() # 加载配置 self.config = self.load_config() # 初始化多列确认配置 self.confirmation_columns = self.config.get("confirmation_columns", []) self.column_order = self.config.get("column_order", []) # 创建界面元素 self.create_widgets() # 初始化数据 self.excel_path = "" self.df = None self.check_states = {} # 存储每个功能点的复选框状态 self.current_sheet = "" self.header_row = 0 self.full_descriptions = {} # 存储完整的功能点描述 self.tooltip = None # 工具提示实例 # 更新列选择器 self.update_col_selector() def set_styles(self): """设置全局样式和字体""" style = ttk.Style() style.theme_use('clam') # 自定义字体 self.title_font = Font(family="Microsoft YaHei", size=16, weight="bold") self.subtitle_font = Font(family="Microsoft YaHei", size=10) self.normal_font = Font(family="Microsoft YaHei", size=9) # 配置样式 style.configure("TFrame", background="#f0f2f5") style.configure("TLabel", font=self.normal_font, background="#f0f2f5", foreground="#333") style.configure("TButton", font=self.normal_font, padding=8) # Treeview样式 - 增加行高以容纳更多内容 style.configure("Treeview.Heading", font=self.subtitle_font, background="#4a76b5", foreground="white") style.configure("Treeview", font=self.normal_font, rowheight=50, background="white", fieldbackground="white") # 添加交替行背景色 style.configure("evenrow.Treeview", background="#f8f9fa") style.configure("oddrow.Treeview", background="white") # 状态栏样式 style.configure("Status.TFrame", background="#e0e0e0") style.configure("Status.TLabel", font=self.normal_font, background="#4a76b5", foreground="#ffffff", padding=5) # 卡片样式 style.configure("Card.TFrame", background="white", borderwidth=0, relief="solid", padding=10, bordercolor="#e1e4e8") style.configure("Card.TLabelframe", background="white", borderwidth=1, relief="solid", padding=10, bordercolor="#e1e4e8") style.configure("Card.TLabelframe.Label", font=self.subtitle_font, foreground="#2c3e50", background="white") # 按钮样式 style.map("Primary.TButton", background=[("active", "#3a66a5"), ("pressed", "#2a5685")], foreground=[("active", "white"), ("pressed", "white")]) style.configure("Primary.TButton", background="#4a76b5", foreground="white", font=self.subtitle_font, borderwidth=0) style.map("Success.TButton", background=[("active", "#28a745"), ("pressed", "#218838")], foreground=[("active", "white"), ("pressed", "white")]) style.configure("Success.TButton", background="#28a745", foreground="white", font=self.subtitle_font, borderwidth=0) style.map("Danger.TButton", background=[("active", "#dc3545"), ("pressed", "#c82333")], foreground=[("active", "white"), ("pressed", "white")]) style.configure("Danger.TButton", background="#dc3545", foreground="white", font=self.subtitle_font, borderwidth=0) # 输入框样式 style.configure("Custom.TEntry", fieldbackground="#f8f9fa", bordercolor="#ced4da") def load_config(self): """加载配置文件""" config_path = "excel_config.json" default_config = { "id_col": "No.", "desc_col": "レビュー観点(CHN)", "status_col": "レビュー結果", "sheet_name": "", "header_row": 9, "last_dir": os.getcwd(), "custom_status": "OK", "confirmation_columns": [], "column_order": ["selection", "id", "description", "status"], "max_desc_length": 300 # 新增:最大描述长度 } if os.path.exists(config_path): try: with open(config_path, 'r', encoding='utf-8') as f: config = json.load(f) # 确保所有键都存在 for key in default_config: if key not in config: config[key] = default_config[key] return config except Exception as e: logger.error(f"加载配置文件失败: {str(e)}") return default_config return default_config def save_config(self): """保存配置文件""" config_path = "excel_config.json" try: with open(config_path, 'w', encoding='utf-8') as f: json.dump(self.config, f, ensure_ascii=False, indent=2) except Exception as e: logger.error(f"保存配置文件失败: {str(e)}") def create_widgets(self): """创建现代化界面元素""" # 主容器 main_container = ttk.Frame(self.master, style="Card.TFrame") main_container.pack(fill="both", expand=True, padx=20, pady=20) # 标题栏 title_frame = ttk.Frame(main_container, style="Title.TFrame") title_frame.pack(fill="x", pady=(0, 2)) ttk.Label(title_frame, text="功能点确认系统", font=self.title_font, background="#4a76b5", foreground="white", padding=2).pack(side="left", fill="x", expand=True) # 主内容区域 content_frame = ttk.Frame(main_container) content_frame.pack(fill="both", expand=True) # 左侧控制面板 - 可滚动 control_container = ttk.Frame(content_frame, width=350) control_container.pack(side="left", fill="y", padx=(0, 20)) # 创建自定义可滚动框架 scrollable_frame = ScrollableFrame(control_container, width=350) scrollable_frame.pack(fill="both", expand=True) # 获取内部框架 inner_frame = scrollable_frame.scrollable_frame # 美化控制面板 control_card = ttk.LabelFrame(inner_frame, text="控制面板", style="Card.TLabelframe") control_card.pack(fill="both", expand=True, padx=10, pady=10) # 文件选择区域 file_frame = ttk.LabelFrame(control_card, text="Excel文件设置") file_frame.pack(fill="x", padx=10, pady=(0, 15)) ttk.Label(file_frame, text="Excel文件路径:").pack(anchor="w", pady=(0, 5), padx=10) path_frame = ttk.Frame(file_frame) path_frame.pack(fill="x", pady=5, padx=10) self.path_entry = ttk.Entry(path_frame, width=30, style="Custom.TEntry") self.path_entry.pack(side="left", fill="x", expand=True, padx=(0, 5)) ttk.Button(path_frame, text="浏览", command=self.browse_file, width=8).pack(side="left") ttk.Button(file_frame, text="加载数据", command=self.load_data, style="Primary.TButton").pack(fill="x", pady=10, padx=10) # Sheet选择区域 sheet_frame = ttk.LabelFrame(control_card, text="工作表设置") sheet_frame.pack(fill="x", padx=10, pady=(0, 15)) ttk.Label(sheet_frame, text="当前Sheet:").pack(anchor="w", padx=10, pady=(10, 0)) self.sheet_var = tk.StringVar(value=self.config.get("sheet_name", "未选择")) sheet_display = ttk.Label(sheet_frame, textvariable=self.sheet_var, font=self.subtitle_font) sheet_display.pack(anchor="w", pady=(0, 10), padx=10) ttk.Button(sheet_frame, text="选择工作表", command=self.select_sheet, style="Primary.TButton").pack(fill="x", padx=10, pady=(0, 10)) # 表头行设置 header_frame = ttk.LabelFrame(control_card, text="表头设置") header_frame.pack(fill="x", padx=10, pady=(0, 15)) ttk.Label(header_frame, text="表头所在行号:").pack(anchor="w", padx=10, pady=(10, 0)) self.header_var = tk.IntVar(value=self.config.get("header_row", 9)) ttk.Entry(header_frame, textvariable=self.header_var, width=10, style="Custom.TEntry").pack(anchor="w", pady=5, padx=10) # 列名配置区域 col_frame = ttk.LabelFrame(control_card, text="列名配置") col_frame.pack(fill="x", padx=10, pady=(0, 15)) # ID列 ttk.Label(col_frame, text="ID列名:").pack(anchor="w", padx=10, pady=(10, 0)) self.id_col_var = tk.StringVar(value=self.config["id_col"]) ttk.Entry(col_frame, textvariable=self.id_col_var, style="Custom.TEntry").pack(fill="x", pady=(0, 10), padx=10) # 功能点列 ttk.Label(col_frame, text="功能点列名:").pack(anchor="w", padx=10, pady=(0, 0)) self.desc_col_var = tk.StringVar(value=self.config["desc_col"]) ttk.Entry(col_frame, textvariable=self.desc_col_var, style="Custom.TEntry").pack(fill="x", pady=(0, 10), padx=10) # 状态列 ttk.Label(col_frame, text="状态列名:").pack(anchor="w", padx=10, pady=(0, 0)) self.status_col_var = tk.StringVar(value=self.config["status_col"]) ttk.Entry(col_frame, textvariable=self.status_col_var, style="Custom.TEntry").pack(fill="x", pady=(0, 10), padx=10) # 自定义状态 ttk.Label(col_frame, text="自定义确认状态:").pack(anchor="w", padx=10, pady=(0, 0)) self.custom_status_var = tk.StringVar(value=self.config.get("custom_status", "OK")) ttk.Entry(col_frame, textvariable=self.custom_status_var, style="Custom.TEntry").pack(fill="x", pady=(0, 10), padx=10) # 描述长度设置 ttk.Label(col_frame, text="最大描述长度:").pack(anchor="w", padx=10, pady=(0, 0)) self.max_desc_var = tk.IntVar(value=self.config.get("max_desc_length", 300)) ttk.Entry(col_frame, textvariable=self.max_desc_var, style="Custom.TEntry").pack(fill="x", pady=(0, 10), padx=10) # 多列确认设置 multi_col_frame = ttk.LabelFrame(control_card, text="多列确认设置") multi_col_frame.pack(fill="x", padx=10, pady=(0, 15)) # 列选择器 ttk.Label(multi_col_frame, text="选择要确认的列:").pack(anchor="w", padx=10, pady=(10, 0)) col_selector_frame = ttk.Frame(multi_col_frame) col_selector_frame.pack(fill="x", pady=5, padx=10) self.col_selector = ttk.Combobox(col_selector_frame, state="readonly", width=15) self.col_selector.pack(side="left", fill="x", expand=True, padx=(0, 5)) # 添加/移除按钮 ttk.Button(col_selector_frame, text="添加", command=self.add_confirmation_column, width=8).pack(side="left") # 已选列列表 ttk.Label(multi_col_frame, text="已选确认列:").pack(anchor="w", padx=10, pady=(10, 0)) self.selected_cols_listbox = tk.Listbox(multi_col_frame, height=3, font=self.normal_font, bg="#f8f9fa", highlightthickness=0) self.selected_cols_listbox.pack(fill="x", pady=5, padx=10) # 加载已配置的确认列 for col in self.confirmation_columns: self.selected_cols_listbox.insert(tk.END, col) # 移除按钮 remove_btn = ttk.Button(multi_col_frame, text="移除选中列", command=self.remove_confirmation_column) remove_btn.pack(fill="x", pady=(0, 10), padx=10) # 操作按钮区域 btn_frame = ttk.Frame(control_card) btn_frame.pack(fill="x", padx=5, pady=10) ttk.Button(btn_frame, text="保存配置", command=self.save_current_config, style="Success.TButton").pack(fill="x", pady=5) ttk.Button(btn_frame, text="确认选中项", command=self.confirm_selected, style="Primary.TButton").pack(fill="x", pady=5) ttk.Button(btn_frame, text="全选", command=self.select_all, style="Primary.TButton").pack(side="left", fill="x", expand=True, pady=5) ttk.Button(btn_frame, text="取消全选", command=self.deselect_all, style="Danger.TButton").pack(side="left", fill="x", expand=True, pady=5) ttk.Button(btn_frame, text="保存到Excel", command=self.save_to_excel, style="Success.TButton").pack(fill="x", pady=5) # 数据显示区域 data_card = ttk.LabelFrame(content_frame, text="功能点列表", style="Card.TLabelframe") data_card.pack(side="right", fill="both", expand=True) # Treeview容器 tree_frame = ttk.Frame(data_card) tree_frame.pack(fill="both", expand=True, padx=5, pady=5) # 创建Treeview self.tree = ttk.Treeview(tree_frame, columns=[], show="headings", height=20) # 滚动条设置 vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview) hsb = ttk.Scrollbar(tree_frame, orient="horizontal", command=self.tree.xview) self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set) # 使用grid布局管理器 self.tree.grid(row=0, column=0, sticky="nsew") vsb.grid(row=0, column=1, sticky="ns") hsb.grid(row=1, column=0, sticky="ew") # 配置网格权重 tree_frame.columnconfigure(0, weight=1) tree_frame.rowconfigure(0, weight=1) # 添加标签样式 - 使用交替行背景色 self.tree.tag_configure("evenrow", background="#f8f9fa") self.tree.tag_configure("oddrow", background="white") self.tree.tag_configure("confirmed", background="#d4edda") self.tree.tag_configure("pending", background="#f8d7da") # 创建工具提示 (先创建工具提示再绑定事件) self.tooltip = ToolTip(self.tree) # 绑定列调整事件 self.tree.bind("<Configure>", self.on_tree_configure) # 绑定点击事件处理复选框 self.tree.bind("<Button-1>", self.on_tree_click) # 绑定选择事件 self.tree.bind("<<TreeviewSelect>>", self.on_tree_select) # 绑定鼠标移动事件用于显示工具提示 self.tree.bind("<Motion>", self.on_tree_motion) self.tree.bind("<Leave>", self.on_tree_leave) # 详情区域 detail_frame = ttk.LabelFrame(data_card, text="功能点详情", style="Card.TLabelframe") detail_frame.pack(fill="x", padx=5, pady=(0, 5)) self.detail_text = tk.Text(detail_frame, wrap="word", font=self.normal_font, height=3, bg="#f8f9fa", relief="solid", borderwidth=1) scroll_detail = ttk.Scrollbar(detail_frame, command=self.detail_text.yview) self.detail_text.config(yscrollcommand=scroll_detail.set) # 使用grid布局文本框和滚动条 self.detail_text.grid(row=0, column=0, sticky="nsew", padx=5, pady=5) scroll_detail.grid(row=0, column=1, sticky="ns", pady=5) # 配置网格权重 detail_frame.columnconfigure(0, weight=1) detail_frame.rowconfigure(0, weight=1) self.detail_text.config(state="disabled") # 状态栏 self.status_var = tk.StringVar() self.status_var.set("就绪 - 请选择Excel文件开始") # 创建状态栏容器框架 status_container = ttk.Frame(self.master, style="Status.TFrame") status_container.pack(side="bottom", fill="x", padx=0, pady=0) # 状态标签 status_bar = ttk.Label( status_container, textvariable=self.status_var, style="Status.TLabel", anchor="w", padding=(10, 5, 10, 5) ) status_bar.pack(side="bottom", fill="x", expand=False) # 初始化Treeview列 self.update_treeview_columns() def update_treeview_columns(self): """更新Treeview列""" # 基础列 base_columns = ["selection", "id", "description", "status"] # 所有列 = 基础列 + 确认列 all_columns = base_columns + self.confirmation_columns # 如果列顺序未设置或长度不匹配,则重置 if not self.column_order or len(self.column_order) != len(all_columns): self.column_order = all_columns # 重新配置Treeview self.tree.configure(columns=self.column_order) # 设置列标题 column_titles = { "selection": "选择", "id": "ID", "description": "功能点", "status": "状态" } for col in self.column_order: if col in column_titles: self.tree.heading(col, text=column_titles[col]) else: self.tree.heading(col, text=col) # 设置列属性 if col == "selection": self.tree.column(col, width=50, anchor="center") elif col == "id": self.tree.column(col, width=100, anchor="center") elif col == "description": self.tree.column(col, width=400, anchor="w", stretch=True) elif col == "status": self.tree.column(col, width=100, anchor="center") else: # 确认列 self.tree.column(col, width=100, anchor="center") # 调整列宽 self.adjust_columns() def on_tree_configure(self, event): """Treeview大小改变时调整列宽""" self.adjust_columns() def on_tree_select(self, event): """当Treeview中选中行时,显示功能点详情""" selected_items = self.tree.selection() if not selected_items: return item = selected_items[0] # 获取完整功能点描述 full_desc = self.full_descriptions.get(item, "无描述") self.detail_text.config(state="normal") self.detail_text.delete(1.0, tk.END) self.detail_text.insert(tk.END, full_desc) self.detail_text.config(state="disabled") def on_tree_motion(self, event): """当鼠标在Treeview上移动时,显示工具提示""" # 先检查工具提示是否已初始化 if not hasattr(self, 'tooltip') or self.tooltip is None: return region = self.tree.identify("region", event.x, event.y) if region == "cell": column = self.tree.identify_column(event.x) item = self.tree.identify_row(event.y) if not item: return # 获取列索引 col_idx = int(column[1:]) - 1 columns = self.tree["columns"] # 确保索引有效 if col_idx >= len(columns): return # 获取列标识符 col_id = columns[col_idx] # 只处理"功能点"列 if self.tree.heading(col_id, "text") == "功能点": # 获取完整描述 full_desc = self.full_descriptions.get(item, "") # 如果描述很长,显示工具提示 if full_desc and len(full_desc) > self.config.get("max_desc_length", 300): self.tooltip.show_tip(full_desc, event.x, event.y) def on_tree_leave(self, event): """当鼠标离开Treeview时,隐藏工具提示""" # 先检查工具提示是否已初始化 if not hasattr(self, 'tooltip') or self.tooltip is None: return self.tooltip.hide_tip() def on_tree_click(self, event): """处理Treeview点击事件,切换复选框状态""" region = self.tree.identify("region", event.x, event.y) if region == "cell": column = self.tree.identify_column(event.x) item = self.tree.identify_row(event.y) if not item: return # 获取列索引 col_idx = int(column[1:]) - 1 columns = self.tree["columns"] # 确保索引有效 if col_idx >= len(columns): return # 获取列标识符 col_id = columns[col_idx] # 只处理"选择"列 if self.tree.heading(col_id, "text") == "选择": # 获取当前状态 values = list(self.tree.item(item, "values")) current_state = values[col_idx] # 切换状态 if current_state == "☐": new_state = "☑" else: new_state = "☐" # 更新Treeview values[col_idx] = new_state self.tree.item(item, values=values) # 更新状态存储 self.check_states[item] = (new_state == "☑") # 更新行的整体状态显示 self.update_row_status(item) def update_row_status(self, item_id): """根据复选框状态更新行状态显示""" values = list(self.tree.item(item_id, "values")) is_checked = self.check_states.get(item_id, False) # 找到状态列的位置 status_col_idx = None for idx, col_id in enumerate(self.tree["columns"]): if self.tree.heading(col_id, "text") == "状态": status_col_idx = idx break if status_col_idx is not None and status_col_idx < len(values): if is_checked: values[status_col_idx] = "✓ 待确认" else: values[status_col_idx] = "✗ 未确认" self.tree.item(item_id, values=values) def adjust_columns(self, event=None): """根据窗口大小自动调整列宽""" if not self.tree.winfo_exists(): return width = self.tree.winfo_width() if width < 100: # 防止宽度过小 return # 计算可用宽度 available_width = width - 20 # 减去滚动条宽度 # 设置列宽比例 - 增加功能点列的比例 column_weights = { "selection": 0.05, "id": 0.10, "description": 0.65, "status": 0.08 } # 设置基础列宽 for col, weight in column_weights.items(): if col in self.tree["columns"]: col_width = int(available_width * weight) self.tree.column(col, width=col_width) # 设置确认列宽 if self.confirmation_columns: confirm_col_width = int(available_width * 0.06) # 减小确认列宽度 for col in self.confirmation_columns: if col in self.tree["columns"]: self.tree.column(col, width=confirm_col_width) def update_col_selector(self): """更新列选择器""" if self.df is not None: self.col_selector["values"] = list(self.df.columns) def add_confirmation_column(self): """添加确认列""" col = self.col_selector.get() if col and col not in self.confirmation_columns: self.confirmation_columns.append(col) self.selected_cols_listbox.insert(tk.END, col) self.update_treeview_columns() def remove_confirmation_column(self): """移除确认列""" selection = self.selected_cols_listbox.curselection() if selection: index = selection[0] self.confirmation_columns.pop(index) self.selected_cols_listbox.delete(index) self.update_treeview_columns() def browse_file(self): initial_dir = self.config.get("last_dir", os.getcwd()) file_path = filedialog.askopenfilename( initialdir=initial_dir, filetypes=[("Excel文件", "*.xlsx;*.xls")] ) if file_path: self.path_entry.delete(0, tk.END) self.path_entry.insert(0, file_path) # 更新最后访问目录 self.config["last_dir"] = os.path.dirname(file_path) self.save_config() def select_sheet(self): """选择工作表""" file_path = self.path_entry.get() if not file_path or not os.path.exists(file_path): messagebox.showerror("错误", "请先选择有效的Excel文件") return try: # 获取所有sheet名称 xl = pd.ExcelFile(file_path) sheet_names = xl.sheet_names # 创建选择对话框 sheet_dialog = tk.Toplevel(self.master) sheet_dialog.title("选择工作表") sheet_dialog.geometry("400x300") sheet_dialog.transient(self.master) sheet_dialog.grab_set() sheet_dialog.configure(bg="#f5f7fa") ttk.Label(sheet_dialog, text="请选择工作表:", font=self.subtitle_font, background="#f5f7fa").pack(pady=10) # 使用Treeview显示工作表 sheet_tree = ttk.Treeview(sheet_dialog, columns=("名称",), show="headings", height=8) sheet_tree.heading("名称", text="工作表名称") sheet_tree.column("名称", width=350) sheet_tree.pack(fill="both", expand=True, padx=20, pady=5) for name in sheet_names: sheet_tree.insert("", "end", values=(name,)) # 按钮框架 btn_frame = ttk.Frame(sheet_dialog) btn_frame.pack(fill="x", padx=20, pady=10) def on_select(): selected = sheet_tree.selection() if selected: self.current_sheet = sheet_tree.item(selected[0], "values")[0] self.sheet_var.set(self.current_sheet) # 保存工作表名称到配置 self.config["sheet_name"] = self.current_sheet self.save_config() sheet_dialog.destroy() ttk.Button(btn_frame, text="取消", command=sheet_dialog.destroy).pack(side="right", padx=5) ttk.Button(btn_frame, text="确定", command=on_select, style="Primary.TButton").pack(side="right") except Exception as e: messagebox.showerror("错误", f"读取Excel失败: {str(e)}") logger.error(f"选择工作表失败: {str(e)}") def load_data(self): file_path = self.path_entry.get() if not file_path or not os.path.exists(file_path): messagebox.showerror("错误", "无效的文件路径") return # 在状态栏显示加载中 self.status_var.set("正在加载数据...") self.master.update() # 强制更新界面 # 清空Treeview for item in self.tree.get_children(): self.tree.delete(item) self.check_states = {} self.full_descriptions = {} # 获取当前配置 id_col = self.id_col_var.get().strip() desc_col = self.desc_col_var.get().strip() status_col = self.status_col_var.get().strip() sheet_name = self.sheet_var.get() or None header_row = self.header_var.get() - 1 # pandas header是0-based索引 max_desc_length = self.max_desc_var.get() # 最大描述长度 # 直接调用加载任务 self.load_task(file_path, id_col, desc_col, status_col, sheet_name, header_row, max_desc_length) def load_task(self, file_path, id_col, desc_col, status_col, sheet_name, header_row, max_desc_length): try: # 确保使用正确的sheet_name if not sheet_name and self.config.get("sheet_name"): sheet_name = self.config["sheet_name"] # 从配置获取工作表名 # 读取Excel文件 if sheet_name: self.df = pd.read_excel( file_path, sheet_name=sheet_name, header=header_row ) else: # 如果没有指定sheet,尝试读取第一个sheet self.df = pd.read_excel( file_path, header=header_row ) # 尝试获取第一个sheet的名称 xl = pd.ExcelFile(file_path) if xl.sheet_names: self.current_sheet = xl.sheet_names[0] self.sheet_var.set(self.current_sheet) self.excel_path = file_path # 检查列是否存在 missing_cols = [] if id_col not in self.df.columns: missing_cols.append(f"ID列 '{id_col}'") if desc_col not in self.df.columns: missing_cols.append(f"功能点列 '{desc_col}'") if missing_cols: # 提供更详细的错误信息 available_cols = "\n".join(self.df.columns) error_msg = ( f"以下列不存在: {', '.join(missing_cols)}\n\n" f"可用列名:\n{available_cols}\n\n" "请检查表头行设置是否正确(默认为第9行)" ) raise ValueError(error_msg) # 如果状态列不存在,则创建 if status_col not in self.df.columns: self.df[status_col] = "否" # 默认未确认 # 更新列选择器 self.update_col_selector() # 更新Treeview列 self.update_treeview_columns() # 修复ID列显示问题 - 更健壮的处理 if id_col in self.df.columns: # 处理所有可能的缺失值 self.df[id_col] = self.df[id_col].fillna("") # 处理浮点数和整数类型 if self.df[id_col].dtype == float: # 将浮点数转换为整数再转字符串 # 注意:NaN值在转换前已经被fillna("")处理 self.df[id_col] = self.df[id_col].apply( lambda x: str(int(x)) if x != "" and not pd.isna(x) else x ) elif self.df[id_col].dtype == int: self.df[id_col] = self.df[id_col].astype(str) else: # 确保是字符串类型 self.df[id_col] = self.df[id_col].astype(str) # 替换可能的NaN字符串和空字符串 self.df[id_col] = self.df[id_col].replace(['nan', 'NaN', 'NaT', ''], '缺失ID') # 调试日志:记录ID列处理后的值 logger.debug(f"ID列处理后的值: {self.df[id_col].head()}") # 处理描述列显示问题 if desc_col in self.df.columns: # 填充缺失值 self.df[desc_col] = self.df[desc_col].fillna("无描述") # 确保是字符串类型 self.df[desc_col] = self.df[desc_col].astype(str) # 添加换行符使长文本自动换行 max_length = 100 # 每100个字符换行 self.df[desc_col] = self.df[desc_col].apply( lambda x: '\n'.join([x[i:i+max_length] for i in range(0, len(x), max_length)])) # 添加数据到Treeview for i, row in self.df.iterrows(): status_value = row.get(status_col, "否") # 使用图标表示状态 status_icon = "✓" if status_value in ["是", "Y", "y", "Yes", "yes", "OK", "确认"] else "✗" status_text = f"{status_icon} {status_value}" # 获取ID值 id_value = str(row[id_col]) if id_col in row else "缺失ID" # 获取完整描述 full_desc = row[desc_col] if desc_col in row else "无描述" # 创建显示描述(截断过长的描述) display_desc = full_desc if len(full_desc) > max_desc_length: display_desc = full_desc[:max_desc_length] + "..." # 添加省略号 # 构建行数据字典 row_data = { "selection": "☐", "id": id_value, "description": display_desc, "status": status_text } # 添加多列确认数据 for col in self.confirmation_columns: row_data[col] = row[col] if col in row else "" # 按列顺序构建值列表 ordered_values = [row_data.get(col, "") for col in self.column_order] # 确定行样式标签 - 交替行背景色 row_tag = "evenrow" if i % 2 == 0 else "oddrow" status_tag = "confirmed" if status_icon == "✓" else "pending" tags = (row_tag, status_tag) # 插入行 item_id = self.tree.insert("", "end", values=ordered_values, tags=tags) # 存储完整描述 self.full_descriptions[item_id] = full_desc # 存储复选框状态 self.check_states[item_id] = (status_icon == "✓") # 每100行更新一次界面 if i % 100 == 0: self.tree.update() self.master.update_idletasks() # 更新状态 self.status_var.set(f"成功加载: {len(self.df)} 条记录") # 调整列宽并刷新界面 self.adjust_columns() except Exception as e: # 显示详细的错误信息 error_msg = f"读取Excel失败: {str(e)}" self.status_var.set("加载失败") messagebox.showerror("加载错误", error_msg) logger.error(f"加载数据失败: {error_msg}\n{traceback.format_exc()}") def confirm_selected(self): """确认选中的功能点""" selected_items = [] for item_id in self.tree.get_children(): if self.check_states.get(item_id, False): selected_items.append(item_id) if not selected_items: messagebox.showinfo("提示", "请先选择功能点") return custom_status = self.custom_status_var.get().strip() or "OK" for item_id in selected_items: values = list(self.tree.item(item_id, "values")) # 找到状态列的位置 status_col_idx = None for idx, col_id in enumerate(self.tree["columns"]): if self.tree.heading(col_id, "text") == "状态": status_col_idx = idx break # 更新状态列 if status_col_idx is not None and status_col_idx < len(values): values[status_col_idx] = f"✓ {custom_status}" # 更新多列确认 if self.confirmation_columns and self.df is not None: row_idx = self.tree.index(item_id) for col in self.confirmation_columns: if col in self.df.columns: # 找到列在Treeview中的位置 col_idx = self.column_order.index(col) if col_idx < len(values): values[col_idx] = custom_status self.df.at[row_idx, col] = custom_status # 更新Treeview self.tree.item(item_id, values=values) self.status_var.set(f"已确认 {len(selected_items)} 个功能点") # 自动保存 self.auto_save() def select_all(self): """全选功能点""" for item_id in self.tree.get_children(): values = list(self.tree.item(item_id, "values")) # 找到选择列的位置 select_col_idx = None for idx, col_id in enumerate(self.tree["columns"]): if self.tree.heading(col_id, "text") == "选择": select_col_idx = idx break # 更新选择列 if select_col_idx is not None and select_col_idx < len(values): values[select_col_idx] = "☑" self.tree.item(item_id, values=values) self.check_states[item_id] = True self.update_row_status(item_id) self.status_var.set("已全选所有功能点") def deselect_all(self): """取消全选功能点""" for item_id in self.tree.get_children(): values = list(self.tree.item(item_id, "values")) # 找到选择列的位置 select_col_idx = None for idx, col_id in enumerate(self.tree["columns"]): if self.tree.heading(col_id, "text") == "选择": select_col_idx = idx break # 更新选择列 if select_col_idx is not None and select_col_idx < len(values): values[select_col_idx] = "☐" self.tree.item(item_id, values=values) self.check_states[item_id] = False self.update_row_status(item_id) self.status_var.set("已取消全选所有功能点") def save_current_config(self): """保存当前配置""" self.config["id_col"] = self.id_col_var.get().strip() self.config["desc_col"] = self.desc_col_var.get().strip() self.config["status_col"] = self.status_col_var.get().strip() self.config["sheet_name"] = self.sheet_var.get() self.config["header_row"] = self.header_var.get() self.config["custom_status"] = self.custom_status_var.get().strip() self.config["confirmation_columns"] = self.confirmation_columns self.config["column_order"] = self.column_order self.config["max_desc_length"] = self.max_desc_var.get() # 保存最大描述长度 self.save_config() messagebox.showinfo("成功", "配置已保存") def auto_save(self): """自动保存功能""" if self.df is None or not self.excel_path: return try: # 获取当前配置 id_col = self.id_col_var.get().strip() desc_col = self.desc_col_var.get().strip() status_col = self.status_col_var.get().strip() # 更新DataFrame中的确认状态 for i, item_id in enumerate(self.tree.get_children()): # 获取Treeview中的状态值 values = self.tree.item(item_id, "values") # 找到状态列的位置 status_col_idx = None for idx, col_id in enumerate(self.tree["columns"]): if self.tree.heading(col_id, "text") == "状态": status_col_idx = idx break if status_col_idx is not None and status_col_idx < len(values): status_value = values[status_col_idx] if status_value.startswith(("✓", "✗")): status_value = status_value[2:].strip() self.df.at[i, status_col] = status_value # 保存回Excel wb = openpyxl.load_workbook(self.excel_path) if self.current_sheet in wb.sheetnames: del wb[self.current_sheet] ws = wb.create_sheet(self.current_sheet) # 写入数据 for r, row in enumerate(dataframe_to_rows(self.df, index=False, header=True), 1): ws.append(row) wb.save(self.excel_path) self.status_var.set("数据已自动保存") except Exception as e: self.status_var.set("自动保存失败") logger.error(f"自动保存失败: {str(e)}") def save_to_excel(self): if self.df is None: messagebox.showerror("错误", "没有加载的数据") return try: # 执行保存 self.auto_save() messagebox.showinfo("成功", f"数据已保存到:\n{self.excel_path}") self.status_var.set("数据保存成功") except Exception as e: messagebox.showerror("保存错误", f"写入Excel失败: {str(e)}\n请确保文件未被其他程序打开") logger.error(f"保存失败: {str(e)}") def main(): root = tk.Tk() # 添加全局异常处理 def handle_exception(exc_type, exc_value, exc_traceback): error_msg = f"发生未捕获的异常:\n{exc_type.__name__}: {exc_value}" tb_str = "".join(traceback.format_tb(exc_traceback)) logger.error(f"未捕获的异常: {error_msg}\n{tb_str}") messagebox.showerror("严重错误", f"{error_msg}\n\n请查看日志获取详细信息") root.destroy() sys.excepthook = handle_exception app = ExcelControlPanel(root) root.mainloop() if __name__ == "__main__": # 强制使用UTF-8编码 sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8') main() 显示缺失ID,请详细调试
最新发布
08-20
请给出具体且详细的步骤,以下是我的代码:# train.py (完整最终版 - 完全切换到在线数据增强) import os import torch import logging import json import copy # --- Detectron2 Imports --- import detectron2.utils.comm as comm from detectron2 import model_zoo from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, launch from detectron2.config import get_cfg from detectron2.data import MetadataCatalog, DatasetCatalog, build_detection_train_loader from detectron2.data.datasets import register_coco_instances from detectron2.evaluation import COCOEvaluator from detectron2.projects import point_rend # --- [新增] 导入数据增强和工具模块 --- from detectron2.data import detection_utils as utils import detectron2.data.transforms as T # --- 用于格式化输出的辅助函数 (无变化) --- def print_section_header(title): """打印分节标题""" print("\n" + "="*10 + f" {title} " + "="*10) def print_script_header(title): """打印脚本顶部标题""" print("="*50) print(f"{title:^50}") print("="*50 + "\n") def print_script_footer(title): """打印脚本底部脚注""" print("\n" + "="*50) print(f"{title:^50}") print("="*50) # --- [新增] 自定义数据加载器 (在线数据增强核心) --- def custom_mapper(dataset_dict): """ 自定义数据加载和增强函数 (Mapper)。 在每次读取图像时,实时、随机地应用一系列数据增强,以提高模型的鲁棒性。 """ # 必须深拷贝,否则会修改原始的全局数据集字典 dataset_dict = copy.deepcopy(dataset_dict) # 1.文件读取原始图像 image = utils.read_image(dataset_dict["file_name"], format="BGR") # 2. 定义一系列强大的在线数据增强操作 # 这些操作会以随机参数实时应用到每张图像上 augs = T.AugmentationList([ # --- 颜色和光照增强 (模拟不同天气和光照条件) --- T.RandomBrightness(0.8, 1.2), T.RandomContrast(0.8, 1.2), T.RandomSaturation(0.8, 1.2), # --- 几何变换 --- T.RandomFlip(prob=0.5, horizontal=True, vertical=False), # 水平翻转 # --- 模拟图像质量下降和干扰 --- # 随机应用高斯模糊 (模拟失焦或运动模糊) T.RandomApply(T.GaussianBlur(sigma=(0.2, 1.5)), prob=0.4), # 随机应用随机擦除 (模拟部分遮挡) T.RandomApply(T.RandomErasing(scale=(0.02, 0.1), ratio=(0.3, 3.3)), prob=0.5), # --- 尺度变换 (多尺度训练) --- T.ResizeShortestEdge( short_edge_length=cfg.INPUT.MIN_SIZE_TRAIN, # 从配置中获取尺度范围 max_size=cfg.INPUT.MAX_SIZE_TRAIN, sample_style=cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING # 'choice' 或 'range' ) ]) # 3. 应用增强 aug_input = T.AugInput(image) transforms = augs(aug_input) image_transformed = aug_input.image # 4. 转换图像格式以适应模型输入 dataset_dict["image"] = torch.as_tensor(image_transformed.transpose(2, 0, 1).astype("float32")) # 5. 对标注 (边界框、掩码) 应用相同的几何变换 annos = [ utils.transform_instance_annotations(obj, transforms, image_transformed.shape[:2]) for obj in dataset_dict.pop("annotations") if obj.get("iscrowd", 0) == 0 ] instances = utils.annotations_to_instances(annos, image_transformed.shape[:2], mask_format="bitmask") # 6. 过滤掉增强后可能变为空的实例 dataset_dict["instances"] = utils.filter_empty_instances(instances) return dataset_dict # --- 1. 数据集注册 (无变化) --- def setup_datasets(dataset_root="dataset"): # ... (这部分代码与你之前提供的版本完全相同,此处省略) ... """ 注册所有数据集,并返回训练集的类别名称列表。 """ print_section_header("数据集注册与类别名称提取") class_names = None if not os.path.isdir(dataset_root): print(f"[错误] 基础数据集目录 '{dataset_root}' 未找到。无法继续进行数据集注册。") return None for subset in ["train", "valid", "test"]: print(f"[信息] 尝试注册数据集: 'cable_{subset}'") json_file = os.path.join(dataset_root, subset, "_annotations.coco.json") image_root = os.path.join(dataset_root, subset) if not os.path.exists(json_file): print(f"[警告] 未找到 'cable_{subset}' 的标注文件: {json_file},跳过注册。") if subset == "train": print("[错误] 训练集标注文件缺失,无法提取类别名称。") return None continue if not os.path.isdir(image_root): print(f"[警告] 未找到 'cable_{subset}' 的图像目录: {image_root},跳过注册。") continue try: register_coco_instances( name=f"cable_{subset}", metadata={}, json_file=json_file, image_root=image_root ) print(f"[成功] 已注册 'cable_{subset}'") except Exception as e: print(f"[错误] 注册 'cable_{subset}' 失败: {e}") if subset == "train": return None continue print("[信息] 尝试从 'cable_train' 提取类别名称...") train_json_path = os.path.join(dataset_root, "train", "_annotations.coco.json") if not os.path.exists(train_json_path): print(f"[错误] 训练集标注文件 '{train_json_path}' 未找到。") return None try: with open(train_json_path, 'r', encoding='utf-8') as f: coco_json_data = json.load(f) if 'categories' in coco_json_data and coco_json_data['categories']: class_names = [cat['name'] for cat in coco_json_data['categories']] MetadataCatalog.get("cable_train").thing_classes = class_names # 手动为 valid 和 test 也设置元数据,确保评估时一致 if "cable_valid" in DatasetCatalog.list(): MetadataCatalog.get("cable_valid").thing_classes = class_names if "cable_test" in DatasetCatalog.list(): MetadataCatalog.get("cable_test").thing_classes = class_names print(f"[成功] 直接从JSON提取的类别名称: {class_names}") else: print("[错误] 训练JSON中未找到或为空的 'categories' 字段。") return None except Exception as e: print(f"[错误] 类别名称提取过程中发生错误: {e}") return None if class_names: print(f"[信息] 最终派生的类别名称: {class_names}。类别数量: {len(class_names)}") return class_names # --- 2. 自定义评估器Trainer (核心修改) --- class CocoTrainer(DefaultTrainer): @classmethod def build_evaluator(cls, cfg, dataset_name, output_folder=None): if output_folder is None: output_folder = os.path.join(cfg.OUTPUT_DIR, "inference") print(f"[信息] 为数据集 '{dataset_name}' 构建 COCOEvaluator, 输出到 '{output_folder}'") return COCOEvaluator(dataset_name, cfg, True, output_folder) @classmethod def build_train_loader(cls, cfg): """ [核心修改] 重写此方法以使用我们自定义的 `custom_mapper`。 这样,训练数据加载器就会在运行时进行在线数据增强。 """ print("[信息] 使用自定义的 `custom_mapper` 进行在线数据增强。") return build_detection_train_loader(cfg, mapper=custom_mapper) # --- 3. 主训练函数 --- def main(args): print_script_header("Detectron2 PointRend 训练流程开始 (在线增强模式)") class_names = setup_datasets(dataset_root="dataset") if not class_names: print("[严重] 未能获取到有效的类别列表。终止训练。") return num_classes = len(class_names) print(f"[成功] 数据集注册完成。共找到 {num_classes} 个类别: {class_names}") print_section_header("模型配置") global cfg # [新增] 将 cfg 设为全局变量,以便 custom_mapper 可以访问 cfg = get_cfg() config_file_local_path = os.path.join("detectron2-main", "projects", "PointRend", "configs", "InstanceSegmentation", "implicit_pointrend_R_50_FPN_3x_coco.yaml") print(f"[配置] 从本地文件加载基础配置: '{config_file_local_path}'") if not os.path.exists(config_file_local_path): print(f"[错误] 配置文件未找到: '{config_file_local_path}'") return cfg.set_new_allowed(True) cfg.merge_from_file(config_file_local_path) # --- [新增] 配置在线数据增强相关的参数 --- print("[配置] 设置多尺度训练参数...") cfg.INPUT.MIN_SIZE_TRAIN = (640, 672, 704, 736, 768, 800) cfg.INPUT.MIN_SIZE_TRAIN_SAMPLING = "choice" print(f"[配置] 设置训练数据集为: ('cable_train',)") cfg.DATASETS.TRAIN = ("cable_train",) print(f"[配置] 设置测试/验证数据集为: ('cable_valid',)") cfg.DATASETS.TEST = ("cable_valid",) cfg.OUTPUT_DIR = "model_output_pointrend_augmented" # 建议换个名字以区分 print(f"[配置] 设置输出目录为: '{cfg.OUTPUT_DIR}'") os.makedirs(cfg.OUTPUT_DIR, exist_ok=True) print("[配置] 正在从本地加载预训练权重...") local_weight_path = os.path.join("pretrained_models", "model_final_f17282.pkl") if os.path.exists(local_weight_path): cfg.MODEL.WEIGHTS = local_weight_path print(f"[成功] 预训练权重路径已设置为: '{local_weight_path}'") else: print(f"[警告] 本地预训练权重文件未找到: '{local_weight_path}'。模型将从头开始训练。") cfg.MODEL.WEIGHTS = "" print(f"[配置] 为 {num_classes} 个类别调整模型。") cfg.MODEL.ROI_HEADS.NUM_CLASSES = num_classes if hasattr(cfg.MODEL, "POINT_HEAD"): cfg.MODEL.POINT_HEAD.NUM_CLASSES = num_classes # --- 修复 PointRend 缺失配置的健壮写法 --- if "PointRend" in cfg.MODEL.META_ARCHITECTURE: cfg.MODEL.ROI_MASK_HEAD.NUM_CLASSES = num_classes # PointRend的配置文件可能不包含这些,手动添加以确保兼容性 if not hasattr(cfg.MODEL.ROI_MASK_HEAD, "IN_FEATURES") or not cfg.MODEL.ROI_MASK_HEAD.IN_FEATURES: cfg.MODEL.ROI_MASK_HEAD.IN_FEATURES = cfg.MODEL.ROI_HEADS.IN_FEATURES if not hasattr(cfg.MODEL.ROI_MASK_HEAD, "OUTPUT_SIDE_RESOLUTION"): cfg.MODEL.ROI_MASK_HEAD.OUTPUT_SIDE_RESOLUTION = 7 print_section_header("超参数设置") cfg.DATALOADER.NUM_WORKERS = 2 print(f"[配置] DATALOADER.NUM_WORKERS (数据加载器工作进程数): {cfg.DATALOADER.NUM_WORKERS}") cfg.SOLVER.IMS_PER_BATCH = 3 print(f"[配置] SOLVER.IMS_PER_BATCH (每批图像数): {cfg.SOLVER.IMS_PER_BATCH}") cfg.SOLVER.BASE_LR = 0.00025 print(f"[配置] SOLVER.BASE_LR (基础学习率): {cfg.SOLVER.BASE_LR}") # 使用余弦退火学习率调度器,比阶梯下降更平滑 cfg.SOLVER.LR_SCHEDULER_NAME = "WarmupCosineLR" print(f"[配置] SOLVER.LR_SCHEDULER_NAME (学习率调度器): {cfg.SOLVER.LR_SCHEDULER_NAME}") cfg.SOLVER.MAX_ITER = 10000 print(f"[配置] SOLVER.MAX_ITER (最大迭代次数): {cfg.SOLVER.MAX_ITER}") cfg.SOLVER.STEPS = [] # 在使用 WarmupCosineLR 时,此项应为空 cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE = 512 print(f"[配置] MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE (每图RoI批大小): {cfg.MODEL.ROI_HEADS.BATCH_SIZE_PER_IMAGE}") cfg.TEST.EVAL_PERIOD = 500 print(f"[配置] TEST.EVAL_PERIOD (评估周期): {cfg.TEST.EVAL_PERIOD} 次迭代") print("[信息] 执行默认设置 (日志、环境检查等)...") default_setup(cfg, args) print_section_header("训练器初始化与模型结构保存") print("[信息] 正在初始化 CocoTrainer...") trainer = CocoTrainer(cfg) if comm.is_main_process(): # ... (模型结构保存代码无变化,此处省略) ... model_arch_file_path = os.path.join(cfg.OUTPUT_DIR, "model_architecture.txt") try: with open(model_arch_file_path, "w", encoding="utf-8") as f: f.write(f"Model Configuration Path: {config_file_local_path}\n") f.write(f"Number of Classes: {num_classes}\n\n") f.write("Model Architecture:\n") f.write("="*80 + "\n") f.write(str(trainer.model)) print(f"[成功] 模型结构已保存到: {model_arch_file_path}") except Exception as e: print(f"[错误] 保存模型结构到文件 '{model_arch_file_path}' 失败: {e}") resume_training = args.resume print(f"[信息] 训练器将尝试加载权重。是否从检查点恢复: {resume_training}") trainer.resume_or_load(resume=resume_training) print_section_header("训练开始") print(f"[信息] 开始使用 PointRend R-CNN 进行训练,共 {cfg.SOLVER.MAX_ITER} 次迭代...") try: trainer.train() print("\n[成功] 训练成功完成!") except Exception as e: print(f"\n[错误] 训练过程中发生错误: {e}") finally: print_script_footer("Detectron2 PointRend 训练流程结束") if __name__ == "__main__": parser = default_argument_parser() args = parser.parse_args() print_script_header("命令行参数") print("生效的命令行参数:") for arg, value in sorted(vars(args).items()): print(f" {arg}: {value}") print("\n[信息] 正在启动训练过程...") launch( main, args.num_gpus, num_machines=args.num_machines, machine_rank=args.machine_rank, dist_url=args.dist_url, args=(args,), )
06-28
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值