Browse Source

Fix block and mouse handling for tempo track

master
Nils 3 years ago
parent
commit
4fd8c17c32
  1. 218
      qtgui/conductor.py
  2. 7
      qtgui/musicstructures.py
  3. 5
      qtgui/scorescene.py

218
qtgui/conductor.py

@ -152,7 +152,7 @@ class Conductor(QtWidgets.QGraphicsItem):
mousePressEvent is inside and the mouse moves outside for the release event it still counts
as event of this instance"""
if event.button() == 1 and 0 <= event.scenePos().x() < self.staffLine.line().x2(): #within the conductor line: # QtCore.Qt.MouseButton.LeftButton
if self.parentView.mode() in ("notation", "cc") and event.button() == QtCore.Qt.LeftButton and 0 <= event.scenePos().x() < self.staffLine.line().x2(): #within the conductor line.
event.accept()
self.add(event.scenePos()) #create a new tempo point by telling the api a position and then reacting to "delete all, recreate" from the callback.
else:
@ -204,7 +204,7 @@ class ConductorTransparentBlock(QtWidgets.QGraphicsRectItem):
self.trans = QtGui.QColor("transparent")
self.setPen(self.trans)
self.setBrush(self.color)
self.setOpacity(0.2) #mimic background behaviour
self.setOpacity(0.5) #mimic background behaviour
self.staticExportItem = staticExportItem
self.posBeforeMove = None
self.cursorPosOnMoveStart = None
@ -242,57 +242,18 @@ class ConductorTransparentBlock(QtWidgets.QGraphicsRectItem):
stretchRect(self, factor)
self.marker.setX(self.marker.pos().x() * factor)
def mouseMoveEvent(self, event):
"""Don't use the qt system. we move ourselves"""
event.accept()
def mousePressEvent(self, event):
"""Just reroute to the parent score so that mousePressEventCustom can get triggered"""
self.parent.mousePressEvent(event)
def mouseMoveEventCustom(self, event):
"""
Move the whole block, change the tempoTrack form.
Custom gets called by the scene mouse press event directly only when the right keys
are hold down"""
# All the positions below don't work. They work fine when dragging Tracks around but not this Item. I can't be bothered to figure out why.
#scenePos() results ins an item position that is translated down and right. The higher the x/y value the more the offset
#Instead we calculate our delta ourselves.
#self.setPos(self.mapToItem(self, event.scenePos()))
#self.setPos(self.mapFromScene(event.scenePos()))
#posGlobal = QtGui.QCursor.pos()
#posView = self.parent.parentScore.parentView.mapFromGlobal(posGlobal) #a widget
#posScene = self.parent.parentScore.parentView.mapToScene(posView)
#print (posGlobal, posView, posScene, event.scenePos())
if self.cursorPosOnMoveStart:
self.setPos(event.scenePos().x(), self.posBeforeMove.y())
"""
#does not work with zooming
if self.cursorPosOnMoveStart:
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart
new = self.posBeforeMove + delta
if new.x() < 0:
self.setPos(0, self.posBeforeMove.y())
else:
self.setPos(new.x(), self.posBeforeMove.y())
#event.ignore() #this blocks the qt movable object since we already move the object on our own.
"""
super().mouseMoveEvent(event)
def mousePressEventCustom(self, event):
"""Custom gets called by the scene mouse press event directly only when the right keys
are hold down"""
"""Custom gets called by the scene mouse press event"""
self.posBeforeMove = self.pos()
self.cursorPosOnMoveStart = QtGui.QCursor.pos()
#self.setBrush(self.color)
super().mousePressEvent(event)
def mouseReleaseEventCustom(self, event):
"""Custom gets called by the scene mouse press event directly only when the right keys
are hold down"""
#self.setBrush(self.trans)
self.setPos(self.posBeforeMove) #In case the block was moved to a position where no track is (below the tracks) we just reset the graphics. If the moving was correct then the new position will be set by redrawing the whole Conductor.
self.posBeforeMove = None
@ -307,7 +268,7 @@ class ConductorTransparentBlock(QtWidgets.QGraphicsRectItem):
if posRelativeToBlockStart > 0:
api.splitTempoBlock(self.staticExportItem["id"], int(posRelativeToBlockStart))
def contextMenuEvent(self, event):
def contextMenuEventCustom(self, event):
listOfLabelsAndFunctions = [
("edit properties", lambda: TempoBlockPropertiesEdit(self.scene().parentView.mainWindow, staticExportItem = self.staticExportItem)),
("separator", None),
@ -335,7 +296,6 @@ class ConductorBlockHandle(QtWidgets.QGraphicsRectItem):
super().__init__(-3, -2, 3, parent.rect().height()) #x, y, w, h
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable|QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) #QtWidgets.QGraphicsItem.ItemClipsToShape puts the item behind the parent rect and only receives event inside its own shape.
self.setAcceptHoverEvents(True)
self.setCursor(QtCore.Qt.SizeHorCursor)
self.setZValue(_zValuesRelativeToConductor["handle"]) #The handle does not compete with the transparent block but with its contents! Therefore the startItem is set to have a custom lower priority than the handle
self.setBrush(QtGui.QColor("black"))
@ -361,39 +321,45 @@ class ConductorBlockHandle(QtWidgets.QGraphicsRectItem):
return path
def hoverEnterEvent(self, event):
self.setPen(self.activePen)
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.color)
self.setBrush(QtGui.QColor("cyan"))
if self.parentTransparentBlock.parent.parentView.mode() in ("notation", "cc"): #Blockmode is for moving around. not resizing.
self.setCursor(QtCore.Qt.SizeHorCursor)
self.setPen(self.activePen)
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.color)
self.setBrush(QtGui.QColor("cyan"))
def hoverLeaveEvent(self, event):
self.setPen(self.inactivePen)
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.trans)
self.setBrush(QtGui.QColor("black"))
if self.parentTransparentBlock.parent.parentView.mode() in ("notation", "cc"): #Blockmode is for moving around. not resizing.
self.unsetCursor()
self.setPen(self.inactivePen)
#self.parentTransparentBlock.setBrush(self.parentTransparentBlock.trans)
self.setBrush(QtGui.QColor("black"))
def mousePressEvent(self, event):
self.posBeforeMove = self.pos()
self.cursorPosOnMoveStart = QtGui.QCursor.pos()
super().mousePressEvent(event)
if self.parentTransparentBlock.parent.parentView.mode() in ("notation", "cc"): #Blockmode is for moving around. not resizing.
self.posBeforeMove = self.pos()
self.cursorPosOnMoveStart = QtGui.QCursor.pos()
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if not self.cursorPosOnMoveStart:
super().mouseMoveEvent(event)
return None
if self.parentTransparentBlock.parent.parentView.mode() in ("notation", "cc"): #Blockmode is for moving around. not resizing.
if not self.cursorPosOnMoveStart:
super().mouseMoveEvent(event)
return None
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart
new = self.posBeforeMove + delta
delta = QtGui.QCursor.pos() - self.cursorPosOnMoveStart
new = self.posBeforeMove + delta
if not new.x() < self.minimalSize:
self.setPos(new.x(), self.posBeforeMove.y())
pRect = self.parentTransparentBlock.rect()
pRect.setRight(new.x())
self.parentTransparentBlock.setRect(pRect)
if not new.x() < self.minimalSize:
self.setPos(new.x(), self.posBeforeMove.y())
pRect = self.parentTransparentBlock.rect()
pRect.setRight(new.x())
self.parentTransparentBlock.setRect(pRect)
event.accept() #this blocks the qt movable object since we already move the object on our own.
#Don't call the super mouseMoveEvent!
event.accept() #this blocks the qt movable object since we already move the object on our own.
#Don't call the super mouseMoveEvent!
def mouseReleaseEvent(self, event):
if self.cursorPosOnMoveStart:
if self.cursorPosOnMoveStart and self.parentTransparentBlock.parent.parentView.mode() in ("notation", "cc"):
endingRelativeToBlockStart = self.x() * constantsAndConfigs.ticksToPixelRatio - self.parentTransparentBlock.x()
if constantsAndConfigs.snapToGrid:
@ -410,6 +376,7 @@ class ConductorBlockHandle(QtWidgets.QGraphicsRectItem):
class TempoPoint(QtWidgets.QGraphicsItem):
"""A point where the values can be edited by the user.
Only in notation and cc mode. Blockmode is for moving the blocks itself.
The first TempoPoint cannot be hovered. It is instead attached to a block handle.
"""
@ -464,19 +431,22 @@ class TempoPoint(QtWidgets.QGraphicsItem):
return QtCore.QRectF(0,0,25,50) #x, y, w, h
def mouseMoveEvent(self, event):
if self.staticExportItem["positionInBlock"] == 0:
#First in block can't be moved
event.accept()
return
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
if self.staticExportItem["positionInBlock"] == 0:
#First in block can't be moved
event.accept()
return
#toTheRight = True if event.scenePos().x() - event.lastScenePos().x()) > 0 else False
#toTheRight = True if event.scenePos().x() - event.lastScenePos().x()) > 0 else False
delta = event.scenePos().x() - event.lastScenePos().x()
newPos = self.x() + delta
delta = event.scenePos().x() - event.lastScenePos().x()
newPos = self.x() + delta
if 0 <= newPos < self.parentTempoTrack.staffLine.line().x2(): #within the conductor line
self.setX(newPos)
event.accept()
if 0 <= newPos < self.parentTempoTrack.staffLine.line().x2(): #within the conductor line
self.setX(newPos)
event.accept()
else:
event.accept()
def mousePressEvent(self, event):
"""Override the mousePressEvent to deactivate it.
@ -489,42 +459,54 @@ class TempoPoint(QtWidgets.QGraphicsItem):
event.accept() #
def mouseReleaseEvent(self, event):
if self.staticExportItem["positionInBlock"] == 0:
#First in block can't be moved
event.accept()
return
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
if self.staticExportItem["positionInBlock"] == 0:
#First in block can't be moved
event.accept()
return
tickPositionAbsolute = self.scenePos().x() * constantsAndConfigs.ticksToPixelRatio
tickPositionAbsolute = self.scenePos().x() * constantsAndConfigs.ticksToPixelRatio
if constantsAndConfigs.snapToGrid:
tickPositionAbsolute = round(tickPositionAbsolute / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm
if constantsAndConfigs.snapToGrid:
tickPositionAbsolute = round(tickPositionAbsolute / constantsAndConfigs.gridRhythm) * constantsAndConfigs.gridRhythm
api.moveTempoItem(self.staticExportItem["id"], tickPositionAbsolute)
event.accept()
api.moveTempoItem(self.staticExportItem["id"], tickPositionAbsolute)
event.accept()
else:
event.accept()
def hoverEnterEvent(self, event):
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(False) #Delete key collides with our hover-delete.
self.grabKeyboard()
self.wheelEventChangedValue = 0
for n in (self.note, self.number, self.arrow):
if n: n.setDefaultTextColor(QtGui.QColor("cyan"))
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(False) #Delete key collides with our hover-delete.
self.grabKeyboard()
self.wheelEventChangedValue = 0
for n in (self.note, self.number, self.arrow):
if n: n.setDefaultTextColor(QtGui.QColor("cyan"))
else:
event.accept()
def hoverLeaveEvent(self, event):
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete.
self.ungrabKeyboard()
for n in (self.note, self.number, self.arrow):
if n: n.setDefaultTextColor(QtGui.QColor("black"))
if self.wheelEventChangedValue:
api.insertTempoItemAtAbsolutePosition(self.staticExportItem["position"], self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue, self.staticExportItem["referenceTicks"], self.staticExportItem["graphType"])
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete.
self.ungrabKeyboard()
for n in (self.note, self.number, self.arrow):
if n: n.setDefaultTextColor(QtGui.QColor("black"))
if self.wheelEventChangedValue:
api.insertTempoItemAtAbsolutePosition(self.staticExportItem["position"], self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue, self.staticExportItem["referenceTicks"], self.staticExportItem["graphType"])
else:
event.accept()
def wheelEvent(self, event):
"""This buffers until hoverLeaveEvent and then the new value is sent in self.hoverLeaveEvent"""
if event.delta() > 0:
self.wheelEventChangedValue += 1
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
if event.delta() > 0:
self.wheelEventChangedValue += 1
else:
self.wheelEventChangedValue -= 1
self.number.setHtml("<font size='2'>{}</font>".format(str(int(self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue))))
event.accept()
else:
self.wheelEventChangedValue -= 1
self.number.setHtml("<font size='2'>{}</font>".format(str(int(self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue))))
event.accept()
event.accept()
def keyPressEvent(self, event):
"""Handle the delete item key.
@ -532,22 +514,28 @@ class TempoPoint(QtWidgets.QGraphicsItem):
The event will not be sent if it is blocked by a global shortcut.
"""
key = event.key()
if key == 16777223:
#after delete the item and tempo tracks gets recreated so we need to reactivate the shortcut now. It will work without these two lines, but that is only implicit behaviour.
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete.
self.ungrabKeyboard()
api.removeTempoItem(self.staticExportItem["id"])
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
key = event.key()
if key == 16777223:
#after delete the item and tempo tracks gets recreated so we need to reactivate the shortcut now. It will work without these two lines, but that is only implicit behaviour.
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True) #Delete key collides with our hover-delete.
self.ungrabKeyboard()
api.removeTempoItem(self.staticExportItem["id"])
else:
return super().keyPressEvent(event)
else:
return super().keyPressEvent(event)
event.accept()
def contextMenuEvent(self, event):
listOfLabelsAndFunctions = [
("edit properties", lambda: SecondaryTempoChangeMenu(self.scene().parentView.mainWindow, staticExportTempoItem = self.staticExportItem)),
]
if not self.staticExportItem["positionInBlock"] == 0:
listOfLabelsAndFunctions.append(("delete", lambda: api.removeTempoItem(self.staticExportItem["id"])))
callContextMenu(listOfLabelsAndFunctions)
if self.parentTempoTrack.parentView.mode() in ("notation", "cc"):
listOfLabelsAndFunctions = [
("edit properties", lambda: SecondaryTempoChangeMenu(self.scene().parentView.mainWindow, staticExportTempoItem = self.staticExportItem)),
]
if not self.staticExportItem["positionInBlock"] == 0:
listOfLabelsAndFunctions.append(("delete", lambda: api.removeTempoItem(self.staticExportItem["id"])))
callContextMenu(listOfLabelsAndFunctions)
else:
event.accept()
class TimeLine(QtWidgets.QGraphicsItem):
"""Displays the real time."""

7
qtgui/musicstructures.py

@ -131,9 +131,12 @@ class GuiBlockHandle(QtWidgets.QGraphicsRectItem):
self.endLabel.show()
super().mouseReleaseEvent(event)
def contextMenuEventCustom(self):
def contextMenuEventCustom(self, event):
"""The original context menu was too unreliable. We now call it
directly in Track."""
directly in Track.
We do not use event parameter. This is for compatibility.
"""
if self.startLabel.isVisible():
listOfLabelsAndFunctions = [
(translate("musicstructures", "edit properties"), lambda: BlockPropertiesEdit(self.scene().parentView.mainWindow, staticExportItem = self.staticExportItem)),

5
qtgui/scorescene.py

@ -343,7 +343,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
"""
super().mousePressEvent(event)
if self.parentView.mode() in ("block", "cc"):
if self.parentView.mode() in ("block"): #CC Block Moving is done in its own if clause below.
if event.button() == QtCore.Qt.LeftButton:
modifiers = QtWidgets.QApplication.keyboardModifiers()
@ -374,7 +374,8 @@ class GuiScore(QtWidgets.QGraphicsScene):
Good enough is better than good."""
block = self.blockAt(event.scenePos())
if block: #works for note blocks and conductor blocks
block.contextMenuEventCustom()
block.contextMenuEventCustom(event)
event.accept() #eat it

Loading…
Cancel
Save