self.和_的区别(self和下划线的区别)

本文详细解析了在Xcode中属性(self.)与实例变量(_.)的使用区别,阐述了它们在内存管理、访问权限及编码规范上的不同。在手动管理内存时,不当的使用会导致内存泄漏,而在ARC环境下,正确使用仍符合编码规范。

在最新的xcode中,已经不需要我们自己去写 set,get 方法,,系统已经自动帮我们生成set,get方法。

同时我们发现在我们访问我们声明的变量时,会有self. 和 以"_"开头的访问方式,那么这两种方式到底有什么样的区别呢?

我们来一起看一下:

@property (retain, nonatomic) NSMutableArray *nameArray;
self.nameArray是访问属性的,而_nameArray是访问实例变量的.

属性是实例变量加上GET,SET方法的一个整合体,他主要是承担一个外部访问的一个接口!

实例变量只能在本类中才可以访问,外部不可以访问!

总的原则:

在类内部访问变量的时候用“_”;

在类外部也就是其他类里访问这个类的变量时用“.” 语法;

注意事项:

手动管理内存的情况下:

使用“.”语法来初始化变量的时候,会产生内存泄漏的问题:

self.nameArray = [NSMutaleArray alloc] init];

上述代码,造成的问题是,在self.nameArray的时候相当于调用了set方法,引用计数+1,后面alloc的时候,引用计数再次+1。
在我们最后dealloc中release的时候,引用计数只减了一次,并没有完成全部释放,这样就造成了内存泄漏的问题。

解决方法:就是用“_”来初始化以及访问变量,这样就不会产生内存问题,虽不是什么高明的办法,但的确有效。

_nameArray = [NSMutaleArray alloc] init];

上述便是“.”和“_”在使用的时候的简单区别。

如果是在ARC(自动管理内存)的情况下虽然不存在上述问题,但从编码规范来考虑,还是注意点儿的好。

 

 
 

转载于:https://www.cnblogs.com/iosdanran/p/4998986.html

