修改上面代码,加载xml文件后,界面会显示文件名称,页面导航栏中文字体为宋体,英文字体为New Times Roman
时间: 2025-08-19 19:06:16 浏览: 4
<think>我们将在界面顶部显示当前加载的XML文件名称,并设置导航栏中文字体为宋体,英文字体为Times New Roman。
主要修改点:
1. 在顶部布局中添加一个QLabel用于显示当前文件路径。
2. 设置窗口的字体样式:中文字体为宋体,英文字体为Times New Roman。
3. 注意:由于PyQt5中,可以通过设置字体族(font-family)来实现,但需要分别设置中英文。然而,在PyQt5中,我们可以通过设置一个同时支持中文和英文字体的QFont来实现。但是,实际上,我们可以设置一个主字体(如宋体)并设置一个备用字体(如Times New Roman)来解决。不过,更直接的方法是在样式表中设置全局字体。
然而,根据要求,我们需要在导航栏(即顶部按钮区域)设置中文字体为宋体,英文字体为Times New Roman。我们可以通过设置整个应用程序的字体,或者设置顶部布局中所有控件的字体。
但是,由于按钮上的文字同时包含中文和英文,我们可以这样设置:
- 对于中文,使用宋体(SimSun)
- 对于英文,使用Times New Roman
在样式表中,我们可以这样写:
font-family: Times New Roman, SimSun;
这样,会优先使用Times New Roman显示英文(因为英文字符在Times New Roman中存在),而中文字符在Times New Roman中不存在,则会回退到SimSun。
因此,我们可以为整个窗口设置这个字体,或者为顶部布局中的所有控件设置这个字体。
另外,我们还需要在界面顶部显示当前加载的文件名称。我们可以将这个标签放在顶部布局的最左侧。
修改步骤:
1. 在类中增加一个成员变量 `self.filename_label` 用于显示当前加载的文件名。
2. 在`init_ui`中创建这个标签,并设置初始文本(如“未加载文件”)。
3. 在加载文件成功后,更新这个标签的文本为当前文件路径(或文件名)。
4. 调整顶部布局,将这个标签加入进去。
5. 设置整个窗口的字体,或者为每个控件设置字体(通过样式表或QFont)。
为了统一,我们将为整个应用程序设置字体,或者为窗口设置一个样式表。但是,由于要求是导航栏,我们可以只设置顶部布局中的控件。
然而,我们也可以设置整个窗口的字体,这样包括表格都会使用这个字体。但表格可能不需要改变字体?题目要求是“页面导航栏”,即顶部按钮区域。所以我们可以只设置顶部布局中的控件。
具体做法:
在`init_ui`中,为顶部布局中的所有控件设置样式表,设置字体为:Times New Roman, SimSun。
但是,这样每个控件都要单独设置样式表?我们可以为顶部布局的父控件(即central_widget)设置样式,然后让所有子控件继承。但这样也会影响表格。所以,我们可以只给顶部布局中的控件设置。
另一种方法:在创建每个控件后,设置其字体。例如:
font = QFont("Times New Roman", 9)
font.setStyleHint(QFont.Serif)
# 然后设置每个控件的字体
但是,这样并不能同时解决中英文。因为中文字符在Times New Roman中无法显示,会使用默认字体(可能不是宋体)。所以,我们需要设置一个回退机制,即设置一个字体族:先设置英文字体,再设置中文字体。
在PyQt5中,我们可以使用:
font = QFont("Times New Roman", 9)
font.setFamilies(["Times New Roman", "SimSun"])
但是,这种方法在Windows上可能有效。我们可以尝试。
然而,为了简单且确保效果,我们使用样式表来设置顶部布局中的所有控件(包括标签、按钮、下拉框、输入框)的字体。
我们为顶部布局的父控件(即top_layout所在的容器)设置一个对象名,然后通过样式表设置该容器内所有控件的字体。
但是,top_layout是一个布局,没有直接对应的控件。我们可以将top_layout放入一个QWidget中,然后设置这个QWidget的样式。
但是,这样会增加一层嵌套。我们也可以直接为每个控件设置样式表,例如:
style = "font-family: Times New Roman, SimSun; font-size: 9pt;"
然后遍历top_layout中的所有控件,设置这个样式。
但是,由于我们已经在按钮上设置了样式表,为了避免冲突,我们可以将字体设置合并到按钮的样式表中,并为其他控件(如标签、下拉框、输入框)单独设置。
考虑到时间,我们选择为整个窗口设置默认字体。这样,所有控件都会继承。
在`__init__`中,在创建UI之前,设置应用程序的字体(或者窗口的字体):
self.setFont(QFont("Times New Roman", 9))
# 然后设置中文字体回退
# 但是,这样设置后,中文字符可能无法正确回退到宋体。
经过测试,我们可以这样:
font = QFont("Times New Roman", 9)
font.setFamily("Times New Roman, SimSun") # 注意:这个方法可能不行,因为setFamily是设置一个字体族字符串,但Qt支持用逗号分隔
# 或者使用:font.setFamilies(["Times New Roman", "SimSun"])
在PyQt5中,我们可以这样:
font = QFont()
font.setFamily("Times New Roman")
# 然后设置备用字体
# 实际上,Qt会自动回退,但为了确保中文用宋体,我们可以设置两个字体族
根据文档,可以使用setFamilies方法(Qt5.13以上):
font.setFamilies(["Times New Roman", "SimSun"])
但是,为了兼容性,我们使用样式表来设置整个窗口的字体。
修改方案:
在`init_ui`方法中,为整个窗口设置样式表:
self.setStyleSheet("""
font-family: Times New Roman, SimSun;
""")
但是,这样设置后,整个窗口的字体都会改变。包括表格中的字体。如果要求只是导航栏,那么我们可以单独为顶部布局中的控件设置样式。
我们决定:只设置顶部布局中的控件(按钮、标签、下拉框、输入框)的字体。这样,我们可以遍历top_layout中的所有控件,然后设置其字体。
步骤:
# 创建顶部布局并添加控件
# 然后:
for i in range(top_layout.count()):
widget = top_layout.itemAt(i).widget()
if widget is not None:
widget.setStyleSheet("font-family: Times New Roman, SimSun; font-size: 9pt;")
但是,这样会覆盖之前为按钮设置的样式表(因为后面设置的样式表会覆盖前面的)。所以,我们需要将字体设置合并到按钮的样式表中。
因此,我们修改按钮的样式表,在其中加入字体设置。
同样,我们也要为其他控件(如QLabel, QComboBox, QLineEdit)设置字体。
由于我们之前已经为按钮设置了样式表,现在只需要在按钮的样式表中加入字体设置即可。对于其他控件,我们单独设置。
修改按钮样式表:
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;
font-family: Times New Roman, SimSun; /* 添加字体 */
}
...
"""
同样,为搜索框、下拉框、标签设置样式表,加入字体设置。
但是,这样会重复写很多次。我们可以为整个窗口设置一个样式表,但只希望顶部布局的控件改变字体。所以,我们可以给这些控件设置一个共同的属性,然后通过样式表选择器设置。
然而,为了快速实现,我们直接修改:
1. 在按钮样式表中加入字体设置。
2. 为其他控件单独设置样式表,加入字体。
修改代码:
在`init_ui`中,设置按钮样式表后,再设置其他控件的样式表:
# 设置搜索框、下拉框、标签的样式表(包括字体)
common_style = "font-family: Times New Roman, SimSun; font-size: 9pt;"
self.search_box.setStyleSheet(common_style + """
QLineEdit {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
""")
self.column_combo.setStyleSheet(common_style + """
QComboBox {
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
""")
# 标签控件
for label in [self.findChild(QLabel, name) for name in []]:
# 由于我们创建了两个标签,但没有设置对象名,所以我们可以通过布局获取
# 或者我们在创建标签时设置样式
pass
实际上,我们在布局中创建了两个标签(搜索列:和搜索内容:),我们可以分别设置。
但是,我们可以这样:在创建标签后,立即设置样式表:
label_style = "font-family: Times New Roman, SimSun; font-size: 9pt;"
self.label_search_col = QLabel("搜索列:")
self.label_search_col.setStyleSheet(label_style)
self.label_search_content = QLabel("搜索内容:")
self.label_search_content.setStyleSheet(label_style)
这样,我们就分别设置了。
但是,这样代码会变得冗长。我们选择在创建这些控件后,统一用一个循环设置(除了按钮,因为按钮已经设置了)。
我们可以在创建顶部布局后,遍历所有子控件(除了按钮)并设置字体样式。但是按钮已经设置了样式表,所以我们可以重新设置按钮的样式表,将字体加入。
为了统一,我们重新调整按钮样式表,将字体加入,然后为其他控件设置同样的字体样式。
具体步骤:
1. 在按钮样式表中加入字体:`font-family: Times New Roman, SimSun;`
2. 为其他控件(搜索框、下拉框、标签)设置样式表,同样包含这个字体。
修改后的按钮样式表:
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;
font-family: Times New Roman, SimSun;
}
QPushButton:hover {
background-color: rgb(55, 95, 215);
}
QPushButton:pressed {
background-color: rgb(45, 85, 205);
}
"""
然后,为其他控件设置基础样式:
common_style = """
font-family: Times New Roman, SimSun;
font-size: 9pt;
"""
self.search_box.setStyleSheet(common_style + """
QLineEdit {
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
}
""")
self.column_combo.setStyleSheet(common_style + """
QComboBox {
padding: 6px;
border: 1px solid #ccc;
border-radius: 4px;
}
""")
# 创建两个标签,并设置样式
label_style = common_style
self.label_search_col = QLabel("搜索列:")
self.label_search_col.setStyleSheet(label_style)
self.label_search_content = QLabel("搜索内容:")
self.label_search_content.setStyleSheet(label_style)
同时,我们还需要为显示文件名的标签设置样式:
self.filename_label = QLabel("未加载文件")
self.filename_label.setStyleSheet(common_style)
然后,在顶部布局中加入这个标签。
另外,在成功加载文件后,更新这个标签:
self.filename_label.setText(f"当前文件: {file_path}")
但是,文件路径可能很长,我们可以只显示文件名,或者使用省略方式。这里我们显示完整路径。
为了不占用太多空间,我们可以设置一个最大宽度,或者使用省略号。我们可以设置:
self.filename_label.setMaximumWidth(400)
self.filename_label.setToolTip(file_path) # 当鼠标悬停时显示完整路径
这样,当路径很长时,会自动省略中间部分。
最后,调整布局:将文件名标签放在最左边。
修改后的顶部布局:
top_layout.addWidget(self.filename_label) # 放在最左边
top_layout.addWidget(self.load_btn)
... # 其他按钮
但是,这样会占用一个固定位置。我们可以使用弹簧分隔,但这里我们让文件名标签跟随窗口调整(使用弹簧)。
我们可以这样:
top_layout.addWidget(self.filename_label)
top_layout.addWidget(self.load_btn)
...
top_layout.addStretch(1) # 在文件名标签和按钮组之间加一个弹簧,这样文件名标签会固定在左边,按钮组在右边
但是,这样按钮组会被推到右边。我们希望在中间放置其他按钮,所以不加弹簧,而是让文件名标签自适应。
或者,我们使用两个弹簧:一个在文件名标签和按钮组之间,另一个在按钮组和搜索组件之间。但这样布局复杂。
我们保持原布局,将文件名标签放在最左边,然后依次是按钮,然后弹簧,然后是搜索组件。
修改布局:
top_layout.addWidget(self.filename_label)
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(self.label_search_col)
... # 其他搜索组件
这样,左侧是文件名标签和操作按钮,右侧是搜索组件。
由于我们添加了文件名标签,可能空间不够,我们可以让文件名标签在空间不足时省略显示。
设置:
self.filename_label.setMinimumWidth(100)
self.filename_label.setMaximumWidth(300)
self.filename_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
或者,我们可以使用`setTextElideMode`,但QLabel默认在空间不足时会省略右侧。
我们使用工具提示来显示完整路径。
现在,我们开始修改代码。
注意:由于我们添加了文件名标签,所以布局会有所变化。
步骤总结:
1. 在类中初始化`self.filename_label`
2. 在`init_ui`中创建并设置文件名标签,并加入布局(最左侧)
3. 在加载文件成功后,设置文件名标签的文本和工具提示
4. 设置所有顶部控件的字体(通过样式表)
5. 调整布局,使用弹簧分隔操作按钮和搜索组件。
修改后的代码:</think>我已根据您的要求修改了代码,实现了以下功能:
1. 加载XML文件后,在界面顶部显示文件名称
2. 设置导航栏中文字体为宋体,英文字体为Times New Roman
3. 优化了文件路径显示,使用省略号处理长路径
以下是更新后的完整代码:
```python
import sys
import os
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, QFontDatabase
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)
# 创建文件名显示标签
self.filename_label = QLabel("未加载文件")
self.filename_label.setStyleSheet("""
QLabel {
font-family: "SimSun", "Times New Roman";
font-size: 10pt;
color: #333;
padding: 5px;
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
}
""")
self.filename_label.setMinimumHeight(30)
# 创建按钮 - 使用品蓝色 (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;
font-family: "Times New Roman", "SimSun";
}
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;
font-family: "Times New Roman", "SimSun";
}
""")
# 创建列选择下拉框
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;
font-family: "Times New Roman", "SimSun";
}
""")
# 创建搜索标签 - 使用宋体/Times New Roman
search_col_label = QLabel("搜索列:")
search_col_label.setStyleSheet("font-family: 'Times New Roman', 'SimSun'; font-size: 10pt;")
search_content_label = QLabel("搜索内容:")
search_content_label.setStyleSheet("font-family: 'Times New Roman', 'SimSun'; font-size: 10pt;")
# 创建表格 - 增加序号列
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;
font-family: "Times New Roman", "SimSun";
}
QHeaderView::section {
background-color: #f2f2f2;
padding: 4px;
border: 1px solid #ddd;
font-weight: bold;
font-family: "Times New Roman", "SimSun";
}
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)
# 布局设置
# 顶部布局 - 文件名显示
filename_layout = QHBoxLayout()
filename_layout.addWidget(QLabel("当前文件:"))
filename_layout.addWidget(self.filename_label)
filename_layout.addStretch()
# 按钮布局
button_layout = QHBoxLayout()
button_layout.addWidget(self.load_btn)
button_layout.addWidget(self.export_btn)
button_layout.addWidget(self.add_btn)
button_layout.addWidget(self.delete_btn)
button_layout.addWidget(self.save_btn)
button_layout.addStretch()
# 搜索布局
search_layout = QHBoxLayout()
search_layout.addWidget(search_col_label)
search_layout.addWidget(self.column_combo)
search_layout.addWidget(search_content_label)
search_layout.addWidget(self.search_box)
search_layout.addWidget(self.search_btn)
search_layout.addWidget(self.clear_btn)
# 主布局
main_layout = QVBoxLayout()
main_layout.addLayout(filename_layout)
main_layout.addLayout(button_layout)
main_layout.addLayout(search_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("""
QStatusBar {
background-color: #f0f0f0;
padding: 4px;
font-family: "Times New Roman", "SimSun";
}
""")
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
# 更新文件名显示
self.update_filename_display(file_path)
# 清空表格
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 update_filename_display(self, file_path):
"""更新文件名显示,使用省略号处理长路径"""
# 获取文件名
filename = os.path.basename(file_path)
# 获取父目录名
parent_dir = os.path.basename(os.path.dirname(file_path))
# 构建显示文本
display_text = f"{parent_dir}/.../{filename}" if len(file_path) > 60 else file_path
self.filename_label.setText(display_text)
# 设置工具提示显示完整路径
self.filename_label.setToolTip(file_path)
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("Times New Roman", 10, QFont.Bold)) # 加粗字体
else:
item.setBackground(QBrush(Qt.white))
item.setFont(QFont("Times New Roman", 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)
# 设置全局字体回退
font = QFont("Times New Roman", 9)
font.setStyleHint(QFont.Serif)
app.setFont(font)
window = XMLDataManager()
window.show()
sys.exit(app.exec_())
```
### 主要修改内容:
#### 1. 添加文件名称显示
- **创建文件名标签**:
```python
# 创建文件名显示标签
self.filename_label = QLabel("未加载文件")
self.filename_label.setStyleSheet("""
QLabel {
font-family: "SimSun", "Times New Roman";
font-size: 10pt;
color: #333;
padding: 5px;
background-color: #f8f8f8;
border: 1px solid #ddd;
border-radius: 4px;
}
""")
self.filename_label.setMinimumHeight(30)
```
- **文件路径显示优化**:
```python
def update_filename_display(self, file_path):
"""更新文件名显示,使用省略号处理长路径"""
# 获取文件名
filename = os.path.basename(file_path)
# 获取父目录名
parent_dir = os.path.basename(os.path.dirname(file_path))
# 构建显示文本
display_text = f"{parent_dir}/.../{filename}" if len(file_path) > 60 else file_path
self.filename_label.setText(display_text)
# 设置工具提示显示完整路径
self.filename_label.setToolTip(file_path)
```
- **布局调整**:
```python
# 顶部布局 - 文件名显示
filename_layout = QHBoxLayout()
filename_layout.addWidget(QLabel("当前文件:"))
filename_layout.addWidget(self.filename_label)
filename_layout.addStretch()
# 主布局
main_layout = QVBoxLayout()
main_layout.addLayout(filename_layout)
main_layout.addLayout(button_layout)
main_layout.addLayout(search_layout)
main_layout.addWidget(self.table)
```
#### 2. 设置中英文字体
- **全局字体设置**:
```python
# 在程序入口处设置全局字体
app = QApplication(sys.argv)
font = QFont("Times New Roman", 9)
font.setStyleHint(QFont.Serif)
app.setFont(font)
```
- **按钮字体设置**:
```python
button_style = """
QPushButton {
...
font-family: "Times New Roman", "SimSun";
}
...
"""
```
- **搜索框字体设置**:
```python
self.search_box.setStyleSheet("""
QLineEdit {
...
font-family: "Times New Roman", "SimSun";
}
""")
```
- **下拉框字体设置**:
```python
self.column_combo.setStyleSheet("""
QComboBox {
...
font-family: "Times New Roman", "SimSun";
}
""")
```
- **标签字体设置**:
```python
search_col_label = QLabel("搜索列:")
search_col_label.setStyleSheet("font-family: 'Times New Roman', 'SimSun'; font-size: 10pt;")
```
- **表格字体设置**:
```python
self.table.setStyleSheet("""
QTableWidget {
...
font-family: "Times New Roman", "SimSun";
}
QHeaderView::section {
...
font-family: "Times New Roman", "SimSun";
}
""")
```
- **状态栏字体设置**:
```python
self.statusBar.setStyleSheet("""
QStatusBar {
...
font-family: "Times New Roman", "SimSun";
}
""")
```
#### 3. 字体回退机制
- **中文字体优先使用宋体(SimSun)**:
```python
font-family: "Times New Roman", "SimSun";
```
这种设置表示:
- 优先使用Times New Roman显示英文字符
- 中文字符会回退到宋体(SimSun)显示
- 确保中英文混合内容的显示效果最佳
### 功能说明:
1. **文件名称显示**:
- 文件加载后显示在界面顶部
- 长路径自动简化为"父目录/.../文件名"格式
- 鼠标悬停时显示完整路径提示
- 背景为浅灰色,与界面风格协调
2. **字体显示效果**:
- 导航栏英文:Times New Roman
- 导航栏中文:宋体(SimSun)
- 表格内容:Times New Roman/宋体混合
- 状态栏:Times New Roman/宋体混合
3. **字体回退机制**:
- 系统优先尝试使用Times New Roman
- 遇到中文字符时自动切换到宋体
- 确保中英文混排内容显示清晰美观
### 使用示例:
1. **加载文件后**:
```
[当前文件: Documents/.../data_config.xml]
[加载XML文件] [导出XML文件] [添加记录] ...
```
2. **长路径显示**:
- 完整路径:`C:\Users\JohnDoe\Documents\Project\Configurations\data_config.xml`
- 界面显示:`Project/.../data_config.xml`
- 鼠标悬停时显示完整路径
3. **字体效果**:
- 按钮文字:"加载XML文件" - "XML"为Times New Roman,"加载"和"文件"为宋体
- 表格表头:英文列名用Times New Roman,中文字符用宋体
- 搜索框:输入内容遵循相同字体规则
### 注意事项:
1. **字体兼容性**:
- 代码中使用了"Times New Roman"和"SimSun"字体名称
- 这些是Windows系统标准字体,在其他系统可能需要调整
- Linux/macOS用户可能需要替换为等效字体
2. **路径显示优化**:
- 路径显示算法会保留父目录名和文件名
- 当路径超过60字符时自动缩短
- 完整路径始终可通过工具提示查看
3. **界面布局**:
- 文件名显示区域增加了顶部空间
- 所有控件保持原有布局和功能
- 整体界面高度增加了约30像素
阅读全文