PythonOcc实战——step文件导入、零件识别、几何属性、爆炸图初步展示-程序员宅基地

技术标签: 3d  python  可视化  cad  PythonOcc  

本系列文章主要是基于PythonOcc模块进行实例编程。本文主要想实现的功能是将step模型文件加载并封装到pyqt5自制的UI界面中,并初步实现零件识别、零件几何属性计算、产品爆炸图初步展示。
阅读并实现本系列文章需要一定的基础,请参考:
小新快跑

import sys
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox#引入Occ模型绘制模块
from SelfMadeQTviewer3D import qtViewer3d#引入自己修改后的Occ3维模型展示模块,该模块并不通用,
#是由本人修改源代码之后才能使用的,源代码中不能直接import qtViewer3d
#该代码参考https://blog.csdn.net/weixin_42755384/article/details/87925748
class Example(QMainWindow):

    def __init__(self):
        super().__init__()
        self.canva = qtViewer3d(self)
        self.initUI()
    def initUI(self):               
        exitAct = QAction('&Exit', self)        
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(qApp.quit)
        self.statusBar()
        menubar = self.menuBar()
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)
        self.setGeometry(300, 100, 1600, 800)
        self.setWindowTitle('PyOcc Packed In Pyqt5')
        boxshp = BRepPrimAPI_MakeBox(50., 50., 50.).Shape()#绘制occ3维CAD模型
        self.canva._display.DisplayShape(boxshp, update=True)[0]#在画布上展示occ3维CAD模型
        self.setCentralWidget(self.canva)#在画布上展示occ3维CAD模型
        self.show()
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

以上代码可以得到将occ封装入自定义pyqt5的UI界面,如下图所示,关键是:
from SelfMadeQTviewer3D import qtViewer3d。
在这里插入图片描述

接下来将进入实战。将STEP文件导入到PythonOcc中并实现零件识别、零件几何属性计算:

import sys
import time
from math import pi
import random

