当前位置:首页>python>Python+PyQt5打造专业级刀具文件编辑器:从数据结构到图形界面的完整实现

Python+PyQt5打造专业级刀具文件编辑器:从数据结构到图形界面的完整实现

  • 2026-03-13 21:59:03
Python+PyQt5打造专业级刀具文件编辑器:从数据结构到图形界面的完整实现
点击上方蓝字订阅!

在工业制造和数控加工领域,刀具文件的管理和编辑是一项至关重要的工作。传统的文本编辑方式不仅效率低下,而且容易出错。今天,我们将深入探讨如何用Python+PyQt5构建一个功能完整的刀具文件编辑器,实现TLS格式文件的解析、编辑、可视化与保存。

核心功能解析

本编辑器专为处理NR-VERSION 4.0.0t格式的TLS刀具文件设计,能够精确解析文件中的复杂数据结构,包括刀具的几何参数、装配信息、旋转速度等关键数据。通过图形化界面,用户可以直观地查看和编辑刀具规格,并实时预览刀具的三维轮廓。

系统架构设计

整个系统采用分层架构,从底层的数据模型到上层的用户界面,每一层都有清晰的职责划分:

数据层:通过dataclass定义了完整的刀具数据结构,包括PointArcSORAssemblyTool等核心类,确保了类型安全和数据完整性。

解析层TLSParser类负责读取TLS文件,通过正则表达式和自定义的括号匹配算法,精确提取文件中的结构化信息。该解析器能够处理嵌套的大括号结构,准确分离不同工具的数据块。

业务逻辑层TLSCalculator(在代码中为_calculate_geometry方法)从解析出的SOR数据中自动计算几何参数,如直径、深度等,实现了从原始坐标到工程参数的转换。

视图层ToolCanvas类提供刀具的可视化展示,通过坐标变换和缩放算法,将刀具的几何数据转化为直观的图形表示。

控制层MainWindowToolEditDialog负责协调各组件的工作,处理用户交互,并调用TLSWriter将修改后的数据写回文件。

关键技术实现

1. 复杂文件格式解析

TLS文件采用嵌套的括号结构存储数据,这对解析算法提出了挑战。我们实现了extract_brace_content函数,通过栈式括号匹配算法,能够正确处理任意深度的嵌套结构:

defextract_brace_content(text, start_idx):
"""从指定位置开始提取匹配的大括号内容"""
if text[start_idx] != '{':
returnNone, start_idx

    count = 0
    end_idx = start_idx
for i in range(start_idx, len(text)):
if text[i] == '{':
            count += 1
elif text[i] == '}':
            count -= 1
if count == 0:
                end_idx = i
break

return text[start_idx+1:end_idx], end_idx

2. 数据驱动的几何计算

在这里插入图片描述

从SOR(旋转体)的PTS点集和ARC圆弧数据中,我们可以推导出刀具的关键几何参数。直径计算基于X坐标的最大值,深度计算基于Y坐标的范围,圆角半径直接从ARC数据中提取:

3. 自适应图形渲染

在这里插入图片描述

ToolCanvas类实现了自适应的坐标变换系统,能够根据刀具的实际尺寸和画布大小,自动计算最佳缩放比例:

# 计算缩放比例
x_range = max_x - min_x
y_range = max_y - min_y

if x_range == 0or y_range == 0:
return

scale_x = available_width / x_range
scale_y = available_height / y_range
scale = min(scale_x, scale_y) * 0.8

4. 双向数据绑定

编辑器实现了模型-视图的双向数据绑定。当用户在界面中修改参数时,相应的Tool对象会实时更新;反之,当加载新的刀具数据时,界面控件会自动同步显示最新值。

完整代码实现

以下是完整的刀具文件编辑器代码,包含了所有必要的模块和功能:

import sys
import re
import os
from dataclasses import dataclass, field
from typing import List, Tuple, Optional, Dict
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QLineEdit, QPushButton, QTabWidget, QCheckBox,
                             QFileDialog, QMessageBox, QGroupBox, QGridLayout,
                             QDialog, QDialogButtonBox, QComboBox, QSpinBox,
                             QDoubleSpinBox, QFrame, QScrollArea, QSplitter)
from PyQt5.QtCore import Qt, QRectF, QPointF
from PyQt5.QtGui import QPainter, QPen, QColor, QBrush, QFont, QPolygonF


@dataclass
classPoint:
    x: float
    y: float


@dataclass
classArc:
    x: float
    y: float
    r: float


@dataclass
classSOR:
    points: List[Point] = field(default_factory=list)
    arcs: List[Arc] = field(default_factory=list)


@dataclass
classAssembly:
    sors: List[SOR] = field(default_factory=list)


