Browse Source

Reimplement the grid with a simpler and faster version

master
Nils 2 years 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 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) masterLine = QtCore.QLineF(0, 0, 0, 128*constantsAndConfigs.stafflineGap) # (x1, y1, x2, y2)
class RhythmLine(QtWidgets.QGraphicsLineItem): class RhythmLine(QtWidgets.QGraphicsLineItem):
def __init__(self, parentGrid): def __init__(self, parentGrid):
@ -44,8 +47,99 @@ class RhythmLine(QtWidgets.QGraphicsLineItem):
self.setParentItem(parentGrid) self.setParentItem(parentGrid)
self.setAcceptedMouseButtons(QtCore.Qt.NoButton) #we still need this otherwise no rubberband. 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. """The grid consists of vertical lines.
lines help to estimate lines help to estimate
the rhythm or tick positions e.g. when trying to find the right place for a tempo change or CC. 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 """Only allowed to get called by reactToresizeEventOrZoom because we need an updated
self.height value""" self.height value"""
#Check if we need longer lines. Do this before creating new lines, because new lines #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 oldHeight = self.linesVertical[-1].line().y2() #this is a bit less than .length() so we use that
if self.height > oldHeight: if self.height > oldHeight:
for vline in self.linesVertical: for vline in self.linesVertical:

9
qtgui/scorescene.py

@ -70,12 +70,14 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.backColor.setNamedColor("#fdfdff") self.backColor.setNamedColor("#fdfdff")
self.setBackgroundBrush(self.backColor) self.setBackgroundBrush(self.backColor)
self.cachedSceneHeight = 0 #set in self.redraw. Used by updateTrack to set the sceneRect
self.grid = GuiGrid(parentScene=self) self.grid = GuiGrid(parentScene=self)
self.addItem(self.grid) 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.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.grid.setZValue(-50)
self.cachedSceneHeight = 0 #set in self.redraw. Used by updateTrack to set the sceneRect
#All Cursors #All Cursors
self.cursor = Cursor() self.cursor = Cursor()
@ -109,8 +111,8 @@ class GuiScore(QtWidgets.QGraphicsScene):
def updateModeSingleTrackRedraw(self, nameAsString:str, trackId:int, trackExport:tuple): def updateModeSingleTrackRedraw(self, nameAsString:str, trackId:int, trackExport:tuple):
"""trackExport is a tuple of block export dicts""" """trackExport is a tuple of block export dicts"""
self.tracks[trackId].updateMode(nameAsString) self.tracks[trackId].updateMode(nameAsString)
if nameAsString == "block": #if nameAsString == "block":
self.tracks[trackId].stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scoreView.updateMode # self.tracks[trackId].stretchXCoordinates(0.25) #go into small scaling mode. 0.25 is hardcoded and the same as scoreView.updateMode
def maxTrackLength(self): def maxTrackLength(self):
if self.tracks: if self.tracks:
@ -129,6 +131,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.tracks[trackId].redraw(staticRepresentationList) self.tracks[trackId].redraw(staticRepresentationList)
#else: #else:
#hidden track. But this can still happen through the data editor #hidden track. But this can still happen through the data editor
self.grid.reactOnScoreChange()
self.updateSceneRect() self.updateSceneRect()
def trackPaintBlockBackgroundColors(self, trackId, staticBlocksRepresentation): 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 function() #this is most likely an api function
def resizeEvent(self, event): def resizeEvent(self, event):
"""Triggers mostly when new notes are entered""" """Triggers at least when the window is resized"""
self.scoreScene.grid.reactToresizeEventOrZoom() self.scoreScene.grid.reactToresizeEventOrZoom()
super().resizeEvent(event) super().resizeEvent(event)
@ -200,8 +200,8 @@ class ScoreView(QtWidgets.QGraphicsView):
if self.mainWindow.ui.actionCC_Mode.isChecked(): if self.mainWindow.ui.actionCC_Mode.isChecked():
if calledByMenu and self._lastSavedMode == "block": #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.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.ccEditMode()
self.mainWindow.menuActionDatabase.writeProtection(True) self.mainWindow.menuActionDatabase.writeProtection(True)
@ -209,8 +209,8 @@ class ScoreView(QtWidgets.QGraphicsView):
self.mainWindow.menuActionDatabase.loadToolbarContext("cc") self.mainWindow.menuActionDatabase.loadToolbarContext("cc")
elif self.mainWindow.ui.actionNotation_Mode.isChecked(): elif self.mainWindow.ui.actionNotation_Mode.isChecked():
if calledByMenu and self._lastSavedMode == "block": #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.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.noteEditMode()
self.mainWindow.menuActionDatabase.writeProtection(False) self.mainWindow.menuActionDatabase.writeProtection(False)
@ -219,8 +219,8 @@ class ScoreView(QtWidgets.QGraphicsView):
elif self.mainWindow.ui.actionBlock_Mode.isChecked(): 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" 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 #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.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.noteEditMode()
self.mainWindow.menuActionDatabase.writeProtection(True) self.mainWindow.menuActionDatabase.writeProtection(True)
self.scoreScene.updateMode("block") self.scoreScene.updateMode("block")

Loading…
Cancel
Save