from OCC.Core.gp import gp_Ax1, gp_Pnt, gp_Dir, gp_Trsf  # 引入Occ模型几何尺寸元素模块
from OCC.Core.TopLoc import TopLoc_Location  # 引入Occ模型定位模块
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from SelfMadeQTviewer3D import qtViewer3d  # 引入自己修改后的Occ3维模型展示模块
from OCC.Extend.DataExchange import read_step_file#STEP文件导入模块
from OCC.Extend.TopologyUtils import TopologyExplorer#STEP文件导入模块后的拓扑几何分析模块
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
from OCC.Core.GProp import GProp_GProps#几何模型属性
from OCC.Core.BRepGProp import brepgprop_VolumeProperties#solid几何模型属性

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.canva = qtViewer3d(self)

        self.initUI()

    def initUI(self):

        exitAct = QAction('&Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(qApp.quit)        

        self.statusBar()

        menubar = self.menuBar()
        
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)

        
        self.setGeometry(100, 50, 1200, 900)
        self.setWindowTitle('PyOcc Packed In Pyqt5')
        
        self.setCentralWidget(self.canva)
        
        self.StepModelExplore()

        self.show()

    def STEP_shape(self):
        stpshp = read_step_file('GateValveAssembly.STEP')# 读取step文件
        return TopologyExplorer(stpshp)# step文件模型解析

    def StepModelExplore(self):
        self.canva._display.EraseAll()
        self.STEPshp = self.STEP_shape()
        i=0
        for solid in self.STEPshp.solids():
            i=i+1#用于统计零件数目
            props = GProp_GProps()#获取几何属性查询函数
            brepgprop_VolumeProperties(solid, props)#计算出当前solid的集合属性
            #matrix_of_inertia = props.MatrixOfInertia()#当前solid的惯性矩
            cog = props.CentreOfMass()#当前solid的形心
            cog_x, cog_y, cog_z = cog.Coord()
            print("This Solid mass = %s" % props.Mass())#当前solid的体积
            print("Center of Solid: x = %f;y = %f;z = %f;" % (cog_x, cog_y, cog_z))            
            color = Quantity_Color(random.random(), random.random(),random.random(),Quantity_TOC_RGB)
            self.canva._display.DisplayColoredShape(solid, color)
        print("零件数目为:%d" % (i))
        self.canva._display.FitAll() 


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())
2
Number of shapes: 2
This Solid mass = 1671588.029631332
Center of Solid: x = 0.000025;y = 21.327333;z = -90.000000;
This Solid mass = 20416.786227435936
Center of Solid: x = -0.000001;y = 0.723585;z = -67.780266;
This Solid mass = 20416.786227435958
Center of Solid: x = -0.000000;y = 0.723584;z = -112.219734;
This Solid mass = 539264.8949639023
Center of Solid: x = -0.053763;y = 166.657923;z = -90.042440;
This Solid mass = 54103.19372018658
Center of Solid: x = -0.000000;y = 311.553234;z = -90.000000;
This Solid mass = 18954.852646672913
Center of Solid: x = -0.000000;y = 305.792553;z = -90.000000;
This Solid mass = 338469.4612692211
Center of Solid: x = 0.000015;y = 337.857166;z = -90.000001;
This Solid mass = 5220.770297180713
Center of Solid: x = 0.000000;y = 344.000954;z = -90.000000;
This Solid mass = 49449.28479252284
Center of Solid: x = -0.000000;y = 114.460108;z = -90.000000;
This Solid mass = 39139.64044792523
Center of Solid: x = -0.199089;y = 283.199322;z = -90.325002;
This Solid mass = 106966.64358232789
Center of Solid: x = -0.000000;y = 11.964572;z = -90.000007;
This Solid mass = 4781.860167653959
Center of Solid: x = -2.724443;y = 192.999999;z = -42.000000;
This Solid mass = 1309.926227762865
Center of Solid: x = 20.706363;y = 193.000002;z = -42.000000;
This Solid mass = 12662.473567635041
Center of Solid: x = 0.000000;y = 214.240225;z = -42.000000;
This Solid mass = 4781.860167653957
Center of Solid: x = -2.724443;y = 192.999999;z = -138.000000;
This Solid mass = 1309.9262277628643
Center of Solid: x = 20.693637;y = 193.000002;z = -138.000000;
This Solid mass = 12662.473567635043
Center of Solid: x = 0.000000;y = 214.240225;z = -138.000000;
This Solid mass = 36769.10249043011
Center of Solid: x = 0.000000;y = 232.763354;z = -89.999999;
This Solid mass = 10777.165686221655
Center of Solid: x = -0.000000;y = 214.799317;z = -90.000000;
This Solid mass = 1998.9380995506442
Center of Solid: x = -0.000003;y = 244.471533;z = -42.000001;
This Solid mass = 1998.9380995506447
Center of Solid: x = 0.000002;y = 244.471533;z = -137.999998;
This Solid mass = 4.188790204786394
Center of Solid: x = -0.000000;y = 290.500000;z = -47.000000;
This Solid mass = 951.8048002047907
Center of Solid: x = -0.000000;y = 290.500000;z = -56.794693;
This Solid mass = 9196.595426332882
Center of Solid: x = 35.652961;y = 189.204163;z = -62.957503;
This Solid mass = 14.137166941154069
Center of Solid: x = 47.994656;y = 189.204163;z = -52.963397;
This Solid mass = 3131.1503837897526
Center of Solid: x = 0.000000;y = 106.500000;z = -90.000000;
This Solid mass = 12743.227021776174
Center of Solid: x = -0.000000;y = 139.414294;z = -90.000000;
This Solid mass = 5046.897424302504
Center of Solid: x = -0.000004;y = 178.999999;z = -89.999996;
This Solid mass = 2377.662039991875
Center of Solid: x = -0.000000;y = 166.750000;z = -90.000000;
This Solid mass = 2377.662039991875
Center of Solid: x = 0.000000;y = 158.250000;z = -90.000000;
This Solid mass = 2377.6620399918756
Center of Solid: x = -0.000000;y = 191.250000;z = -90.000000;
This Solid mass = 2377.662039991875
Center of Solid: x = -0.000000;y = 199.750000;z = -90.000000;
This Solid mass = 4813.889207068303
Center of Solid: x = 48.000000;y = 131.400017;z = -37.999999;
This Solid mass = 4813.889207068301
Center of Solid: x = -48.000000;y = 131.400017;z = -38.000001;
This Solid mass = 4813.889207068301
Center of Solid: x = 48.000000;y = 131.400017;z = -142.000001;
This Solid mass = 4813.889207068301
Center of Solid: x = -48.000000;y = 131.400017;z = -142.000001;
This Solid mass = 4813.889207068302
Center of Solid: x = 48.000000;y = 78.599983;z = -141.999999;
This Solid mass = 4813.889207068303
Center of Solid: x = 48.000000;y = 78.599983;z = -37.999999;
This Solid mass = 4813.889207068305
Center of Solid: x = -48.000000;y = 78.599983;z = -141.999999;
This Solid mass = 4813.889207068301
Center of Solid: x = -48.000001;y = 78.599983;z = -37.999999;
This Solid mass = 16082.638427578428
Center of Solid: x = 48.000000;y = 105.372204;z = -38.000000;
This Solid mass = 16082.638427578428
Center of Solid: x = -48.000000;y = 105.372204;z = -38.000000;
This Solid mass = 16082.638427578428
Center of Solid: x = 48.000000;y = 105.372204;z = -142.000000;
This Solid mass = 16082.638427578413
Center of Solid: x = -48.000000;y = 105.372204;z = -142.000000;
零件数目为:44

