mirror of
https://github.com/alicevision/Meshroom.git
synced 2025-05-29 17:06:35 +02:00
[ui] new EdgeMouseArea QtQuick Component
QtQuick components providing mouse interaction for edges (cubic splines)
This commit is contained in:
parent
23541db54d
commit
af53dd4ae7
3 changed files with 219 additions and 0 deletions
|
@ -7,6 +7,7 @@ from PySide2.QtQml import QQmlApplicationEngine
|
||||||
from meshroom.ui.reconstruction import Reconstruction
|
from meshroom.ui.reconstruction import Reconstruction
|
||||||
from meshroom.ui.utils import QmlInstantEngine
|
from meshroom.ui.utils import QmlInstantEngine
|
||||||
|
|
||||||
|
from meshroom.ui import components
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
@ -17,6 +18,8 @@ if __name__ == "__main__":
|
||||||
engine = QmlInstantEngine()
|
engine = QmlInstantEngine()
|
||||||
engine.addFilesFromDirectory(qmlDir)
|
engine.addFilesFromDirectory(qmlDir)
|
||||||
engine.setWatching(os.environ.get("MESHROOM_INSTANT_CODING", False))
|
engine.setWatching(os.environ.get("MESHROOM_INSTANT_CODING", False))
|
||||||
|
components.registerTypes()
|
||||||
|
|
||||||
r = Reconstruction()
|
r = Reconstruction()
|
||||||
engine.rootContext().setContextProperty("_reconstruction", r)
|
engine.rootContext().setContextProperty("_reconstruction", r)
|
||||||
|
|
||||||
|
|
6
meshroom/ui/components/__init__.py
Executable file
6
meshroom/ui/components/__init__.py
Executable file
|
@ -0,0 +1,6 @@
|
||||||
|
|
||||||
|
def registerTypes():
|
||||||
|
from PySide2.QtQml import qmlRegisterType
|
||||||
|
from meshroom.ui.components.edge import EdgeMouseArea
|
||||||
|
|
||||||
|
qmlRegisterType(EdgeMouseArea, "GraphEditor", 1, 0, "EdgeMouseArea")
|
210
meshroom/ui/components/edge.py
Executable file
210
meshroom/ui/components/edge.py
Executable file
|
@ -0,0 +1,210 @@
|
||||||
|
from PySide2.QtCore import Signal, Property, QPointF, Qt, QObject
|
||||||
|
from PySide2.QtGui import QPainterPath, QVector2D
|
||||||
|
from PySide2.QtQuick import QQuickItem
|
||||||
|
|
||||||
|
|
||||||
|
class MouseEvent(QObject):
|
||||||
|
"""
|
||||||
|
Simple MouseEvent object, since QQuickMouseEvent is not accessible in the public API
|
||||||
|
"""
|
||||||
|
def __init__(self, evt):
|
||||||
|
super(MouseEvent, self).__init__()
|
||||||
|
self._x = evt.x()
|
||||||
|
self._y = evt.y()
|
||||||
|
self._button = evt.button()
|
||||||
|
|
||||||
|
x = Property(float, lambda self: self._x, constant=True)
|
||||||
|
y = Property(float, lambda self: self._y, constant=True)
|
||||||
|
button = Property(Qt.MouseButton, lambda self: self._button, constant=True)
|
||||||
|
|
||||||
|
|
||||||
|
class EdgeMouseArea(QQuickItem):
|
||||||
|
"""
|
||||||
|
Provides a MouseArea shaped as a cubic spline for mouse interaction with edges.
|
||||||
|
|
||||||
|
Note: for performance reason, shape is updated only when geometry changes since this is the main use-case with edges.
|
||||||
|
TODOs:
|
||||||
|
- update when start/end points change too
|
||||||
|
- review this when using new QML Shape module
|
||||||
|
"""
|
||||||
|
def __init__(self, parent=None):
|
||||||
|
super(EdgeMouseArea, self).__init__(parent)
|
||||||
|
|
||||||
|
self._viewScale = 1.0
|
||||||
|
self._startX = 0.0
|
||||||
|
self._startY = 0.0
|
||||||
|
self._endX = 0.0
|
||||||
|
self._endY = 0.0
|
||||||
|
self._curveScale = 0.7
|
||||||
|
self._edgeThickness = 1.0
|
||||||
|
self._hullThickness = 2.0
|
||||||
|
self._containsMouse = False
|
||||||
|
self._path = None # type: QPainterPath
|
||||||
|
|
||||||
|
self.setAcceptHoverEvents(True)
|
||||||
|
self.setAcceptedMouseButtons(Qt.AllButtons)
|
||||||
|
|
||||||
|
def contains(self, point):
|
||||||
|
return self._path.contains(point)
|
||||||
|
|
||||||
|
def hoverEnterEvent(self, evt):
|
||||||
|
self.setContainsMouse(True)
|
||||||
|
super(EdgeMouseArea, self).hoverEnterEvent(evt)
|
||||||
|
|
||||||
|
def hoverLeaveEvent(self, evt):
|
||||||
|
self.setContainsMouse(False)
|
||||||
|
super(EdgeMouseArea, self).hoverLeaveEvent(evt)
|
||||||
|
|
||||||
|
def geometryChanged(self, newGeometry, oldGeometry):
|
||||||
|
super(EdgeMouseArea, self).geometryChanged(newGeometry, oldGeometry)
|
||||||
|
self.updateShape()
|
||||||
|
|
||||||
|
def mousePressEvent(self, evt):
|
||||||
|
if not self.acceptedMouseButtons() & evt.button():
|
||||||
|
evt.setAccepted(False)
|
||||||
|
return
|
||||||
|
e = MouseEvent(evt)
|
||||||
|
self.pressed.emit(e)
|
||||||
|
e.deleteLater()
|
||||||
|
|
||||||
|
def mouseReleaseEvent(self, evt):
|
||||||
|
e = MouseEvent(evt)
|
||||||
|
self.released.emit(e)
|
||||||
|
e.deleteLater()
|
||||||
|
|
||||||
|
def updateShape(self):
|
||||||
|
p1 = QPointF(self._startX, self._startY)
|
||||||
|
p2 = QPointF(self._endX, self._endY)
|
||||||
|
ctrlPt = QPointF(self.ctrlPtDist, 0)
|
||||||
|
path = QPainterPath(p1)
|
||||||
|
path.cubicTo(p1 + ctrlPt, p2 - ctrlPt, p2)
|
||||||
|
|
||||||
|
# Compute offset on x and y axis
|
||||||
|
hullOffset = self._edgeThickness * self._viewScale + self._hullThickness
|
||||||
|
v = QVector2D(p2 - p1).normalized()
|
||||||
|
offset = QPointF(hullOffset * -v.y(), hullOffset * v.x())
|
||||||
|
|
||||||
|
self._path = QPainterPath(path.toReversed())
|
||||||
|
self._path.translate(-offset)
|
||||||
|
path.translate(offset)
|
||||||
|
self._path.connectPath(path)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thickness(self):
|
||||||
|
return self._hullThickness
|
||||||
|
|
||||||
|
@thickness.setter
|
||||||
|
def thickness(self, value):
|
||||||
|
if self._hullThickness == value:
|
||||||
|
return
|
||||||
|
self._hullThickness = value
|
||||||
|
self.thicknessChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def edgeThickness(self):
|
||||||
|
return self._edgeThickness
|
||||||
|
|
||||||
|
@edgeThickness.setter
|
||||||
|
def edgeThickness(self, value):
|
||||||
|
if self._edgeThickness == value:
|
||||||
|
return
|
||||||
|
self._edgeThickness = value
|
||||||
|
self.thicknessChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def viewScale(self):
|
||||||
|
return self._viewScale
|
||||||
|
|
||||||
|
@viewScale.setter
|
||||||
|
def viewScale(self, value):
|
||||||
|
if self.viewScale == value:
|
||||||
|
return
|
||||||
|
self._viewScale = value
|
||||||
|
self.viewScaleChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def startX(self):
|
||||||
|
return self._startX
|
||||||
|
|
||||||
|
@startX.setter
|
||||||
|
def startX(self, value):
|
||||||
|
self._startX = value
|
||||||
|
self.startXChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def startY(self):
|
||||||
|
return self._startY
|
||||||
|
|
||||||
|
@startY.setter
|
||||||
|
def startY(self, value):
|
||||||
|
self._startY = value
|
||||||
|
self.startYChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def endX(self):
|
||||||
|
return self._endX
|
||||||
|
|
||||||
|
@endX.setter
|
||||||
|
def endX(self, value):
|
||||||
|
self._endX = value
|
||||||
|
self.endXChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def endY(self):
|
||||||
|
return self._endY
|
||||||
|
|
||||||
|
@endY.setter
|
||||||
|
def endY(self, value):
|
||||||
|
self._endY = value
|
||||||
|
self.endYChanged.emit()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def curveScale(self):
|
||||||
|
return self._curveScale
|
||||||
|
|
||||||
|
@curveScale.setter
|
||||||
|
def curveScale(self, value):
|
||||||
|
self._curveScale = value
|
||||||
|
self.curveScaleChanged.emit()
|
||||||
|
self.updateShape()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def ctrlPtDist(self):
|
||||||
|
return self.width() * self.curveScale * (-1 if self._startX > self._endX else 1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def containsMouse(self):
|
||||||
|
return self._containsMouse
|
||||||
|
|
||||||
|
def setContainsMouse(self, value):
|
||||||
|
if self._containsMouse == value:
|
||||||
|
return
|
||||||
|
self._containsMouse = value
|
||||||
|
self.containsMouseChanged.emit()
|
||||||
|
|
||||||
|
thicknessChanged = Signal()
|
||||||
|
thickness = Property(float, thickness.fget, thickness.fset, notify=thicknessChanged)
|
||||||
|
edgeThicknessChanged = Signal()
|
||||||
|
edgeThickness = Property(float, edgeThickness.fget, edgeThickness.fset, notify=edgeThicknessChanged)
|
||||||
|
viewScaleChanged = Signal()
|
||||||
|
viewScale = Property(float, viewScale.fget, viewScale.fset, notify=viewScaleChanged)
|
||||||
|
startXChanged = Signal()
|
||||||
|
startX = Property(float, startX.fget, startX.fset, notify=startXChanged)
|
||||||
|
startYChanged = Signal()
|
||||||
|
startY = Property(float, startY.fget, startY.fset, notify=startYChanged)
|
||||||
|
endXChanged = Signal()
|
||||||
|
endX = Property(float, endX.fget, endX.fset, notify=endXChanged)
|
||||||
|
endYChanged = Signal()
|
||||||
|
endY = Property(float, endY.fget, endY.fset, notify=endYChanged)
|
||||||
|
curveScaleChanged = Signal()
|
||||||
|
curveScale = Property(float, curveScale.fget, curveScale.fset, notify=curveScaleChanged)
|
||||||
|
ctrlPtDistChanged = Signal()
|
||||||
|
ctrlPtDist = Property(float, ctrlPtDist.fget, notify=ctrlPtDist)
|
||||||
|
containsMouseChanged = Signal()
|
||||||
|
containsMouse = Property(float, containsMouse.fget, notify=containsMouseChanged)
|
||||||
|
acceptedButtons = Property(int,
|
||||||
|
lambda self: super(EdgeMouseArea, self).acceptedMouseButtons,
|
||||||
|
lambda self, value: super(EdgeMouseArea, self).setAcceptedMouseButtons(value))
|
||||||
|
|
||||||
|
pressed = Signal(MouseEvent)
|
||||||
|
released = Signal(MouseEvent)
|
Loading…
Add table
Add a link
Reference in a new issue