关于range table的一点特别之处

本文探讨了在处理RangeTable时,如何正确地使用关键字ranges进行append操作。特别关注在不同条件下选择正确的OPTION参数,如使用GE或BT等,以确保数据范围被准确地设置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    对关键字ranges定义的range table进行append操作,有个小问题需要注意:

            假设需要将范围设置在low和high两个值之间。

            若append的数据中,LOW不为初始值,HIGH为初始值,则OPTION应使用GE;若LOW为初始值,HIGH不为初始值,

            则OPTION应使用BT,而不是LE。

    下面是SAP HELP的标准说明:

    

  • OPTION

The data type of OPTION is C with length 2. OPTION contains the selection operator. The following operators are available:

– If HIGH is empty, you can use EQ, NE, GT, LE, LT,CP, and NP. These operators are the same as those that are used for logical expressions. Yet operators CP and NP do not have the full functional scope they have in normal logical expressions. They are only allowed if wildcards ( '*' or '+' ) are used in the input fields. If wildcards are entered on the selection screen, the system automatically uses the operator CP. The escape character is defined as #.

– If HIGH is filled, you can use BT (BeTween) and NB (Not Between). These operators correspond to BETWEEN and NOT BETWEEN that you use when you check if a field belongs to a range. You cannot use wildcard characters.

            