该零件数目统计结果与用solidworks等商业软件导入文件时的结果一致。
在这里插入图片描述

下面要做爆炸图展示,想法是根据各个零件形心所在位置按照不同方向比例做平移。

import sys
import time
from math import pi
import random

from OCC.Core.gp import gp_Ax1, gp_Pnt, gp_Dir, gp_Trsf ,gp_Vec # 引入Occ模型几何尺寸元素模块,零件移动关键模块
from OCC.Core.TopLoc import TopLoc_Location  # 引入Occ模型定位模块
from PyQt5.QtWidgets import QMainWindow, QAction, qApp, QApplication
from SelfMadeQTviewer3D import qtViewer3d  # 引入自己修改后的Occ3维模型展示模块
from OCC.Extend.DataExchange import read_step_file#STEP文件导入模块
from OCC.Extend.TopologyUtils import TopologyExplorer#STEP文件导入模块后的拓扑几何分析模块
from OCC.Core.Quantity import Quantity_Color, Quantity_TOC_RGB
from OCC.Core.GProp import GProp_GProps#几何模型属性
from OCC.Core.BRepGProp import brepgprop_VolumeProperties#solid几何模型属性

class Example(QMainWindow):

    def __init__(self):
        super().__init__()

        self.canva = qtViewer3d(self)

        self.initUI()

    def initUI(self):

        exitAct = QAction('&Exit', self)
        exitAct.setShortcut('Ctrl+Q')
        exitAct.setStatusTip('Exit application')
        exitAct.triggered.connect(qApp.quit)        

        self.statusBar()

        menubar = self.menuBar()
        
        fileMenu = menubar.addMenu('&File')
        fileMenu.addAction(exitAct)

        
        self.setGeometry(100, 50, 1200, 900)
        self.setWindowTitle('PyOcc Packed In Pyqt5')
        
        self.setCentralWidget(self.canva)
        
        self.StepModelExplore()

        self.show()

    def STEP_shape(self):
        stpshp = read_step_file('GateValveAssembly.STEP')# 读取step文件
        return TopologyExplorer(stpshp)# step文件模型解析

    def StepModelExplore(self):
        self.canva._display.EraseAll()
        self.STEPshp = self.STEP_shape()
        i=0
        for solid in self.STEPshp.solids():
            i=i+1#用于统计零件数目
            props = GProp_GProps()#获取几何属性查询函数
            brepgprop_VolumeProperties(solid, props)#计算出当前solid的集合属性
            #matrix_of_inertia = props.MatrixOfInertia()#当前solid的惯性矩
            cog = props.CentreOfMass()#当前solid的形心
            cog_x, cog_y, cog_z = cog.Coord()
            print("This Solid mass = %s" % props.Mass())#当前solid的体积