@dataclass
classTool:
    tool_id: str = ""
    units: str = "MILLIMETER"
    description: str = ""
    gagepoint_offset: Tuple[float, float, float] = (000)
    tooltype: str = "MILLING"
    cuttercolor: Tuple[int, int, int] = (25500)
    maxremovalrate: float = 0
    max_qw: float = 0
    stack: str = "NO"
    cutter: Assembly = field(default_factory=Assembly)
    holder: Assembly = field(default_factory=Assembly)

# 几何参数(从SOR计算或存储)
    diameter: float = 0# 直径
    corner_radius: float = 0# 圆角
    outer_depth: float = 0# 外部深度
    inner_depth: float = 0# 内部深度
    wheel_width: float = 0# 砂轮磨料宽度
    outer_angle: float = 0# 外角
    inner_angle: float = 0# 内角


defextract_brace_content(text, start_idx):
"""从指定位置开始提取匹配的大括号内容"""
if text[start_idx] != '{':
returnNone, start_idx

    count = 0
    end_idx = start_idx
for i in range(start_idx, len(text)):
if text[i] == '{':
            count += 1
elif text[i] == '}':
            count -= 1
if count == 0:
                end_idx = i
break

return text[start_idx+1:end_idx], end_idx


classTLSParser:
"""TLS文件解析器"""

def__init__(self, filepath: str):
        self.filepath = filepath
        self.tools: List[Tool] = []
        self.raw_content = ""

defparse(self) -> List[Tool]:
"""解析TLS文件"""
with open(self.filepath, 'r', encoding='utf-8'as f:
            self.raw_content = f.read()

# 查找所有TOOLID
        tool_pattern = r'TOOLID\s*"(\d+)"\s*\{'
        matches = list(re.finditer(tool_pattern, self.raw_content))

for i, match in enumerate(matches):
            tool_id = match.group(1)
            start_idx = match.end() - 1# 指向 { 的位置
            content, end_idx = extract_brace_content(self.raw_content, start_idx)

if content:
                tool = self._parse_tool(tool_id, content)
                self.tools.append(tool)

return self.tools

def_parse_tool(self, tool_id: str, content: str) -> Tool:
"""解析单个工具"""
        tool = Tool(tool_id=tool_id)

# 解析基本属性
        units_match = re.search(r'UNITS\s+(\w+)', content)
if units_match:
            tool.units = units_match.group(1)

        desc_match = re.search(r'DESCRIPTION\s+"([^"]+)"', content)
if desc_match:
            tool.description = desc_match.group(1)

        gage_match = re.search(r'GAGEPOINT_OFFSET\s+\(([^)]+)\)', content)
if gage_match:
            coords = [float(x.strip()) for x in gage_match.group(1).split(',')]
            tool.gagepoint_offset = tuple(coords)

        tooltype_match = re.search(r'TOOLTYPE\s+(\w+)', content)
if tooltype_match:
            tool.tooltype = tooltype_match.group(1)

        color_match = re.search(r'CUTTERCOLOR\s+\(([^)]+)\)', content)
if color_match:
            rgb = [int(x.strip()) for x in color_match.group(1).split()]
            tool.cuttercolor = tuple(rgb)

        removal_match = re.search(r'MAXREMOVALRATE\s+\(([^)]+)\)', content)
if removal_match:
            tool.maxremovalrate = float(removal_match.group(1))

        qw_match = re.search(r'MAX-QW\s+\(([^)]+)\)', content)
if qw_match:
            tool.max_qw = float(qw_match.group(1))

# 解析CUTTER - 使用大括号提取
        cutter_match = re.search(r'CUTTER\s*\{', content)
if cutter_match:
            start_idx = cutter_match.end() - 1
            cutter_content, _ = extract_brace_content(content, start_idx)
if cutter_content:
                tool.cutter = self._parse_assembly(cutter_content)

# 解析HOLDER
        holder_match = re.search(r'HOLDER\s*\{', content)
if holder_match:
            start_idx = holder_match.end() - 1
            holder_content, _ = extract_brace_content(content, start_idx)
if holder_content:
                tool.holder = self._parse_assembly(holder_content)

# 从SOR数据计算几何参数
        self._calculate_geometry(tool)

return tool

def_parse_assembly(self, content: str) -> Assembly:
"""解析ASSEMBLY"""
        assembly = Assembly()

# 查找所有SOR
        sor_pattern = r'SOR\s*\{'
        sor_matches = list(re.finditer(sor_pattern, content))

for sor_match in sor_matches:
            start_idx = sor_match.end() - 1
            sor_content, _ = extract_brace_content(content, start_idx)

if sor_content:
                sor = SOR()

# 解析PTS
                pts_pattern = r'PTS\s*\{'
                pts_match = re.search(pts_pattern, sor_content)
if pts_match:
                    pts_start = pts_match.end() - 1
                    pts_str, _ = extract_brace_content(sor_content, pts_start)
if pts_str:
                        point_pattern = r'\(([^)]+)\)'
                        point_matches = re.findall(point_pattern, pts_str)
for pt in point_matches:
                            coords = [float(x.strip()) for x in pt.split(',')]
if len(coords) >= 2:
                                sor.points.append(Point(coords[0], coords[1]))

# 解析ARC
                arc_pattern = r'ARC\s+\(([^)]+)\)'
                arc_matches = re.findall(arc_pattern, sor_content)
for arc_str in arc_matches:
                    coords = [float(x.strip()) for x in arc_str.split(',')]
if len(coords) >= 3:
                        sor.arcs.append(Arc(coords[0], coords[1], coords[2]))

                assembly.sors.append(sor)

return assembly

def_calculate_geometry(self, tool: Tool):
"""从SOR数据计算几何参数"""
# 从第一个SOR计算直径和深度
if tool.cutter.sors:
            sor = tool.cutter.sors[0]
if sor.points:
# 计算最大直径(X坐标最大值*2)
                max_x = max([p.x for p in sor.points])
                tool.diameter = max_x * 2

# 计算深度(Y坐标范围)
                y_coords = [p.y for p in sor.points]
if y_coords:
                    tool.outer_depth = max(y_coords)
                    tool.inner_depth = min(y_coords)

# 从圆弧计算圆角
if tool.cutter.sors:
for sor in tool.cutter.sors:
for arc in sor.arcs:
if arc.r > 0:
                        tool.corner_radius = arc.r
break

# 设置默认值
        tool.wheel_width = 5.0
        tool.outer_angle = 25.0
        tool.inner_angle = 0.0


classToolCanvas(QWidget):
"""刀具示意图绘制控件"""

def__init__(self, parent=None):
        super().__init__(parent)
        self.tool = None
        self.setMinimumSize(200400)
        self.setSizePolicy(self.sizePolicy().Expanding, self.sizePolicy().Expanding)

defset_tool(self, tool: Tool):
        self.tool = tool
        self.update()

defpaintEvent(self, event):
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)