templat_editor.py文件已修改,但现在我的main_window.py的完整代码如下:#主窗口 - PyQt6 版本 import os import cv2 import numpy as np import sys import logging import faulthandler # 用于捕获崩溃信息 import ctypes # Windows API 调用支持 from typing import Optional from pyzbar.pyzbar import decode from PyQt6.QtGui import QPainter, QPolygonF, QPixmap from PyQt6.QtCore import QPointF from PyQt6.QtCore import PYQT_VERSION_STR, QRectF from PyQt6.QtGui import QAction from PyQt6.QtWidgets import QDockWidget, QWidget,QMainWindow, QStatusBar, \ QTabWidget, QToolBar, QApplication, QMessageBox, QFileDialog, QDoubleSpinBox from PyQt6.QtWidgets import ( QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QComboBox, QGroupBox, QLineEdit, QFormLayout, QSpinBox, QColorDialog, QRadioButton, QButtonGroup, QCheckBox, QTextEdit, QFontComboBox ) from PyQt6.QtCore import Qt, pyqtSignal from PyQt6.QtGui import QColor, QFont, QTextCharFormat from ui.template_editor import TemplateCanvas # 启用故障处理程序,帮助调试崩溃问题 faulthandler.enable() # 设置日志系统,记录运行时信息 logging.basicConfig( level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler("app_debug.log"), # 输出到文件 logging.StreamHandler() # 输出到控制台 ] ) logger = logging.getLogger(__name__) logger.info("应用程序启动") logger.info(f"Python 路径: {sys.executable}") logger.info(f"参数: {sys.argv}") logger.info(f"系统路径: {sys.path}") logger.info(f"工作目录: {os.getcwd()}") logger.info(f"PyQt6 版本: {'未导入' if 'PYQT_VERSION_STR' not in globals() else PYQT_VERSION_STR}") # 检查是否运行在虚拟环境中 if not hasattr(sys, 'real_prefix') and not hasattr(sys, 'base_prefix'): logging.warning("警告: 未在虚拟环境中运行,建议使用虚拟环境") # 设置调试标志环境变量 os.environ['QT_DEBUG_PLUGINS'] = '1' os.environ['QTWEBENGINE_CHROMIUM_FLAGS'] = '--disable-gpu --no-sandbox' # Windows 错误报告设置 if sys.platform == 'win32': ctypes.windll.kernel32.SetErrorMode.argtypes = [ctypes.c_uint] ctypes.windll.kernel32.SetErrorMode(ctypes.c_uint(0x0001 | 0x0002)) # 设置堆栈保护 os.environ['QT_FATAL_CRITICALS'] = '1' os.environ['QT_FATAL_WARNINGS'] = '1' class BasePropertiesPanel(QWidget): """属性面板基类,定义通用信号与接口""" propertyChanged = pyqtSignal(str, object) # 属性变化信号 def __init__(self, parent=None): super().__init__(parent) self.region = None self.setup_ui() def setup_ui(self): pass def set_region(self, region): """绑定当前编辑区域对象""" self.region = region self.update_ui_from_region() def update_ui_from_region(self): """根据区域数据更新 UI""" pass def update_region_from_ui(self): """根据 UI 数据更新区域属性""" pass class TextPropertiesPanel(BasePropertiesPanel): """文本属性面板 UI 实现 """ # 定义信号用于与画布通信 textChanged = pyqtSignal(str) fontChanged = pyqtSignal(QFont) colorChanged = pyqtSignal(QColor) alignmentChanged = pyqtSignal(Qt.AlignmentFlag) positionChanged = pyqtSignal(float, float) sizeChanged = pyqtSignal(float, float) def __init__(self, parent=None): super().__init__(parent) self.current_font = QFont() self.current_color = QColor(0, 0, 0) # 默认黑色 self.current_alignment = Qt.AlignmentFlag.AlignLeft def setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # 文本内容编辑区域 content_group = QGroupBox("文本内容") content_layout = QVBoxLayout(content_group) self.text_edit = QTextEdit() self.text_edit.setPlaceholderText("输入文本内容...") self.text_edit.setMinimumHeight(80) self.text_edit.textChanged.connect(self.on_text_changed) content_layout.addWidget(self.text_edit) layout.addWidget(content_group) # 字体属性区域 font_group = QGroupBox("字体属性") font_layout = QFormLayout(font_group) # 字体选择 self.font_combo = QFontComboBox() self.font_combo.currentFontChanged.connect(self.on_font_changed) # 字号选择 self.font_size_spin = QSpinBox() self.font_size_spin.setRange(6, 120) self.font_size_spin.setValue(12) self.font_size_spin.valueChanged.connect(self.on_font_size_changed) # 字体样式 style_layout = QHBoxLayout() self.bold_check = QCheckBox("粗体") self.italic_check = QCheckBox("斜体") self.underline_check = QCheckBox("下划线") self.bold_check.toggled.connect(self.on_style_changed) self.italic_check.toggled.connect(self.on_style_changed) self.underline_check.toggled.connect(self.on_style_changed) style_layout.addWidget(self.bold_check) style_layout.addWidget(self.italic_check) style_layout.addWidget(self.underline_check) # 字体颜色 color_layout = QHBoxLayout() self.color_label = QLabel() self.color_label.setFixedSize(20, 20) self.color_label.setStyleSheet("background-color: black; border: 1px solid gray;") self.color_btn = QPushButton("选择颜色...") self.color_btn.clicked.connect(self.choose_color) color_layout.addWidget(QLabel("颜色:")) color_layout.addWidget(self.color_label) color_layout.addWidget(self.color_btn) color_layout.addStretch() font_layout.addRow("字体:", self.font_combo) font_layout.addRow("字号:", self.font_size_spin) font_layout.addRow("样式:", style_layout) font_layout.addRow(color_layout) layout.addWidget(font_group) # 对齐方式 align_group = QGroupBox("对齐方式") align_layout = QHBoxLayout(align_group) self.align_group = QButtonGroup(self) self.align_left = QRadioButton("左对齐") self.align_center = QRadioButton("居中对齐") self.align_right = QRadioButton("右对齐") self.align_justify = QRadioButton("两端对齐") self.align_group.addButton(self.align_left, 1) self.align_group.addButton(self.align_center, 2) self.align_group.addButton(self.align_right, 3) self.align_group.addButton(self.align_justify, 4) self.align_left.setChecked(True) self.align_group.buttonToggled.connect(self.on_alignment_changed) align_layout.addWidget(self.align_left) align_layout.addWidget(self.align_center) align_layout.addWidget(self.align_right) align_layout.addWidget(self.align_justify) layout.addWidget(align_group) # 位置尺寸 position_group = QGroupBox("位置尺寸") position_layout = QFormLayout(position_group) self.pos_x_spin = QDoubleSpinBox() self.pos_x_spin.setRange(0, 1000) self.pos_x_spin.setSuffix(" mm") self.pos_x_spin.valueChanged.connect(self.on_position_changed) self.pos_y_spin = QDoubleSpinBox() self.pos_y_spin.setRange(0, 1000) self.pos_y_spin.setSuffix(" mm") self.pos_y_spin.valueChanged.connect(self.on_position_changed) self.width_spin = QDoubleSpinBox() self.width_spin.setRange(10, 500) self.width_spin.setSuffix(" mm") self.width_spin.setValue(100) self.width_spin.valueChanged.connect(self.on_size_changed) self.height_spin = QDoubleSpinBox() self.height_spin.setRange(10, 500) self.height_spin.setSuffix(" mm") self.height_spin.setValue(30) self.height_spin.valueChanged.connect(self.on_size_changed) position_layout.addRow("X 位置:", self.pos_x_spin) position_layout.addRow("Y 位置:", self.pos_y_spin) position_layout.addRow("宽度:", self.width_spin) position_layout.addRow("高度:", self.height_spin) layout.addWidget(position_group) self.setLayout(layout) def set_text_properties(self, text, font, color, alignment, x, y, width, height): """设置文本属性值""" # 文本内容 self.text_edit.setPlainText(text) # 字体 self.current_font = font self.font_combo.setCurrentFont(font) self.font_size_spin.setValue(font.pointSize()) self.bold_check.setChecked(font.bold()) self.italic_check.setChecked(font.italic()) self.underline_check.setChecked(font.underline()) # 颜色 self.current_color = color self.color_label.setStyleSheet(f"background-color: {color.name()}; border: 1px solid gray;") # 对齐方式 self.current_alignment = alignment if alignment == Qt.AlignmentFlag.AlignLeft: self.align_left.setChecked(True) elif alignment == Qt.AlignmentFlag.AlignHCenter: self.align_center.setChecked(True) elif alignment == Qt.AlignmentFlag.AlignRight: self.align_right.setChecked(True) elif alignment == Qt.AlignmentFlag.AlignJustify: self.align_justify.setChecked(True) # 位置尺寸 self.pos_x_spin.setValue(x) self.pos_y_spin.setValue(y) self.width_spin.setValue(width) self.height_spin.setValue(height) def on_text_changed(self): """文本内容变化处理""" text = self.text_edit.toPlainText() self.textChanged.emit(text) def on_font_changed(self, font): """字体变化处理""" self.current_font.setFamily(font.family()) self.fontChanged.emit(self.current_font) def on_font_size_changed(self, size): """字号变化处理""" self.current_font.setPointSize(size) self.fontChanged.emit(self.current_font) def on_style_changed(self, checked): """字体样式变化处理""" self.current_font.setBold(self.bold_check.isChecked()) self.current_font.setItalic(self.italic_check.isChecked()) self.current_font.setUnderline(self.underline_check.isChecked()) self.fontChanged.emit(self.current_font) def choose_color(self): """选择字体颜色""" color = QColorDialog.getColor() if color.isValid(): self.current_color = color self.color_label.setStyleSheet(f"background-color: {color.name()}; border: 1px solid gray;") self.colorChanged.emit(color) def on_alignment_changed(self, button, checked): """对齐方式变化处理""" if not checked: return align_map = { 1: Qt.AlignmentFlag.AlignLeft, 2: Qt.AlignmentFlag.AlignHCenter, 3: Qt.AlignmentFlag.AlignRight, 4: Qt.AlignmentFlag.AlignJustify } align = align_map.get(self.align_group.checkedId(), Qt.AlignmentFlag.AlignLeft) self.current_alignment = align self.alignmentChanged.emit(align) def on_position_changed(self): """位置变化处理""" x = self.pos_x_spin.value() y = self.pos_y_spin.value() self.positionChanged.emit(x, y) def on_size_changed(self): """尺寸变化处理""" width = self.width_spin.value() height = self.height_spin.value() self.sizeChanged.emit(width, height) class QRPropertiesPanel(BasePropertiesPanel): """二维码属性面板 UI 实现,包含自动识别功能""" def __init__(self, parent=None): super().__init__(parent) self.qr_position_x = None self.qr_correction_combo = None self.qr_content_edit = None self.qr_position_y = None self.qr_fixed_check = None self.apply_btn = None self.qr_size_spin = None self.qr_type_combo = None self.qr_data_label = None self.detection_status = None self.qr_selector = None self.qr_preview = None self.detect_btn = None self.detected_qr_codes = [] # 存储检测到的二维码信息 self.current_qr_index = -1 # 当前选中的二维码索引 def setup_ui(self): layout = QVBoxLayout(self) layout.setSpacing(10) # 自动检测区域 auto_detect_group = QGroupBox("二维码自动检测") auto_layout = QVBoxLayout(auto_detect_group) self.detect_btn = QPushButton("自动识别二维码") self.detect_btn.clicked.connect(self.auto_detect_qr_codes) self.detect_btn.setStyleSheet("background-color: #4CAF50; color: white;") self.detection_status = QLabel("就绪") self.detection_status.setAlignment(Qt.AlignmentFlag.AlignCenter) auto_layout.addWidget(self.detect_btn) auto_layout.addWidget(self.detection_status) # 二维码选择器 self.qr_selector = QComboBox() self.qr_selector.currentIndexChanged.connect(self.select_qr_code) auto_layout.addWidget(QLabel("检测到的二维码:")) auto_layout.addWidget(self.qr_selector) # 预览区域 preview_layout = QHBoxLayout() self.qr_preview = QLabel() self.qr_preview.setFixedSize(150, 150) self.qr_preview.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;") self.qr_preview.setAlignment(Qt.AlignmentFlag.AlignCenter) self.qr_data_label = QLabel("二维码数据将显示在这里") self.qr_data_label.setWordWrap(True) self.qr_data_label.setStyleSheet("background-color: #f8f8f8; padding: 5px;") preview_layout.addWidget(self.qr_preview) preview_layout.addWidget(self.qr_data_label, 1) auto_layout.addLayout(preview_layout) layout.addWidget(auto_detect_group) # 二维码属性编辑区域 props_group = QGroupBox("二维码属性") form_layout = QFormLayout(props_group) self.qr_type_combo = QComboBox() self.qr_type_combo.addItems(["QR Code", "Data Matrix", "PDF417", "Aztec"]) self.qr_content_edit = QLineEdit() self.qr_content_edit.setPlaceholderText("输入二维码内容") self.qr_size_spin = QDoubleSpinBox() self.qr_size_spin.setRange(5, 100) self.qr_size_spin.setValue(30) self.qr_size_spin.setSuffix(" mm") self.qr_correction_combo = QComboBox() self.qr_correction_combo.addItems(["L ()", "M ()", "Q ()", "H (最高)"]) self.qr_correction_combo.setCurrentIndex(1) self.qr_position_x = QDoubleSpinBox() self.qr_position_x.setRange(0, 1000) self.qr_position_x.setSuffix(" mm") self.qr_position_y = QDoubleSpinBox() self.qr_position_y.setRange(0, 1000) self.qr_position_y.setSuffix(" mm") self.qr_fixed_check = QCheckBox("固定位置") self.qr_fixed_check.setChecked(True) form_layout.addRow("类型:", self.qr_type_combo) form_layout.addRow("内容:", self.qr_content_edit) form_layout.addRow("尺寸:", self.qr_size_spin) form_layout.addRow("纠错级别:", self.qr_correction_combo) form_layout.addRow("位置 X:", self.qr_position_x) form_layout.addRow("位置 Y:", self.qr_position_y) form_layout.addRow(self.qr_fixed_check) # 应用按钮 self.apply_btn = QPushButton("应用更改") self.apply_btn.clicked.connect(self.apply_qr_properties) form_layout.addRow(self.apply_btn) layout.addWidget(props_group) layout.addStretch(1) self.setLayout(layout) # 连接信号 self.qr_content_edit.textChanged.connect(self.update_qr_preview) self.qr_size_spin.valueChanged.connect(self.update_qr_preview) self.qr_correction_combo.currentIndexChanged.connect(self.update_qr_preview) def auto_detect_qr_codes(self): """自动检测二维码""" if not self.region or not self.region.template_image: self.detection_status.setText("错误: 没有可用的模板图像") self.detection_status.setStyleSheet("color: red;") return try: # 转换图像为OpenCV格式 qimage = self.region.template_image width, height = qimage.width(), qimage.height() ptr = qimage.bits() ptr.setsize(qimage.sizeInBytes()) img_np = np.array(ptr).reshape(height, width, 4) # RGBA格式 # 转换为BGR格式并转为灰度图 img_bgr = cv2.cvtColor(img_np, cv2.COLOR_RGBA2BGR) gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY) # 使用pyzbar检测二维码 self.detected_qr_codes = decode(gray) if not self.detected_qr_codes: self.detection_status.setText("未检测到二维码") self.detection_status.setStyleSheet("color: orange;") return # 更新UI显示结果 self.detection_status.setText(f"检测到 {len(self.detected_qr_codes)} 个二维码") self.detection_status.setStyleSheet("color: green;") # 更新二维码选择器 self.qr_selector.clear() for i, qr in enumerate(self.detected_qr_codes): self.qr_selector.addItem(f"二维码 {i + 1}") # 自动选择第一个二维码 if self.detected_qr_codes: self.qr_selector.setCurrentIndex(0) except Exception as e: self.detection_status.setText(f"检测失败: {str(e)}") self.detection_status.setStyleSheet("color: red;") logger.error(f"二维码检测失败: {str(e)}", exc_info=True) def select_qr_code(self, index): """选择检测到的二维码""" if index < 0 or index >= len(self.detected_qr_codes): return self.current_qr_index = index qr = self.detected_qr_codes[index] # 显示二维码数据 qr_data = qr.data.decode('utf-8') self.qr_data_label.setText(f"类型: {qr.type}\n\n数据:\n{qr_data}") # 更新属性编辑器 self.qr_content_edit.setText(qr_data) # 计算二维码位置 (转换为毫米,假设300dpi) points = qr.polygon if len(points) >= 4: # 计算中心点 center_x = sum([p.x for p in points]) / 4 center_y = sum([p.y for p in points]) / 4 # 转换为毫米 (300dpi: 1英寸=25.4mm, 300像素=25.4mm) mm_per_pixel = 25.4 / 300 self.qr_position_x.setValue(center_x * mm_per_pixel) self.qr_position_y.setValue(center_y * mm_per_pixel) # 计算尺寸 width = max(p.x for p in points) - min(p.x for p in points) height = max(p.y for p in points) - min(p.y for p in points) size_mm = max(width, height) * mm_per_pixel self.qr_size_spin.setValue(size_mm) # 更新预览 self.update_qr_preview() # 在模板画布上高亮显示选中的二维码 self.highlight_selected_qr(qr) def highlight_selected_qr(self, qr): """在模板画布上高亮显示选中的二维码""" if not self.region or not self.region.template_image: return # 创建原始图像的副本 image = self.region.template_image.copy() # 创建QPainter绘制高亮框 painter = QPainter(image) painter.setPen(QColor(255, 0, 0, 200)) # 半透明红色 painter.setBrush(Qt.BrushStyle.NoBrush) # 绘制二维码边界框 points = qr.polygon if len(points) == 4: polygon = QPolygonF() for point in points: polygon.append(QPointF(point.x, point.y)) painter.drawPolygon(polygon) # 绘制中心点 center_x = sum([p.x for p in points]) / 4 center_y = sum([p.y for p in points]) / 4 painter.setBrush(QColor(255, 0, 0, 150)) painter.drawEllipse(QPointF(center_x, center_y), 5, 5) painter.end() # 更新画布显示 self.region.setPixmap(QPixmap.fromImage(image)) def update_qr_preview(self): """更新二维码预览图像""" # 在实际应用中,这里应该生成二维码预览图 # 为了简化,我们只显示一个占位符 pixmap = QPixmap(150, 150) pixmap.fill(QColor(240, 240, 240)) painter = QPainter(pixmap) painter.setPen(Qt.GlobalColor.darkGray) painter.drawRect(10, 10, 130, 130) # 显示二维码类型内容摘要 qr_type = self.qr_type_combo.currentText() content = self.qr_content_edit.text()[:15] + "..." if len( self.qr_content_edit.text()) > 15 else self.qr_content_edit.text() painter.drawText(QRectF(0, 0, 150, 150), Qt.AlignmentFlag.AlignCenter, f"{qr_type}\n\n{content}") painter.end() self.qr_preview.setPixmap(pixmap) def apply_qr_properties(self): """应用二维码属性更改""" if not self.region: return # 在实际应用中,这里应该更新二维码对象的属性 qr_type = self.qr_type_combo.currentText() content = self.qr_content_edit.text() size = self.qr_size_spin.value() position_x = self.qr_position_x.value() position_y = self.qr_position_y.value() logger.info( f"更新二维码属性: 类型={qr_type}, 内容={content[:20]}..., 尺寸={size}mm, 位置=({position_x},{position_y})mm") # 这里应该调用画布更新二维码显示 # self.region.update_qr_display() def update_ui_from_region(self): """根据区域数据更新 UI""" if not self.region: return # 在实际应用中,这里应该从区域对象加载二维码属性 # 现在只是设置一些示例值 self.qr_content_edit.setText("https://example.com/product/12345") self.qr_size_spin.setValue(25.0) self.qr_position_x.setValue(50.0) self.qr_position_y.setValue(30.0) self.update_qr_preview() def update_region_from_ui(self): """根据 UI 数据更新区域属性""" if not self.region: return # 在实际应用中,这里应该更新二维码对象的属性 content = self.qr_content_edit.text() size = self.qr_size_spin.value() position_x = self.qr_position_x.value() position_y = self.qr_position_y.value() self.region.set_content(content) self.region.set_size(size) self.region.set_position(position_x, position_y) logger.info(f"二维码区域属性已更新") class LogoPropertiesPanel(BasePropertiesPanel): """Logo属性面板 UI 实现""" def setup_ui(self): layout = QVBoxLayout(self) layout.addWidget(QLabel("Logo属性面板")) self.setLayout(layout) class MainWindow(QMainWindow): def __init__(self, data_path: Optional[str] = None): super().__init__() self.run_test_add_regions = None self.logger = logging.getLogger(__name__) self.logger.info("主窗口初始化开始") self.setWindowTitle("自动打印系统") self.resize(800, 600) self.data_path = data_path or os.path.join(os.path.dirname(__file__), "../../../data") # 初始化所有实例属性为 None self.left_layout = None self.btn_upload = None self.btn_add_text = None self.btn_add_qr = None self.template_canvas = None self.props_tabs = None self.text_props_panel = None self.qr_props_panel = None self.logo_props_panel = None self.right_layout = None self.btn_import_data = None self.btn_save_config = None self.data_mapper = None self.field_mapping_widget = None self.history_combo = None self.btn_history = None self.btn_generate = None self.btn_rerun = None self.project_manager = None self.font_manager = None self.compositor = None # 初始化控件 self.init_components() try: # 创建项目管理器 from src.auto_print_system.core.project_manager import ProjectManager self.project_manager = ProjectManager(self.data_path) # 创建字体管理器 from src.auto_print_system.core.font_manager import FontManager self.font_manager = FontManager(self.data_path) # 创建合成引擎 from src.auto_print_system.core.file_compositor import FileCompositor self.compositor = FileCompositor(self.data_path, self.font_manager) except ImportError as e: self.logger.error(f"初始化核心组件失败: {str(e)}", exc_info=True) QMessageBox.critical(self, "错误", f"初始化核心组件失败:\n{str(e)}") # 设置状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.status_bar.showMessage("就绪") self.current_project = None self.logo_path = None self.spot_color_path = None # 加载样式 self.apply_styles() def init_components(self) -> None: """初始化所有界面组件""" self.logger.info("开始初始化组件") central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 左侧模板编辑区 left_dock = QDockWidget("模板编辑区", self) left_widget = QWidget() self.left_layout = QVBoxLayout(left_widget) btn_layout = QHBoxLayout() self.btn_upload = QPushButton("上传模板") self.btn_add_text = QPushButton("添加文本框") self.btn_add_qr = QPushButton("添加二维码区") self.btn_upload.clicked.connect(self.upload_template) self.btn_add_text.clicked.connect(self.add_text_region) self.btn_add_qr.clicked.connect(self.add_qr_region) btn_layout.addWidget(self.btn_upload) btn_layout.addWidget(self.btn_add_text) btn_layout.addWidget(self.btn_add_qr) self.left_layout.addLayout(btn_layout) try: from src.auto_print_system.ui.template_editor import TemplateCanvas self.template_canvas = TemplateCanvas(data_path=self.data_path) self.template_canvas.setAlignment(Qt.AlignmentFlag.AlignCenter) self.template_canvas.setStyleSheet("background-color: #f0f0f0; border: 1px solid #ccc;") self.left_layout.addWidget(self.template_canvas) except ImportError as e: self.logger.error(f"初始化模板画布失败: {str(e)}", exc_info=True) self.template_canvas = None # 属性面板 self.props_tabs = QTabWidget() self.text_props_panel = TextPropertiesPanel() self.qr_props_panel = QRPropertiesPanel() self.logo_props_panel = LogoPropertiesPanel() self.props_tabs.addTab(self.text_props_panel, "文本属性") self.props_tabs.addTab(self.qr_props_panel, "二维码属性") self.props_tabs.addTab(self.logo_props_panel, "Logo属性") self.left_layout.addWidget(self.props_tabs) left_dock.setWidget(left_widget) self.addDockWidget(Qt.DockWidgetArea.LeftDockWidgetArea, left_dock) # 右侧数据映射区 right_dock = QDockWidget("数据映射区", self) right_widget = QWidget() self.right_layout = QVBoxLayout(right_widget) data_btn_layout = QHBoxLayout() self.btn_import_data = QPushButton("导入数据") self.btn_save_config = QPushButton("保存配置") self.btn_import_data.clicked.connect(self.import_data) self.btn_save_config.clicked.connect(self.save_config) data_btn_layout.addWidget(self.btn_import_data) data_btn_layout.addWidget(self.btn_save_config) self.right_layout.addLayout(data_btn_layout) try: from src.auto_print_system.ui.data_mapper import DataMapperWidget self.data_mapper = DataMapperWidget(self) self.right_layout.addWidget(self.data_mapper, 1) except ImportError as e: self.logger.error(f"初始化数据映射器失败: {str(e)}", exc_info=True) self.field_mapping_widget = QLabel("拖拽字段到模板区域进行映射") self.field_mapping_widget.setStyleSheet("min-height: 100px; background-color: #fff; border: 1px dashed #999;") self.right_layout.addWidget(self.field_mapping_widget) right_dock.setWidget(right_widget) self.addDockWidget(Qt.DockWidgetArea.RightDockWidgetArea, right_dock) # 底部操作区 bottom_widget = QWidget() bottom_layout = QHBoxLayout(bottom_widget) self.history_combo = QComboBox() self.btn_history = QPushButton("管理") self.btn_generate = QPushButton("开始合成") self.btn_rerun = QPushButton("执行翻单") self.btn_rerun.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 8px 16px;") self.btn_rerun.clicked.connect(self.rerun_project) self.btn_rerun.setEnabled(False) # 设置按钮初始状态为不可点击 bottom_layout.addWidget(QLabel("翻单历史:")) bottom_layout.addWidget(self.history_combo) bottom_layout.addWidget(self.btn_history) bottom_layout.addSpacing(20) bottom_layout.addWidget(self.btn_generate) bottom_layout.addWidget(self.btn_rerun) main_layout.addWidget(bottom_widget) # 翻单按钮状态初始化 self.create_menus() self.create_toolbar() self.update_rerun_button_state() # 连接文本属性面板信号到画布 if self.template_canvas: # 文本内容变化 self.text_props_panel.textChanged.connect( self.template_canvas.update_selected_text_region_text ) # 字体变化 self.text_props_panel.fontChanged.connect( self.template_canvas.update_selected_text_region_font ) # 颜色变化 self.text_props_panel.colorChanged.connect( self.template_canvas.update_selected_text_region_color ) # 对齐方式变化 self.text_props_panel.alignmentChanged.connect( self.template_canvas.update_selected_text_region_alignment ) # 位置变化 self.text_props_panel.positionChanged.connect( self.template_canvas.update_selected_text_region_position ) # 尺寸变化 self.text_props_panel.sizeChanged.connect( self.template_canvas.update_selected_text_region_size ) # ===== 功能槽函数 ===== def update_rerun_button_state(self) -> None: """根据项目状态更新翻单按钮的可用性""" if not hasattr(self, 'template_canvas') or not hasattr(self, 'data_mapper'): self.logger.warning("模板画布或数据映射器未初始化") return can_rerun = ( self.template_canvas is not None and self.template_canvas.template_item is not None and self.data_mapper is not None ) if self.btn_rerun: self.btn_rerun.setEnabled(can_rerun) else: self.logger.warning("翻单按钮未初始化,无法更新状态") def rerun_project(self) -> None: """执行翻单操作""" if self.btn_rerun is not None: self.btn_rerun.setEnabled(False) # 防止多次点击 else: logging.error("btn_rerun 控件未正确初始化") return # ... 其他业务逻辑 ... def create_menus(self) -> None: """创建菜单栏""" menubar = self.menuBar() file_menu = menubar.addMenu('文件') new_action = QAction('新建项目', self) open_action = QAction('打开项目', self) save_action = QAction('保存项目', self) exit_action = QAction('退出', self) exit_action.triggered.connect(self.close) file_menu.addAction(new_action) file_menu.addAction(open_action) file_menu.addAction(save_action) file_menu.addSeparator() file_menu.addAction(exit_action) edit_menu = menubar.addMenu('编辑') edit_menu.addAction('撤销') edit_menu.addAction('重做') def create_toolbar(self) -> None: """创建工具栏""" toolbar = QToolBar("主工具栏") self.addToolBar(toolbar) toolbar.addAction("打开模板", self.upload_template) toolbar.addAction("导入数据", self.import_data) toolbar.addSeparator() toolbar.addAction("添加文本", self.add_text_region) toolbar.addAction("添加二维码", self.add_qr_region) toolbar.addSeparator() toolbar.addAction("开始合成", self.generate_output) def apply_styles(self) -> None: """应用基本样式表""" self.setStyleSheet(""" QMainWindow { background-color: #f5f5f5; } QPushButton { padding: 5px 10px; border: 1px solid #ccc; border-radius: 4px; } QPushButton:hover { background-color: #e9e9e9; } """) def upload_template(self) -> None: """上传模板文件""" self.logger.debug("上传模板操作被触发") file_dialog = QFileDialog(self) file_dialog.setNameFilter("模板文件 (*.pdf *.png *.jpg *.jpeg)") file_dialog.setWindowTitle("选择模板文件") if file_dialog.exec(): template_path = file_dialog.selectedFiles()[0] self.logger.info(f"选择了模板文件: {template_path}") try: if self.template_canvas: success = self.template_canvas.load_template(template_path) if success: self.status_bar.showMessage(f"模板加载成功: {os.path.basename(template_path)}") else: self.status_bar.showMessage("模板加载失败,请检查文件格式") self.logger.error(f"模板加载失败: {template_path}") else: self.logger.error("模板画布未初始化") self.status_bar.showMessage("模板画布未初始化,无法加载模板") except Exception as e: self.logger.error(f"模板加载失败: {str(e)}", exc_info=True) self.status_bar.showMessage("模板加载失败,请检查文件格式或权限") def add_text_region(self) -> None: """添加文本区域""" self.logger.debug("添加文本区域操作被触发") if self.template_canvas: try: self.template_canvas.set_mode(TemplateCanvas.MODE_TEXT) self.status_bar.showMessage("已切换到【添加文本区域】模式") except Exception as e: self.logger.error(f"设置文本区域模式失败: {str(e)}", exc_info=True) self.status_bar.showMessage("设置模式失败,请查看日志") else: self.logger.error("模板画布未初始化") self.status_bar.showMessage("模板画布未初始化,无法添加文本区域") def add_qr_region(self) -> None: """添加二维码区域""" self.logger.debug("添加二维码区域操作被触发") if self.template_canvas: try: self.template_canvas.set_mode(TemplateCanvas.MODE_QR) self.status_bar.showMessage("添加二维码区域模式激活") except Exception as e: self.logger.error(f"设置二维码区域模式失败: {str(e)}", exc_info=True) self.status_bar.showMessage("设置二维码区域模式失败,请重试") else: self.logger.error("模板画布未初始化") self.status_bar.showMessage("模板画布未初始化,无法添加二维码区域") def import_data(self) -> None: """导入数据文件""" self.logger.debug("导入数据操作被触发") file_dialog = QFileDialog(self) file_dialog.setNameFilter("数据文件 (*.csv *.xlsx *.xls)") file_dialog.setWindowTitle("选择数据文件") if file_dialog.exec(): data_path = file_dialog.selectedFiles()[0] self.logger.info(f"选择了数据文件: {data_path}") try: if self.data_mapper: self.data_mapper.import_data() self.status_bar.showMessage(f"数据文件已导入: {os.path.basename(data_path)}") else: self.logger.error("数据映射器未初始化") self.status_bar.showMessage("数据映射器未初始化,无法导入数据文件") except Exception as e: self.logger.error(f"导入数据失败: {str(e)}", exc_info=True) self.status_bar.showMessage("导入数据失败,请检查文件格式或权限") if self.data_mapper: self.data_mapper.import_data() self.update_rerun_button_state() #更新按钮状态 def save_config(self) -> None: """保存配置文件""" self.logger.debug("保存配置操作被触发") try: if self.data_mapper: mapping_config = self.data_mapper.get_mapping() if mapping_config: # 假设配置文件保存到 JSON 文件 config_path = os.path.join(self.data_path, "config.json") with open(config_path, "w", encoding="utf-8") as config_file: import json json.dump(mapping_config, config_file, ensure_ascii=False, indent=4) self.logger.info(f"配置文件已保存至: {config_path}") self.status_bar.showMessage("配置文件保存成功") else: self.logger.warning("无映射配置可保存") self.status_bar.showMessage("无映射配置可保存") else: self.logger.error("数据映射器未初始化") self.status_bar.showMessage("数据映射器未初始化,无法保存配置") except Exception as e: self.logger.error(f"保存配置失败: {str(e)}", exc_info=True) self.status_bar.showMessage("保存配置失败,请检查文件权限") def generate_output(self) -> None: """生成输出文件""" self.logger.debug("开始合成操作被触发") if not self.current_project: self.logger.error("未选择项目,无法生成输出文件") self.status_bar.showMessage("未选择项目,无法生成输出文件") return try: # 检查是否有有效的模板数据映射 if not self.template_canvas or not self.template_canvas.template_item: self.logger.error("无有效模板,无法生成输出文件") self.status_bar.showMessage("无有效模板,无法生成输出文件") return if not self.data_mapper or not self.data_mapper.data_frame: self.logger.error("无有效数据,无法生成输出文件") self.status_bar.showMessage("无有效数据,无法生成输出文件") return # 开始合成操作 self.status_bar.showMessage("正在生成输出文件...") self.logger.info("开始合成输出文件") output_path = os.path.join(self.data_path, "output.pdf") self.compositor.compose(output_path) self.status_bar.showMessage("输出文件生成成功") self.logger.info(f"输出文件已生成至: {output_path}") except Exception as e: self.logger.error(f"生成输出文件失败: {str(e)}", exc_info=True) self.status_bar.showMessage("生成输出文件失败,请检查设置") def rerun_project(self) -> None: """执行翻单操作""" self.logger.debug("执行翻单操作被触发") if self.btn_rerun is not None: self.btn_rerun.setEnabled(False) # 防止多次点击 else: self.logger.error("btn_rerun 控件未正确初始化") return if not self.current_project: self.logger.error("未选择项目,无法执行翻单") self.status_bar.showMessage("未选择项目,无法执行翻单") return try: # 检查是否有有效的模板数据映射 if not self.template_canvas or not self.template_canvas.template_item: self.logger.error("无有效模板,无法执行翻单") self.status_bar.showMessage("无有效模板,无法执行翻单") return if not self.data_mapper or not self.data_mapper.data_frame: self.logger.error("无有效数据,无法执行翻单") self.status_bar.showMessage("无有效数据,无法执行翻单") return # 执行翻单操作 self.status_bar.showMessage("正在执行翻单...") self.logger.info("开始执行翻单") output_path = os.path.join(self.data_path, "rerun_output.pdf") self.compositor.compose(output_path) self.status_bar.showMessage("翻单执行成功") self.logger.info(f"翻单输出文件已生成至: {output_path}") # 恢复按钮状态 self.btn_rerun.setEnabled(True) except Exception as e: self.logger.error(f"翻单执行失败: {str(e)}", exc_info=True) self.status_bar.showMessage("翻单执行失败,请检查设置") # 恢复按钮状态 self.btn_rerun.setEnabled(True) # 测试运行 if __name__ == "__main__": app = QApplication(sys.argv) window = MainWindow() window.show() sys.exit(app.exec()) 根据以上修改建议,请帮我输出完整的修改后的main_window.py的代码
07-16
import sys from PyQt5.QtWidgets import ( QApplication, QMainWindow, QTextEdit, QToolBar, QAction, QFileDialog, QFontComboBox, QComboBox ) from PyQt5.QtGui import ( QTextCharFormat, QFont, QTextCursor, QTextBlockFormat, QImage, QKeySequence ) from PyQt5.QtCore import Qt, QMimeData, QByteArray, QRegularExpression from PyQt5.QtGui import QFontDatabase class WordLikeEditor(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("Word 风格富文本编辑器") self.setGeometry(100, 100, 1000, 700) self.updating_font = False # 创建文本编辑区域 self.text_edit = QTextEdit(self) self.setCentralWidget(self.text_edit) # 重写 keyPressEvent 以支持粘贴 self.text_edit.keyPressEvent = self.handle_key_press # 初始化工具栏菜单 self.init_toolbar() self.init_menu() # 监听光标变化以同步工具栏状态 self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_size) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_style) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_font_color) self.text_edit.cursorPositionChanged.connect(self.update_toolbar_paragraph_style) def init_toolbar(self): toolbar = QToolBar("工具栏") self.addToolBar(toolbar) # ==== 字体设置区域 ==== self.font_combo = QFontComboBox() self.font_combo.setWritingSystem(QFontDatabase.SimplifiedChinese) self.font_combo.setEditable(False) self.font_combo.setToolTip("字体") self.font_combo.setFixedWidth(150) self.font_combo.currentFontChanged.connect(self.change_font) toolbar.addWidget(self.font_combo) self.size_combo = QComboBox() self.size_combo.setEditable(False) self.size_combo.setToolTip("字号") self.size_combo.setFixedWidth(60) for size in range(8, 72, 2): self.size_combo.addItem(str(size)) self.size_combo.setCurrentText("12") self.size_combo.currentTextChanged.connect(self.change_font_size) toolbar.addWidget(self.size_combo) self.style_combo = QComboBox() self.style_combo.setEditable(False) self.style_combo.setToolTip("字体样式") self.style_combo.setFixedWidth(80) self.style_combo.addItems(["普通", "粗体", "斜体", "粗斜体", "下划线"]) self.style_combo.currentTextChanged.connect(self.change_font_style) toolbar.addWidget(self.style_combo) self.color_combo = QComboBox() self.color_combo.setEditable(False) self.color_combo.setToolTip("字体颜色") self.color_combo.setFixedWidth(80) self.color_combo.addItems(["黑色", "红色", "蓝色", "绿色", "紫色", "橙色"]) self.color_combo.currentTextChanged.connect(self.change_font_color) toolbar.addWidget(self.color_combo) toolbar.addSeparator() # ==== 段落样式 ==== self.paragraph_combo = QComboBox() self.paragraph_combo.setEditable(False) self.paragraph_combo.setToolTip("段落样式") self.paragraph_combo.setFixedWidth(100) self.paragraph_combo.addItems(["正文", "标题1", "标题2", "标题3", "引用", "列表项"]) self.paragraph_combo.currentTextChanged.connect(self.change_paragraph_style) toolbar.addWidget(self.paragraph_combo) toolbar.addSeparator() # ==== 对齐方式 ==== left_align = QAction("左对齐", self) left_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignLeft)) toolbar.addAction(left_align) center_align = QAction("居中", self) center_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignCenter)) toolbar.addAction(center_align) right_align = QAction("右对齐", self) right_align.triggered.connect(lambda: self.text_edit.setAlignment(Qt.AlignRight)) toolbar.addAction(right_align) toolbar.addSeparator() # ==== 图片操作 ==== insert_image = QAction("插入图片", self) insert_image.triggered.connect(self.insert_image) toolbar.addAction(insert_image) paste_image = QAction("粘贴图片", self) paste_image.triggered.connect(self.paste_image) toolbar.addAction(paste_image) def init_menu(self): menu_bar = self.menuBar() file_menu = menu_bar.addMenu("文件") open_action = QAction("打开", self) open_action.setShortcut(QKeySequence.Open) open_action.triggered.connect(self.open_file) file_menu.addAction(open_action) save_action = QAction("保存", self) save_action.setShortcut(QKeySequence.Save) save_action.triggered.connect(self.save_file) file_menu.addAction(save_action) exit_action = QAction("退出", self) exit_action.triggered.connect(self.close) file_menu.addAction(exit_action) def change_font(self, font): if self.updating_font: return self.updating_font = True fmt = self.text_edit.currentCharFormat() fmt.setFont(font) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_font_size(self, size): if self.updating_font: return try: size = int(size) if size > 0: self.updating_font = True fmt = self.text_edit.currentCharFormat() fmt.setFontPointSize(size) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False except Exception as e: print("设置字号失败:", e) def change_font_style(self, style): if self.updating_font: return self.updating_font = True fmt = self.text_edit.currentCharFormat() if style == "普通": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(False) fmt.setFontUnderline(False) elif style == "粗体": fmt.setFontWeight(QFont.Bold) fmt.setFontItalic(False) fmt.setFontUnderline(False) elif style == "斜体": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(True) fmt.setFontUnderline(False) elif style == "粗斜体": fmt.setFontWeight(QFont.Bold) fmt.setFontItalic(True) fmt.setFontUnderline(False) elif style == "下划线": fmt.setFontWeight(QFont.Normal) fmt.setFontItalic(False) fmt.setFontUnderline(True) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_font_color(self, color_name): if self.updating_font: return self.updating_font = True color_map = { "黑色": Qt.black, "红色": Qt.red, "蓝色": Qt.blue, "绿色": Qt.green, "紫色": Qt.magenta, "橙色": Qt.darkYellow, } color = color_map.get(color_name, Qt.black) fmt = self.text_edit.currentCharFormat() fmt.setForeground(color) self.text_edit.mergeCurrentCharFormat(fmt) self.updating_font = False def change_paragraph_style(self, style): if self.updating_font: return self.updating_font = True cursor = self.text_edit.textCursor() char_fmt = cursor.charFormat() block_fmt = cursor.blockFormat() if style == "正文": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setTopMargin(5) block_fmt.setBottomMargin(5) char_fmt.setFontPointSize(12) char_fmt.setFontWeight(QFont.Normal) elif style == "标题1": block_fmt.setHeadingLevel(1) block_fmt.setLineHeight(150, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(10) block_fmt.setBottomMargin(10) char_fmt.setFontPointSize(16) char_fmt.setFontWeight(QFont.Bold) elif style == "标题2": block_fmt.setHeadingLevel(2) block_fmt.setLineHeight(140, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(8) block_fmt.setBottomMargin(8) char_fmt.setFontPointSize(14) char_fmt.setFontWeight(QFont.Bold) elif style == "标题3": block_fmt.setHeadingLevel(3) block_fmt.setLineHeight(130, QTextBlockFormat.FixedHeight) block_fmt.setTopMargin(6) block_fmt.setBottomMargin(6) char_fmt.setFontPointSize(13) char_fmt.setFontWeight(QFont.Bold) elif style == "引用": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setLeftMargin(20) block_fmt.setBackground(Qt.lightGray) elif style == "列表项": block_fmt.setHeadingLevel(0) block_fmt.setLineHeight(100, QTextBlockFormat.ProportionalHeight) block_fmt.setLeftMargin(15) cursor.select(QTextCursor.BlockUnderCursor) cursor.setBlockFormat(block_fmt) cursor.setCharFormat(char_fmt) self.updating_font = False def insert_image(self): filename, _ = QFileDialog.getOpenFileName(self, "插入图片", "", "图像文件 (*.png *.jpg *.bmp)") if filename: cursor = self.text_edit.textCursor() cursor.insertImage(filename) self.restore_font_format(cursor) def open_file(self): filename, _ = QFileDialog.getOpenFileName(self, "打开文件", "", "文本文件 (*.txt);;所有文件 (*)") if filename: with open(filename, 'r', encoding='utf-8') as f: self.text_edit.setText(f.read()) def save_file(self): filename, _ = QFileDialog.getSaveFileName(self, "保存文件", "", "文本文件 (*.txt);;所有文件 (*)") if filename: with open(filename, 'w', encoding='utf-8') as f: f.write(self.text_edit.toPlainText()) def handle_key_press(self, event): if event.key() == Qt.Key_V and (event.modifiers() & Qt.ControlModifier): self.paste_image() else: QTextEdit.keyPressEvent(self.text_edit, event) def paste_image(self): clipboard = QApplication.clipboard() mime_data = clipboard.mimeData() if mime_data.hasImage(): image = clipboard.image() if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("剪贴板中的图片为空") elif mime_data.hasUrls(): url = mime_data.urls()[0] image = QImage(url.toLocalFile()) if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("无法加载图片文件:", url.toLocalFile()) elif mime_data.hasHtml(): html = mime_data.html() if 'data:image/' in html: regex = QRegularExpression(r"src=\"data:image/([^;\"]+);base64,([^\"\)]+)") match = regex.match(html) if match.hasMatch(): image_format = match.captured(1) base64_data = match.captured(2) byte_array = QByteArray.fromBase64(base64_data.encode()) image = QImage() image.loadFromData(byte_array) if not image.isNull(): cursor = self.text_edit.textCursor() cursor.insertImage(image) self.restore_font_format(cursor) else: print("无法加载 base64 图片") else: print("base64 匹配失败") else: print("HTML 中没有 base64 图片数据") else: print("剪贴板中没有支持的图片格式") def restore_font_format(self, cursor): fmt = self.text_edit.currentCharFormat() cursor.setCharFormat(fmt) self.text_edit.setTextCursor(cursor) self.text_edit.setFocus() def update_toolbar_font(self): fmt = self.text_edit.currentCharFormat() font = fmt.font() self.updating_font = True self.font_combo.setCurrentFont(font) self.updating_font = False def update_toolbar_font_size(self): fmt = self.text_edit.currentCharFormat() size = fmt.fontPointSize() if size > 0: self.updating_font = True self.size_combo.setCurrentText(str(int(size))) self.updating_font = False def update_toolbar_font_style(self): fmt = self.text_edit.currentCharFormat() weight = fmt.fontWeight() italic = fmt.fontItalic() underline = fmt.fontUnderline() if underline: style = "下划线" elif weight == QFont.Bold and italic: style = "粗斜体" elif weight == QFont.Bold: style = "粗体" elif italic: style = "斜体" else: style = "普通" self.updating_font = True self.style_combo.setCurrentText(style) self.updating_font = False def update_toolbar_font_color(self): fmt = self.text_edit.currentCharFormat() color = fmt.foreground().color() if color == Qt.red: style = "红色" elif color == Qt.blue: style = "蓝色" elif color == Qt.green: style = "绿色" elif color == Qt.magenta: style = "紫色" elif color == Qt.darkYellow: style = "橙色" else: style = "黑色" self.updating_font = True self.color_combo.setCurrentText(style) self.updating_font = False def update_toolbar_paragraph_style(self): cursor = self.text_edit.textCursor() block = cursor.block() fmt = block.blockFormat() level = fmt.headingLevel() if level == 1: style = "标题1" elif level == 2: style = "标题2" elif level == 3: style = "标题3" else: if fmt.leftMargin() > 10 and fmt.background().color() == Qt.lightGray: style = "引用" elif fmt.leftMargin() > 10: style = "列表项" else: style = "正文" self.updating_font = True self.paragraph_combo.setCurrentText(style) self.updating_font = False if __name__ == '__main__': # 把窗口实例化 from PyQt5 import QtCore from PyQt5 import QtGui QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_EnableHighDpiScaling,True) QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps,True) QtGui.QGuiApplication.setAttribute(QtCore.Qt.HighDpiScaleFactorRoundingPolicy.PassThrough) # 在designer中是以像素为基本单位,但python中是以dp为基本单位,所以必须这么改才能保持大小 app = QApplication(sys.argv) # 初始化qt应用框架 # 获取屏幕 DPI screen = app.screens()[0] dpi = screen.logicalDotsPerInch() # 基准 DPI 为 96,按比例调整字体大小 base_dpi = 96.0 scale_factor = dpi / base_dpi font_size = max(12, int(12 * scale_factor)) # 最小字体大小为 10 font = app.font() font.setPointSize(font_size) app.setFont(font) window = WordLikeEditor() # 给予的第一个实例 window.show() sys.exit(app.exec()) 在这个的基础上加入能自己修改行间距的功能
07-31
text_widget.delete(1.0, tk.END) text_widget.insert(tk.END, pretty_xml) text_widget.config(state=tk.DISABLED) # 高亮空ID节点(可选) self.highlight_empty_id_nodes(text_widget, pretty_xml) except Exception as e: messagebox.showerror("显示错误", f"格式化XML显示失败: {str(e)}") def highlight_empty_id_nodes(self, text_widget, xml_content): """高亮显示id为空的data节点""" if not xml_content: return # 查找所有id属性为空的data节点 pattern = r'(<data\b.*?\bid="")' matches = re.finditer(pattern, xml_content) # 设置高亮样式 text_widget.tag_configure("empty_id", background="#ffe6e6") # 应用高亮 for match in matches: start_index = f"1.0 + {match.start()} chars" end_index = f"1.0 + {match.end()} chars" text_widget.tag_add("empty_id", start_index, end_index) def init_data_structures(self): """初始化数据结构""" self.rawdata_template = [] self.rawdata_source = None self.event_template = [] self.event_source = None self.current_filter = "" self.variable_mapping = {} self.module_mapping = {} self.init_module() self.event_categories = [] self.special_mapping_id = {} self.special_mapping_name = {} self.init_special_mapping() def init_module(self): """初始化moduleID与SVEC模板中一致""" self.module_mapping["System"] = "00" self.module_mapping["CryoPump"] = "00" self.module_mapping["HeatExchanger"] = "00" self.module_mapping["EFEM"] = "01" self.module_mapping["LP1"] = "01" self.module_mapping["LP2"] = "01" self.module_mapping["LP3"] = "01" self.module_mapping["Transfer"] = "02" self.module_mapping["TC"] = "02" self.module_mapping["Buffer"] = "03" self.module_mapping["BF"] = "03" self.module_mapping["Bufferr"] = "03" self.module_mapping["LA"] = "11" self.module_mapping["LB"] = "12" self.module_mapping["LC"] = "13" self.module_mapping["LD"] = "14" self.module_mapping["LAB"] = "15" self.module_mapping["LCD"] = "16" self.module_mapping["Ch1"] = "21" self.module_mapping["Ch2"] = "22" self.module_mapping["Ch3"] = "23" self.module_mapping["Ch4"] = "24" self.module_mapping["Ch5"] = "25" self.module_mapping["Ch6"] = "26" self.module_mapping["ChA"] = "27" self.module_mapping["ChB"] = "28" self.module_mapping["ChC"] = "29" self.module_mapping["ChD"] = "30" self.module_mapping["ChE"] = "31" self.module_mapping["ChF"] = "32" def get_module_id(self, module_name): """根据module_name映射表获取id""" if module_name not in self.module_mapping: return None else: return self.module_mapping[module_name] # ================ RawdataConfig 功能方法 ================ def load_rawdata_template(self): """加载Rawdata模板文件(.txt)""" file_path = filedialog.askopenfilename( title="选择Rawdata模板文件", filetypes=[("Text files", "*.txt"), ("All files", "*.*")] ) if not file_path: return try: with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() self.rawdata_template = [] self.variable_mapping = {} # 清空表格 for item in self.template_table.get_children(): self.template_table.delete(item) # 解析模板文件 for line in lines: line = line.strip() if not line: continue # 分割行数据 parts = line.split() if len(parts) < 2: continue # 提取数据 variable = parts[0] id = parts[1] if len(parts) > 1 else "" mapping_info = parts[2] if len(parts) > 2 else None self.rawdata_template.append({ "variable": variable, "id": id, "mapping": mapping_info }) if mapping_info: mapping_parts = mapping_info.split(';') for map in mapping_parts: if map in self.variable_mapping: messagebox.showwarning("信息有误","请确认模板文件中mapping列是否有重复信息"+map) return else: self.variable_mapping[map] = variable # 添加到表格 self.template_table.insert("", tk.END, values=(variable, id, mapping_info)) self.status_var.set(f"已加载Rawdata模板: {os.path.basename(file_path)} - {len(self.rawdata_template)} 条记录") except Exception as e: messagebox.showerror("加载错误", f"加载Rawdata模板文件失败: {str(e)}") def load_rawdata_source(self): """加载Rawdata源文件(.xml)""" file_path = filedialog.askopenfilename( title="选择Rawdata源文件", filetypes=[("XML files", "*.xml"), ("All files", "*.*")] ) if not file_path: return try: self.rawdata_tree = ET.parse(file_path) self.rawdata_root = self.rawdata_tree.getroot() self.rawdata_source = self.rawdata_tree # 在文本框中显示XML内容 self.display_xml_content(self.rawdata_tree, self.rawdata_xml_text) self.status_var.set(f"已加载Rawdata源文件: {os.path.basename(file_path)}") except Exception as e: messagebox.showerror("加载错误", f"加载Rawdata源文件失败: {str(e)}") def add_id_to_rawdata(self): """为Rawdata源文件添加ID属性""" if not self.rawdata_source: messagebox.showwarning("无数据", "请先加载Rawdata源文件") return # 添加ID属性 count = 0 for data in self.rawdata_root.findall('.//data'): if 'id' not in data.attrib: # 在属性开头位置添加id属性 new_attrib = OrderedDict() new_attrib['id'] = '' for key, value in data.attrib.items(): new_attrib[key] = value # 更新属性 data.attrib.clear() for key, value in new_attrib.items(): data.set(key, value) count += 1 # 更新XML显示 self.display_xml_content(self.rawdata_tree, self.rawdata_xml_text) self.status_var.set(f"已为{count}个data标签添加id属性") def filter_template_vars(self): """筛选模板Variable""" if not self.rawdata_template: messagebox.showwarning("无数据", "请先加载Rawdata模板文件") return # 提取Variable后半部分并去重 var_parts = set() for item in self.rawdata_template: variable = item["variable"] parts = variable.split('_', 1) # 只分割第一个下划线 if len(parts) > 1: var_parts.add(parts[1]) # 更新列表 self.template_filter_var.set(tuple(sorted(var_parts))) self.status_var.set(f"已筛选出 {len(var_parts)} 个模板Variable") def filter_source_vars(self): """筛选源文件Variable""" if not self.rawdata_source: messagebox.showwarning("无数据", "请先加载Rawdata源文件") return # 提取Variable后半部分并去重 var_parts = set() for data in self.rawdata_root.findall('.//data'): variable = data.get('variable', '') if variable: parts = variable.split('_', 1) # 只分割第一个下划线 if len(parts) > 1: var_parts.add(parts[1]) # 更新列表 self.source_filter_var.set(tuple(sorted(var_parts))) self.status_var.set(f"已筛选出 {len(var_parts)} 个源文件Variable") def compare_vars(self): """对比模板源文件Variable""" template_vars = self.template_filter_box.get(0, tk.END) source_vars = self.source_filter_box.get(0, tk.END) template_mapping_vars = self.variable_mapping.values() if not template_vars or not source_vars: messagebox.showwarning("无数据", "请先筛选模板源文件Variable") return # 清空对比结果 self.compare_result.config(state=tk.NORMAL) self.compare_result.delete(1.0, tk.END) # 查找源文件中有但模板中没有的Variable missing_in_template = set(source_vars) - set(template_vars) - set(template_mapping_vars) # 更新源文件列表,标红缺失项 self.source_filter_box.delete(0, tk.END) for var in sorted(source_vars): self.source_filter_box.insert(tk.END, var) if var in missing_in_template: self.source_filter_box.itemconfig(tk.END, fg='red') # 显示对比结果 self.compare_result.insert(tk.END, "对比结果:\n") self.compare_result.insert(tk.END, f"模板Variable数量: {len(template_vars)}\n") self.compare_result.insert(tk.END, f"源文件Variable数量: {len(source_vars)}\n") self.compare_result.insert(tk.END, f"源文件中有但模板中缺失的数量: {len(missing_in_template)}\n\n") if missing_in_template: self.compare_result.insert(tk.END, "缺失的Variable:\n") for var in sorted(missing_in_template): self.compare_result.insert(tk.END, f" - {var}\n") self.compare_result.config(state=tk.DISABLED) self.status_var.set(f"对比完成: 发现 {len(missing_in_template)} 个缺失项") def export_highlighted(self): """导出标红内容""" # 获取标红的项 highlighted_items = [] for i in range(self.source_filter_box.size()): if self.source_filter_box.itemcget(i, "fg") == "red": highlighted_items.append(self.source_filter_box.get(i)) if not highlighted_items: messagebox.showinfo("无数据", "没有标红的项可导出") return # 选择保存位置 file_path = filedialog.asksaveasfilename( title="保存标红内容", defaultextension=".txt", filetypes=[("Text files", "*.txt"), ("All files", "*.*")] ) if not file_path: return # 写入文件 try: with open(file_path, 'w', encoding='utf-8') as file: file.write("标红的Variable项:\n") for item in highlighted_items: file.write(f"{item}\n") self.status_var.set(f"已导出 {len(highlighted_items)} 个标红项到 {os.path.basename(file_path)}") messagebox.showinfo("导出成功", "标红项已成功导出") except Exception as e: messagebox.showerror("导出错误", f"导出文件失败: {str(e)}") def fill_ids(self): """填充ID属性""" if not self.rawdata_source: messagebox.showwarning("无数据", "请先加载Rawdata源文件") return if not self.rawdata_root: messagebox.showwarning("无数据", "请先加载Rawdata源文件") return if not self.rawdata_template: messagebox.showwarning("无数据", "请先加载模板文件") return fill_count = 0 not_exist = [] not_exist_all = [] for data_elem in self.rawdata_tree.findall('.//data'): variable = data_elem.get('variable') parts = variable.split('_', 1) if len(parts) < 2 or variable in self.special_mapping_id: if variable in self.special_mapping_id and variable in self.special_mapping_name: temp_var = self.special_mapping_name[variable] temp_id = self.special_mapping_id[variable] else: continue else: module = parts[0] var = parts[1] temp_var, temp_id = self.get_id(module, var) if temp_var and temp_id: data_elem.set('variable', temp_var) data_elem.set('id', temp_id) fill_count += 1 else: if temp_id is None and var not in not_exist: not_exist.append(var) if temp_id is None: not_exist_all.append(variable) # 更新XML显示 self.display_xml_content(self.rawdata_tree, self.rawdata_xml_text) self.status_var.set(f"已为{fill_count}个data标签添加id属性,剩余{len(not_exist)}无对应值。总共剩余{len(not_exist_all)}个未设置") def filter_empty_id_nodes(self): """删除id属性为空的data节点并刷新显示""" if not self.rawdata_source: messagebox.showwarning("无数据", "请先加载Rawdata源文件") return # 获取XML根节点 root = self.rawdata_tree.getroot() # 查找所有id属性为空的data节点 empty_id_nodes = [] for data_node in root.iter('data'): if data_node.get('id', '') == '': empty_id_nodes.append(data_node) if not empty_id_nodes: messagebox.showinfo("无操作", "未找到id为空的data节点") return # 删除这些节点(使用替代方法获取父节点) for node in empty_id_nodes: for parent in root.iter(): if node in list(parent): # 检查节点是否是当前父节点的直接子节点 parent.remove(node) break # 刷新XML显示 self.display_xml_content(self.rawdata_tree, self.rawdata_xml_text) # 更新状态 self.status_var.set(f"已删除 {len(empty_id_nodes)} 个id为空的data节点") def export_rawdata(self): """导出Rawdata源文件""" if not self.rawdata_source: messagebox.showwarning("无数据", "没有可导出的数据") return file_path = filedialog.asksaveasfilename( title="保存Rawdata文件", defaultextension=".xml", filetypes=[("XML files", "*.xml"), ("All files", "*.*")] ) if not file_path: return try: self.rawdata_tree.write(file_path, encoding='utf-8', xml_declaration=True) self.status_var.set(f"Rawdata文件已成功导出到: {os.path.basename(file_path)}") messagebox.showinfo("导出成功", "Rawdata配置文件已成功导出") except Exception as e: messagebox.showerror("导出错误", f"导出文件失败: {str(e)}") def init_special_mapping(self): """初始化特殊映射""" self.special_mapping_id['LP1_BypassReadID'] = '201210000' self.special_mapping_name['LP1_BypassReadID'] = 'EFEM_LP1BypassReadID' self.special_mapping_id['LP1_LoadPortTout'] = '201210001' self.special_mapping_name['LP1_LoadPortTout'] = 'EFEM_LP1LoadPortTout' self.special_mapping_id['LP2_BypassReadID'] = '201211000' self.special_mapping_name['LP2_BypassReadID'] = 'EFEM_LP2BypassReadID' self.special_mapping_id['LP2_LoadPortTout'] = '201211001' self.special_mapping_name['LP2_LoadPortTout'] = 'EFEM_LP2LoadPortTout' self.special_mapping_id['LP3_BypassReadID'] = '201212000' self.special_mapping_name['LP3_BypassReadID'] = 'EFEM_LP3BypassReadID' self.special_mapping_id['LP3_LoadPortTout'] = '201212001' self.special_mapping_name['LP3_LoadPortTout'] = 'EFEM_LP3LoadPortTout' self.special_mapping_id['CJobSubstProcStatusList'] = '100000004' self.special_mapping_name['CJobSubstProcStatusList'] = 'CJobSubstProcStatusList' self.special_mapping_id['ToolState'] = '100000000' self.special_mapping_name['ToolState'] = 'ToolState' self.special_mapping_id['Aligner_AngleLA'] = '201209000' self.special_mapping_name['Aligner_AngleLA'] = 'EFEM_AlignerAngleLA' self.special_mapping_id['Aligner_AngleLB'] = '201209001' self.special_mapping_name['Aligner_AngleLB'] = 'EFEM_AlignerAngleLB' self.special_mapping_id['Aligner_AngleX'] = '201209002' self.special_mapping_name['Aligner_AngleX'] = 'EFEM_AlignerAngleX' self.special_mapping_id['Aligner_NotchSupport'] = '201209003' self.special_mapping_name['Aligner_NotchSupport'] = 'EFEM_AlignerNotchSupport' self.special_mapping_id['Aligner_Bypass'] = '201209004' self.special_mapping_name['Aligner_Bypass'] = 'EFEM_AlignerBypass' self.special_mapping_id['CryoPump_CompressorPressure'] = '101122000' self.special_mapping_name['CryoPump_CompressorPressure'] = 'System_CryoPumpCompressorPressure' self.special_mapping_id['CryoPump_CompressorDiffPressure'] = '101122001' self.special_mapping_name['CryoPump_CompressorDiffPressure'] = 'System_CryoPumpCompressorDiffPressure' self.special_mapping_id['CryoPump_CompressorSupplyPressure'] = '101122002' self.special_mapping_name['CryoPump_CompressorSupplyPressure'] = 'System_CryoPumpCompressorSupplyPressure' self.special_mapping_id['HeatExchanger_RS'] = '100409000' self.special_mapping_name['HeatExchanger_RS'] = 'System_HeatExchangerRS' def get_id(self, module_name, var): """获取ID""" if var in self.variable_mapping: #来自mapping,含有前缀CHXXX temp_var = self.variable_mapping[var] variable = temp_var.replace('CHXXX', module_name) # Ch1_LotID else: temp_var = "CHXXX_" + var #来自配置,不含有前缀CHXXX variable = module_name + '_' + var # Ch1_LotID module_id = self.get_module_id(module_name) temp_id = self.get_id_by_variable(temp_var) if temp_id and module_id: if 'XX' in temp_id: id = temp_id.replace('XX', str(module_id)) return variable.replace('CHXXX', module_name), id else: return None, None def get_id_by_variable(self, variable): """根据变量名获取ID""" for item in self.rawdata_template: if item['variable'] == variable: return item['id'] return None def split_export(self): """拆分并导出文件""" if not self.rawdata_source: messagebox.showwarning("无数据", "没有可拆分的数据") return # 创建分组字典,前缀为data节点列表 groups = defaultdict(list) # 遍历所有data节点 for data in self.rawdata_root.findall('.//data'): variable = data.get('variable','') if '_' in variable: prefix = variable.split('_', 1)[0] groups[prefix].append(data) # 为每个分组创建新的xml文件 for prefix, data in groups.items(): # 创建新根节点(复制原始根节点标签属性) new_root = ET.Element(self.rawdata_root.tag, attrib=self.rawdata_root.attrib) # 复制原始根节点的命名空间 for ns, uri in self.rawdata_root.nsmap.items(): if ns: ET.register_namespace(ns, uri) # 添加原始非 data 节点 for child in self.rawdata_root: if child.tag != 'data': new_root.append(child) # 添加分组中的 data 节点 for node in data: new_root.append(node) # 创建 XML 树并写入文件 new_tree = ET.ElementTree(new_root) filename = f"RawData_{prefix}.xml" new_tree.write(filename, encoding='utf-8', xml_declaration=True) messagebox.showinfo("成功", f"已拆分为 {len(groups)} 个文件") # ================ EventConfig 功能方法 ================ def load_event_template(self): """加载Event模板文件(.txt)""" file_path = filedialog.askopenfilename( title="选择Event模板文件", filetypes=[("Text files", "*.txt"), ("All files", "*.*")] ) if not file_path: return try: with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() self.event_template = [] self.event_mapping = {} # 清空表格 for item in self.event_template_table.get_children(): self.event_template_table.delete(item) # 解析模板文件 for line in lines: line = line.strip() if not line: continue # 分割行数据 parts = line.split() if len(parts) < 2: continue # 提取数据 name = parts[0] id = parts[1] if len(parts) > 1 else "" mapping = parts[2] if len(parts) > 2 else None # 提取type name_parts = name.split('_',1) if len(name_parts) < 2: continue type = name_parts[1] self.event_categories.append(type) if mapping: mapping_parts = mapping.split(';') for map in mapping_parts: if map in self.event_mapping: messagebox.showwarning("数据有误","请确认Event模板数据中mapping列是否有重复数据") else: self.event_mapping[map] = name self.event_template.append({ "name": name, "id": id, "type": type, "mapping": mapping }) # 添加到表格 self.event_template_table.insert("", tk.END, values=(name, id, type, mapping)) self.event_categories = sorted(self.event_categories, key=len, reverse=True) self.status_var.set(f"已加载Event模板: {os.path.basename(file_path)} - {len(self.event_template)} 条记录") except Exception as e: messagebox.showerror("加载错误", f"加载Event模板文件失败: {str(e)}") def load_event_source(self): """加载Event源文件(.xml)""" file_path = filedialog.askopenfilename( title="选择Event源文件", filetypes=[("XML files", "*.xml"), ("All files", "*.*")] ) if not file_path: return try: self.event_tree = ET.parse(file_path) self.event_root = self.event_tree.getroot() self.event_source = self.event_tree # 在文本框中显示XML内容 self.display_xml_content(self.event_tree, self.event_xml_text) self.status_var.set(f"已加载Event源文件: {os.path.basename(file_path)}") except Exception as e: messagebox.showerror("加载错误", f"加载Event源文件失败: {str(e)}") def add_id_to_event(self): """为Event源文件添加ID属性""" if not self.event_source: messagebox.showwarning("无数据", "请先加载Event源文件") return # 添加ID属性 count = 0 for event in self.event_root.findall('.//event'): if 'id' not in event.attrib: # 在属性开头位置添加id属性 new_attrib = OrderedDict() new_attrib['id'] = '' for key, value in event.attrib.items(): new_attrib[key] = value # 更新属性 event.attrib.clear() for key, value in new_attrib.items(): event.set(key, value) count += 1 # 更新XML显示 self.display_xml_content(self.event_tree, self.event_xml_text) self.status_var.set(f"已为{count}个event元素添加ID属性") def get_id_from_event_template(self, event): """从Event模板获取ID""" for item in self.event_template: if item['name'] == event: return item['id'] return None def get_id_event(self, module, cat): """获取Event ID""" if cat in self.event_mapping: temp_name = self.event_mapping[cat] else: temp_name = "CHXXX_" + cat module_id = self.get_module_id(module) temp_id = self.get_id_from_event_template(temp_name) name = module + '_' + cat if temp_id and module_id: id = temp_id.replace('XX', str(module_id)) return name, id else: return None, None def split_by_type(self, event_name): """按类型拆分""" if not self.event_categories: return None, None for cat in self.event_categories: if event_name.endswith(cat): module = event_name[:-len(cat)] return module, cat return None, None def fill_event_attributes(self): """填充Event属性""" if not self.event_source: messagebox.showwarning("无数据", "请先加载EventConfig源文件") return if not self.event_template: messagebox.showwarning("无数据", "请先加载EventConfig模板文件") return fill_count = 0 not_exist = [] not_exist_all = [] for data_elem in self.event_tree.findall('.//event'): name = data_elem.get('name') module, cat = self.split_by_type(name) if not module or not cat: continue temp_name, temp_id = self.get_id_event(module, cat) if temp_name and temp_id: data_elem.set('name', temp_name) data_elem.set('id', temp_id) fill_count += 1 else: if temp_id is None and cat not in not_exist: not_exist.append(cat) if temp_id is None: not_exist_all.append(name) # 更新XML显示 self.display_xml_content(self.event_tree, self.event_xml_text) self.status_var.set(f"已为{fill_count}个event补充id属性,剩余{len(not_exist)}个不在模板中,总共剩余{len(not_exist_all)}个") def export_event(self): """导出Event源文件""" if not self.event_source: messagebox.showwarning("无数据", "没有可导出的数据") return file_path = filedialog.asksaveasfilename( title="保存Event文件", defaultextension=".xml", filetypes=[("XML files", "*.xml"), ("All files", "*.*")] ) if not file_path: return try: self.event_tree.write(file_path, encoding='utf-8', xml_declaration=True) self.status_var.set(f"Event文件已成功导出到: {os.path.basename(file_path)}") messagebox.showinfo("导出成功", "Event配置文件已成功导出") except Exception as e: messagebox.showerror("导出错误", f"导出文件失败: {str(e)}") def split_export_event(self): """拆分并导出Event文件""" if not self.event_source: messagebox.showwarning("无数据", "没有可拆分的数据") return # 创建分组字典,前缀为event节点列表 groups = defaultdict(list) # 遍历所有event节点 for event in self.event_root.findall('.//event'): name = event.get('name','') if '_' in name: prefix = name.split('_', 1)[0] groups[prefix].append(event) # 为每个分组创建新的xml文件 for prefix, data in groups.items(): # 创建新根节点(复制原始根节点标签属性) new_root = ET.Element(self.event_root.tag, attrib=self.event_root.attrib) # 复制原始根节点的命名空间 for ns, uri in self.event_root.nsmap.items(): if ns: ET.register_namespace(ns, uri) # 添加原始非 event 节点 for child in self.event_root: if child.tag != 'event': new_root.append(child) # 添加分组中的 event 节点 for node in data: new_root.append(node) # 创建 XML 树并写入文件 new_tree = ET.ElementTree(new_root) filename = f"Event_{prefix}.xml" new_tree.write(filename, encoding='utf-8', xml_declaration=True) messagebox.showinfo("成功", f"已拆分为 {len(groups)} 个文件") if name == “main”: root = tk.Tk() app = ConfigUpdaterApp(root) root.mainloop() 改为代码格式,缩进格式之类
08-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值