#             print("Center of Solid: x = %f;y = %f;z = %f;" % (cog_x, cog_y, cog_z)) 
            # ----------爆炸图展示载入的3维模型----------------------
            T=gp_Trsf()#定义一个变换函数
            T.SetTranslation(gp_Vec(5*cog_x,5*cog_y,10*cog_z))#将变换函数设置为沿着指定向量平移
            loc=TopLoc_Location(T)#将变换函数重新定位到几何拓扑上
            solid.Location(loc)#将该几何拓扑solid定位
            # ----------爆炸图展示载入的3维模型----------------------
            color = Quantity_Color(random.random(), random.random(),random.random(),Quantity_TOC_RGB)
            self.canva._display.DisplayColoredShape(solid, color)
        print("零件数目为:%d" % (i))
        self.canva._display.FitAll() 


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())
2
Number of shapes: 2
This Solid mass = 1671588.029631332
This Solid mass = 20416.786227435936
This Solid mass = 20416.786227435958
This Solid mass = 539264.8949639023
This Solid mass = 54103.19372018658
This Solid mass = 18954.852646672913
This Solid mass = 338469.4612692211
This Solid mass = 5220.770297180713
This Solid mass = 49449.28479252284
This Solid mass = 39139.64044792523
This Solid mass = 106966.64358232789
This Solid mass = 4781.860167653959
This Solid mass = 1309.926227762865
This Solid mass = 12662.473567635041
This Solid mass = 4781.860167653957
This Solid mass = 1309.9262277628643
This Solid mass = 12662.473567635043
This Solid mass = 36769.10249043011
This Solid mass = 10777.165686221655
This Solid mass = 1998.9380995506442
This Solid mass = 1998.9380995506447
This Solid mass = 4.188790204786394
This Solid mass = 951.8048002047907
This Solid mass = 9196.595426332882
This Solid mass = 14.137166941154069
This Solid mass = 3131.1503837897526
This Solid mass = 12743.227021776174
This Solid mass = 5046.897424302504
This Solid mass = 2377.662039991875
This Solid mass = 2377.662039991875
This Solid mass = 2377.6620399918756
This Solid mass = 2377.662039991875
This Solid mass = 4813.889207068303
This Solid mass = 4813.889207068301
This Solid mass = 4813.889207068301
This Solid mass = 4813.889207068301
This Solid mass = 4813.889207068302
This Solid mass = 4813.889207068303
This Solid mass = 4813.889207068305
This Solid mass = 4813.889207068301
This Solid mass = 16082.638427578428
This Solid mass = 16082.638427578428
This Solid mass = 16082.638427578428
This Solid mass = 16082.638427578413
零件数目为:44

最终爆炸图结果如下所示:
在这里插入图片描述

实际上,经过深入理解step文件内容,可以实现每一个零件的特征认定(比如step文件中颜色、材料、体积等),并实现对任意零件的操作(平移、旋转、删除)
SelfMadeQTviewer3D.py文件内容如下:

import ctypes
import logging
import os
import sys

from OCC.Display import OCCViewer
from PyQt5 import QtCore, QtGui, QtOpenGL, QtWidgets

# check if signal available, not available
# on PySide
HAVE_PYQT_SIGNAL = hasattr(QtCore, 'pyqtSignal')

logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)
log = logging.getLogger(__name__)


