diff --git a/qtgui/graphs.py b/qtgui/graphs.py index ff65b44..d3d9540 100644 --- a/qtgui/graphs.py +++ b/qtgui/graphs.py @@ -59,6 +59,7 @@ class CCPath(QtWidgets.QGraphicsRectItem): self.parentDataTrackId = parentDataTrackId self.parentGuiTrack = parentGuiTrack + self.hoveringOverItem = None #user point or block end marker. Used for scoreScene mousePressEvent detection self.userItems = [] self.interpolatedItems = [] self.other = [] @@ -76,6 +77,7 @@ class CCPath(QtWidgets.QGraphicsRectItem): return super().itemChange(changeEnum, value) def mousePressEventToAdd(self, event): + """Called by scorescene mousePressEvent""" if event.button() == 1: #QtCore.Qt.LeftButton self.add(event) @@ -137,6 +139,7 @@ class CCPath(QtWidgets.QGraphicsRectItem): tbh.scene().removeWhenIdle(tbh) self.transparentBlockHandles = [] #reset + self.hoveringOverItem = None #because moving an item, triggering delete and recreation, will never hoverOutEvent #removeInstancesFromScene(CCGraphTransparentBlock) #this removes ALL blocks in all tracks from the scene. don't. for dictExportItem in staticRepresentationList: guiBlock = CCGraphTransparentBlock(parent = self, staticExportItem = dictExportItem, x = 0, y = -28, w = dictExportItem["duration"] / constantsAndConfigs.ticksToPixelRatio, h = 2*28) @@ -408,16 +411,17 @@ class CCGraphBlockEndMarker(QtWidgets.QGraphicsLineItem): def hoverEnterEvent(self, event): #self.setZValue(20) #self.parentTransparentBlock.setZValue(19) + self.parentCCPath.hoveringOverItem = self self.setPen(self.activePen) self.parentTransparentBlock.setBrush(self.parentTransparentBlock.color) def hoverLeaveEvent(self, event): #self.setZValue(10) #self.parentTransparentBlock.setZValue(9) + self.parentCCPath.hoveringOverItem = None #When moving the blockEndMarker this is never called because it will be deleted before hoverOut. We reset the variable when recreating though. self.setPen(self.inactivePen) self.parentTransparentBlock.setBrush(self.parentTransparentBlock.trans) - class CCInterpolatedPoint(QtWidgets.QGraphicsEllipseItem): """This is practically read-only""" def __init__(self): @@ -425,7 +429,9 @@ class CCInterpolatedPoint(QtWidgets.QGraphicsEllipseItem): self.setOpacity(0.6) self.setBrush(QtCore.Qt.black) #fill self.setEnabled(False) - self.setZValue(1) + self.setZValue(-5) + self.setAcceptHoverEvents(False) + self.setAcceptedMouseButtons(QtCore.Qt.NoButton) class CCUserPoint(QtWidgets.QGraphicsEllipseItem): """the position is set by the parent""" @@ -434,10 +440,12 @@ class CCUserPoint(QtWidgets.QGraphicsEllipseItem): #color = QtGui.QColor() #color.setHsl(127-abs(value), 255, 127) #l(h, s, l) # 8bit values. Sets a HSL color value; h is the hue, s is the saturation, l is the lightness. l goes from black(0), over color(255/2) to white (255). #0 hue is green, 128 is red #self.setBrush(color) #fill - self.setBrush(QtGui.QColor("cyan")) + self.inactive = QtGui.QColor("cyan") + self.active = QtGui.QColor("black") + self.setBrush(self.inactive) self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable) self.setAcceptHoverEvents(True) - self.setCursor(QtCore.Qt.SizeAllCursor) + #self.setCursor(QtCore.Qt.SizeAllCursor) self.parentCCPath = parentCCPath self.staticExportItem = staticExportItem self.interpolatedItemsRight = [] @@ -446,29 +454,41 @@ class CCUserPoint(QtWidgets.QGraphicsEllipseItem): def shape(self): """Return a more accurate shape for this item so that - mouse hovering is more accurate""" + mouse hovering is more accurate + + affects boundingRect + """ path = QtGui.QPainterPath() path.addEllipse(QtCore.QRectF(-5, -5, 14, 14)) #this is directly related to inits parameter. -3, -3, 6, 6. return path def mousePressEvent(self, event): - super().mousePressEvent(event) + """Context menu is handled by the qt function below""" self.lastPos = self.pos() + super().mousePressEvent(event) if event.button() == 1: #QtCore.Qt.LeftButton self.setCursor(QtCore.Qt.BlankCursor) for i in self.interpolatedItemsLeft + self.interpolatedItemsRight: i.hide() + + def mouseDoubleClickEvent(self, event): self.changeCCValue() def hoverEnterEvent(self, event): + self.setZValue(100) + self.parentCCPath.hoveringOverItem = self + self.setBrush(self.active) self.grabKeyboard() #self.setFocus() #do NOT set the focus. GrabKeyboard is enough. The focus will stay even after hoverLeaveEvent so that Delete will delete the last hovered item. not good! def hoverLeaveEvent(self, event): """reverse hoverEnterEvent""" + self.setZValue(9) + self.setBrush(self.inactive) + self.parentCCPath.hoveringOverItem = None self.ungrabKeyboard() def keyPressEvent(self, event): @@ -483,7 +503,7 @@ class CCUserPoint(QtWidgets.QGraphicsEllipseItem): def mouseReleaseEvent(self, event): """After moving a point around send an update to the backend""" - super(CCUserPoint, self).mouseReleaseEvent(event) + super().mouseReleaseEvent(event) self.setCursor(QtCore.Qt.SizeAllCursor) if event.button() == 1: #QtCore.Qt.LeftButton api.changeGraphItem(self.staticExportItem["id"], self.getXDifferenceAsBackendValue(), self.getYAsBackendValue()) #send update to the backend, don't wait for callback. @@ -492,7 +512,7 @@ class CCUserPoint(QtWidgets.QGraphicsEllipseItem): """Only active when the item is also selected and left mouse button is down. Not any mouse event, no hover.""" modifiers = QtWidgets.QApplication.keyboardModifiers() - super(CCUserPoint, self).mouseMoveEvent(event) + super(CCUserPoint, self).mouseMoveEvent(event) #same as setPos, but this triggers the safeguards withinBlock below, while setPos does not. if modifiers == QtCore.Qt.ShiftModifier: self.setY(self.lastPos.y()) elif modifiers == QtCore.Qt.AltModifier: diff --git a/qtgui/musicstructures.py b/qtgui/musicstructures.py index 897f8b3..a66b8d8 100644 --- a/qtgui/musicstructures.py +++ b/qtgui/musicstructures.py @@ -65,8 +65,8 @@ class GuiBlockHandle(QtWidgets.QGraphicsRectItem): self.setPen(self.trans) self.setBrush(self.trans) #self.setOpacity(0.4) #slightly fuller than background - self.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity, True) - self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) + #self.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity, True) + #self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True) self.setParentItem(parent) self.setZValue(10) #This is the z value within GuiTrack self.staticExportItem = staticExportItem @@ -591,6 +591,7 @@ class GuiTrack(QtWidgets.QGraphicsItem): for tbh in self.transparentBlockHandles: tbh.itemMode() + elif nameAsString == "block": self.blockModeNameGraphic.show() self.nameGraphic.hide() diff --git a/qtgui/scorescene.py b/qtgui/scorescene.py index 1909e2d..264ea60 100644 --- a/qtgui/scorescene.py +++ b/qtgui/scorescene.py @@ -399,13 +399,19 @@ class GuiScore(QtWidgets.QGraphicsScene): block.mousePressEventCustom(event) event.accept() #eat it return - else: #CC mode but no modifiers + else: #CC mode but no modifiers. Add or move item. #implicit qt scene click detection was unreliable. In the end the manual route was the best option: block = self.blockAt(event.scenePos()) - if block and type(block) is CCGraphTransparentBlock: - block.parentCCPath.mousePressEventToAdd(event) + if block and type(block) is CCGraphTransparentBlock: #We are within the boundaries of a block + super().mousePressEvent(event) #Apparently we need the double event call. Without this we select the wrong items, but not all the time. The result is not being able to move the cc user point. Just leave it in. Solving this "properly" would need refactoring all mouse events. + if block.parentCCPath.hoveringOverItem: + #this is maybe a CC User Item or block end marker. don't handle here, let the item handle it. + super().mousePressEvent(event) + return + else: + #Empty Block click + block.parentCCPath.mousePressEventToAdd(event) return - super().mousePressEvent(event)