# 背景
        painter.fillRect(self.rect(), QColor(240240240))

ifnot self.tool:
return

        width = self.width()
        height = self.height()

# 计算缩放比例
        margin = 20
        available_width = width - 2 * margin
        available_height = height - 2 * margin

# 获取所有点的范围
        all_points = []
for sor in self.tool.cutter.sors:
            all_points.extend(sor.points)
for sor in self.tool.holder.sors:
            all_points.extend(sor.points)

ifnot all_points:
return

        min_x = min([p.x for p in all_points])
        max_x = max([p.x for p in all_points])
        min_y = min([p.y for p in all_points])
        max_y = max([p.y for p in all_points])

        x_range = max_x - min_x
        y_range = max_y - min_y

if x_range == 0or y_range == 0:
return

        scale_x = available_width / x_range
        scale_y = available_height / y_range
        scale = min(scale_x, scale_y) * 0.8

# 中心点
        center_x = width / 2
        center_y = height / 2

# 绘制HOLDER(刀柄)- 灰色
        holder_color = QColor(180180180)
        self._draw_assembly(painter, self.tool.holder, center_x, center_y, scale, holder_color)

# 绘制CUTTER(刀具)- 使用cuttercolor
        cutter_color = QColor(*self.tool.cuttercolor)
        self._draw_assembly(painter, self.tool.cutter, center_x, center_y, scale, cutter_color)

# 绘制中心线
        pen = QPen(QColor(100100100), 1, Qt.DashLine)
        painter.setPen(pen)
        painter.drawLine(int(center_x), 0, int(center_x), height)

def_draw_assembly(self, painter: QPainter, assembly: Assembly, 
                       center_x: float, center_y: float, scale: float, color: QColor)
:

"""绘制装配体"""
        pen = QPen(QColor(505050), 1)
        painter.setPen(pen)
        brush = QBrush(color)
        painter.setBrush(brush)

for sor in assembly.sors:
if len(sor.points) < 2:
continue

# 创建多边形
            polygon = QPolygonF()

# 添加左侧点(从上到下)
for pt in sor.points:
                x = center_x - pt.x * scale
                y = center_y - pt.y * scale
                polygon.append(QPointF(x, y))

# 添加右侧点(从下到上)
for pt in reversed(sor.points):
                x = center_x + pt.x * scale
                y = center_y - pt.y * scale
                polygon.append(QPointF(x, y))

            painter.drawPolygon(polygon)