class qtBaseViewer(QtOpenGL.QGLWidget):
    ''' The base Qt Widget for an OCC viewer
    '''
    def __init__(self, parent=None):
        super(qtBaseViewer, self).__init__(parent)
        self._display = OCCViewer.Viewer3d()
        self._inited = False

        # enable Mouse Tracking
        self.setMouseTracking(True)

        # Strong focus
        self.setFocusPolicy(QtCore.Qt.WheelFocus)

        self.setAttribute(QtCore.Qt.WA_NativeWindow)
        self.setAttribute(QtCore.Qt.WA_PaintOnScreen)
        self.setAttribute(QtCore.Qt.WA_NoSystemBackground)

        self.setAutoFillBackground(False)

    def GetHandle(self):
        ''' returns an the identifier of the GUI widget.
        It must be an integer
        '''
        win_id = self.winId()  # this returns either an int or voitptr
        if "%s" % type(win_id) == "<type 'PyCObject'>":  # PySide
            ### with PySide, self.winId() does not return an integer
            if sys.platform == "win32":
                ## Be careful, this hack is py27 specific
                ## does not work with python31 or higher
                ## since the PyCObject api was changed
                ctypes.pythonapi.PyCObject_AsVoidPtr.restype = ctypes.c_void_p
                ctypes.pythonapi.PyCObject_AsVoidPtr.argtypes = [ctypes.py_object]
                win_id = ctypes.pythonapi.PyCObject_AsVoidPtr(win_id)
        elif not isinstance(win_id, int):  # PyQt4 or 5
            ## below integer cast may be required because self.winId() can
            ## returns a sip.voitptr according to the PyQt version used
            ## as well as the python version
            win_id = int(win_id)
        return win_id

    def resizeEvent(self, event):
        super(qtBaseViewer, self).resizeEvent(event)
        self._display.View.MustBeResized()

    def paintEngine(self):
        return None


