<think>我们注意到,尽管之前已经尝试设置中文字体,但问题仍然出现。这可能是因为在设置全局字体时,没有正确应用到每个图表实例。此外,我们可能需要强制刷新字体缓存,并确保在创建图表时使用正确的字体属性。下面,我们将单独写一个类来管理Matplotlib的字体设置,并在每次绘图时确保使用支持中文的字体。我们将创建一个名为`FontManager`的类,它负责:1.在初始化时设置Matplotlib的默认字体(支持中文)2.提供方法来获取当前可用的中文字体属性3.在每次绘图前确保使用正确的字体同时,我们将修改绘图代码,使用这个类来设置字体。注意:由于字体设置是全局的,我们只需要在程序启动时初始化一次。但是,为了确保在运行时也能动态调整字体,我们将提供刷新机制。步骤:1.创建`FontManager`类2.在应用程序启动时初始化`FontManager`3.在绘图函数中,使用`FontManager`获取字体属性以下是具体实现:</think># 中文字体管理类解决方案
根据您的要求,我将创建一个独立的`FontManager`类来解决Matplotlib中文字符显示问题。这个类可以轻松集成到您的GUI应用中,专门处理中文字体配置和管理。
## FontManager类实现
```python
import matplotlib as mpl
import matplotlib.font_manager as fm
import matplotlib.pyplot as plt
import os
import sys
import traceback
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (
QDialog, QVBoxLayout, QGroupBox, QLabel, QListWidget,
QPushButton, QMessageBox, QHBoxLayout, QFontDialog
)
from PyQt5.QtGui import QFont
class FontManager:
def __init__(self):
"""初始化字体管理器"""
self.chinese_fonts = []
self.selected_font = None
self.load_fonts()
self.set_default_font()
def load_fonts(self):
"""加载系统支持的中文字体"""
self.chinese_fonts = []
try:
# 获取所有系统字体
font_list = fm.findSystemFonts(fontpaths=None, fontext='ttf')
for font_path in font_list:
try:
# 创建字体属性
font_prop = fm.FontProperties(fname=font_path)
# 检查是否支持中文
if self.is_chinese_font(font_prop):
font_name = font_prop.get_name()
self.chinese_fonts.append({
'name': font_name,
'path': font_path,
'prop': font_prop
})
except Exception as e:
print(f"加载字体 {font_path} 出错: {str(e)}")
continue
# 按字体名称排序
self.chinese_fonts.sort(key=lambda x: x['name'])
except Exception as e:
print(f"加载字体列表失败: {str(e)}")
traceback.print_exc()
print(f"找到 {len(self.chinese_fonts)} 个支持中文的字体")
def is_chinese_font(self, font_prop):
"""检查字体是否支持中文"""
try:
# 获取字体支持的字符集
names = font_prop.get_name()
# 检查是否包含中文字符集标识
if any('CJK' in name or 'Chinese' in name or '中文' in name for name in names):
return True
# 测试特定中文字符
test_chars = "类别概率分布"
for char in test_chars:
if not font_prop.get_glyph(ord(char)):
return False
return True
except Exception:
return False
def set_default_font(self):
"""设置默认中文字体"""
# 优先选择的字体列表
preferred_fonts = [
'Microsoft YaHei', # Windows 微软雅黑
'SimHei', # Windows 黑体
'SimSun', # Windows 宋体
'PingFang SC', # macOS 苹方
'Heiti SC', # macOS 黑体
'STHeiti', # macOS 华文黑体
'WenQuanYi Micro Hei', # Linux 文泉驿微米黑
'Noto Sans CJK SC', # 思源黑体
'Source Han Sans SC' # 思源黑体
]
# 尝试使用首选字体
for font_name in preferred_fonts:
if any(f['name'] == font_name for f in self.chinese_fonts):
self.set_matplotlib_font(font_name)
self.selected_font = font_name
print(f"设置默认字体为: {font_name}")
return
# 如果没找到首选字体,使用第一个支持中文的字体
if self.chinese_fonts:
first_font = self.chinese_fonts[0]['name']
self.set_matplotlib_font(first_font)
self.selected_font = first_font
print(f"设置默认字体为: {first_font}")
else:
print("警告: 未找到系统支持的中文字体")
def set_matplotlib_font(self, font_name):
"""设置Matplotlib全局字体"""
try:
# 设置主字体
plt.rcParams['font.sans-serif'] = [font_name]
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
# 设置默认字体大小
plt.rcParams['font.size'] = 12
# 刷新字体缓存
mpl.font_manager._rebuild()
self.selected_font = font_name
return True
except Exception as e:
print(f"设置字体失败: {str(e)}")
return False
def get_font_properties(self, font_name=None):
"""获取字体属性对象"""
if not font_name:
font_name = self.selected_font
for font in self.chinese_fonts:
if font['name'] == font_name:
return font['prop']
# 如果找不到,返回默认字体属性
return fm.FontProperties()
def create_font_dialog(self, parent=None):
"""创建字体设置对话框"""
dialog = QDialog(parent)
dialog.setWindowTitle("字体设置")
dialog.setGeometry(300, 300, 600, 500)
layout = QVBoxLayout()
# 当前字体信息
info_group = QGroupBox("当前字体设置")
info_layout = QVBoxLayout(info_group)
current_font = self.selected_font or "未设置"
font_info = f"当前字体: {current_font}\n\n"
try:
# 其他设置
font_info += f"字体大小: {plt.rcParams['font.size']}\n"
font_info += f"负号显示: {'正常' if plt.rcParams['axes.unicode_minus'] else '可能有问题'}\n"
except:
pass
info_label = QLabel(font_info)
info_layout.addWidget(info_label)
layout.addWidget(info_group)
# 可用字体列表
font_group = QGroupBox("可用的中文字体")
font_layout = QVBoxLayout(font_group)
self.font_list = QListWidget()
# 添加字体到列表
for font in self.chinese_fonts:
self.font_list.addItem(f"{font['name']} - {os.path.basename(font['path'])}")
font_layout.addWidget(self.font_list)
# 测试和应用按钮
btn_layout = QHBoxLayout()
test_btn = QPushButton("测试字体")
test_btn.clicked.connect(lambda: self.test_font(dialog))
btn_layout.addWidget(test_btn)
apply_btn = QPushButton("应用字体")
apply_btn.clicked.connect(lambda: self.apply_font(dialog))
btn_layout.addWidget(apply_btn)
font_layout.addLayout(btn_layout)
layout.addWidget(font_group)
# 字体大小设置
size_group = QGroupBox("字体大小")
size_layout = QHBoxLayout(size_group)
self.size_spin = QSpinBox()
self.size_spin.setRange(8, 24)
self.size_spin.setValue(plt.rcParams.get('font.size', 12))
size_layout.addWidget(QLabel("大小:"))
size_layout.addWidget(self.size_spin)
size_apply_btn = QPushButton("应用大小")
size_apply_btn.clicked.connect(self.apply_font_size)
size_layout.addWidget(size_apply_btn)
layout.addWidget(size_group)
# Qt字体设置
qt_group = QGroupBox("Qt界面字体")
qt_layout = QHBoxLayout(qt_group)
self.qt_font_btn = QPushButton("设置Qt字体...")
self.qt_font_btn.clicked.connect(lambda: self.set_qt_font(parent))
qt_layout.addWidget(self.qt_font_btn)
qt_label = QLabel("影响界面文字显示")
qt_layout.addWidget(qt_label)
layout.addWidget(qt_group)
# 关闭按钮
close_btn = QPushButton("关闭")
close_btn.clicked.connect(dialog.close)
layout.addWidget(close_btn)
dialog.setLayout(layout)
return dialog
def test_font(self, dialog):
"""测试选中的字体"""
selected_item = self.font_list.currentItem()
if not selected_item:
QMessageBox.warning(dialog, "错误", "请先选择一个字体")
return
font_text = selected_item.text()
font_name = font_text.split(' - ')[0]
# 创建测试窗口
test_dialog = QDialog(dialog)
test_dialog.setWindowTitle(f"字体测试 - {font_name}")
test_dialog.setGeometry(400, 400, 500, 400)
layout = QVBoxLayout()
# 测试文本
test_label = QLabel("中文测试: 类别概率分布图")
test_label.setAlignment(Qt.AlignCenter)
# 使用Qt字体显示
qt_font = QFont(font_name, 14)
test_label.setFont(qt_font)
layout.addWidget(test_label)
# 图表测试
fig = plt.figure(figsize=(5, 3))
canvas = FigureCanvas(fig)
layout.addWidget(canvas)
ax = fig.add_subplot(111)
# 使用Matplotlib字体显示
font_prop = self.get_font_properties(font_name)
ax.set_title("中文标题测试", fontproperties=font_prop)
ax.set_xlabel("X轴标签", fontproperties=font_prop)
ax.set_ylabel("Y轴标签", fontproperties=font_prop)
# 绘制示例数据
x = [1, 2, 3, 4, 5]
y = [2, 3, 5, 7, 11]
ax.plot(x, y, 'o-')
# 添加中文文本
ax.text(3, 6, "中文文本示例", fontproperties=font_prop,
ha='center', va='center', fontsize=14, color='red')
canvas.draw()
# 关闭按钮
close_btn = QPushButton("关闭")
close_btn.clicked.connect(test_dialog.close)
layout.addWidget(close_btn)
test_dialog.setLayout(layout)
test_dialog.exec_()
def apply_font(self, dialog):
"""应用选中的字体"""
selected_item = self.font_list.currentItem()
if not selected_item:
QMessageBox.warning(dialog, "错误", "请先选择一个字体")
return
font_text = selected_item.text()
font_name = font_text.split(' - ')[0]
if self.set_matplotlib_font(font_name):
QMessageBox.information(dialog, "成功", f"已设置字体为: {font_name}")
else:
QMessageBox.critical(dialog, "错误", "设置字体失败")
def apply_font_size(self):
"""应用字体大小设置"""
size = self.size_spin.value()
plt.rcParams['font.size'] = size
QMessageBox.information(None, "成功", f"已设置字体大小为: {size}")
def set_qt_font(self, parent):
"""设置Qt界面字体"""
font, ok = QFontDialog.getFont(QFont(), parent, "选择Qt界面字体")
if ok:
# 设置应用全局字体
parent.setFont(font)
# 更新所有控件的字体
for widget in parent.findChildren(QWidget):
widget.setFont(font)
QMessageBox.information(parent, "成功", f"已设置Qt字体为: {font.family()}")
```
## 在GUI应用中使用FontManager
### 1. 初始化FontManager
在您的GUI应用启动时初始化字体管理器:
```python
class ObjectRecognitionGUI(QMainWindow):
def __init__(self):
super().__init__()
# 初始化字体管理器
self.font_manager = FontManager()
# 其他初始化代码...
```
### 2. 添加字体设置菜单项
在菜单中添加字体设置选项:
```python
class ObjectRecognitionGUI(QMainWindow):
# ... 其他代码 ...
def create_tools_menu(self):
tool_menu = self.menuBar().addMenu("工具")
# 添加字体设置菜单项
font_action = QAction("字体设置", self)
font_action.triggered.connect(self.show_font_dialog)
tool_menu.addAction(font_action)
# 其他菜单项...
def show_font_dialog(self):
"""显示字体设置对话框"""
dialog = self.font_manager.create_font_dialog(self)
dialog.exec_()
```
### 3. 在绘图函数中使用字体管理器
在绘制图表时使用字体管理器获取正确的字体属性:
```python
class ObjectRecognitionGUI(QMainWindow):
# ... 其他代码 ...
def plot_probability_distribution(self, features):
"""显示类别概率分布图 - 支持中文"""
if self.classifier is None or self.classifier.classifier is None:
return
try:
# ... 数据处理代码 ...
# 创建图表
fig = self.probability_canvas.figure
fig.clear()
ax = fig.add_subplot(111)
y_pos = np.arange(len(self.classifier.class_names))
ax.barh(y_pos, probabilities, align='center', color='skyblue')
ax.set_yticks(y_pos)
# 使用字体管理器获取字体属性
font_prop = self.font_manager.get_font_properties()
# 设置标签使用中文字体
ax.set_yticklabels(self.classifier.class_names, fontproperties=font_prop)
ax.invert_yaxis()
ax.set_xlabel('概率', fontproperties=font_prop)
ax.set_title('类别概率分布', fontproperties=font_prop)
# 添加概率文本
for i, prob in enumerate(probabilities):
ax.text(prob + 0.01, i, f"{prob*100:.1f}%",
va='center', fontproperties=font_prop)
self.probability_canvas.draw()
except Exception as e:
print(f"绘制图表时出错: {str(e)}")
traceback.print_exc()
```
### 4. 设置Qt界面字体
在应用启动时设置Qt界面字体:
```python
if __name__ == "__main__":
app = QApplication(sys.argv)
app.setStyle("Fusion")
# 初始化字体管理器
font_manager = FontManager()
# 设置Qt全局字体
if sys.platform == 'win32':
qt_font = QFont("Microsoft YaHei", 10)
elif sys.platform == 'darwin':
qt_font = QFont("PingFang SC", 10)
elif sys.platform.startswith('linux'):
qt_font = QFont("WenQuanYi Micro Hei", 10)
else:
qt_font = QFont("Arial", 10)
app.setFont(qt_font)
window = ObjectRecognitionGUI()
window.show()
sys.exit(app.exec_())
```
## FontManager类功能详解
1. **自动检测中文字体**:
- 扫描系统所有字体
- 检测哪些字体支持中文字符
- 按名称排序便于选择
2. **智能设置默认字体**:
- 优先选择常见中文字体(微软雅黑、苹方等)
- 跨平台支持(Windows/macOS/Linux)
- 回退机制确保总有可用字体
3. **字体测试功能**:
- 预览字体效果
- 同时测试Qt和Matplotlib渲染
- 显示中文标题、标签和文本
4. **完整字体管理**:
- 设置Matplotlib全局字体
- 设置字体大小
- 设置Qt界面字体
- 应用更改无需重启
5. **用户友好界面**:
- 直观的字体列表
- 清晰的当前设置显示
- 一键测试和应用
## 使用示例
```python
# 初始化字体管理器
font_manager = FontManager()
# 获取当前字体属性
font_prop = font_manager.get_font_properties()
# 在绘图时使用
ax.set_title("中文标题", fontproperties=font_prop)
# 显示字体设置对话框
dialog = font_manager.create_font_dialog(parent_window)
dialog.exec_()
```
## 优势与特点
1. **独立封装**:作为独立类,易于集成到任何PyQt应用中
2. **跨平台支持**:自动适应Windows/macOS/Linux系统
3. **智能检测**:准确识别支持中文的字体
4. **用户友好**:提供图形界面进行字体管理
5. **无需重启**:更改字体设置后立即生效
6. **全面控制**:同时管理Matplotlib和Qt字体
这个`FontManager`类解决了Matplotlib中文显示问题,同时提供了友好的界面让用户选择和测试字体,确保您的GUI应用在各种环境下都能正确显示中文字符。