Meshroom/meshroom/ui/components/edge.py
Yann Lanthony af53dd4ae7 [ui] new EdgeMouseArea QtQuick Component
QtQuick components providing mouse interaction for edges (cubic splines)
2017-10-13 18:01:49 +02:00

210 lines
6.4 KiB
Python
Executable file

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)