class qtViewer3d(qtBaseViewer):

    # emit signal when selection is changed
    # is a list of TopoDS_*
    if HAVE_PYQT_SIGNAL:
        sig_topods_selected = QtCore.pyqtSignal(list)

    def __init__(self, *kargs):
        qtBaseViewer.__init__(self, *kargs)

        self.setObjectName("qt_viewer_3d")

        self._drawbox = False
        self._zoom_area = False
        self._select_area = False
        self._inited = False
        self._leftisdown = False
        self._middleisdown = False
        self._rightisdown = False
        self._selection = None
        self._drawtext = True
        self._qApp = QtWidgets.QApplication.instance()
        self._key_map = {
    }
        self._current_cursor = "arrow"
        self._available_cursors = {
    }

    @property
    def qApp(self):
        # reference to QApplication instance
        return self._qApp

    @qApp.setter
    def qApp(self, value):
        self._qApp = value

    def InitDriver(self):
        self._display.Create(window_handle=self.GetHandle(), parent=self)
        # background gradient
        self._display.SetModeShaded()
        self._inited = True
        # dict mapping keys to functions
        self._key_map = {
    ord('W'): self._display.SetModeWireFrame,
                         ord('S'): self._display.SetModeShaded,
                         ord('A'): self._display.EnableAntiAliasing,
                         ord('B'): self._display.DisableAntiAliasing,
                         ord('H'): self._display.SetModeHLR,
                         ord('F'): self._display.FitAll,
                         ord('G'): self._display.SetSelectionMode}
        self.createCursors()

    def createCursors(self):
        module_pth = os.path.abspath(os.path.dirname(__file__))
        icon_pth = os.path.join(module_pth, "icons")

        _CURSOR_PIX_ROT = QtGui.QPixmap(os.path.join(icon_pth, "cursor-rotate.png"))
        _CURSOR_PIX_PAN = QtGui.QPixmap(os.path.join(icon_pth, "cursor-pan.png"))
        _CURSOR_PIX_ZOOM = QtGui.QPixmap(os.path.join(icon_pth, "cursor-magnify.png"))
        _CURSOR_PIX_ZOOM_AREA = QtGui.QPixmap(os.path.join(icon_pth, "cursor-magnify-area.png"))

        self._available_cursors = {
    
            "arrow": QtGui.QCursor(QtCore.Qt.ArrowCursor),  # default
            "pan": QtGui.QCursor(_CURSOR_PIX_PAN),
            "rotate": QtGui.QCursor(_CURSOR_PIX_ROT),
            "zoom": QtGui.QCursor(_CURSOR_PIX_ZOOM),
            "zoom-area": QtGui.QCursor(_CURSOR_PIX_ZOOM_AREA),
        }

        self._current_cursor = "arrow"

    def keyPressEvent(self, event):
        code = event.key()
        if code in self._key_map:
            self._key_map[code]()
        elif code in range(256):
            log.info('key: "%s"(code %i) not mapped to any function' % (chr(code), code))
        else:
            log.info('key: code %i not mapped to any function' % code)

    def focusInEvent(self, event):
        if self._inited:
            self._display.Repaint()

    def focusOutEvent(self, event):
        if self._inited:
            self._display.Repaint()

    def paintEvent(self, event):
        if not self._inited:
            self.InitDriver()

        self._display.Context.UpdateCurrentViewer()

        if self._drawbox:
            painter = QtGui.QPainter(self)
            painter.setPen(QtGui.QPen(QtGui.QColor(0, 0, 0), 2))
            rect = QtCore.QRect(*self._drawbox)
            painter.drawRect(rect)

    def wheelEvent(self, event):
        try:  # PyQt4/PySide
            delta = event.delta()
        except:  # PyQt5
            delta = event.angleDelta().y()
        if delta > 0:
            zoom_factor = 2.
        else:
            zoom_factor = 0.5
        self._display.ZoomFactor(zoom_factor)

    @property
    def cursor(self):
        return self._current_cursor

    @cursor.setter
    def cursor(self, value):
        if not self._current_cursor == value:

            self._current_cursor = value
            cursor = self._available_cursors.get(value)

            if cursor:
                self.qApp.setOverrideCursor(cursor)
            else:
                self.qApp.restoreOverrideCursor()

    def mousePressEvent(self, event):
        self.setFocus()
        ev = event.pos()
        self.dragStartPosX = ev.x()
        self.dragStartPosY = ev.y()
        self._display.StartRotation(self.dragStartPosX, self.dragStartPosY)

    def mouseReleaseEvent(self, event):
        pt = event.pos()
        modifiers = event.modifiers()

        if event.button() == QtCore.Qt.LeftButton:
            if self._select_area:
                [Xmin, Ymin, dx, dy] = self._drawbox
                self._display.SelectArea(Xmin, Ymin, Xmin + dx, Ymin + dy)
                self._select_area = False
            else:
                # multiple select if shift is pressed
                if modifiers == QtCore.Qt.ShiftModifier:
                    self._display.ShiftSelect(pt.x(), pt.y())
                else:
                    # single select otherwise
                    self._display.Select(pt.x(), pt.y())

                    if (self._display.selected_shapes is not None) and HAVE_PYQT_SIGNAL:
                        self.sig_topods_selected.emit(self._display.selected_shapes)


        elif event.button() == QtCore.Qt.RightButton:
            if self._zoom_area:
                [Xmin, Ymin, dx, dy] = self._drawbox
                self._display.ZoomArea(Xmin, Ymin, Xmin + dx, Ymin + dy)
                self._zoom_area = False

        self.cursor = "arrow"

    def DrawBox(self, event):
        tolerance = 2
        pt = event.pos()
        dx = pt.x() - self.dragStartPosX
        dy = pt.y() - self.dragStartPosY
        if abs(dx) <= tolerance and abs(dy) <= tolerance:
            return
        self._drawbox = [self.dragStartPosX, self.dragStartPosY, dx, dy]


    def mouseMoveEvent(self, evt):
        pt = evt.pos()
        buttons = int(evt.buttons())
        modifiers = evt.modifiers()
        # ROTATE
        if (buttons == QtCore.Qt.LeftButton and
                not modifiers == QtCore.Qt.ShiftModifier):
            self.cursor = "rotate"
            self._display.Rotation(pt.x(), pt.y())
            self._drawbox = False
        # DYNAMIC ZOOM
        elif (buttons == QtCore.Qt.RightButton and
              not modifiers == QtCore.Qt.ShiftModifier):
            self.cursor = "zoom"
            self._display.Repaint()
            self._display.DynamicZoom(abs(self.dragStartPosX),
                                      abs(self.dragStartPosY), abs(pt.x()),
                                      abs(pt.y()))
            self.dragStartPosX = pt.x()
            self.dragStartPosY = pt.y()
            self._drawbox = False
        # PAN
        elif buttons == QtCore.Qt.MidButton:
            dx = pt.x() - self.dragStartPosX
            dy = pt.y() - self.dragStartPosY
            self.dragStartPosX = pt.x()
            self.dragStartPosY = pt.y()
            self.cursor = "pan"
            self._display.Pan(dx, -dy)
            self._drawbox = False
        # DRAW BOX
        # ZOOM WINDOW
        elif (buttons == QtCore.Qt.RightButton and
              modifiers == QtCore.Qt.ShiftModifier):
            self._zoom_area = True
            self.cursor = "zoom-area"
            self.DrawBox(evt)
            self.update()
        # SELECT AREA
        elif (buttons == QtCore.Qt.LeftButton and
              modifiers == QtCore.Qt.ShiftModifier):
            self._select_area = True
            self.DrawBox(evt)
            self.update()
        else:
            self._drawbox = False
            self._display.MoveTo(pt.x(), pt.y())
            self.cursor = "arrow"

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42220370/article/details/114920011

