#1050 : 树中的最长路

本文介绍了一个寻找树中两个结点间最长距离的问题及其解决方案。通过深度优先搜索算法(DFS)遍历多叉树,计算任意两点间的最大距离。文章包含了一个C++实现的例子。

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

#1050 : 树中的最长路

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
描述

上回说到,小Ho得到了一棵二叉树玩具,这个玩具是由小球和木棍连接起来的,而在拆拼它的过程中,小Ho发现他不仅仅可以拼凑成一棵二叉树!还可以拼凑成一棵多叉树——好吧,其实就是更为平常的树而已。

但是不管怎么说,小Ho喜爱的玩具又升级换代了,于是他更加爱不释手(其实说起来小球和木棍有什么好玩的是吧= =)。小Ho手中的这棵玩具树现在由N个小球和N-1根木棍拼凑而成,这N个小球都被小Ho标上了不同的数字,并且这些数字都是出于1..N的范围之内,每根木棍都连接着两个不同的小球,并且保证任意两个小球间都不存在两条不同的路径可以互相到达。总而言之,是一个相当好玩的玩具啦!

但是小Hi瞧见小Ho这个样子,觉得他这样沉迷其中并不是一件好事,于是寻思着再找点问题让他来思考思考——不过以小Hi的水准,自然是手到擒来啦!

于是这天食过早饭后,小Hi便对着又拿着树玩具玩的不亦乐乎的小Ho道:“你说你天天玩这个东西,我就问你一个问题,看看你可否知道?”

“不好!”小Ho想都不想的拒绝了。

“那你就继续玩吧,一会回国的时候我不叫上你了~”小Hi严肃道。

“诶!别别别,你说你说,我听着呢。”一向习惯于开启跟随模式的小Ho忍不住了,马上喊道。

小Hi满意的点了点头,随即说道:“这才对嘛,我的问题很简单,就是——你这棵树中哪两个结点之间的距离最长?当然,这里的距离是指从一个结点走到另一个结点经过的木棍数。”。

“啊?”小Ho低头看了看手里的玩具树,困惑了。

提示一:路总有折点,路径也不例外!
输入

每个测试点(输入文件)有且仅有一组测试数据。

每组测试数据的第一行为一个整数N,意义如前文所述。

每组测试数据的第2~N行,每行分别描述一根木棍,其中第i+1行为两个整数Ai,Bi,表示第i根木棍连接的两个小球的编号。

对于20%的数据,满足N<=10。

对于50%的数据,满足N<=10^3。

对于100%的数据,满足N<=10^5,1<=Ai<=N, 1<=Bi<=N

小Hi的Tip:那些用数组存储树边的记得要开两倍大小哦!

输出

对于每组测试数据,输出一个整数Ans,表示给出的这棵树中距离最远的两个结点之间相隔的距离。

样例输入
8
1 2
1 3
1 4
4 5
3 6
6 7
7 8
样例输出
6
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

int DFS(int num, int& ans, const vector<vector<int>>& graph)
{
	if (graph[num].empty())    //出口
		return 1; 
	int tmp, big1 = 0, big2 = 0;    //多叉树,big1和big2分别是两条到本结点的最长路径
	for (int i = 0; i < graph[num].size(); i++)
	{
		tmp = DFS(graph[num][i], ans, graph);
		if (tmp > big1) //始终保持big1和big2为到以graph[num][i]为根节点的不重合的两条最长路劲
		{
			big2 = big1;
			big1 = tmp;
		}
		else if (tmp > big2)
			big2 = tmp;
	}
	if (big1 + big2 > ans)    //计算以本节点为转折点的最长路径
		ans = big1 + big2;
	return big1 + 1;
}