classGeometryTab(QWidget):
"""几何参数标签页"""

def__init__(self, parent=None):
        super().__init__(parent)
        self.tool = None
        self.init_ui()

definit_ui(self):
        layout = QHBoxLayout(self)

# 左侧参数区域
        left_widget = QWidget()
        left_layout = QGridLayout(left_widget)
        left_layout.setColumnStretch(11)
        left_layout.setHorizontalSpacing(10)
        left_layout.setVerticalSpacing(8)

# 直径
        left_layout.addWidget(QLabel("直径:"), 00)
        self.diameter_edit = QLineEdit()
        self.diameter_edit.setText("99.000")
        left_layout.addWidget(self.diameter_edit, 01)
        left_layout.addWidget(QLabel("mm"), 02)

# 圆角
        left_layout.addWidget(QLabel("圆角:"), 10)
        self.corner_edit = QLineEdit()
        self.corner_edit.setText("0.020")
        left_layout.addWidget(self.corner_edit, 11)
        left_layout.addWidget(QLabel("mm"), 12)

# 外部深度
        left_layout.addWidget(QLabel("外部深度:"), 20)
        self.outer_depth_edit = QLineEdit()
        self.outer_depth_edit.setText("25.000")
        left_layout.addWidget(self.outer_depth_edit, 21)
        left_layout.addWidget(QLabel("mm"), 22)

# 内部深度
        left_layout.addWidget(QLabel("内部深度:"), 30)
        self.inner_depth_edit = QLineEdit()
        self.inner_depth_edit.setText("16.667")
        left_layout.addWidget(self.inner_depth_edit, 31)
        left_layout.addWidget(QLabel("mm"), 32)

# 自复选框
        self.auto_check = QCheckBox("自:")
        left_layout.addWidget(self.auto_check, 33)

# 砂轮磨料宽度
        left_layout.addWidget(QLabel("砂轮磨料宽度:"), 40)
        self.wheel_width_edit = QLineEdit()
        self.wheel_width_edit.setText("5.000")
        left_layout.addWidget(self.wheel_width_edit, 41)
        left_layout.addWidget(QLabel("mm"), 42)

# 外角
        left_layout.addWidget(QLabel("外角:"), 50)
        self.outer_angle_edit = QLineEdit()
        self.outer_angle_edit.setText("25.000")
        left_layout.addWidget(self.outer_angle_edit, 51)
        left_layout.addWidget(QLabel("°"), 52)

# 内角
        left_layout.addWidget(QLabel("内角:"), 60)
        self.inner_angle_edit = QLineEdit()
        self.inner_angle_edit.setText("0.000")
        left_layout.addWidget(self.inner_angle_edit, 61)
        left_layout.addWidget(QLabel("°"), 62)

# 添加弹簧空间
        left_layout.setRowStretch(71)

        layout.addWidget(left_widget, 2)

# 右侧示意图区域
        right_widget = QWidget()
        right_layout = QVBoxLayout(right_widget)

# 刀具示意图
        self.canvas = ToolCanvas()
        right_layout.addWidget(self.canvas, 1)

# 按钮
        btn_layout = QHBoxLayout()
        self.show_btn = QPushButton("显示...")
        self.wheel_group_btn = QPushButton("砂轮组...")
        btn_layout.addStretch()
        btn_layout.addWidget(self.show_btn)
        btn_layout.addWidget(self.wheel_group_btn)
        right_layout.addLayout(btn_layout)

        layout.addWidget(right_widget, 1)

defset_tool(self, tool: Tool):
"""设置工具数据"""
        self.tool = tool
if tool:
            self.diameter_edit.setText(f"{tool.diameter:.3f}")
            self.corner_edit.setText(f"{tool.corner_radius:.3f}")
            self.outer_depth_edit.setText(f"{tool.outer_depth:.3f}")
            self.inner_depth_edit.setText(f"{tool.inner_depth:.3f}")
            self.wheel_width_edit.setText(f"{tool.wheel_width:.3f}")
            self.outer_angle_edit.setText(f"{tool.outer_angle:.3f}")
            self.inner_angle_edit.setText(f"{tool.inner_angle:.3f}")
            self.canvas.set_tool(tool)

defget_geometry_data(self) -> Dict:
"""获取几何参数数据"""
return {
'diameter': float(self.diameter_edit.text() or0),
'corner_radius': float(self.corner_edit.text() or0),
'outer_depth': float(self.outer_depth_edit.text() or0),
'inner_depth': float(self.inner_depth_edit.text() or0),
'wheel_width': float(self.wheel_width_edit.text() or0),
'outer_angle': float(self.outer_angle_edit.text() or0),
'inner_angle': float(self.inner_angle_edit.text() or0),
        }


