Очистить экран в PythonOCC или в другой библиотеке - PullRequest
0 голосов
/ 05 июня 2019

Я занимаюсь разработкой прикладного программного обеспечения, в котором я могу визуализировать трехмерную графику и затем манипулировать ею, например, я нарисовал куб, а затем хочу изменить форму этого куба, просто перетаскивая его вершины в трехмерном мире.

точно так же, как this

Прежде чем попасть в PythonOCC Я пытался сделать то же самое в OpenGL , но в OpenGL яне смог этого сделать.

В OpenGL я не смог точно получить трехмерные координаты трехмерного мира по двумерным координатам видового экрана.

Позже я обнаружил в этой библиотеке библиотеку Python PythonOCC, в которой очень легко получить 3D-координаты по 2D-координатам курсора мыши.поэтому в PythonOCC то, что я делаю, чтобы изменить форму куба, просто отслеживаю курсор мыши, а в mouseMoveEvent я преобразую координаты мыши в трехмерные координаты, меняю координату одной вершины куба и рисую этот куб с новым набором.координат вершин.

, но я не могу удалить предыдущий куб или вы можете сказать, что я не могу очистить экран одновременно.Я попробовал метод в pythonOCC, который _display.eraseAll(), но он не работает для меня.

Как мне этого добиться?Вы можете предложить любую другую библиотеку, в которой я могу достичь своей цели, но она должна работать с python и может отображаться в графическом интерфейсе Qt5.

Вот код, который я сделал для достижения своей цели в pythonOCC

from __future__ import print_function
import random
import sys
from OCC.Display.backend import load_any_qt_backend, get_qt_modules
load_any_qt_backend()
QtCore, QtGui, QtWidgets, QtOpenGL = get_qt_modules()
from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox

# Code this Below
from QtDisplay import qtViewer3d

class AppFrame(QtWidgets.QMainWindow):
    def __init__(self, *args):
        QtWidgets.QMainWindow.__init__(self, *args)
        self.setWindowTitle(self.tr("qtDisplay3d overpainting example"))
        self.canva = qtViewer3d(self)
        self.canva.InitDriver()
        self.resize(600, 500)
        self.setCentralWidget(self.canva)
        if sys.platform != 'darwin':
            self.menu_bar = self.menuBar()
        else:
            self.menu_bar = QtWidgets.QMenuBar()
        self._menus = {}
        self._menu_methods = {}

if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    frame = AppFrame()
    frame.show()
    frame.canva.InitDriver()
    display = frame.canva._display
    frame.canva.InitDriver()
    frame.canva.qApp = app
    frame.raise_()
    app.exec_()

QtDisplay.py - один из модулей pythonOCC, но я немного изменил его, чтобы отслеживать мышь и переопределять mouseMoveEvent.

from __future__ import print_function

from PyQt5.QtWidgets import QSplitter

import logging
import os
import sys

k=0

from OCC.Core.gp import gp_Pnt
from OCC.Core.TColgp import TColgp_Array2OfPnt
from OCC.Core.GeomAPI import GeomAPI_PointsToBSplineSurface
from OCC.Core.GeomAbs import GeomAbs_C2

from OCC.Display import OCCViewer
from OCC.Display.backend import get_qt_modules

QtCore, QtGui, QtWidgets, QtOpenGL = get_qt_modules()

# 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__)

p = [gp_Pnt(0, 0, 0), gp_Pnt(40, 0, 0), gp_Pnt(20, 20, 0), gp_Pnt(0, 20, 0), gp_Pnt(0, 0, 20), gp_Pnt(20, 0, 20),
     gp_Pnt(20, 20, 20), gp_Pnt(0, 20, 20)]


display =None

pnt = None


from OCC.Core.TopoDS import topods_Vertex
from OCC.Core.BRep import BRep_Tool

def vertex_clicked(shp, *kwargs):
    """ This function is called whenever a vertex is selected
    """
    global pnt
    print(shp)
    for shape in shp:  # this should be a TopoDS_Vertex
        print("Face selected: ", shape)
        v = topods_Vertex(shape)
        pnt = BRep_Tool.Pnt(v)
        print("3d gp_Pnt selected coordinates : X=", pnt.X(),
              "Y=", pnt.Y(), "Z=", pnt.Z())
        # then convert to screen coordinates
        screen_coord = display.View.Convert(pnt.X(), pnt.Y(), pnt.Z())
        print("2d screen coordinates : ", screen_coord)


