From 4fd8c17c326457e1b1bb7d2c1790c28cc32a4a09 Mon Sep 17 00:00:00 2001 From: Nils <> Date: Thu, 17 Dec 2020 23:46:57 +0100 Subject: [PATCH] Fix block and mouse handling for tempo track --- qtgui/conductor.py | 218 ++++++++++++++++++--------------------- qtgui/musicstructures.py | 7 +- qtgui/scorescene.py | 5 +- 3 files changed, 111 insertions(+), 119 deletions(-) diff --git a/qtgui/conductor.py b/qtgui/conductor.py index 292998d..5327e26 100644 --- a/qtgui/conductor.py +++ b/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("{}".format(str(int(self.staticExportItem["unitsPerMinute"] + self.wheelEventChangedValue)))) + event.accept() else: - self.wheelEventChangedValue -= 1 - self.number.setHtml("{}".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.""" diff --git a/qtgui/musicstructures.py b/qtgui/musicstructures.py index ea78a1f..bc09547 100644 --- a/qtgui/musicstructures.py +++ b/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)), diff --git a/qtgui/scorescene.py b/qtgui/scorescene.py index a298be6..2769625 100644 --- a/qtgui/scorescene.py +++ b/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