int main()
{
	int N, i, ai, bi;
	scanf("%d", &N);
	vector<vector<int>> graph(N + 1);    //树以临接表记录(有向图)
	for (i = 1; i < N; ++i)   //边条数
	{
		scanf("%d%d", &ai, &bi);  //用c读入会省一半时间
		graph[ai].push_back(bi);  //邻接表形式存储从 ai 到 bi 的边
	}

	int ans = 0;
	DFS(1, ans, graph);

	printf("%d\n", ans);

	system("pause");
	return 0;
}
import sys sys.path.append(‘/UI/Login’) from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtWidgets import QApplication, QWidget #from PyQt5.QtCore import QThread, pyqtSignal,QTimer from Login import Ui_Form as Login_ui from CanOBD import Ui_MainWindow as CanOBD_ui from PyQt5.QtWidgets import QMessageBox import time #import threading class LogInViewUI(QtWidgets.QWidget,Login_ui): def int(self): super(LogInViewUI,self).int() self.setupUi(self) def connectslot(self): #self.m_loginbuttonOn.clicked.connect(self.login_button) self.mloginButton.clicked.connect(self.login_button) def login_button(self): if self.mPassWordEdit.acceptRichText() == “”: QMessageBox.warning(self, ‘警告’, ‘密码不能为空,请输入!’) return None # 1打开新窗口 CanOBDWidget.show() # 2关闭本窗口 LoginWidget.close() class CanOBDViewUI(QtWidgets.QWidget,CanOBD_ui): def int(self): super(CanOBDViewUI,self).int() self.setupUi(self) def update_tree_widgets(self, signals): self.signals_data = signals # 更新 Speed Tree # if “CCVS1” in signals: # self.mSpeedTreeWidget.topLevelItem(0).setText(3, f"{signals[‘CCVS1’]} km/h") # # # 更新 RPM Tree # if “EEC1_rpm” in signals: # self.mRPMTreeWidget.topLevelItem(0).setText(3, f"{signals[‘EEC1_rpm’]} rpm") # if “EEC1_torque” in signals: # self.mEECTreeWidget.topLevelItem(0).setText(3, f"{signals[‘EEC1_torque’]} %“) # # # 更新 VDHR (里程) # if “VDHR” in signals: # self.mVDHRTreeWidget.topLevelItem(0).setText(3, f”{signals[‘VDHR’]} km") # # # 更新 HOURS (工作时长) # if “HOURS” in signals: # self.mHoursTreeWidget.topLevelItem(0).setText(3, f"{signals[‘HOURS’]} hours") # # # 更新 LFE (平均油耗) # if “LFE” in signals: # self.mLFETreeWidget.topLevelItem(0).setText(3, f"{signals[‘LFE’]} L/100km") # # # 更新 ET1 (冷却液温度) # if “ET1” in signals: # self.mET1TreeWidget.topLevelItem(0).setText(3, f"{signals[‘ET1’]} °C") # # # 更新 AT1T1I (燃油液面) # if “AT1T1I” in signals: # self.mAT1T1ITreeWidget.topLevelItem(0).setText(3, f"{signals[‘AT1T1I’]} %") # # # 更新 ETC2 (档位) # if “ETC2” in signals: # self.mETC2TreeWidget.topLevelItem(0).setText(3, signals[‘ETC2’]) if name == “main”: app = QtWidgets.QApplication(sys.argv) #MainWidget = QtWidgets.QDockWidget() LoginWidget = QtWidgets.QWidget() CanOBDWidget = QtWidgets.QMainWindow() loginui = LogInViewUI() CanOBDui = CanOBDViewUI() loginui.setupUi(LoginWidget) loginui.connectslot() CanOBDui.setupUi(CanOBDWidget) CanOBDui.Serialconnectslot() LoginWidget.show() sys.exit(app.exec_()) 这是main文件 – coding: utf-8 – import threading Form implementation generated from reading ui file ‘CanOBD.ui’ Created by: PyQt5 UI code generator 5.15.9 WARNING: Any manual changes made to this file will be lost when pyuic5 is run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets from PyQt5.QtCore import QThread, pyqtSignal,QTimer from PyQt5.QtWidgets import QApplication, QTableWidget, QTableWidgetItem from SerialPro import SerialPort as SerialThread from SerialPro import CanOBDItemList from can_parser import parse_custom_can_frame import time import serial import serial.tools.list_ports class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(“MainWindow”) MainWindow.resize(1220, 940) font = QtGui.QFont() font.setPointSize(12) MainWindow.setFont(font) self.centralwidget = QtWidgets.QWidget(MainWindow) self.centralwidget.setObjectName(“centralwidget”) self.tabWidget = QtWidgets.QTabWidget(self.centralwidget) self.tabWidget.setGeometry(QtCore.QRect(0, 0, 1041, 940)) self.tabWidget.setObjectName(“tabWidget”) self.tab = QtWidgets.QWidget() self.tab.setObjectName(“tab”) self.tableWidget = QtWidgets.QTableWidget(self.tab) self.tableWidget.setGeometry(QtCore.QRect(0, 0, 1031, 940)) self.tableWidget.setObjectName(“tableWidget”) self.tableWidget.setColumnCount(5) self.tableWidget.setRowCount(150) for num in range(0,150,1): item = QtWidgets.QTableWidgetItem() self.tableWidget.setVerticalHeaderItem(num, item) item = QtWidgets.QTableWidgetItem() font = QtGui.QFont() font.setKerning(False) item.setFont(font) for line in range(0,5,1): self.tableWidget.setHorizontalHeaderItem(line, item) item = QtWidgets.QTableWidgetItem() self.tabWidget.addTab(self.tab, “”) self.tab_2 = QtWidgets.QWidget() self.tab_2.setObjectName(“CanOBD Cfg Set”) self.mSpeedTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mSpeedTreeWidget.setGeometry(QtCore.QRect(10, 0, 1031, 101)) self.mSpeedTreeWidget.setObjectName(“mSpeedTreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mSpeedTreeWidget.headerItem().setFont(0, font) self.mSpeedTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mSpeedTreeWidget.headerItem().setFont(3, font) self.mRPMTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mRPMTreeWidget.setGeometry(QtCore.QRect(10, 100, 1031, 91)) self.mRPMTreeWidget.setObjectName(“mRPMTreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mRPMTreeWidget.headerItem().setFont(0, font) self.mRPMTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mRPMTreeWidget.headerItem().setFont(3, font) self.mVDHRTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mVDHRTreeWidget.setGeometry(QtCore.QRect(10, 190, 1031, 91)) self.mVDHRTreeWidget.setObjectName(“mVDHRTreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mVDHRTreeWidget.headerItem().setFont(0, font) self.mVDHRTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mVDHRTreeWidget.headerItem().setFont(3, font) self.mHoursTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mHoursTreeWidget.setGeometry(QtCore.QRect(10, 280, 1031, 101)) self.mHoursTreeWidget.setObjectName(“mHoursTreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mHoursTreeWidget.headerItem().setFont(0, font) self.mHoursTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mHoursTreeWidget.headerItem().setFont(3, font) self.mEECTreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mEECTreeWidget.setGeometry(QtCore.QRect(10, 380, 1031, 91)) self.mEECTreeWidget.setObjectName(“mEECTreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mEECTreeWidget.headerItem().setFont(0, font) self.mEECTreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mEECTreeWidget.headerItem().setFont(3, font) self.mET1TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mET1TreeWidget.setGeometry(QtCore.QRect(10, 470, 1031, 101)) self.mET1TreeWidget.setObjectName(“mET1TreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mET1TreeWidget.headerItem().setFont(0, font) self.mET1TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mET1TreeWidget.headerItem().setFont(3, font) self.mAT1T1ITreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mAT1T1ITreeWidget.setGeometry(QtCore.QRect(10, 570, 1031, 91)) self.mAT1T1ITreeWidget.setObjectName(“mAT1T1ITreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mAT1T1ITreeWidget.headerItem().setFont(0, font) self.mAT1T1ITreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mAT1T1ITreeWidget.headerItem().setFont(3, font) self.mLFETreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mLFETreeWidget.setGeometry(QtCore.QRect(10, 660, 1031, 101)) self.mLFETreeWidget.setObjectName(“mLFETreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mLFETreeWidget.headerItem().setFont(0, font) self.mLFETreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mLFETreeWidget.headerItem().setFont(3, font) self.mETC2TreeWidget = QtWidgets.QTreeWidget(self.tab_2) self.mETC2TreeWidget.setGeometry(QtCore.QRect(10, 760, 1031, 101)) self.mETC2TreeWidget.setObjectName(“mETC2TreeWidget”) font = QtGui.QFont() font.setPointSize(12) self.mETC2TreeWidget.headerItem().setFont(0, font) self.mETC2TreeWidget.headerItem().setTextAlignment(1, QtCore.Qt.AlignJustify|QtCore.Qt.AlignVCenter) font = QtGui.QFont() font.setKerning(True) self.mETC2TreeWidget.headerItem().setFont(3, font) self.tabWidget.addTab(self.tab_2, “”) self.mComCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mComCfgBox.setGeometry(QtCore.QRect(1040, 10, 191, 231)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mComCfgBox.setFont(font) self.mComCfgBox.setObjectName(“mComCfgBox”) self.mPortName = QtWidgets.QLabel(self.mComCfgBox) self.mPortName.setGeometry(QtCore.QRect(20, 30, 61, 21)) self.mPortName.setObjectName(“mPortName”) self.mBpsName = QtWidgets.QLabel(self.mComCfgBox) self.mBpsName.setGeometry(QtCore.QRect(20, 60, 61, 21)) self.mBpsName.setObjectName(“mBpsName”) self.mDatabitName = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitName.setGeometry(QtCore.QRect(20, 90, 61, 21)) self.mDatabitName.setObjectName(“mDatabitName”) self.mStopName = QtWidgets.QLabel(self.mComCfgBox) self.mStopName.setGeometry(QtCore.QRect(20, 120, 61, 21)) self.mStopName.setObjectName(“mStopName”) self.mOddName = QtWidgets.QLabel(self.mComCfgBox) self.mOddName.setGeometry(QtCore.QRect(20, 150, 61, 21)) self.mOddName.setObjectName(“mOddName”) self.mDatabitVal = QtWidgets.QLabel(self.mComCfgBox) self.mDatabitVal.setGeometry(QtCore.QRect(100, 90, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mDatabitVal.setFont(font) self.mDatabitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mDatabitVal.setAlignment(QtCore.Qt.AlignCenter) self.mDatabitVal.setObjectName(“mDatabitVal”) self.mStopBitVal = QtWidgets.QLabel(self.mComCfgBox) self.mStopBitVal.setGeometry(QtCore.QRect(100, 120, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mStopBitVal.setFont(font) self.mStopBitVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mStopBitVal.setAlignment(QtCore.Qt.AlignCenter) self.mStopBitVal.setObjectName(“mStopBitVal”) self.mOddVal = QtWidgets.QLabel(self.mComCfgBox) self.mOddVal.setGeometry(QtCore.QRect(100, 150, 54, 21)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mOddVal.setFont(font) self.mOddVal.setLayoutDirection(QtCore.Qt.LeftToRight) self.mOddVal.setAlignment(QtCore.Qt.AlignCenter) self.mOddVal.setObjectName(“mOddVal”) self.mPortVal = QtWidgets.QComboBox(self.mComCfgBox) self.mPortVal.setGeometry(QtCore.QRect(90, 30, 81, 22)) self.mPortVal.setObjectName(“mPortVal”) self.mPortVal.addItem(“”) self.mPortVal.addItem(“”) self.mPortVal.addItem(“”) self.mPortVal.addItem(“”) self.mPortVal.addItem(“”) self.mPortVal.addItem(“”) self.mBPSVal = QtWidgets.QComboBox(self.mComCfgBox) self.mBPSVal.setGeometry(QtCore.QRect(90, 60, 81, 22)) self.mBPSVal.setObjectName(“mBPSVal”) self.mBPSVal.addItem(“”) self.mBPSVal.addItem(“”) self.mBPSVal.addItem(“”) self.mBPSVal.addItem(“”) self.mBPSVal.addItem(“”) self.mBPSVal.addItem(“”) self.mOpenSerial = QtWidgets.QDialogButtonBox(self.mComCfgBox) self.mOpenSerial.setGeometry(QtCore.QRect(20, 190, 156, 31)) self.mOpenSerial.setStandardButtons(QtWidgets.QDialogButtonBox.Close|QtWidgets.QDialogButtonBox.Open) self.mOpenSerial.setObjectName(“mOpenSerial”) self.mCycleCfgBox = QtWidgets.QGroupBox(self.centralwidget) self.mCycleCfgBox.setGeometry(QtCore.QRect(1040, 260, 191, 221)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mCycleCfgBox.setFont(font) self.mCycleCfgBox.setObjectName(“mCycleCfgBox”) self.mcheck1000ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck1000ms.setGeometry(QtCore.QRect(20, 180, 141, 31)) self.mcheck1000ms.setObjectName(“mcheck1000ms”) self.mcheck500ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck500ms.setGeometry(QtCore.QRect(20, 150, 141, 31)) self.mcheck500ms.setObjectName(“mcheck500ms”) self.mcheck100ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck100ms.setGeometry(QtCore.QRect(20, 90, 141, 31)) self.mcheck100ms.setObjectName(“mcheck100ms”) self.mcheck50ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck50ms.setGeometry(QtCore.QRect(20, 60, 141, 31)) self.mcheck50ms.setObjectName(“mcheck50ms”) self.mcheck20ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck20ms.setGeometry(QtCore.QRect(20, 30, 141, 31)) self.mcheck20ms.setObjectName(“mcheck20ms”) self.mcheck200ms = QtWidgets.QCheckBox(self.mCycleCfgBox) self.mcheck200ms.setGeometry(QtCore.QRect(20, 120, 141, 31)) self.mcheck200ms.setObjectName(“mcheck200ms”) self.mEventSigBox = QtWidgets.QGroupBox(self.centralwidget) self.mEventSigBox.setGeometry(QtCore.QRect(1050, 490, 191, 151)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mEventSigBox.setFont(font) self.mEventSigBox.setObjectName(“mEventSigBox”) self.radioLeftREvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioLeftREvent.setGeometry(QtCore.QRect(10, 30, 151, 16)) self.radioLeftREvent.setObjectName(“radioLeftREvent”) self.radioKiilEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioKiilEvent.setGeometry(QtCore.QRect(10, 90, 151, 16)) self.radioKiilEvent.setObjectName(“radioKiilEvent”) self.radioPEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioPEvent.setGeometry(QtCore.QRect(10, 120, 151, 16)) self.radioPEvent.setObjectName(“radioPEvent”) self.radioOpenCloseEvent = QtWidgets.QRadioButton(self.mEventSigBox) self.radioOpenCloseEvent.setGeometry(QtCore.QRect(10, 60, 151, 16)) self.radioOpenCloseEvent.setObjectName(“radioOpenCloseEvent”) self.mReadOBDinfBox = QtWidgets.QGroupBox(self.centralwidget) self.mReadOBDinfBox.setGeometry(QtCore.QRect(1050, 660, 191, 171)) font = QtGui.QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.mReadOBDinfBox.setFont(font) self.mReadOBDinfBox.setObjectName(“mReadOBDinfBox”) self.radioVinRead = QtWidgets.QRadioButton(self.mReadOBDinfBox) self.radioVinRead.setGeometry(QtCore.QRect(10, 40, 141, 21)) self.radioVinRead.setObjectName(“radioVinRead”) self.mVinInfShow = QtWidgets.QTextBrowser(self.mReadOBDinfBox) self.mVinInfShow.setGeometry(QtCore.QRect(10, 70, 171, 91)) self.mVinInfShow.setObjectName(“mVinInfShow”) MainWindow.setCentralWidget(self.centralwidget) self.statusbar = QtWidgets.QStatusBar(MainWindow) self.statusbar.setObjectName(“statusbar”) MainWindow.setStatusBar(self.statusbar) self.retranslateUi(MainWindow) self.tabWidget.setCurrentIndex(0) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): _translate = QtCore.QCoreApplication.translate MainWindow.setWindowTitle(_translate(“MainWindow”, “MainWindow”)) for num in range(0, 150, 1): item = self.tableWidget.verticalHeaderItem(num) item.setText(_translate(“MainWindow”, str(num +1))) item = self.tableWidget.horizontalHeaderItem(0) item.setText(_translate(“MainWindow”, “时间标识”)) item = self.tableWidget.horizontalHeaderItem(1) item.setText(_translate(“MainWindow”, “帧ID”)) item = self.tableWidget.horizontalHeaderItem(2) item.setText(_translate(“MainWindow”, “帧类型”)) item = self.tableWidget.horizontalHeaderItem(3) item.setText(_translate(“MainWindow”, “长度”)) item = self.tableWidget.horizontalHeaderItem(4) item.setText(_translate(“MainWindow”, “数据 (BIT7–BIT0 大端模式)”)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate(“MainWindow”, “Tab 1”)) self.mSpeedTreeWidget.headerItem().setText(0, _translate(“MainWindow”, “速度[CCVS1]”)) self.mSpeedTreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mSpeedTreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mSpeedTreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(km/h)”)) self.mSpeedTreeWidget.setColumnWidth(0, 150) self.mSpeedTreeWidget.setColumnWidth(1, 150) self.mSpeedTreeWidget.setColumnWidth(2,550) self.mSpeedTreeWidget.setColumnWidth(3, 150) self.mRPMTreeWidget.headerItem().setText(0, _translate(“MainWindow”, “转速[EEC1]”)) self.mRPMTreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mRPMTreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mRPMTreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(rpm)”)) self.mRPMTreeWidget.setColumnWidth(0, 150) self.mRPMTreeWidget.setColumnWidth(1, 150) self.mRPMTreeWidget.setColumnWidth(2,550) self.mRPMTreeWidget.setColumnWidth(3, 150) self.mVDHRTreeWidget.headerItem().setText(0, _translate(“MainWindow”, “里程[VDHR]”)) self.mVDHRTreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mVDHRTreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mVDHRTreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(km)”)) self.mVDHRTreeWidget.setColumnWidth(0, 150) self.mVDHRTreeWidget.setColumnWidth(1, 150) self.mVDHRTreeWidget.setColumnWidth(2,550) self.mVDHRTreeWidget.setColumnWidth(3, 150) self.mHoursTreeWidget.headerItem().setText(0, _translate(“MainWindow”, “工作时长[HOURS]”)) self.mHoursTreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mHoursTreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mHoursTreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(hours)”)) self.mHoursTreeWidget.setColumnWidth(0, 150) self.mHoursTreeWidget.setColumnWidth(1, 150) self.mHoursTreeWidget.setColumnWidth(2,550) self.mHoursTreeWidget.setColumnWidth(3, 150) self.mEECTreeWidget.headerItem().setText(0, _translate(“MainWindow”, “发动机负载[EEC1]”)) self.mEECTreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mEECTreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mEECTreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(%)”)) self.mEECTreeWidget.setColumnWidth(0, 150) self.mEECTreeWidget.setColumnWidth(1, 150) self.mEECTreeWidget.setColumnWidth(2,550) self.mEECTreeWidget.setColumnWidth(3, 150) self.mET1TreeWidget.headerItem().setText(0, _translate(“MainWindow”, “冷却液温度[ET1]”)) self.mET1TreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mET1TreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mET1TreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(°)”)) self.mET1TreeWidget.setColumnWidth(0, 150) self.mET1TreeWidget.setColumnWidth(1, 150) self.mET1TreeWidget.setColumnWidth(2,550) self.mET1TreeWidget.setColumnWidth(3, 150) self.mAT1T1ITreeWidget.headerItem().setText(0, _translate(“MainWindow”, “燃油液面[AT1T1I]”)) self.mAT1T1ITreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mAT1T1ITreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mAT1T1ITreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(%)”)) self.mAT1T1ITreeWidget.setColumnWidth(0, 150) self.mAT1T1ITreeWidget.setColumnWidth(1, 150) self.mAT1T1ITreeWidget.setColumnWidth(2,550) self.mAT1T1ITreeWidget.setColumnWidth(3, 150) self.mLFETreeWidget.headerItem().setText(0, _translate(“MainWindow”, “平均油耗[LFE]”)) self.mLFETreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mLFETreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mLFETreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal(L/h)”)) self.mLFETreeWidget.setColumnWidth(0, 150) self.mLFETreeWidget.setColumnWidth(1, 150) self.mLFETreeWidget.setColumnWidth(2,550) self.mLFETreeWidget.setColumnWidth(3, 150) self.mETC2TreeWidget.headerItem().setText(0, _translate(“MainWindow”, “档位[ETC2]”)) self.mETC2TreeWidget.headerItem().setText(1, _translate(“MainWindow”, “CanID”)) self.mETC2TreeWidget.headerItem().setText(2, _translate(“MainWindow”, “Data”)) self.mETC2TreeWidget.headerItem().setText(3, _translate(“MainWindow”, “Signal”)) self.mETC2TreeWidget.setColumnWidth(0, 150) self.mETC2TreeWidget.setColumnWidth(1, 150) self.mETC2TreeWidget.setColumnWidth(2,550) self.mETC2TreeWidget.setColumnWidth(3, 150) self.tableWidget.setColumnWidth(0, 200) self.tableWidget.setColumnWidth(1, 150) self.tableWidget.setColumnWidth(4,450) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate(“MainWindow”, “CanOBD Inf Show”)) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate(“MainWindow”, “CanOBD J1939 Show”)) self.mComCfgBox.setTitle(_translate(“MainWindow”, “串口配置”)) self.mPortName.setText(_translate(“MainWindow”, “端口号”)) self.mBpsName.setText(_translate(“MainWindow”, “波特率”)) self.mDatabitName.setText(_translate(“MainWindow”, “数据位”)) self.mStopName.setText(_translate(“MainWindow”, “停止位”)) self.mOddName.setText(_translate(“MainWindow”, “检验位”)) self.mDatabitVal.setText(_translate(“MainWindow”, “8”)) self.mStopBitVal.setText(_translate(“MainWindow”, “1”)) self.mOddVal.setText(_translate(“MainWindow”, “无”)) self.mBPSVal.setItemText(0, _translate(“MainWindow”, “9600”)) self.mBPSVal.setItemText(1, _translate(“MainWindow”, “19200”)) self.mBPSVal.setItemText(2, _translate(“MainWindow”, “115200”)) self.mBPSVal.setItemText(3, _translate(“MainWindow”, “230400”)) self.mBPSVal.setItemText(4, _translate(“MainWindow”, “256000”)) self.mBPSVal.setItemText(5, _translate(“MainWindow”, “460800”)) port_list = list(serial.tools.list_ports.comports()) if port_list.len() is not 0: for num in range(port_list.len()): self.mPortVal.setItemText(num, _translate(“MainWindow”, str(port_list[num].device))) serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial = SerialThread(serialport, serialbaudrate) self.mCycleCfgBox.setTitle(_translate(“MainWindow”, “过滤设置(周期)”)) self.mcheck1000ms.setText(_translate(“MainWindow”, “1000ms 周期”)) self.mcheck500ms.setText(_translate(“MainWindow”, “500ms 周期”)) self.mcheck100ms.setText(_translate(“MainWindow”, “100ms 周期”)) self.mcheck50ms.setText(_translate(“MainWindow”, “50ms 周期”)) self.mcheck20ms.setText(_translate(“MainWindow”, “20ms 周期”)) self.mcheck200ms.setText(_translate(“MainWindow”, “200ms 周期”)) self.mEventSigBox.setTitle(_translate(“MainWindow”, “事件信号策略”)) self.radioLeftREvent.setText(_translate(“MainWindow”, “左右转 事件”)) self.radioKiilEvent.setText(_translate(“MainWindow”, “刹车 事件”)) self.radioPEvent.setText(_translate(“MainWindow”, “档位 事件”)) self.radioOpenCloseEvent.setText(_translate(“MainWindow”, “开关门 事件”)) self.mReadOBDinfBox.setTitle(_translate(“MainWindow”, “主动读取信息”)) self.radioVinRead.setText(translate(“MainWindow”, “VIN 信息”)) def OpenSerial(self): if self.LSerial != None: if self.LSerial.SerialIsOpen(): self.LSerial.del() port_list = list(serial.tools.list_ports.comports()) if port_list.len() != 0: serialport = self.mPortVal.currentText() serialbaudrate = self.mBPSVal.currentText() self.LSerial.init(serialport,serialbaudrate) # 开启线程 self.thread = Worker() # 创建线程对象 self.thread.update_signal.connect(self.CanOBDdatarefresh) # 连接信号和槽 self.thread.update_signal.connect(self.LSerial.Com_read_frame) # 连接信号和槽 self.thread.start() # 启动线程 #self.LSerial.Com_read_frame() def CloseSerial(self): if self.LSerial.SerialIsOpen(): self.LSerial.close() def Serialconnectslot(self): self.mOpenSerial.accepted.connect(self.OpenSerial) self.mOpenSerial.rejected.connect(self.CloseSerial) def update_j1939_widgets(self, can_id, data): “”“根据 CAN ID 和原始数据更新对应的 J1939 显示控件”“” # 解析 CAN 帧 signals = parse_custom_can_frame(can_id, data) # 定义信号组与控件的映射关系 signal_group_map = { “CCVS1”: self.mSpeedTreeWidget, “EEC1_RPM”: self.mRPMTreeWidget, “VDHR”: self.mVDHRTreeWidget, “HOURS”: self.mHoursTreeWidget, “EEC1_Torque”: self.mEECTreeWidget, “ET1”: self.mET1TreeWidget, “AT1T1I”: self.mAT1T1ITreeWidget, “LFE”: self.mLFETreeWidget, “ETC2”: self.mETC2TreeWidget } # 遍历解析出的信号 for signal_name, signal_value in signals.items(): # 获取信号组前缀(例如 “CCVS1”) signal_group = signal_name.split(&#39;‘)[0] # 找到对应的 TreeWidget tree_widget = None for key, widget in signal_group_map.items(): if key.startswith(signal_group): tree_widget = widget break if not tree_widget: continue # 清除现有内容(可选,根据需要决定是否清除) tree_widget.clear() # 创建节点 item = QtWidgets.QTreeWidgetItem(tree_widget) # 设置各列数据 item.setText(0, signal_name) # 信号名称 item.setText(1, f"0x{can_id:08X}“) # CAN ID (十六进制格式) # 格式化原始数据为十六进制字符串 if isinstance(data, bytes): hex_data = ’ &#39;.join(f”{b:02X}" for b in data) else: hex_data = str(data) item.setText(2, hex_data) # 原始数据 # 显示信号值 item.setText(3, str(signal_value)) # 设置列宽自适应 for i in range(tree_widget.columnCount()): tree_widget.resizeColumnToContents(i) def parse_can_data(self, raw_data): “”“增强版CAN数据解析”“” try: # 尝试直接处理字节数据 if isinstance(raw_data, (bytes, bytearray)): return raw_data # 处理整数类型 if isinstance(raw_data, int): # 将整数转换为字节数组 length = (raw_data.bit_length() + 7) // 8 return raw_data.to_bytes(length, ‘big’) # 处理字符串类型 cleaned_str = str(raw_data).strip().upper() # 移除常见前缀 for prefix in [‘0X’, ‘\X’, ‘X’, ‘HEX:’]: if cleaned_str.startswith(prefix): cleaned_str = cleaned_str[len(prefix):] # 移除所有非十六进制字符 import re cleaned_str = re.sub(r’0-9A-F’, ‘’, cleaned_str) # 处理空字符串 if not cleaned_str: return b’’ # 处理奇数长度 if len(cleaned_str) % 2 != 0: cleaned_str = ‘0’ + cleaned_str # 高位补零 return bytes.fromhex(cleaned_str) except Exception as e: print(f"解析CAN数据错误: {raw_data} -> {e}“) return b’’ def CanOBDdatarefresh(self): index = 0 for indexitem in CanOBDItemList: itemTime = QTableWidgetItem(str(indexitem[0])) itemCanID = QTableWidgetItem(str(indexitem[1])) itemType = QTableWidgetItem(str(indexitem[2])) itemLen = QTableWidgetItem(str(indexitem[3])) itemData = QTableWidgetItem(str(indexitem[4])) self.tableWidget.setItem(index,0,itemTime) self.tableWidget.setItem(index, 1, itemCanID) self.tableWidget.setItem(index, 2, itemType) self.tableWidget.setItem(index, 3, itemLen) self.tableWidget.setItem(index, 4, itemData) # 新增:更新 J1939 显示 # try: # # 确保 CAN ID 是整数 # can_id = int(indexitem[1], 16) if isinstance(indexitem[1], str) else indexitem[1] # data = self.parse_can_data(item[4]) # 使用安全解析方法 # # self.update_j1939_widgets(can_id, data) # except Exception as e: # print(f"Error updating J1939 widgets: {e}”) index += 1 #CanOBDItemList.clear() self.tableWidget.show() class Worker(QThread): update_signal = pyqtSignal(int) # 定义一个信号,用于传递更新信息到主线程 def run(self): 模拟耗时操作 while True: time.sleep(0.001) self.update_signal.emit(1) # 发射信号,传递更新信息 这是canobd文件 import serial import time import struct from datetime import datetime CanOBDItemList = [[0, 0, 0, 0, 0]] Frame_start = b’\xFF’ Frame_end = b’\x55’ Frame_data_style_len = 6 Frame_Data_Len = 0 frame_buffer = bytearray() class CanInfShow_Item: def int(self, CanID, CanFramType, Len, CanDataInf): self.SystemCycle = datetime.now().strftime(“%Y-%m-%d %H:%M:%S.%f”)[:-3], self.CanID = CanID, self.CanFrame = CanFramType, self.CanDataLen = Len, self.CanData = CanDataInf class SerialPort: def init(self, port, baudrate): 初始化串口参数 self.port = port self.baudrate = baudrate self.ser = serial.Serial( port=self.port, baudrate=self.baudrate, bytesize=serial.EIGHTBITS, parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, timeout=0.1 # 添加串口读取超时 ) 添加超时检测机制 self.last_receive_time = time.time() # 记录最后接收时间 self.timeout_threshold = 1.5 # 超时阈值(秒) self.data_active = True # 初始状态为活跃 self.timeout_counter = 0 # 超时计数器 self.max_timeout_count = 3 # 连续超时3次后判定为停止 self.frame_count = 0 # 帧计数器 self.last_frame_count = 0 # 上次帧计数 等待串口连接稳定 time.sleep(0.2) if not self.ser.isOpen(): print(“串口打开失败!”) def close(self): # 关闭串口连接 if self.ser.isOpen(): self.ser.close() def send(self, data): # 发送数据 if self.ser.isOpen(): try: self.ser.write(data.encode(‘utf-8’)) except serial.SerialException as e: print(f"发送数据失败: {e}“) def recv(self): # 接收数据 - 使用非阻塞读取 if self.ser.isOpen(): try: # 读取所有可用数据,最多1024字节 data = self.ser.read(min(1024, self.ser.in_waiting)) return data except serial.SerialException as e: print(f"接收数据错误: {e}”) return b’’ def del(self): self.close() def SerialIsOpen(self): return 1 if self.ser.isOpen() else 0 def Frame_check_sum(self, data, length): “”“计算校验和”“” checksum = 0 for i in range(length): checksum += data[i] return checksum % 0x100 # 检查数据超时状态 def check_data_timeout(self): “”“检查数据接收是否超时并更新状态”“” current_time = time.time() elapsed = current_time - self.last_receive_time # 检查帧计数是否变化 if self.frame_count == self.last_frame_count: # 帧计数未变化,更新超时计数器 if elapsed > self.timeout_threshold: self.timeout_counter += 1 else: # 有新帧,重置计数器和时间 self.timeout_counter = 0 self.last_frame_count = self.frame_count self.last_receive_time = current_time # 连续超时达到阈值 if self.timeout_counter >= self.max_timeout_count: if self.data_active: print(f"数据接收超时({elapsed:.2f}s),停止界面更新") self.data_active = False return True # 数据活跃状态 if not self.data_active and self.timeout_counter == 0: print(“检测到数据流,开始更新界面”) self.data_active = True return False # 发送数据并接收响应 def Com_read_frame(self): global frame_buffer global Frame_Data_Len # 检查超时状态 if self.check_data_timeout(): # 超时状态下不处理数据 return byte_data = self.recv() if byte_data: # 更新最后接收时间 self.last_receive_time = time.time() frame_buffer.extend(byte_data) # 添加缓冲区大小限制(1MB) if len(frame_buffer) > 1024 * 1024: # 保留最后512KB frame_buffer = frame_buffer[-512 * 1024:] # 如果没有足够数据,提前返回 if len(frame_buffer) < Frame_data_style_len: return # 查找帧起始和结束位置 start_index = frame_buffer.find(Frame_start) end_index = frame_buffer.find(Frame_end) # 如果找不到起始帧或结束帧 if start_index == -1 or end_index == -1: # 清除无效数据(保留最后几个字节) if len(frame_buffer) > 10: frame_buffer = frame_buffer[-10:] return # 处理帧起始和结束位置关系 if start_index + 1 == end_index: # 计算帧长度 if start_index + 3 >= len(frame_buffer): return # 数据不足 Frame_Data_Len = (frame_buffer[start_index + 3] << 8) | frame_buffer[start_index + 2] # 调整缓冲区,使起始位置在0 frame_buffer = frame_buffer[start_index:] start_index = 0 elif end_index < start_index: # 结束标志在起始标志前,调整缓冲区 frame_buffer = frame_buffer[start_index:] start_index = 0 # 重新查找结束位置 end_index = frame_buffer.find(Frame_end) # 检查是否有完整帧 if len(frame_buffer) >= Frame_Data_Len + 5: # 计算校验和 data_start = start_index + 2 data_end = data_start + Frame_Data_Len + 2 if data_end > len(frame_buffer): return # 数据不足 checksum = self.Frame_check_sum(frame_buffer[data_start:data_end], Frame_Data_Len + 2) expected_checksum = frame_buffer[start_index + Frame_Data_Len + 4] if checksum == expected_checksum: # 提取完整帧 frame_end_pos = start_index + Frame_Data_Len + 5 frame_procee = frame_buffer[start_index:frame_end_pos] # 处理帧数据 self.Frame_analoy_process(frame_procee) self.frame_count += 1 # 增加帧计数 # 更新缓冲区 if len(frame_buffer) == frame_end_pos: frame_buffer.clear() else: frame_buffer = frame_buffer[frame_end_pos:] Frame_Data_Len = 0 else: # 校验失败,跳过这一帧 skip_pos = start_index + Frame_Data_Len + 6 if skip_pos < len(frame_buffer): frame_buffer = frame_buffer[skip_pos:] else: frame_buffer.clear() Frame_Data_Len = 0 # 报文解析 def Frame_analoy_process(self, Framedata): # 检查数据活跃状态 if not self.data_active: return # 数据超时状态下不更新界面 # 检查帧类型 (0x0C 0x98) if len(Framedata) < 8 or Framedata[4] != 0x0C or Framedata[5] != 0x98: return try: FrameNum = int(Framedata[7]) except IndexError: return # 检查是否有足够数据 if len(Framedata) < 12 * FrameNum + 8: return for index in range(FrameNum): # 时间戳 Cantime = datetime.now().strftime(“%Y-%m-%d %H:%M:%S.%f”)[:-3] # 提取ID字节 try: id_bytes = [ Framedata[12 * index + 11], # LSB Framedata[12 * index + 10], Framedata[12 * index + 9], Framedata[12 * index + 8] # MSB ] except IndexError: continue # 格式化为8位大写十六进制 CanID = ‘’.join(format(b, ‘02X’) for b in id_bytes) CanFramType = “Cycle” Len = 8 # 提取数据部分 try: CanDataSpace = ’ &#39;.join( format(Framedata[12 * index + 12 + posindex], ‘02X’) for posindex in range(8) ) except IndexError: continue CanItemData = [Cantime, CanID, CanFramType, Len, CanDataSpace] # 更新列表 if not CanOBDItemList or CanOBDItemList[0][0] == 0: if CanOBDItemList and CanOBDItemList[0][0] == 0: CanOBDItemList.pop(0) CanOBDItemList.insert(0, CanItemData) else: Listpos = self.find_in_2d_list(CanOBDItemList, CanID) if Listpos is not None: CanOBDItemList[Listpos[0]] = CanItemData else: CanOBDItemList.append(CanItemData) def find_in_2d_list(self, matrix, target): “”“在二维列表中查找目标”“” for i, row in enumerate(matrix): if target in row: return (i, row.index(target)) return None 这是serialpro文件,现在程序存在一定问题,就是当can数据停止发送后,tab1界面仍然有较长时间在刷新数据,要求在can数据停止发送后,tab界面能够较快地(1s内)停止刷新,现在有一个思路是清除缓冲区文件,你认为应该怎么做?
最新发布
07-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值