def build_surf():

    global p

    array = TColgp_Array2OfPnt(1, 2, 1, 2)
    array.SetValue(1, 1, p[0])
    array.SetValue(1, 2, p[1])
    array.SetValue(2, 2, p[2])
    array.SetValue(2, 1, p[3])

    array2 = TColgp_Array2OfPnt(1, 2, 1, 2)
    array2.SetValue(1, 1, p[4])
    array2.SetValue(1, 2, p[5])
    array2.SetValue(2, 2, p[6])
    array2.SetValue(2, 1, p[7])

    array3 = TColgp_Array2OfPnt(1, 2, 1, 2)
    array3.SetValue(1, 1, p[2])
    array3.SetValue(1, 2, p[3])
    array3.SetValue(2, 2, p[7])
    array3.SetValue(2, 1, p[6])

    array4 = TColgp_Array2OfPnt(1, 2, 1, 2)
    array4.SetValue(1, 1, p[0])
    array4.SetValue(1, 2, p[3])
    array4.SetValue(2, 2, p[7])
    array4.SetValue(2, 1, p[4])

    array5 = TColgp_Array2OfPnt(1, 2, 1, 2)
    array5.SetValue(1, 1, p[0])
    array5.SetValue(1, 2, p[4])
    array5.SetValue(2, 2, p[5])
    array5.SetValue(2, 1, p[1])

    array6 = TColgp_Array2OfPnt(1, 2, 1, 2)
    array6.SetValue(1, 1, p[1])
    array6.SetValue(1, 2, p[2])
    array6.SetValue(2, 2, p[6])
    array6.SetValue(2, 1, p[5])
    # array.SetValue(2, 2, p5)
    # array.SetValue(3, 2, p6)
    bspl_surf1 = GeomAPI_PointsToBSplineSurface(array, 3, 8, GeomAbs_C2,
                                               0.001).Surface()

    bspl_surf2 = GeomAPI_PointsToBSplineSurface(array2, 3, 8, GeomAbs_C2,
                                                0.001).Surface()

    bspl_surf3 = GeomAPI_PointsToBSplineSurface(array3, 3, 8, GeomAbs_C2,
                                                0.001).Surface()

    bspl_surf4 = GeomAPI_PointsToBSplineSurface(array4, 3, 8, GeomAbs_C2,
                                                0.001).Surface()

    bspl_surf5 = GeomAPI_PointsToBSplineSurface(array5, 3, 8, GeomAbs_C2,
                                                0.001).Surface()

    bspl_surf6 = GeomAPI_PointsToBSplineSurface(array6, 3, 8, GeomAbs_C2,
                                                0.001).Surface()

    face = [bspl_surf1, bspl_surf2, bspl_surf3, bspl_surf4, bspl_surf5, bspl_surf6]
    return face



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

        # enable Mouse Tracking
        self.setMouseTracking(True)

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

        # required for overpainting the widget
        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
                import ctypes
                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):
        if self._inited:
            super(qtBaseViewer, self).resizeEvent(event)
            self._display.OnResize()


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 = OCCViewer.Viewer3d(window_handle=self.GetHandle(), parent=self)
        self._display.Create()
        # 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()


        #------------------------------------------------------------------------------------
        # My Code From Here
        #------------------------------------------------------------------------------------

        self._display.EraseAll()
        self._display.SetSelectionModeVertex()

        self._display.register_select_callback(vertex_clicked)
        surf = build_surf()

        global display
        display = self._display

        for face in surf:
            self._display.DisplayShape(face, update=True)

    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 self._drawbox:
            self._display.Repaint()
            self._display.Repaint()
            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):
        global pnt
        pt = event.pos()
        modifiers = event.modifiers()
        if pnt:
            pnt=None

        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:
            # cor = self._display.View.ConvertWithProj(pt.x(), pt.y())
            # print(cor)
            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):
        global p, pnt
        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
            i=-1
            if pnt:
                # print(pnt.X(), pnt.Y(), pnt.Z())
                for ver in p:
                    # print(ver,i)
                    i+=1
                    if ver.X() == pnt.X() and ver.Y() == pnt.Y() and ver.Z() == pnt.Z():
                        pnt = None;
                        break

            cor = self._display.View.ConvertWithProj(pt.x(),pt.y())
            # print(i)
            print(cor[0], cor[1], cor[2])
            if (i>=0):
                p[i]=gp_Pnt(cor[0],cor[1],cor[2])

            # p[1]=gp_Pnt(20,0,0)
            # self.update()
            surf = build_surf()

            for face in surf:
                self._display.DisplayShape(face, update=True)

            global display
            display.EraseAll()
            # pass
        # 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"

https://www.khanacademy.org/computer-programming/interactive-3d-cube/6436122472742912

...