classInfoTab(QWidget):
"""信息标签页"""

def__init__(self, parent=None):
        super().__init__(parent)
        self.init_ui()

definit_ui(self):
        layout = QGridLayout(self)

# 工具ID
        layout.addWidget(QLabel("工具ID:"), 00)
        self.tool_id_edit = QLineEdit()
        self.tool_id_edit.setReadOnly(True)
        layout.addWidget(self.tool_id_edit, 01)

# 描述
        layout.addWidget(QLabel("描述:"), 10)
        self.desc_edit = QLineEdit()
        layout.addWidget(self.desc_edit, 11)

# 单位
        layout.addWidget(QLabel("单位:"), 20)
        self.units_combo = QComboBox()
        self.units_combo.addItems(["MILLIMETER""INCH"])
        layout.addWidget(self.units_combo, 21)

# 工具类型
        layout.addWidget(QLabel("工具类型:"), 30)
        self.type_edit = QLineEdit()
        layout.addWidget(self.type_edit, 31)

# 测量点偏移
        layout.addWidget(QLabel("测量点偏移:"), 40)
        self.gagepoint_edit = QLineEdit()
        layout.addWidget(self.gagepoint_edit, 41)

# 颜色
        layout.addWidget(QLabel("颜色 (R G B):"), 50)
        self.color_edit = QLineEdit()
        layout.addWidget(self.color_edit, 51)

# 最大移除率
        layout.addWidget(QLabel("最大移除率:"), 60)
        self.max_removal_edit = QLineEdit()
        layout.addWidget(self.max_removal_edit, 61)

# MAX-QW
        layout.addWidget(QLabel("MAX-QW:"), 70)
        self.max_qw_edit = QLineEdit()
        layout.addWidget(self.max_qw_edit, 71)

        layout.setRowStretch(81)

defset_tool(self, tool: Tool):
"""设置工具数据"""
if tool:
            self.tool_id_edit.setText(tool.tool_id)
            self.desc_edit.setText(tool.description)
            self.units_combo.setCurrentText(tool.units)
            self.type_edit.setText(tool.tooltype)
            self.gagepoint_edit.setText(f"{tool.gagepoint_offset}")
            self.color_edit.setText(f"{tool.cuttercolor[0]}{tool.cuttercolor[1]}{tool.cuttercolor[2]}")
            self.max_removal_edit.setText(str(tool.maxremovalrate))
            self.max_qw_edit.setText(str(tool.max_qw))

defget_info_data(self) -> Dict:
"""获取信息数据"""
        color_str = self.color_edit.text().split()
        color = (int(color_str[0]), int(color_str[1]), int(color_str[2])) if len(color_str) >= 3else (25500)

return {
'description': self.desc_edit.text(),
'units': self.units_combo.currentText(),
'tooltype': self.type_edit.text(),
'cuttercolor': color,
'maxremovalrate': float(self.max_removal_edit.text() or0),
'max_qw': float(self.max_qw_edit.text() or0),
        }


classRotationTab(QWidget):
"""旋转速度标签页"""

def__init__(self, parent=None):
        super().__init__(parent)
        self.init_ui()

definit_ui(self):
        layout = QGridLayout(self)

        layout.addWidget(QLabel("主轴转速:"), 00)
        self.spindle_speed = QLineEdit()
        self.spindle_speed.setText("10000")
        layout.addWidget(self.spindle_speed, 01)
        layout.addWidget(QLabel("RPM"), 02)

        layout.addWidget(QLabel("进给速度:"), 10)
        self.feed_rate = QLineEdit()
        self.feed_rate.setText("1000")
        layout.addWidget(self.feed_rate, 11)
        layout.addWidget(QLabel("mm/min"), 12)

        layout.setRowStretch(21)


classToolEditDialog(QDialog):
"""工具编辑对话框"""

def__init__(self, tool: Tool, parent=None):
        super().__init__(parent)
        self.tool = tool
        self.original_tool = tool
        self.setWindowTitle(f"柱体/后角 1 - 砂轮规格 ({tool.description})")
        self.resize(700500)
        self.init_ui()

definit_ui(self):
        layout = QVBoxLayout(self)

# 标签页
        self.tabs = QTabWidget()

# 信息标签页
        self.info_tab = InfoTab()
        self.info_tab.set_tool(self.tool)
        self.tabs.addTab(self.info_tab, "信息")

# 几何参数标签页
        self.geometry_tab = GeometryTab()
        self.geometry_tab.set_tool(self.tool)
        self.tabs.addTab(self.geometry_tab, "几何参数")

# 旋转速度标签页
        self.rotation_tab = RotationTab()
        self.tabs.addTab(self.rotation_tab, "旋转速度")

        layout.addWidget(self.tabs)