修改上面程序,保存修改只改内存里的不要改源文件的内容;搜索后,修改搜索到的内容表格,点击保存修改,点击清空搜索能够显示表格所有内容;搜索后,修改搜索到的内容表格,点击保存修改,再点击搜索,可以搜索到相关内容,点击清空搜索能够显示表格所有内容。import sys import xml.etree.ElementTree as ET from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QPushButton, QFileDialog, QLineEdit, QLabel, QMessageBox, QHBoxLayout, QVBoxLayout, QWidget, QHeaderView, QAbstractItemView, QComboBox, QSizePolicy, QStyledItemDelegate) from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QBrush, QColor, QFont import os class ComboBoxDelegate(QStyledItemDelegate): """自定义委托,用于在表格单元格中显示可编辑的下拉框""" def __init__(self, parent=None, options=None): super().__init__(parent) self.options = options or [] def createEditor(self, parent, option, index): editor = QComboBox(parent) editor.setEditable(True) editor.addItems(self.options) return editor def setEditorData(self, editor, index): value = index.data(Qt.DisplayRole) if value: editor.setCurrentText(value) def setModelData(self, editor, model, index): model.setData(index, editor.currentText(), Qt.EditRole) class XMLDataManager(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("RawDataConfig编辑器") self.setGeometry(100, 100, 1400, 800) # 初始化变量 self.current_file = None self.table_data = [] self.original_table_data = [] self.row_counter = 1 self.type_options = set() # 存储type列的所有选项 self.data_type_id_options = set() # 存储data_type_id列的所有选项 # 定义列名列表 self.column_names = [ "path", "type", "variable", "id", "data_type_id", "subsystem_id", "parts", "parts_id", "attribute", "attribute_id", "mapping" ] # 定义必填项列(除了mapping列) self.required_columns = [col for col in self.column_names if col != "mapping"] # 创建UI self.init_ui() def init_ui(self): # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 创建按钮 self.load_btn = QPushButton("加载文件") self.add_btn = QPushButton("添加记录") self.delete_btn = QPushButton("删除记录") self.save_btn = QPushButton("保存修改") self.export_btn = QPushButton("导出文件") self.search_btn = QPushButton("搜索") self.clear_btn = QPushButton("清空搜索") # 设置按钮样式 button_style = """ QPushButton { background-color: rgb(65, 105, 225); color: white; border: none; padding: 8px 16px; text-align: center; font-size: 14px; margin: 4px 2px; border-radius: 4px; } QPushButton:hover { background-color: rgb(55, 95, 215); } QPushButton:pressed { background-color: rgb(45, 85, 205); } """ for btn in [self.load_btn, self.export_btn, self.add_btn, self.delete_btn, self.save_btn, self.search_btn, self.clear_btn]: btn.setStyleSheet(button_style) # 创建搜索框 self.search_box = QLineEdit() self.search_box.setPlaceholderText("输入搜索内容...") self.search_box.setMinimumWidth(300) self.search_box.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } """) # 创建列选择下拉框 self.column_combo = QComboBox() self.column_combo.setMinimumWidth(120) self.column_combo.addItem("所有列") self.column_combo.addItems(self.column_names) self.column_combo.setCurrentIndex(0) self.column_combo.setStyleSheet(""" QComboBox { padding: 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } """) # 创建表格 - 增加序号列 self.table = QTableWidget() self.table.setColumnCount(len(self.column_names) + 1) # 增加序号列 headers = ["序号"] + self.column_names # 添加序号列头 self.table.setHorizontalHeaderLabels(headers) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.table.horizontalHeader().setStretchLastSection(True) self.table.setSelectionBehavior(QTableWidget.SelectRows) self.table.setSelectionMode(QAbstractItemView.ExtendedSelection) # 连接单元格更改信号(用于必填项背景色提示) self.table.itemChanged.connect(self.update_cell_background) # 设置表格样式 self.table.setStyleSheet(""" QTableWidget { gridline-color: #ddd; font-size: 12px; } QHeaderView::section { background-color: #f2f2f2; padding: 4px; border: 1px solid #ddd; font-weight: bold; } QTableCornerButton::section { background-color: #f2f2f2; border: 1px solid #ddd; } """) # 设置序号列样式 self.table.setColumnWidth(0, 60) self.table.verticalHeader().setVisible(False) # 设置列宽策略 self.table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # 布局设置 - 调整按钮顺序 top_layout = QHBoxLayout() top_layout.addWidget(self.load_btn) top_layout.addWidget(self.add_btn) top_layout.addWidget(self.delete_btn) top_layout.addWidget(self.save_btn) # 保存修改按钮 top_layout.addWidget(self.export_btn) # 导出XML文件按钮放在保存按钮后面 top_layout.addStretch() search_col_label = QLabel("搜索列:") search_col_label.setStyleSheet("font-size: 14px;") # 设置字号为14像素 top_layout.addWidget(search_col_label) top_layout.addWidget(self.column_combo) search_col_label1 = QLabel("搜索内容:") search_col_label1.setStyleSheet("font-size: 14px;") # 设置字号为14像素 top_layout.addWidget(search_col_label1) top_layout.addWidget(self.search_box) top_layout.addWidget(self.search_btn) top_layout.addWidget(self.clear_btn) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.table) central_widget.setLayout(main_layout) # 连接信号 self.load_btn.clicked.connect(self.load_xml) self.export_btn.clicked.connect(self.export_xml) self.add_btn.clicked.connect(self.add_record) self.delete_btn.clicked.connect(self.delete_record) self.save_btn.clicked.connect(self.save_changes) self.search_btn.clicked.connect(self.search_records) self.clear_btn.clicked.connect(self.clear_search) # 状态栏 self.statusBar = self.statusBar() self.statusBar.setStyleSheet("background-color: #f0f0f0; padding: 4px;") def update_cell_background(self, item): """更新单元格背景色(仅用于视觉提示,不弹出警告)""" col_idx = item.column() - 1 # 减去序号列 if col_idx < 0 or col_idx >= len(self.column_names): return col_name = self.column_names[col_idx] # 更新必填项背景色 if col_name in self.required_columns: if not item.text().strip(): item.setBackground(QColor(255, 200, 200)) # 浅红色背景 else: item.setBackground(QColor(255, 255, 255)) # 白色背景 def load_xml(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择XML文件", "", "XML文件 (*.xml)" ) if not file_path: return try: tree = ET.parse(file_path) root = tree.getroot() self.current_file = file_path file_name = os.path.basename(file_path) self.setWindowTitle(f"RawDataConfig编辑器 - {file_name}") # 清空表格 self.table.setRowCount(0) self.table_data = [] self.original_table_data = [] self.row_counter = 1 self.type_options = set() self.data_type_id_options = set() # 解析XML并填充表格 for record in root.findall('Record'): row_data = {} for col_name in self.column_names: value = record.get(col_name, '') row_data[col_name] = value # 收集type和data_type_id的值作为下拉选项 if col_name == 'type' and value: self.type_options.add(value) elif col_name == 'data_type_id' and value: self.data_type_id_options.add(value) self.table_data.append(row_data) # 保存原始数据副本 self.original_table_data = self.table_data.copy() self.populate_table() self.adjust_column_widths() self.statusBar.showMessage(f"成功加载文件: {file_path}", 3000) except Exception as e: QMessageBox.critical(self, "错误", f"加载XML文件失败:\n{str(e)}") def populate_table(self): # 暂时断开itemChanged信号 if hasattr(self, 'table') and self.table: try: self.table.itemChanged.disconnect(self.update_cell_background) except: pass self.table.setRowCount(len(self.table_data)) # 为type和data_type_id列设置下拉框委托 type_col_index = self.column_names.index('type') + 1 data_type_id_col_index = self.column_names.index('data_type_id') + 1 # 创建委托并设置选项 type_delegate = ComboBoxDelegate(self.table, sorted(self.type_options)) data_type_id_delegate = ComboBoxDelegate(self.table, sorted(self.data_type_id_options)) self.table.setItemDelegateForColumn(type_col_index, type_delegate) self.table.setItemDelegateForColumn(data_type_id_col_index, data_type_id_delegate) for row_idx, record in enumerate(self.table_data): # 添加序号列 seq_item = QTableWidgetItem(str(self.row_counter + row_idx)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(row_idx, 0, seq_item) # 添加数据列 for col_idx, col_name in enumerate(self.column_names): value = record.get(col_name, '') item = QTableWidgetItem(value) # 设置下拉框列可编辑 if col_name in ['type', 'data_type_id']: item.setFlags(item.flags() | Qt.ItemIsEditable) # 设置必填项背景色 if col_name in self.required_columns and not value.strip(): item.setBackground(QColor(255, 200, 200)) # 浅红色背景 self.table.setItem(row_idx, col_idx + 1, item) # 重新连接itemChanged信号 if hasattr(self, 'table') and self.table: self.table.itemChanged.connect(self.update_cell_background) def adjust_column_widths(self): self.table.setColumnWidth(0, 60) for col in range(1, self.table.columnCount()): self.table.resizeColumnToContents(col) if self.table.columnWidth(col) < 100: self.table.setColumnWidth(col, 100) def export_xml(self): if not self.table_data: QMessageBox.warning(self, "警告", "没有数据可导出") return file_path, _ = QFileDialog.getSaveFileName( self, "导出XML文件", "", "XML文件 (*.xml)" ) if not file_path: return try: with open(file_path, 'wb') as f: f.write(b'<?xml version="1.0" ?>\n') f.write(b'<DataRecords>\n') for record in self.table_data: attrs = ' '.join([f'{k}="{v}"' for k, v in record.items()]) f.write(f' <Record {attrs}/>\n'.encode('utf-8')) f.write(b'</DataRecords>') self.statusBar.showMessage(f"成功导出到: {file_path}", 3000) except Exception as e: QMessageBox.critical(self, "错误", f"导出XML文件失败:\n{str(e)}") # ... 前面的代码保持不变 ... def add_record(self): # 获取选中的行 selected_rows = set(index.row() for index in self.table.selectedIndexes()) insert_row = self.table.rowCount() # 默认插入到末尾 if selected_rows: # 取第一个选中的行作为参考行 insert_row = min(selected_rows) + 1 # 插入到选中行之后 # 在表格中插入新行 self.table.insertRow(insert_row) # 创建新记录 new_record = {col_name: "" for col_name in self.column_names} # 在table_data中插入新记录 self.table_data.insert(insert_row, new_record) # 添加序号列 seq_item = QTableWidgetItem(str(self.row_counter + insert_row)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(insert_row, 0, seq_item) # 为新行创建数据单元格 for col_idx, col_name in enumerate(self.column_names): item = QTableWidgetItem("") # 设置下拉框列可编辑 if col_name in ['type', 'data_type_id']: item.setFlags(item.flags() | Qt.ItemIsEditable) # 设置必填项背景色(浅红色提示) if col_name in self.required_columns: item.setBackground(QColor(255, 200, 200)) self.table.setItem(insert_row, col_idx + 1, item) # 更新所有行的序号 self.update_sequence_numbers() # 滚动到新行并自动选中 self.table.scrollToItem(self.table.item(insert_row, 0)) self.table.selectRow(insert_row) self.statusBar.showMessage("已添加新记录", 2000) # ... 后面的代码保持不变 ... def add_record(self): # 获取选中的行 selected_rows = set(index.row() for index in self.table.selectedIndexes()) insert_row = self.table.rowCount() # 默认插入到末尾 if selected_rows: # 取第一个选中的行作为参考行 insert_row = min(selected_rows) + 1 # 插入到选中行之后 # 在表格中插入新行 self.table.insertRow(insert_row) # 创建新记录 new_record = {col_name: "" for col_name in self.column_names} # 在table_data中插入新记录 self.table_data.insert(insert_row, new_record) # 添加序号列 seq_item = QTableWidgetItem(str(self.row_counter + insert_row)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(insert_row, 0, seq_item) # 为新行创建数据单元格 for col_idx, col_name in enumerate(self.column_names): item = QTableWidgetItem("") # 设置下拉框列可编辑 if col_name in ['type', 'data_type_id']: item.setFlags(item.flags() | Qt.ItemIsEditable) # 设置必填项背景色(浅红色提示) if col_name in self.required_columns: item.setBackground(QColor(255, 200, 200)) self.table.setItem(insert_row, col_idx + 1, item) # 更新所有行的序号 self.update_sequence_numbers() # 滚动到新行并自动选中 self.table.scrollToItem(self.table.item(insert_row, 0)) self.table.selectRow(insert_row) self.statusBar.showMessage("已添加新记录", 2000) def delete_record(self): selected_rows = set(index.row() for index in self.table.selectedIndexes()) if not selected_rows: QMessageBox.warning(self, "警告", "请先选择要删除的记录") return reply = QMessageBox.question(self, "确认删除", f"确定要删除选中的 {len(selected_rows)} 条记录吗?", QMessageBox.Yes | QMessageBox.No) if reply != QMessageBox.Yes: return for row in sorted(selected_rows, reverse=True): self.table.removeRow(row) if row < len(self.table_data): del self.table_data[row] self.update_sequence_numbers() self.statusBar.showMessage(f"已删除 {len(selected_rows)} 条记录", 3000) def update_sequence_numbers(self): for row_idx in range(self.table.rowCount()): seq_item = QTableWidgetItem(str(self.row_counter + row_idx)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(row_idx, 0, seq_item) def save_changes(self): if not self.current_file: QMessageBox.warning(self, "警告", "请先加载XML文件") return # 检查是否有空必填项 empty_fields = [] for row_idx in range(self.table.rowCount()): for col_idx, col_name in enumerate(self.column_names): if col_name in self.required_columns: item = self.table.item(row_idx, col_idx + 1) if item is None or not item.text().strip(): seq_item = self.table.item(row_idx, 0) row_num = seq_item.text() if seq_item else str(row_idx + 1) empty_fields.append(f"行 {row_num} 的 '{col_name}' 列") if empty_fields: error_msg = "以下必填项为空,请填写完整后保存:\n" error_msg += "\n".join(empty_fields[:10]) # 最多显示10个错误 if len(empty_fields) > 10: error_msg += f"\n...(共{len(empty_fields)}个错误)" QMessageBox.warning(self, "保存失败", error_msg) return try: # 更新内存中的数据 for row_idx in range(self.table.rowCount()): if row_idx >= len(self.table_data): self.table_data.append({col_name: "" for col_name in self.column_names}) for col_idx, col_name in enumerate(self.column_names): item = self.table.item(row_idx, col_idx + 1) if item: self.table_data[row_idx][col_name] = item.text() # 更新下拉框选项 if col_name == 'type' and item.text(): self.type_options.add(item.text()) elif col_name == 'data_type_id' and item.text(): self.data_type_id_options.add(item.text()) else: self.table_data[row_idx][col_name] = "" # 保存到原文件 with open(self.current_file, 'wb') as f: f.write(b'<?xml version="1.0" ?>\n') f.write(b'<DataRecords>\n') for record in self.table_data: attrs = ' '.join([f'{k}="{v}"' for k, v in record.items()]) f.write(f' <Record {attrs}/>\n'.encode('utf-8')) f.write(b'</DataRecords>') # 更新原始数据和序号计数器 self.original_table_data = self.table_data.copy() # self.row_counter = self.table.rowCount() + 1 # 更新下拉框委托 self.populate_table() self.statusBar.showMessage("修改已保存", 2000) except Exception as e: QMessageBox.critical(self, "错误", f"保存失败: {str(e)}") def search_records(self): search_text = self.search_box.text().strip().lower() selected_column = self.column_combo.currentText() if not search_text: self.clear_search() return if not self.original_table_data: self.original_table_data = self.table_data.copy() matched_rows = [] matched_indices = [] for row_idx, record in enumerate(self.original_table_data): match_found = False if selected_column == "所有列": for col_name in self.column_names: value = record.get(col_name, '').lower() if search_text in value: match_found = True break else: value = record.get(selected_column, '').lower() if search_text in value: match_found = True if match_found: matched_rows.append(record) matched_indices.append(row_idx + 1) self.table_data = matched_rows self.populate_table_with_original_indices(matched_indices) self.adjust_column_widths() if matched_rows: for row in range(self.table.rowCount()): for col in range(1, self.table.columnCount()): item = self.table.item(row, col) if item: col_name = self.column_names[col - 1] value = item.text().lower() if (selected_column == "所有列" and search_text in value) or \ (col_name == selected_column and search_text in value): item.setBackground(QBrush(QColor(255, 255, 0))) item.setFont(QFont("Arial", 10, QFont.Bold)) else: # 恢复必填项背景色 if col_name in self.required_columns and not item.text().strip(): item.setBackground(QColor(255, 200, 200)) else: item.setBackground(QBrush(Qt.white)) item.setFont(QFont("Arial", 9)) self.statusBar.showMessage(f"找到 {len(matched_rows)} 条匹配记录", 3000) else: QMessageBox.information(self, "搜索结果", "未找到匹配记录") self.statusBar.showMessage("未找到匹配记录", 2000) def populate_table_with_original_indices(self, indices): # 暂时断开itemChanged信号 if hasattr(self, 'table') and self.table: try: self.table.itemChanged.disconnect(self.update_cell_background) except: pass self.table.setRowCount(len(self.table_data)) # 为type和data_type_id列设置下拉框委托 type_col_index = self.column_names.index('type') + 1 data_type_id_col_index = self.column_names.index('data_type_id') + 1 type_delegate = ComboBoxDelegate(self.table, sorted(self.type_options)) data_type_id_delegate = ComboBoxDelegate(self.table, sorted(self.data_type_id_options)) self.table.setItemDelegateForColumn(type_col_index, type_delegate) self.table.setItemDelegateForColumn(data_type_id_col_index, data_type_id_delegate) for row_idx, record in enumerate(self.table_data): seq_item = QTableWidgetItem(str(indices[row_idx])) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(row_idx, 0, seq_item) for col_idx, col_name in enumerate(self.column_names): value = record.get(col_name, '') item = QTableWidgetItem(value) if col_name in ['type', 'data_type_id']: item.setFlags(item.flags() | Qt.ItemIsEditable) # 设置必填项背景色 if col_name in self.required_columns and not value.strip(): item.setBackground(QColor(255, 200, 200)) self.table.setItem(row_idx, col_idx + 1, item) # 重新连接itemChanged信号 if hasattr(self, 'table') and self.table: self.table.itemChanged.connect(self.update_cell_background) def clear_search(self): if self.original_table_data: self.table_data = self.original_table_data.copy() self.populate_table() self.adjust_column_widths() self.search_box.clear() self.statusBar.showMessage("搜索已清除", 2000) if __name__ == "__main__": app = QApplication(sys.argv) window = XMLDataManager() window.show() sys.exit(app.exec_()) 显示修改后的完整程序
最新发布
08-21
对于type,data_type_id, 添加记录时,可以根据之前内容设置下拉框选择,也可以手动输入import sys import xml.etree.ElementTree as ET from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QPushButton, QFileDialog, QLineEdit, QLabel, QMessageBox, QHBoxLayout, QVBoxLayout, QWidget, QHeaderView, QAbstractItemView, QComboBox, QSizePolicy) from PyQt5.QtCore import Qt, QSize from PyQt5.QtGui import QBrush, QColor, QFont import os class XMLDataManager(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("RawData配置管理器") self.setGeometry(100, 100, 1400, 800) # 增大初始窗口尺寸 # 初始化变量 self.current_file = None self.table_data = [] self.original_table_data = [] # 保存原始数据用于恢复 self.row_counter = 1 # 序号计数器 # 定义列名列表 self.column_names = [ "path", "type", "variable", "id", "data_type_id", "subsystem_id", "parts", "parts_id", "attribute", "attribute_id", "mapping" ] # 创建UI self.init_ui() def init_ui(self): # 创建中央部件 central_widget = QWidget() self.setCentralWidget(central_widget) # 创建按钮 - 使用品蓝色 (RGB:65,105,225) self.load_btn = QPushButton("加载XML文件") self.export_btn = QPushButton("导出XML文件") self.add_btn = QPushButton("添加记录") self.delete_btn = QPushButton("删除记录") self.save_btn = QPushButton("保存修改") self.search_btn = QPushButton("搜索") self.clear_btn = QPushButton("清空搜索") # 设置按钮样式 - 品蓝色 (RGB:65,105,225) button_style = """ QPushButton { background-color: rgb(65, 105, 225); color: white; border: none; padding: 8px 16px; text-align: center; font-size: 14px; margin: 4px 2px; border-radius: 4px; } QPushButton:hover { background-color: rgb(55, 95, 215); } QPushButton:pressed { background-color: rgb(45, 85, 205); } """ for btn in [self.load_btn, self.export_btn, self.add_btn, self.delete_btn, self.save_btn, self.search_btn, self.clear_btn]: btn.setStyleSheet(button_style) # 创建搜索框 self.search_box = QLineEdit() self.search_box.setPlaceholderText("输入搜索内容...") self.search_box.setMinimumWidth(300) self.search_box.setStyleSheet(""" QLineEdit { padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } """) # 创建列选择下拉框 self.column_combo = QComboBox() self.column_combo.setMinimumWidth(120) self.column_combo.addItem("所有列") self.column_combo.addItems(self.column_names) self.column_combo.setCurrentIndex(0) self.column_combo.setStyleSheet(""" QComboBox { padding: 6px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; } """) # 创建表格 - 增加序号列 self.table = QTableWidget() self.table.setColumnCount(len(self.column_names) + 1) # 增加序号列 headers = ["序号"] + self.column_names # 添加序号列头 self.table.setHorizontalHeaderLabels(headers) self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Interactive) self.table.horizontalHeader().setStretchLastSection(True) # 最后一列自动拉伸 self.table.setSelectionBehavior(QTableWidget.SelectRows) # 设置为多行选择模式 self.table.setSelectionMode(QAbstractItemView.ExtendedSelection) # 设置表格样式 self.table.setStyleSheet(""" QTableWidget { gridline-color: #ddd; font-size: 12px; } QHeaderView::section { background-color: #f2f2f2; padding: 4px; border: 1px solid #ddd; font-weight: bold; } QTableCornerButton::section { background-color: #f2f2f2; border: 1px solid #ddd; } """) # 设置序号列样式 self.table.setColumnWidth(0, 60) # 序号列固定宽度 self.table.verticalHeader().setVisible(False) # 隐藏行号列 # 设置列宽策略 self.table.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # 布局设置 - 移除了"显示全部"按钮 top_layout = QHBoxLayout() top_layout.addWidget(self.load_btn) top_layout.addWidget(self.export_btn) top_layout.addWidget(self.add_btn) top_layout.addWidget(self.delete_btn) top_layout.addWidget(self.save_btn) top_layout.addStretch() top_layout.addWidget(QLabel("搜索列:")) top_layout.addWidget(self.column_combo) top_layout.addWidget(QLabel("搜索内容:")) top_layout.addWidget(self.search_box) top_layout.addWidget(self.search_btn) top_layout.addWidget(self.clear_btn) main_layout = QVBoxLayout() main_layout.addLayout(top_layout) main_layout.addWidget(self.table) central_widget.setLayout(main_layout) # 连接信号 self.load_btn.clicked.connect(self.load_xml) self.export_btn.clicked.connect(self.export_xml) self.add_btn.clicked.connect(self.add_record) self.delete_btn.clicked.connect(self.delete_record) self.save_btn.clicked.connect(self.save_changes) self.search_btn.clicked.connect(self.search_records) self.clear_btn.clicked.connect(self.clear_search) # 状态栏 self.statusBar = self.statusBar() self.statusBar.setStyleSheet("background-color: #f0f0f0; padding: 4px;") def load_xml(self): file_path, _ = QFileDialog.getOpenFileName( self, "选择XML文件", "", "XML文件 (*.xml)" ) if not file_path: return try: tree = ET.parse(file_path) root = tree.getroot() self.current_file = file_path file_name = os.path.basename(file_path) self.setWindowTitle(f"RawData配置管理器 - {file_name}") # 清空表格 self.table.setRowCount(0) self.table_data = [] self.original_table_data = [] # 重置原始数据 self.row_counter = 1 # 重置序号计数器 # 解析XML并填充表格 for record in root.findall('Record'): row_data = {} for col_name in self.column_names: row_data[col_name] = record.get(col_name, '') self.table_data.append(row_data) # 保存原始数据副本 self.original_table_data = self.table_data.copy() self.populate_table() self.adjust_column_widths() # 调整列宽以适应内容 self.statusBar.showMessage(f"成功加载文件: {file_path}", 3000) except Exception as e: QMessageBox.critical(self, "错误", f"加载XML文件失败:\n{str(e)}") def populate_table(self): self.table.setRowCount(len(self.table_data)) for row_idx, record in enumerate(self.table_data): # 添加序号列(从1开始) seq_item = QTableWidgetItem(str(self.row_counter + row_idx)) seq_item.setTextAlignment(Qt.AlignCenter) # 居中显示 seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) # 设置为不可编辑 self.table.setItem(row_idx, 0, seq_item) # 添加数据列 for col_idx, col_name in enumerate(self.column_names): value = record.get(col_name, '') item = QTableWidgetItem(value) self.table.setItem(row_idx, col_idx + 1, item) def adjust_column_widths(self): """自动调整列宽以适应内容""" # 序号列固定宽度 self.table.setColumnWidth(0, 60) # 调整数据列宽度 for col in range(1, self.table.columnCount()): self.table.resizeColumnToContents(col) if self.table.columnWidth(col) < 100: self.table.setColumnWidth(col, 100) def export_xml(self): if not self.table_data: QMessageBox.warning(self, "警告", "没有数据可导出") return file_path, _ = QFileDialog.getSaveFileName( self, "导出XML文件", "", "XML文件 (*.xml)" ) if not file_path: return try: # 创建XML结构 root = ET.Element("DataRecords") for record in self.table_data: record_elem = ET.SubElement(root, "Record") for col_name in self.column_names: value = record.get(col_name, '') record_elem.set(col_name, value) # 美化XML输出 with open(file_path, 'wb') as f: f.write(b'<?xml version="1.0" ?>\n') f.write(b'<DataRecords>\n') for record in self.table_data: attrs = ' '.join([f'{k}="{v}"' for k, v in record.items()]) f.write(f' <Record {attrs}/>\n'.encode('utf-8')) f.write(b'</DataRecords>') self.statusBar.showMessage(f"成功导出到: {file_path}", 3000) except Exception as e: QMessageBox.critical(self, "错误", f"导出XML文件失败:\n{str(e)}") def add_record(self): # 添加空行 row_count = self.table.rowCount() self.table.insertRow(row_count) # 创建空记录 new_record = {col_name: "" for col_name in self.column_names} self.table_data.append(new_record) # 添加序号列 seq_item = QTableWidgetItem(str(self.row_counter + row_count)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) # 设置为不可编辑 self.table.setItem(row_count, 0, seq_item) # 为新行创建数据单元格 for col_idx, col_name in enumerate(self.column_names): item = QTableWidgetItem("") self.table.setItem(row_count, col_idx + 1, item) # 滚动到新行并自动选中 self.table.scrollToBottom() self.table.selectRow(row_count) self.statusBar.showMessage("已添加新记录", 2000) def delete_record(self): # 获取所有选中的行(支持多选) selected_rows = set(index.row() for index in self.table.selectedIndexes()) if not selected_rows: QMessageBox.warning(self, "警告", "请先选择要删除的记录") return # 确认删除 reply = QMessageBox.question(self, "确认删除", f"确定要删除选中的 {len(selected_rows)} 条记录吗?", QMessageBox.Yes | QMessageBox.No) if reply != QMessageBox.Yes: return # 从后往前删除避免索引问题 for row in sorted(selected_rows, reverse=True): self.table.removeRow(row) if row < len(self.table_data): del self.table_data[row] # 更新序号显示 self.update_sequence_numbers() self.statusBar.showMessage(f"已删除 {len(selected_rows)} 条记录", 3000) def update_sequence_numbers(self): """更新所有行的序号显示""" for row_idx in range(self.table.rowCount()): seq_item = QTableWidgetItem(str(self.row_counter + row_idx)) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(row_idx, 0, seq_item) def save_changes(self): if not self.current_file: QMessageBox.warning(self, "警告", "请先加载XML文件") return try: # 更新内存中的数据 for row_idx in range(self.table.rowCount()): # 确保数据行存在 if row_idx >= len(self.table_data): self.table_data.append({col_name: "" for col_name in self.column_names}) # 更新数据列(跳过序号列) for col_idx, col_name in enumerate(self.column_names): item = self.table.item(row_idx, col_idx + 1) # 从第1列开始 if item: self.table_data[row_idx][col_name] = item.text() else: self.table_data[row_idx][col_name] = "" # 保存到原文件 with open(self.current_file, 'wb') as f: f.write(b'<?xml version="1.0" ?>\n') f.write(b'<DataRecords>\n') for record in self.table_data: attrs = ' '.join([f'{k}="{v}"' for k, v in record.items()]) f.write(f' <Record {attrs}/>\n'.encode('utf-8')) f.write(b'</DataRecords>') # 更新原始数据 self.original_table_data = self.table_data.copy() self.row_counter = self.table.rowCount() + 1 # 重置序号计数器 self.statusBar.showMessage("修改已保存", 2000) except Exception as e: QMessageBox.critical(self, "错误", f"保存失败: {str(e)}") def search_records(self): search_text = self.search_box.text().strip().lower() selected_column = self.column_combo.currentText() if not search_text: self.clear_search() return # 保存原始数据(如果当前是过滤状态) if not self.original_table_data: self.original_table_data = self.table_data.copy() # 过滤匹配的行 matched_rows = [] matched_indices = [] # 记录匹配行的原始序号 for row_idx, record in enumerate(self.original_table_data): match_found = False # 检查是否匹配 if selected_column == "所有列": # 在所有列中搜索 for col_name in self.column_names: value = record.get(col_name, '').lower() if search_text in value: match_found = True break else: # 在指定列中搜索 value = record.get(selected_column, '').lower() if search_text in value: match_found = True if match_found: matched_rows.append(record) # 记录原始行号(从1开始) matched_indices.append(row_idx + 1) # 更新表格数据 self.table_data = matched_rows self.populate_table_with_original_indices(matched_indices) # 使用原始序号 self.adjust_column_widths() # 调整列宽 # 高亮匹配项 if matched_rows: # 高亮匹配单元格 for row in range(self.table.rowCount()): for col in range(1, self.table.columnCount()): # 从第1列开始(跳过序号列) item = self.table.item(row, col) if item: col_name = self.column_names[col - 1] # 调整列索引 value = item.text().lower() # 仅在匹配列高亮 if (selected_column == "所有列" and search_text in value) or \ (col_name == selected_column and search_text in value): item.setBackground(QBrush(QColor(255, 255, 0))) # 黄色高亮 item.setFont(QFont("Arial", 10, QFont.Bold)) # 加粗字体 else: item.setBackground(QBrush(Qt.white)) item.setFont(QFont("Arial", 9)) # 显示状态信息 self.statusBar.showMessage(f"找到 {len(matched_rows)} 条匹配记录", 3000) else: # 未找到匹配记录时弹窗提示 QMessageBox.information(self, "搜索结果", "未找到匹配记录") self.statusBar.showMessage("未找到匹配记录", 2000) def populate_table_with_original_indices(self, indices): """使用原始序号填充表格""" self.table.setRowCount(len(self.table_data)) for row_idx, record in enumerate(self.table_data): # 使用原始序号 seq_item = QTableWidgetItem(str(indices[row_idx])) seq_item.setTextAlignment(Qt.AlignCenter) seq_item.setFlags(seq_item.flags() & ~Qt.ItemIsEditable) self.table.setItem(row_idx, 0, seq_item) # 添加数据列 for col_idx, col_name in enumerate(self.column_names): value = record.get(col_name, '') item = QTableWidgetItem(value) self.table.setItem(row_idx, col_idx + 1, item) def clear_search(self): """清除搜索高亮并恢复所有数据""" if self.original_table_data: self.table_data = self.original_table_data.copy() self.populate_table() self.adjust_column_widths() # 清除搜索框内容 self.search_box.clear() self.statusBar.showMessage("搜索已清除", 2000) if __name__ == "__main__": app = QApplication(sys.argv) window = XMLDataManager() window.show() sys.exit(app.exec_())
08-20
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值