Я занимаюсь разработкой прикладного программного обеспечения, в котором я могу визуализировать трехмерную графику и затем манипулировать ею, например, я нарисовал куб, а затем хочу изменить форму этого куба, просто перетаскивая его вершины в трехмерном мире.
точно так же, как 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