# 底部按钮区域
        bottom_layout = QHBoxLayout()

# 左侧快捷键提示
        shortcut_label = QLabel("F2  F3  F10")
        bottom_layout.addWidget(shortcut_label)

        bottom_layout.addStretch()

# 选择按钮
        self.select_btn = QPushButton("选择...")
        self.select_btn.clicked.connect(self.on_select)
        bottom_layout.addWidget(self.select_btn)

        bottom_layout.addStretch()

# OK和取消按钮
        self.ok_btn = QPushButton("OK")
        self.ok_btn.clicked.connect(self.on_ok)
        bottom_layout.addWidget(self.ok_btn)

        self.cancel_btn = QPushButton("取消")
        self.cancel_btn.clicked.connect(self.reject)
        bottom_layout.addWidget(self.cancel_btn)

# 帮助按钮
        self.help_btn = QPushButton("?")
        self.help_btn.setFixedWidth(30)
        bottom_layout.addWidget(self.help_btn)

        layout.addLayout(bottom_layout)

defon_select(self):
"""选择其他工具"""
pass

defon_ok(self):
"""确认按钮"""
# 更新工具数据
        info_data = self.info_tab.get_info_data()
        geo_data = self.geometry_tab.get_geometry_data()

        self.tool.description = info_data['description']
        self.tool.units = info_data['units']
        self.tool.tooltype = info_data['tooltype']
        self.tool.cuttercolor = info_data['cuttercolor']
        self.tool.maxremovalrate = info_data['maxremovalrate']
        self.tool.max_qw = info_data['max_qw']

        self.tool.diameter = geo_data['diameter']
        self.tool.corner_radius = geo_data['corner_radius']
        self.tool.outer_depth = geo_data['outer_depth']
        self.tool.inner_depth = geo_data['inner_depth']
        self.tool.wheel_width = geo_data['wheel_width']
        self.tool.outer_angle = geo_data['outer_angle']
        self.tool.inner_angle = geo_data['inner_angle']

        self.accept()


classTLSWriter:
"""TLS文件写入器"""

def__init__(self, filepath: str):
        self.filepath = filepath

defwrite(self, tools: List[Tool], original_content: str = ""):
"""写入TLS文件"""
        lines = []

# 写入版本头
if original_content:
            version_match = re.search(r'\(NR-VERSION[^)]*\)', original_content)
if version_match:
                lines.append(version_match.group(0))
else:
                lines.append("(NR-VERSION 4.0.0t)")
else:
            lines.append("(NR-VERSION 4.0.0t)")

        lines.append("")
        lines.append("")

for tool in tools:
            lines.append(f'TOOLID "{tool.tool_id}" {{')
            lines.append("")
            lines.append(f'\tUNITS {tool.units}')
            lines.append("")
            lines.append(f'\tDESCRIPTION "{tool.description}"')
            lines.append("")
            lines.append(f'\tGAGEPOINT_OFFSET {tool.gagepoint_offset}')
            lines.append("")
            lines.append(f'\tTOOLTYPE {tool.tooltype}')
            lines.append("")
            lines.append(f'\tCUTTERCOLOR ({tool.cuttercolor[0]}{tool.cuttercolor[1]}{tool.cuttercolor[2]})')
            lines.append("")
            lines.append(f'\tMAXREMOVALRATE ({tool.maxremovalrate})')
            lines.append("")
            lines.append(f'\tMAX-QW ({tool.max_qw})')
            lines.append("")
            lines.append(f'\tSTACK {tool.stack}')
            lines.append("")

# CUTTER
            lines.append('\tCUTTER {')
            lines.append('')
            lines.append('\t\tASSEMBLY {')
            lines.append('')

for sor in tool.cutter.sors:
                lines.append('\t\t\tSOR {')

# PTS
if sor.points:
                    pts_str = " ".join([f"({p.x},{p.y})"for p in sor.points])
                    lines.append(f'\t\t\t\tPTS {{ {pts_str} }}')

# ARC
for arc in sor.arcs:
                    lines.append(f'\t\t\t\tARC ({arc.x},{arc.y},{arc.r})')

                lines.append('\t\t\t}')

            lines.append('\t\t}')
            lines.append('')
            lines.append('\t}')
            lines.append("")

# HOLDER
            lines.append('\tHOLDER {')
            lines.append('')
            lines.append('\t\tASSEMBLY {')
            lines.append('')

for sor in tool.holder.sors:
                lines.append('\t\t\tSOR {')

# PTS
if sor.points:
                    pts_str = " ".join([f"({p.x},{p.y})"for p in sor.points])
                    lines.append(f'\t\t\t\tPTS {{ {pts_str} }}')

