#!/usr/bin/env python # -*- coding: utf-8 -*- ''' # @Time : 2026/1/7 10:41 # @Author : reenrr # @File : 3D.py # @Desc : 界面上的线条3D显示 ''' from re import T import sys from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout from matplotlib.backends.backend_qtagg import FigureCanvasQTAgg as FigureCanvas import matplotlib.pyplot as plt from PySide6.QtCore import Qt class Target3DWidget(QWidget): """ 3D显示窗口,支持类型A和类型B """ VERTICES_A = [ # 底部截面(Z=0平面) (0, 3, 0), (100, 3, 0), (100, 3, 0), (100, 0, 0), # 中间截面(Z=0.5平面) (0, 0.5, 0.5), (0, 3, 0.5), (0, 3, 0.5), (100, 3, 0.5), (100, 3, 0.5), (100, 0.5, 0.5), (100, 0.5, 0.5), (0, 0.5, 0.5), # 顶部界面(Z=2平面) (0, 0, 2), (0, 0.5, 2), (0, 0.5, 2), (100, 0.5, 2), (100, 0.5, 2), (100, 0, 2), (100, 0, 2), (0, 0, 2), # z方向的连线 (0, 3, 0.5), (0, 3, 0), (100, 3, 0.5), (100, 3, 0), (0, 0.5, 2), (0, 0.5, 0.5), (100, 0.5, 2), (100, 0.5, 0.5), (100, 0, 2), (100, 0, 0), ] VERTICES_B = [ # 底部截面(Z=0平面) (0, 3, 0), (100, 3, 0), (100, 3, 0), (100, 0, 0), # 中间截面(Z=0.5平面) (0, 0.5, 0.5), (0, 2.5, 0.5), (100, 2.5, 0.5), (100, 0.5, 0.5), (100, 0.5, 0.5), (0, 0.5, 0.5), # 中间截面(Z=1平面) (0, 2.5, 1), (0, 3, 1), (0, 3, 1), (100, 3, 1), (100, 3, 1), (100, 2.5, 1), (100, 2.5, 1), (0, 2.5, 1), # 顶部界面(Z=2平面) (0, 0, 2), (0, 0.5, 2), (0, 0.5, 2), (100, 0.5, 2), (100, 0.5, 2), (100, 0, 2), (100, 0, 2), (0, 0, 2), # z方向的连线 (0, 3, 1), (0, 3, 0), (100, 3, 1), (100, 3, 0), (0, 0.5, 2), (0, 0.5, 0.5), (100, 0.5, 2), (100, 0.5, 0.5), (100, 0, 2), (100, 0, 0), (0, 2.5, 1), (0, 2.5, 0.5), (100, 2.5, 1), (100, 2.5, 0.5) ] ANNOTATIONS_A = [ ("a", 100, 0.25, 2, 'red'), ("b", 100, 0.5, 1.25, 'magenta'), ("c", 100, 1, 0, 'green'), ("d", 100, 3, 0.25, 'blue'), ] ANNOTATIONS_B = [ ("a", 100, 0.25, 2, 'red'), ("b", 100, 0.5, 1.25, 'magenta'), ("c", 100, 1, 0, 'green'), ("d", 100, 3, 0.5, 'blue'), ("e", 100, 2.5, 0.6, 'orange'), ("f", 100, 2.75, 1, 'purple') ] def __init__(self, anno: bool, index: int = 0, parent=None): super().__init__(parent) self._index = index self._anno = anno self.setWindowFlags(Qt.Window | Qt.FramelessWindowHint) # 无边框窗口 self.setAttribute(Qt.WA_TranslucentBackground) # 设置背景为透明 self.setStyleSheet("background: transparent;") self.init_3d_plot() self.setLayout(QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) # 移除布局的内边距 self.layout().addWidget(self.canvas) def init_3d_plot(self): # 3D画布 self.fig = plt.figure() self.fig.patch.set_facecolor('none') # 设置figure背景透明 self.canvas = FigureCanvas(self.fig) self.ax = self.fig.add_subplot(111, projection='3d') self.ax.set_facecolor('none') # 设置axes背景透明 # 绘制3D形状 self.draw_target_shape() # 添加标注 if self._anno: self.add_annotations() # ========== 核心修改:X/Y/Z轴统一为1cm单位 ========== self.ax.set_xlabel('X') self.ax.set_ylabel('Y') self.ax.set_zlabel('Z') # 1. 清空刻度数值(隐藏刻度数字) self.ax.set_xticklabels([]) self.ax.set_yticklabels([]) self.ax.set_zticklabels([]) # 2. 隐藏刻度线(针对3D轴的特殊设置) self.ax.tick_params(axis='x', which='both', length=0) # X轴刻度线长度设为0 self.ax.tick_params(axis='y', which='both', length=0) # Y轴刻度线长度设为0 self.ax.tick_params(axis='z', which='both', length=0) # Z轴刻度线长度设为0 # 轴范围保留(保证3D图形显示范围正确) self.ax.set_xlim(0, 120) self.ax.set_ylim(0, 5) self.ax.set_zlim(0, 5) # 调整视角(适配X轴1cm刻度) self.ax.view_init(elev=20, azim=60) def draw_target_shape(self): """绘制3D结构""" vertices = self.VERTICES_A if self._index == 0 else self.VERTICES_B # 绘制所有棱边 for i in range(0, len(vertices), 2): x = [vertices[i][0], vertices[i + 1][0]] y = [vertices[i][1], vertices[i + 1][1]] z = [vertices[i][2], vertices[i + 1][2]] self.ax.plot(x, y, z, color='black', linewidth=2) def add_annotations(self): """标注位置(适配X轴1cm单位)""" annotations = self.ANNOTATIONS_A if self._index == 0 else self.ANNOTATIONS_B for text, x, y, z, color in annotations: self.ax.text(x, y, z, text, fontsize=14, color=color, weight='bold') # ----------对外接口---------- def show(x: int, y: int, w: int, h: int, index: int): """ 显示3D视图窗口 :param x: 窗口横坐标 :param y: 窗口纵坐标 :param w: 窗口宽度 :param h: 窗口高度 :param index: 窗口索引,0为A视图,1为B视图 """ if index not in (0, 1): raise ValueError("index must be 0 or 1") app = QApplication(sys.argv) window = Target3DWidget(anno=True, index=index) window.setGeometry(x, y, w, h) window.show() sys.exit(app.exec()) # ----------测试接口---------- if __name__ == '__main__': # show(100, 100, 700, 700, 0) # 显示A视图窗口 show(100, 100, 700, 700, 1) # 显示B视图窗口