Browse Source

Reimplement the grid with a simpler and faster version

master
Nils 4 months ago
parent
commit
3e775b605b
  1. 98
      qtgui/grid.py
  2. 9
      qtgui/scorescene.py
  3. 14
      qtgui/scoreview.py

98
qtgui/grid.py

@ -36,6 +36,9 @@ from .constantsAndConfigs import constantsAndConfigs
import engine.api as api
gridPen = QtGui.QPen(QtCore.Qt.DotLine)
gridPen.setCosmetic(True)
masterLine = QtCore.QLineF(0, 0, 0, 128*constantsAndConfigs.stafflineGap) # (x1, y1, x2, y2)
class RhythmLine(QtWidgets.QGraphicsLineItem):
def __init__(self, parentGrid):
@ -44,8 +47,99 @@ class RhythmLine(QtWidgets.QGraphicsLineItem):
self.setParentItem(parentGrid)
self.setAcceptedMouseButtons(QtCore.Qt.NoButton) #we still need this otherwise no rubberband.
class ActualGrid(QtWidgets.QGraphicsItem):
def boundingRect(self):
return self.childrenBoundingRect()
class GuiGrid(QtWidgets.QGraphicsItem):
"""Third version of the grid.
Version 1: QGraphicsItemGroup with horizontal and vertical lines. Slow, disturbing to look at
even when the horizontal lines where nice ledger lines.
Drawing all lines for the whole score.
Version 2: QGraphicsItemGroup only vertical lines.
Only a few lines were created and were moved on scrolling.
QGraphicsItemGroupp.addToGroup and the constant updating was still too much.
Version 3: QGraphicsItem. Only vertical. Draw full scene. No scrolling trickery.
As simple as possible. Leave as much of the work to Qt.
We have a subitem that is actually the grid, so we can show/hide/delete that at once.
"""
def __init__(self, parentScene):
super(GuiGrid, self).__init__()
self.parent = parentScene #QGraphicsScene
self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
self.setAcceptedMouseButtons(QtCore.Qt.NoButton)
self.setEnabled(False)
self.setOpacity(constantsAndConfigs.gridOpacity)
self.actualGridItem = None
self.nrOfVerticalLines = 0 #remember for optimisations
self._rememberSceneHeight = 0
def reactOnScoreChange(self):
"""Entry point for callbacks and parent functions"""
self.redrawTickGrid()
def boundingRect(self):
return self.childrenBoundingRect()
def updateMode(self, nameAsString):
assert nameAsString in constantsAndConfigs.availableEditModes
if nameAsString == "block":
self.hide()
else:
self.show()
def redrawTickGrid(self):
"""A complete redraw when triggered."""
gapVertical = constantsAndConfigs.gridRhythm / constantsAndConfigs.ticksToPixelRatio #this is necessary every time after stretching (ticksToPixelRatio) and after changing the gridRhythm
width = self.parent.maxTrackLength()
height = self.parent.cachedSceneHeight
lines = int(width / gapVertical) + 8 # + extra ones for a good feeling
if lines == self.nrOfVerticalLines and self._rememberSceneHeight == height:
return
else: #change
self.nrOfVerticalLines = lines
self._rememberSceneHeight = height
try:
self.parent.removeWhenIdle(self.actualGridItem) #remove all at once
except:
pass #will fail the first time on start
self.actualGridItem = ActualGrid(self)
self.actualGridItem.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents, True) #only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
self.actualGridItem.setParentItem(self)
self.actualGridItem.setPos(0,0)
for i in range(lines):
l = QtWidgets.QGraphicsLineItem(0,0,0, height, self.actualGridItem) #x1, y1, x2, y2, parent
l.setPos(i * gapVertical, 0)
def reactToresizeEventOrZoom(self):
"""Called by the Views resizeEvent.
When the views geometry changes or zooms"""
pass
def stretchXCoordinates(self, factor):
"""Reposition the items on the X axis.
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates.
Docstring there."""
gapVertical = constantsAndConfigs.gridRhythm / constantsAndConfigs.ticksToPixelRatio #this is necessary every time after stretching (ticksToPixelRatio) and after changing the gridRhythm
for vline in self.actualGridItem.childItems(): #rhythm- and barlines
vline.setX(vline.x() * factor)
class GuiGrid(QtWidgets.QGraphicsItemGroup):
class OldGuiGrid(QtWidgets.QGraphicsItemGroup):
#Don't use that. QGraphicsItemGroup is very slow.
"""The grid consists of vertical lines.
lines help to estimate
the rhythm or tick positions e.g. when trying to find the right place for a tempo change or CC.
@ -129,7 +223,7 @@ class GuiGrid(QtWidgets.QGraphicsItemGroup):
"""Only allowed to get called by reactToresizeEventOrZoom because we need an updated
self.height value"""
#Check if we need longer lines. Do this before creating new lines, because new lines
#have the heighest
#have the highest
oldHeight = self.linesVertical[-1].line().y2() #this is a bit less than .length() so we use that
if self.height > oldHeight:
for vline in self.linesVertical:

9
qtgui/scorescene.py

@ -70,12 +70,14 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.backColor.setNamedColor("#fdfdff")
self.setBackgroundBrush(self.backColor)
self.cachedSceneHeight = 0 #set in self.redraw. Used by updateTrack to set the sceneRect
self.grid = GuiGrid(parentScene=self)
self.addItem(self.grid)
self.grid.setPos(0, -20 * constantsAndConfigs.stafflineGap) #this is more calculation than simply using self.yStart, and might require manual adjustment in the future, but at least it guarantees the grid matches the staffline positions
self.grid.setZValue(-50)
self.cachedSceneHeight = 0 #set in self.redraw. Used by updateTrack to set the sceneRect
#All Cursors
self.cursor = Cursor()
@ -109,8 +111,8 @@ class GuiScore(QtWidgets.QGraphicsScene):
def updateModeSingleTrackRedraw(self, nameAsString:str, trackId:int, trackExport:tuple):
"""trackExport is a tuple of block export dicts"""
self.tracks[trackId].updateMode(nameAsString)
if nameAsString == "block":
self.tracks[trackId].stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scoreView.updateMode
#if nameAsString == "block":
# self.tracks[trackId].stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scoreView.updateMode
def maxTrackLength(self):
if self.tracks:
@ -129,6 +131,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.tracks[trackId].redraw(staticRepresentationList)
#else:
#hidden track. But this can still happen through the data editor
self.grid.reactOnScoreChange()
self.updateSceneRect()
def trackPaintBlockBackgroundColors(self, trackId, staticBlocksRepresentation):

14
qtgui/scoreview.py

@ -139,7 +139,7 @@ class ScoreView(QtWidgets.QGraphicsView):
function() #this is most likely an api function
def resizeEvent(self, event):
"""Triggers mostly when new notes are entered"""
"""Triggers at least when the window is resized"""
self.scoreScene.grid.reactToresizeEventOrZoom()
super().resizeEvent(event)
@ -200,8 +200,8 @@ class ScoreView(QtWidgets.QGraphicsView):
if self.mainWindow.ui.actionCC_Mode.isChecked():
if calledByMenu and self._lastSavedMode == "block":
self.stretchXCoordinates(4.0) #return from half sized mode. If we do not come from block mode this is not needed. CC and Note mode have the same scaling
#if calledByMenu and self._lastSavedMode == "block":
# self.stretchXCoordinates(4.0) #return from half sized mode. If we do not come from block mode this is not needed. CC and Note mode have the same scaling
self.mainWindow.menuActionDatabase.ccEditMode()
self.mainWindow.menuActionDatabase.writeProtection(True)
@ -209,8 +209,8 @@ class ScoreView(QtWidgets.QGraphicsView):
self.mainWindow.menuActionDatabase.loadToolbarContext("cc")
elif self.mainWindow.ui.actionNotation_Mode.isChecked():
if calledByMenu and self._lastSavedMode == "block":
self.stretchXCoordinates(4.0) #return from half sized mode. If we do not come from block mode this is not needed. CC and Note mode have the same scaling
#if calledByMenu and self._lastSavedMode == "block":
# self.stretchXCoordinates(4.0) #return from half sized mode. If we do not come from block mode this is not needed. CC and Note mode have the same scaling
self.mainWindow.menuActionDatabase.noteEditMode()
self.mainWindow.menuActionDatabase.writeProtection(False)
@ -219,8 +219,8 @@ class ScoreView(QtWidgets.QGraphicsView):
elif self.mainWindow.ui.actionBlock_Mode.isChecked():
assert calledByMenu, "We currently assume that only the program start calls this function not via the menu, and that chooses notation mode"
if not self._lastSavedMode == "block": #check that we don't go from block mode to block mode again, since XScaling is cumulative
self.stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scene.updateModeSingleTrackRedraw
#if not self._lastSavedMode == "block": #check that we don't go from block mode to block mode again, since XScaling is cumulative
# self.stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scene.updateModeSingleTrackRedraw
self.mainWindow.menuActionDatabase.noteEditMode()
self.mainWindow.menuActionDatabase.writeProtection(True)
self.scoreScene.updateMode("block")

Loading…
Cancel
Save