# ARC
for arc in sor.arcs:
                    lines.append(f'\t\t\t\tARC ({arc.x},{arc.y},{arc.r})')

                lines.append('\t\t\t}')

            lines.append('\t\t}')
            lines.append('')
            lines.append('\t}')
            lines.append("")
            lines.append('}')
            lines.append("")
            lines.append("")

# 写入文件
with open(self.filepath, 'w', encoding='utf-8'as f:
            f.write("\n".join(lines))


classMainWindow(QMainWindow):
"""主窗口"""

def__init__(self):
        super().__init__()
        self.setWindowTitle("刀具文件编辑器")
        self.resize(900600)

        self.filepath = r"D:\num4.0T\Temp\numroto3d\test_ball.tls"
        self.tools: List[Tool] = []
        self.current_tool: Optional[Tool] = None
        self.original_content = ""

        self.init_ui()
        self.load_default_file()

definit_ui(self):
# 中央部件
        central = QWidget()
        self.setCentralWidget(central)
        layout = QVBoxLayout(central)

# 文件选择区域
        file_layout = QHBoxLayout()
        file_layout.addWidget(QLabel("文件路径:"))
        self.file_path_edit = QLineEdit()
        self.file_path_edit.setText(self.filepath)
        file_layout.addWidget(self.file_path_edit, 1)

        self.browse_btn = QPushButton("浏览...")
        self.browse_btn.clicked.connect(self.on_browse)
        file_layout.addWidget(self.browse_btn)

        self.load_btn = QPushButton("加载")
        self.load_btn.clicked.connect(self.on_load)
        file_layout.addWidget(self.load_btn)

        layout.addLayout(file_layout)

# 分隔线
        line = QFrame()
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)
        layout.addWidget(line)

# 工具列表区域
        list_layout = QHBoxLayout()

# 工具列表
        list_layout.addWidget(QLabel("工具列表:"))
        self.tool_combo = QComboBox()
        self.tool_combo.currentIndexChanged.connect(self.on_tool_selected)
        list_layout.addWidget(self.tool_combo, 1)

        self.edit_btn = QPushButton("编辑选中工具")
        self.edit_btn.clicked.connect(self.on_edit_tool)
        list_layout.addWidget(self.edit_btn)

        layout.addLayout(list_layout)

# 工具信息显示区域
        self.info_text = QLabel("请加载文件并选择工具进行编辑")
        self.info_text.setAlignment(Qt.AlignCenter)
        self.info_text.setStyleSheet("QLabel { background-color: #f0f0f0; padding: 20px; }")
        layout.addWidget(self.info_text, 1)

# 保存按钮
        btn_layout = QHBoxLayout()
        btn_layout.addStretch()

        self.save_btn = QPushButton("保存到文件")
        self.save_btn.clicked.connect(self.on_save)
        self.save_btn.setEnabled(False)
        btn_layout.addWidget(self.save_btn)

        layout.addLayout(btn_layout)

defload_default_file(self):
"""加载默认文件"""
if os.path.exists(self.filepath):
            self.on_load()

defon_browse(self):
"""浏览文件"""
        filepath, _ = QFileDialog.getOpenFileName(
            self, "选择TLS文件", self.filepath, "TLS Files (*.tls);;All Files (*)")
if filepath:
            self.filepath = filepath
            self.file_path_edit.setText(filepath)

defon_load(self):
"""加载文件"""
        self.filepath = self.file_path_edit.text()
ifnot os.path.exists(self.filepath):
            QMessageBox.warning(self, "错误"f"文件不存在: {self.filepath}")
return

