From a508a830a3e8ba42ef270715727f09e48161d4ae Mon Sep 17 00:00:00 2001 From: Nils <> Date: Sat, 18 Jul 2020 22:45:32 +0200 Subject: [PATCH] Refactor bounding rects --- qtgui/conductor.py | 13 -------- qtgui/cursor.py | 17 ----------- qtgui/graphs.py | 12 -------- qtgui/grid.py | 5 --- qtgui/items.py | 66 +++++++++------------------------------- qtgui/musicstructures.py | 31 +++++++++---------- qtgui/scorescene.py | 4 +-- 7 files changed, 31 insertions(+), 117 deletions(-) diff --git a/qtgui/conductor.py b/qtgui/conductor.py index 8e52396..8e9755a 100644 --- a/qtgui/conductor.py +++ b/qtgui/conductor.py @@ -28,8 +28,6 @@ from template.helper import pairwise from .submenus import SecondaryTempoChangeMenu, TempoBlockPropertiesEdit import engine.api as api -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - _zValuesRelativeToConductor = { #Only use for objects added directly to the Conductor, not their children. "line":0, "startItem":1, @@ -238,17 +236,6 @@ class ConductorTransparentBlock(QtWidgets.QGraphicsRectItem): #self.setZValue(_zValuesRelativeToConductor["handle"]) #includes the handle - - #Doesn't need it because only pure QGraphicItem subclasses need that. But we are already a rect. - #def boundingRect(self, *args): - # return oneRectToReturnThemAll - - #Doesn't need it because only pure QGraphicItem subclasses need that. But we are already a rect. - #def paint(self, *args): - # """Prevent the selection rectangle when clicking the item""" - #!! This also prevents the rectangle to show up. Very bad decision. - # pass - def stretchXCoordinates(self, factor): """Reposition the items on the X axis. Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. diff --git a/qtgui/cursor.py b/qtgui/cursor.py index f867190..510f4a7 100644 --- a/qtgui/cursor.py +++ b/qtgui/cursor.py @@ -31,8 +31,6 @@ pen.setJoinStyle(QtCore.Qt.RoundJoin) pen.setWidth(2) #pen.setColor(QtGui.QColor("red")) -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - class PitchCursor(QtWidgets.QGraphicsRectItem): def __init__(self): """Does not need the actual dotOnLine. @@ -44,8 +42,6 @@ class PitchCursor(QtWidgets.QGraphicsRectItem): self.setPen(pen) self.setEnabled(False) - def boundingRect(self, *args): return oneRectToReturnThemAll - class PositionCursor(QtWidgets.QGraphicsRectItem): def __init__(self): """Does not need the actual position. @@ -57,9 +53,6 @@ class PositionCursor(QtWidgets.QGraphicsRectItem): self.setScale(0.8) self.setEnabled(False) - def boundingRect(self, *args): - return oneRectToReturnThemAll - class Cursor(QtWidgets.QGraphicsItemGroup): """A cursor that shows the vertical @@ -75,10 +68,6 @@ class Cursor(QtWidgets.QGraphicsItemGroup): api.callbacks.setCursor.append(self.setCursor) self.setEnabled(False) - def boundingRect(self, *args): - return oneRectToReturnThemAll - - def clearItemHighlight(self): """Gets called before a track changes. Most of the time when a new item is inserted/deleted. This means the gui track will be recreated and a current highlight on an item might get @@ -154,10 +143,6 @@ class Playhead(QtWidgets.QGraphicsLineItem): #self.hide() #self.maxHeight = QtWidgets.QDesktopWidget().geometry().height() #we really hope the screen resolution does not change during the session. - def boundingRect(self, *args): - return oneRectToReturnThemAll - - def setCursorPosition(self, tickindex:int, playbackStatus:bool): """Set the playhead to the right position, but keep the viewport stable. Shift the entire "page" if the cursor becomes invisible because its steps outside the viewport""" @@ -235,8 +220,6 @@ class Selection(QtWidgets.QGraphicsRectItem): self.setEnabled(False) api.callbacks.setSelection.append(self.setSelection) - def boundingRect(self, *args): - return oneRectToReturnThemAll def stretchXCoordinates(self, factor): """Reposition the items on the X axis. diff --git a/qtgui/graphs.py b/qtgui/graphs.py index e2ddb5e..2198f72 100644 --- a/qtgui/graphs.py +++ b/qtgui/graphs.py @@ -25,8 +25,6 @@ from .constantsAndConfigs import constantsAndConfigs from template.qtgui.helper import stringToColor, removeInstancesFromScene, callContextMenu, stretchLine, stretchRect import engine.api as api -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - class CCPath(QtWidgets.QGraphicsRectItem): """ A CCPath only exists when the backend track has a cc-part activated. @@ -68,8 +66,6 @@ class CCPath(QtWidgets.QGraphicsRectItem): self.blockdictCache = {} #blockId:guiBlock updated through self.updateGraphBlockTrack self.transparentBlockHandles = [] #transparentBlockHandles in correct order. updated through self.updateGraphBlockTrack - def boundingRect(self, *args): return oneRectToReturnThemAll - def itemChange(self, changeEnum, value): if changeEnum == QtWidgets.QGraphicsItem.ItemVisibleHasChanged: #12 if self.isVisible(): @@ -257,8 +253,6 @@ class CCGraphTransparentBlock(QtWidgets.QGraphicsRectItem): self.posBeforeMove = None self.cursorPosOnMoveStart = None - def boundingRect(self, *args): return oneRectToReturnThemAll - def stretchXCoordinates(self, factor): """Reposition the items on the X axis. Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. @@ -365,8 +359,6 @@ class CCGraphBlockEndMarker(QtWidgets.QGraphicsLineItem): self.activePen = QtGui.QPen(pen) self.activePen.setColor(QtGui.QColor("cyan")) - def boundingRect(self, *args): return oneRectToReturnThemAll - def allItemsRightOfMe(self): for item in self.parentCCPath.items: if item.x() > self.x(): @@ -430,8 +422,6 @@ class CCInterpolatedPoint(QtWidgets.QGraphicsEllipseItem): self.setEnabled(False) self.setZValue(1) - def boundingRect(self, *args): return oneRectToReturnThemAll - class CCUserPoint(QtWidgets.QGraphicsEllipseItem): """the position is set by the parent""" def __init__(self, parentCCPath, staticExportItem): @@ -449,8 +439,6 @@ class CCUserPoint(QtWidgets.QGraphicsEllipseItem): self.interpolatedItemsLeft = [] self.setZValue(9) - def boundingRect(self, *args): return oneRectToReturnThemAll - def shape(self): """Return a more accurate shape for this item so that mouse hovering is more accurate""" diff --git a/qtgui/grid.py b/qtgui/grid.py index d6a0954..a7d311f 100644 --- a/qtgui/grid.py +++ b/qtgui/grid.py @@ -36,9 +36,6 @@ from .constantsAndConfigs import constantsAndConfigs import engine.api as api -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - - masterLine = QtCore.QLineF(0, 0, 0, 128*constantsAndConfigs.stafflineGap) # (x1, y1, x2, y2) class RhythmLine(QtWidgets.QGraphicsLineItem): def __init__(self, parentGrid): @@ -98,8 +95,6 @@ class GuiGrid(QtWidgets.QGraphicsItemGroup): gridPen = QtGui.QPen(QtCore.Qt.DotLine) gridPen.setCosmetic(True) - def boundingRect(self, *args): return oneRectToReturnThemAll - def reactToHorizontalScroll(self, value): if not self.initialGridExists: return diff --git a/qtgui/items.py b/qtgui/items.py index 817e224..aaeefdc 100644 --- a/qtgui/items.py +++ b/qtgui/items.py @@ -41,8 +41,6 @@ setPos(0,0)""" #Atomic items: -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - class GuiTieCurveGraphicsItem(QtWidgets.QGraphicsPathItem): pen = QtGui.QPen() pen.setCapStyle(QtCore.Qt.RoundCap) @@ -60,9 +58,6 @@ class GuiTieCurveGraphicsItem(QtWidgets.QGraphicsPathItem): self.noteExportObject = noteExportObject self.draw() - def boundingRect(self, *args): - return oneRectToReturnThemAll - def draw(self): lengthInPixel = self.noteExportObject["tieDistanceInTicks"] / constantsAndConfigs.ticksToPixelRatio path = QtGui.QPainterPath() @@ -89,13 +84,13 @@ class GuiTupletNumber(QtWidgets.QGraphicsItem): pen.setColor(QtGui.QColor("darkGrey")) pen.setWidth(1) - def paint(self, *args): - pass - def boundingRect(self, *args): - return oneRectToReturnThemAll + def boundingRect(self): + return self.childrenBoundingRect() def __init__(self, upper, lower): super().__init__() + self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden + #self.upper = QtWidgets.QGraphicsSimpleTextItem(str(upper)) #self.divider = QtWidgets.QGraphicsLineItem(0,0,10,0) #self.divider.rotate(-70) @@ -117,6 +112,7 @@ class GuiItem(QtWidgets.QGraphicsItem): """A blank item. Subclass to implement your own""" def __init__(self, staticItem): super().__init__() + self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden self.staticItem = staticItem dur = self.staticItem["completeDuration"] exceptions = ["BlockEndMarker"] @@ -137,10 +133,9 @@ class GuiItem(QtWidgets.QGraphicsItem): in GuiChord""" pass - def paint(self, *args): - pass - def boundingRect(self, *args): - return oneRectToReturnThemAll + + def boundingRect(self): + return self.childrenBoundingRect() def stretchXCoordinates(self, factor): """Reposition the items on the X axis. @@ -185,22 +180,6 @@ class GuiRectangleNotehead(QtWidgets.QGraphicsRectItem): self.accidental.setPos(0, 0) #not analogue to the notehead acciental position because here we choose the rectangle as parent self.accidental.setParentItem(self) - #NOTE: this was from a time when mouse modification was possible. Leave for later use. - #def shape(self): - # """Qt Function - # Return a more accurate shape for this item so that - # mouse hovering is more accurate. - # - # Must be within the bounding rect.""" - # return self.path - - #def boundingRect(self, *args): - # """Keep in syn with self.shape()""" - # return self.pathRect - - #def paint(self, *args): - #Do NOT implement this. This actually works already. The parent item needs this. - class GuiRectangleVelocity(QtWidgets.QGraphicsRectItem): """Displays the velocity of one note when in rectangle view mode. @@ -250,21 +229,6 @@ class GuiRectangleVelocity(QtWidgets.QGraphicsRectItem): #Misc Qt Flags self.setAcceptHoverEvents(False) #Make this explicit so it will be remembered that the gui rectangle velocity is a display only. - #NOTE: this was from a time when mouse modification was possible. Leave for later use. - #def shape(self): - # """Qt Function - # Return a more accurate shape for this item so that - # mouse hovering is more accurate. - # - # Must be within the bounding rect.""" - # return self.path - - #def boundingRect(self, *args): - # """Keep in syn with self.shape()""" - # return self.pathRect - - #def paint(self, *args): - #Do NOT implement this. This actually works already. The parent item needs this. class GuiRectangle(QtWidgets.QGraphicsItem): """An alternative notehead, used to change velocity and duration @@ -281,24 +245,22 @@ class GuiRectangle(QtWidgets.QGraphicsItem): def __init__(self, noteExportObject): super().__init__() + self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden self.notehead = GuiRectangleNotehead(parent = self, noteExportObject = noteExportObject) self.velocity = GuiRectangleVelocity(parent = self, noteExportObject = noteExportObject) self.notehead.setZValue(10) #within GuiRectangle - def boundingRect(self, *args): - return oneRectToReturnThemAll - def paint(self, *args): - pass + def boundingRect(self): + return self.childrenBoundingRect() class GuiNote(QtWidgets.QGraphicsItem): def __init__(self, noteExportObject, directionRightAndUpwards): super(GuiNote, self).__init__() + self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden self.createNote(noteExportObject, directionRightAndUpwards) - def paint(self, *args): - pass - def boundingRect(self, *args): - return oneRectToReturnThemAll + def boundingRect(self): + return self.childrenBoundingRect() def createNote(self, noteExportObject, directionRightAndUpwards): #note head diff --git a/qtgui/musicstructures.py b/qtgui/musicstructures.py index 026756f..9999527 100644 --- a/qtgui/musicstructures.py +++ b/qtgui/musicstructures.py @@ -43,8 +43,6 @@ from .submenus import BlockPropertiesEdit cosmeticPen = QtGui.QPen() cosmeticPen.setCosmetic(True) -oneRectToReturnThemAll = QtCore.QRectF(0,0,0,0) #prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection. - class GuiBlockHandle(QtWidgets.QGraphicsRectItem): """A simplified version of a Block. Since we don't use blocks in the GUI, only in the backend we still need them sometimes as macro strutures, where we don't care about the content. @@ -95,8 +93,6 @@ class GuiBlockHandle(QtWidgets.QGraphicsRectItem): self.startLabel = QtWidgets.QGraphicsSimpleTextItem("") self.endLabel = QtWidgets.QGraphicsSimpleTextItem("") - def boundingRect(self, *args): return oneRectToReturnThemAll - def stretchXCoordinates(self, factor): """Reposition the items on the X axis. Call goes through all parents/children, starting from ScoreView._stretchXCoordinates. @@ -212,8 +208,6 @@ class GuiTrack(QtWidgets.QGraphicsItem): self.nameGraphic = self.NameGraphic(self.staticExportItem["name"], parent = self) self.staticItems.append(self.nameGraphic) - - #Add one central "Create new CC Path" button which is for all non-existing CC Paths of this track and reacte to the current constantsAndConfig.ccValue #This button is not in the CCPath object because those only get created for existing backend-CCs. self.universalCreateFirstCCBlock = QtWidgets.QGraphicsSimpleTextItem(translate("musicstructures", "Create CC Path")) @@ -225,7 +219,6 @@ class GuiTrack(QtWidgets.QGraphicsItem): #self.secondStageInitNowThatWeHaveAScene gets called by the ScoreScene.redraw(), where new tracks get created. After it was inserted into the scene. - def boundingRect(self, *args): return oneRectToReturnThemAll class NameGraphic(QtWidgets.QGraphicsSimpleTextItem): def __init__(self, text, parent): @@ -243,8 +236,6 @@ class GuiTrack(QtWidgets.QGraphicsItem): if result[1]: api.setTrackName(self.parent.staticExportItem["id"], nameString = result[0], initialInstrumentName = self.parent.staticExportItem["initialInstrumentName"], initialShortInstrumentName = self.parent.staticExportItem["initialShortInstrumentName"]) #keep the old lilypond names - #def boundingRect(self, *args): return oneRectToReturnThemAll - def contextMenuEvent(self, event): listOfLabelsAndFunctions = [ (translate("musicstructures", "edit name"), self._editName), ] callContextMenu(listOfLabelsAndFunctions) @@ -255,6 +246,18 @@ class GuiTrack(QtWidgets.QGraphicsItem): has a position, opacity, parent item etc. (All of that is not read in normal __init__)""" pass + + def boundingRect(self): + """Without bounding rect not mousePressEvents for children""" + if self.staticExportItem["double"]: #double track, twice as high + h = 10 * constantsAndConfigs.stafflineGap + else: + h = 4 * constantsAndConfigs.stafflineGap + + w = self.lengthInPixel + + return QtCore.QRectF(0,0,w,h) #x,y,w,h - relative to self, so pos is always 0,0 + def toggleNoteheadsRectangles(self): for item in self.items: item.updateVisibility() @@ -397,18 +400,13 @@ class GuiTrack(QtWidgets.QGraphicsItem): y = beamNumberOffset + positionAsStaffline * constantsAndConfigs.stafflineGap / 2 - 1 shifterAnchor.setPos(x, y) - class TrackAnchor(QtWidgets.QGraphicsItem): + class TrackAnchor(QtWidgets.QGraphicsItemGroup): """Handling all items as individuals when deleting a track to redraw it is too much. Better let Qt handle it all at once.""" def __init__(self, parent): super().__init__() self.parent = parent - self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden - self.setFlag(QtWidgets.QGraphicsItem.ItemContainsChildrenInShape, True) - - def boundingRect(self, *args): return oneRectToReturnThemAll - def createGraphicItemsFromData(self, staticRepresentationList): """Create staff objects including simple barlines""" @@ -426,7 +424,8 @@ class GuiTrack(QtWidgets.QGraphicsItem): metaDataDict = staticRepresentationList.pop() for staticItem in staticRepresentationList: item = staticItem2Item(staticItem) - item.setParentItem(self.anchor) + #item.setParentItem(self.anchor) + self.anchor.addToGroup(item) itemsAppend(item) item.setPos(item.pixelPosition, 0) # Y axis is set by the position of the track. This sets the position of the whole ItemGroup. The actual pitch of a chord/note is set locally by the chord itself and of no concern here. item.setZValue(5) #This is the z value within GuiTrack diff --git a/qtgui/scorescene.py b/qtgui/scorescene.py index 65f7f6e..3072b86 100644 --- a/qtgui/scorescene.py +++ b/qtgui/scorescene.py @@ -35,7 +35,7 @@ from .constantsAndConfigs import constantsAndConfigs #from .grid import GuiGrid from .conductor import Conductor, ConductorTransparentBlock from .musicstructures import GuiBlockHandle, GuiTrack -from .graphs import CCGraphTransparentBlock +from .graphs import CCGraphTransparentBlock, CCPath from .cursor import Cursor, Playhead, Selection @@ -160,7 +160,7 @@ class GuiScore(QtWidgets.QGraphicsScene): guiCCs.remove(backendCC) #all right. no update needed. else: #new CC. not existent in the Gui yet. Create. #This is the place where we create new CCPaths - new = graphs.CCPath(parentGuiTrack = self.tracks[trackId], parentDataTrackId = trackId) + new = CCPath(parentGuiTrack = self.tracks[trackId], parentDataTrackId = trackId) self.tracks[trackId].ccPaths[backendCC] = new #store in the GUI Track new.setParentItem(self.tracks[trackId]) new.setPos(0,0)