智能推荐

分布式光纤传感器的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告_预计2026年中国分布式传感器市场规模有多大-程序员宅基地

文章浏览阅读3.2k次。本文研究全球与中国市场分布式光纤传感器的发展现状及未来发展趋势,分别从生产和消费的角度分析分布式光纤传感器的主要生产地区、主要消费地区以及主要的生产商。重点分析全球与中国市场的主要厂商产品特点、产品规格、不同规格产品的价格、产量、产值及全球和中国市场主要生产商的市场份额。主要生产商包括:FISO TechnologiesBrugg KabelSensor HighwayOmnisensAFL GlobalQinetiQ GroupLockheed MartinOSENSA Innovati_预计2026年中国分布式传感器市场规模有多大

07_08 常用组合逻辑电路结构——为IC设计的延时估计铺垫_基4布斯算法代码-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏12次。常用组合逻辑电路结构——为IC设计的延时估计铺垫学习目的:估计模块间的delay,确保写的代码的timing 综合能给到多少HZ,以满足需求!_基4布斯算法代码

OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版-程序员宅基地

文章浏览阅读3.3k次,点赞3次,收藏5次。OpenAI Manager助手(基于SpringBoot和Vue)_chatgpt网页版

关于美国计算机奥赛USACO,你想知道的都在这_usaco可以多次提交吗-程序员宅基地

文章浏览阅读2.2k次。USACO自1992年举办,到目前为止已经举办了27届,目的是为了帮助美国信息学国家队选拔IOI的队员,目前逐渐发展为全球热门的线上赛事,成为美国大学申请条件下,含金量相当高的官方竞赛。USACO的比赛成绩可以助力计算机专业留学,越来越多的学生进入了康奈尔,麻省理工,普林斯顿,哈佛和耶鲁等大学,这些同学的共同点是他们都参加了美国计算机科学竞赛(USACO),并且取得过非常好的成绩。适合参赛人群USACO适合国内在读学生有意向申请美国大学的或者想锻炼自己编程能力的同学,高三学生也可以参加12月的第_usaco可以多次提交吗

MySQL存储过程和自定义函数_mysql自定义函数和存储过程-程序员宅基地

文章浏览阅读394次。1.1 存储程序1.2 创建存储过程1.3 创建自定义函数1.3.1 示例1.4 自定义函数和存储过程的区别1.5 变量的使用1.6 定义条件和处理程序1.6.1 定义条件1.6.1.1 示例1.6.2 定义处理程序1.6.2.1 示例1.7 光标的使用1.7.1 声明光标1.7.2 打开光标1.7.3 使用光标1.7.4 关闭光标1.8 流程控制的使用1.8.1 IF语句1.8.2 CASE语句1.8.3 LOOP语句1.8.4 LEAVE语句1.8.5 ITERATE语句1.8.6 REPEAT语句。_mysql自定义函数和存储过程

半导体基础知识与PN结_本征半导体电流为0-程序员宅基地

文章浏览阅读188次。半导体二极管——集成电路最小组成单元。_本征半导体电流为0