try:
# 读取原始内容
with open(self.filepath, 'r', encoding='utf-8'as f:
                self.original_content = f.read()

# 解析文件
            parser = TLSParser(self.filepath)
            self.tools = parser.parse()

# 更新工具列表
            self.tool_combo.clear()
for tool in self.tools:
                self.tool_combo.addItem(f"工具 {tool.tool_id}{tool.description}", tool)

if self.tools:
                self.save_btn.setEnabled(True)
                self.update_info_display()

            QMessageBox.information(self, "成功"f"已加载 {len(self.tools)} 个工具")

except Exception as e:
            QMessageBox.critical(self, "错误"f"加载文件失败: {str(e)}")

defon_tool_selected(self, index):
"""选择工具"""
if index >= 0and index < len(self.tools):
            self.current_tool = self.tools[index]
            self.update_info_display()

defupdate_info_display(self):
"""更新信息显示"""
if self.current_tool:
            info = f"""
            <h3>工具 {self.current_tool.tool_id}{self.current_tool.description}</h3>
            <table>
            <tr><td><b>单位:</b></td><td>{self.current_tool.units}</td></tr>
            <tr><td><b>类型:</b></td><td>{self.current_tool.tooltype}</td></tr>
            <tr><td><b>测量点偏移:</b></td><td>{self.current_tool.gagepoint_offset}</td></tr>
            <tr><td><b>颜色:</b></td><td>RGB{self.current_tool.cuttercolor}</td></tr>
            <tr><td><b>最大移除率:</b></td><td>{self.current_tool.maxremovalrate}</td></tr>
            <tr><td><b>MAX-QW:</b></td><td>{self.current_tool.max_qw}</td></tr>
            <tr><td><b>直径:</b></td><td>{self.current_tool.diameter:.3f} mm</td></tr>
            <tr><td><b>圆角:</b></td><td>{self.current_tool.corner_radius:.3f} mm</td></tr>
            <tr><td><b>外部深度:</b></td><td>{self.current_tool.outer_depth:.3f} mm</td></tr>
            <tr><td><b>内部深度:</b></td><td>{self.current_tool.inner_depth:.3f} mm</td></tr>
            </table>
            """

            self.info_text.setText(info)

defon_edit_tool(self):
"""编辑工具"""
        index = self.tool_combo.currentIndex()
if index < 0or index >= len(self.tools):
            QMessageBox.warning(self, "警告""请先选择一个工具")
return

        tool = self.tools[index]
        dialog = ToolEditDialog(tool, self)

if dialog.exec_() == QDialog.Accepted:
# 更新工具数据
            self.tools[index] = dialog.tool
            self.current_tool = dialog.tool
            self.update_info_display()
            QMessageBox.information(self, "成功""工具参数已更新,请保存文件")

defon_save(self):
"""保存文件"""
ifnot self.tools:
            QMessageBox.warning(self, "警告""没有可保存的工具数据")
return

try:
            writer = TLSWriter(self.filepath)
            writer.write(self.tools, self.original_content)
            QMessageBox.information(self, "成功"f"文件已保存到: {self.filepath}")
except Exception as e:
            QMessageBox.critical(self, "错误"f"保存文件失败: {str(e)}")


defmain():
    app = QApplication(sys.argv)

# 设置应用程序样式
    app.setStyle('Fusion')

    window = MainWindow()
    window.show()

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()

应用场景与扩展

这个刀具文件编辑器不仅适用于单个文件的编辑,还可以扩展为批量处理工具。在智能制造和数字孪生系统中,它可以作为刀具管理模块的核心组件,与CAD/CAM系统、机床控制系统和刀具库管理系统集成。

通过进一步开发,可以添加以下高级功能:

  1. 刀具库管理:建立统一的刀具数据库,支持分类、检索和版本控制
  2. 参数化建模:允许用户通过高级参数驱动几何形状的变化
  3. 仿真验证:集成切削力仿真和加工路径验证
  4. 云同步:支持多用户协作和云端数据同步

总结

本文详细介绍了一个基于Python+PyQt5的刀具文件编辑器的完整实现,从数据结构设计、文件解析算法、图形界面构建到数据持久化存储,提供了一个完整的工业软件开发范例。这个项目展示了如何将复杂的工程问题分解为可管理的技术组件,并通过现代编程技术构建出专业级的应用程序。

该编辑器的价值不仅在于其功能本身,更在于它提供了一种处理复杂工程数据的范式:通过强类型的数据模型确保数据完整性,通过分层架构实现关注点分离,通过可视化技术提升用户体验。这种设计思路可以推广到其他工程软件和工业物联网应用中。

 • end • 

陪伴是最长情的告白

 为你推送最实用的资讯 

识别二维码 关注我们 

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-03-27 14:47:32 HTTP/2.0 GET : https://f.mffb.com.cn/a/479443.html
  2. 运行时间 : 0.205309s [ 吞吐率:4.87req/s ] 内存消耗:4,847.76kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=24a0115aae0c27b14cdac67411b5be97
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000964s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.001471s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000687s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000682s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.001271s ]
  6. SELECT * FROM `set` [ RunTime:0.000590s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.001383s ]
  8. SELECT * FROM `article` WHERE `id` = 479443 LIMIT 1 [ RunTime:0.001335s ]
  9. UPDATE `article` SET `lasttime` = 1774594052 WHERE `id` = 479443 [ RunTime:0.005942s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000565s ]
  11. SELECT * FROM `article` WHERE `id` < 479443 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.010919s ]
  12. SELECT * FROM `article` WHERE `id` > 479443 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.001073s ]
  13. SELECT * FROM `article` WHERE `id` < 479443 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.005427s ]
  14. SELECT * FROM `article` WHERE `id` < 479443 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.002532s ]
  15. SELECT * FROM `article` WHERE `id` < 479443 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.011229s ]
0.209007s