import sys
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
from matplotlib import patheffects, animation
from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas
from PySide6.QtWidgets import (
QApplication, QMainWindow, QPushButton, QDialog, QVBoxLayout,
QLabel, QComboBox, QWidget
)
from matplotlib.font_manager import FontProperties
# 设置中文字体(确保系统支持)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 数据:单位为小时
data = {
"T001": [2.78, 1.88, 1.65, 0.9, 0.97, 2.33],
"T002": [2.27, 1.85, 2.87, 2.08, 1.2, 1.13],
"T003": [2.02, 2.23, 1.05, 1.37, 2.46, 0.98],
"T004": [1.82, 1.27, 1.07, 1.0, 2.15, 2.02],
}
# 流程名称
process_names = ['接车', '送车1', '送车2', '确报1', '确报2', '编成', '发车']
class ChartDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("流程时间折线图")
self.layout = QVBoxLayout(self)
# 流程选择下拉框
self.process_combo = QComboBox()
self.process_combo.addItems(process_names)
self.process_combo.setCurrentIndex(0)
self.layout.addWidget(QLabel("选择流程:"))
self.layout.addWidget(self.process_combo)
# 图表画布
self.figure = plt.figure(figsize=(8, 6), facecolor='#0f0f0f')
self.canvas = FigureCanvas(self.figure)
self.layout.addWidget(self.canvas)
# 绑定下拉框选择变化事件
self.process_combo.currentIndexChanged.connect(self.plot_selected)
# 初始绘制图表
self.plot_selected()
# 动态动画
self.anim = animation.FuncAnimation(self.figure, self.update_animation, interval=2000, repeat=True)
def update_animation(self, frame):
if hasattr(self, 'line'):
y = [data[tid][self.process_combo.currentIndex()][0] for tid in sorted(data.keys())]
self.line.set_ydata(y)
self.figure.canvas.draw_idle()
def plot_selected(self):
selected_index = self.process_combo.currentIndex()
selected_process = process_names[selected_index]
self.figure.clear()
ax = self.figure.add_subplot(111)
self.figure.set_facecolor('#0f0f0f')
ax.set_facecolor('#0f0f0f')
train_ids = sorted(data.keys())
x = list(range(len(train_ids)))
y = [data[tid][selected_index] for tid in train_ids]
avg_time = sum(y) / len(y)
# 折线样式
line, = ax.plot(x, y, marker='o', color='#00ffe1', linewidth=2.5, markersize=8,
linestyle='-', markeredgecolor='white', zorder=2, label='流程时间')
# 平均线
ax.axhline(avg_time, linestyle='--', color='#ff6600', alpha=0.8, linewidth=2.2, zorder=1, label='平均时间')
# 标题
ax.set_title(f"列车{selected_process}组织时间采集折线图", fontsize=18, fontweight='bold', color='white', pad=20)
# 坐标轴标签
ax.set_xticks(x)
ax.set_xticklabels(train_ids, color='white', fontsize=12)
ax.set_yticklabels([f"{y:.1f}" for y in ax.get_yticks()], color='white', fontsize=12)
ax.set_xlabel("")
ax.set_ylabel("")
# 边框颜色
for spine in ax.spines.values():
spine.set_edgecolor('#444')
spine.set_linewidth(1.2)
# 背景渐变
gradient = np.linspace(0, 1, 256).reshape(1, -1)
ax.imshow(gradient, aspect='auto', extent=[*ax.get_xlim(), *ax.get_ylim()],
cmap='inferno', alpha=0.05, zorder=0)
# 图例
ax.legend(loc='upper right', facecolor='#2e2e2e', edgecolor='none', fontsize=12, labelcolor='white')
# 光晕效果
line.set_path_effects([
patheffects.Stroke(linewidth=6, foreground='black', alpha=0.5),
patheffects.Normal()
])
# 悬停提示
self.line = line
self.ax = ax
self.train_ids = train_ids
self.x = x
self.y = y
self.figure.canvas.mpl_connect('motion_notify_event', self.on_mouse_move)
self.canvas.draw()
def on_mouse_move(self, event):
if event.inaxes != self.ax:
if hasattr(self, 'tooltip'):
self.tooltip.remove()
del self.tooltip
self.canvas.draw_idle()
return
xdata = self.line.get_xdata()
ydata = self.line.get_ydata()
distances = [(x - event.xdata) ** 2 + (y - event.ydata) ** 2 for x, y in zip(xdata, ydata)]
min_index = np.argmin(distances)
min_distance = distances[min_index]
if min_distance < 0.05:
x = xdata[min_index]
y = ydata[min_index]
train_id = self.train_ids[min_index]
if hasattr(self, 'tooltip'):
self.tooltip.remove()
self.tooltip = self.ax.text(
0.98, 0.95, f"车次: {train_id}\n时间: {y:.2f} 小时",
transform=self.ax.transAxes,
fontsize=14,
verticalalignment='top',
horizontalalignment='right',
color='white',
bbox=dict(boxstyle="round", facecolor="#333", edgecolor="none", alpha=0.9)
)
self.canvas.draw_idle()
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("列车流程时间图表")
self.setGeometry(100, 100, 400, 300)
self.button = QPushButton("显示图表")
self.button.clicked.connect(self.show_chart_dialog)
layout = QVBoxLayout()
layout.addWidget(self.button)
container = QWidget()
container.setLayout(layout)
self.setCentralWidget(container)
def show_chart_dialog(self):
dialog = ChartDialog(self)
dialog.exec()
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec()) 程序报错