随便推点

【Unity3d Shader】水面和岩浆效果_unity 岩浆shader-程序员宅基地

文章浏览阅读2.8k次,点赞3次,收藏18次。游戏水面特效实现方式太多。咱们这边介绍的是一最简单的UV动画(无顶点位移),整个mesh由4个顶点构成。实现了水面效果(左图),不动代码稍微修改下参数和贴图可以实现岩浆效果(右图)。有要思路是1,uv按时间去做正弦波移动2,在1的基础上加个凹凸图混合uv3,在1、2的基础上加个水流方向4,加上对雾效的支持,如没必要请自行删除雾效代码(把包含fog的几行代码删除)S..._unity 岩浆shader

广义线性模型——Logistic回归模型(1)_广义线性回归模型-程序员宅基地

文章浏览阅读5k次。广义线性模型是线性模型的扩展,它通过连接函数建立响应变量的数学期望值与线性组合的预测变量之间的关系。广义线性模型拟合的形式为:其中g(μY)是条件均值的函数(称为连接函数)。另外,你可放松Y为正态分布的假设,改为Y 服从指数分布族中的一种分布即可。设定好连接函数和概率分布后,便可以通过最大似然估计的多次迭代推导出各参数值。在大部分情况下,线性模型就可以通过一系列连续型或类别型预测变量来预测正态分布的响应变量的工作。但是,有时候我们要进行非正态因变量的分析,例如:(1)类别型.._广义线性回归模型

HTML+CSS大作业 环境网页设计与实现(垃圾分类) web前端开发技术 web课程设计 网页规划与设计_垃圾分类网页设计目标怎么写-程序员宅基地

文章浏览阅读69次。环境保护、 保护地球、 校园环保、垃圾分类、绿色家园、等网站的设计与制作。 总结了一些学生网页制作的经验:一般的网页需要融入以下知识点:div+css布局、浮动、定位、高级css、表格、表单及验证、js轮播图、音频 视频 Flash的应用、ul li、下拉导航栏、鼠标划过效果等知识点,网页的风格主题也很全面:如爱好、风景、校园、美食、动漫、游戏、咖啡、音乐、家乡、电影、名人、商城以及个人主页等主题,学生、新手可参考下方页面的布局和设计和HTML源码(有用点赞△) 一套A+的网_垃圾分类网页设计目标怎么写

C# .Net 发布后,把dll全部放在一个文件夹中,让软件目录更整洁_.net dll 全局目录-程序员宅基地

文章浏览阅读614次,点赞7次,收藏11次。之前找到一个修改 exe 中 DLL地址 的方法, 不太好使,虽然能正确启动, 但无法改变 exe 的工作目录,这就影响了.Net 中很多获取 exe 执行目录来拼接的地址 ( 相对路径 ),比如 wwwroot 和 代码中相对目录还有一些复制到目录的普通文件 等等,它们的地址都会指向原来 exe 的目录, 而不是自定义的 “lib” 目录,根本原因就是没有修改 exe 的工作目录这次来搞一个启动程序,把 .net 的所有东西都放在一个文件夹,在文件夹同级的目录制作一个 exe._.net dll 全局目录

BRIEF特征点描述算法_breif description calculation 特征点-程序员宅基地

文章浏览阅读1.5k次。本文为转载,原博客地址:http://blog.csdn.net/hujingshuang/article/details/46910259简介 BRIEF是2010年的一篇名为《BRIEF:Binary Robust Independent Elementary Features》的文章中提出,BRIEF是对已检测到的特征点进行描述,它是一种二进制编码的描述子,摈弃了利用区域灰度..._breif description calculation 特征点

房屋租赁管理系统的设计和实现,SpringBoot计算机毕业设计论文_基于spring boot的房屋租赁系统论文-程序员宅基地

文章浏览阅读4.1k次,点赞21次,收藏79次。本文是《基于SpringBoot的房屋租赁管理系统》的配套原创说明文档,可以给应届毕业生提供格式撰写参考,也可以给开发类似系统的朋友们提供功能业务设计思路。_基于spring boot的房屋租赁系统论文