Browse Source

wip after undo

master
Nils 5 years ago
parent
commit
3ba193f063
  1. 286
      engine/api.py
  2. 4
      engine/ccsubtrack.py
  3. 43
      engine/tempotrack.py
  4. 2
      engine/track.py
  5. 11
      qtgui/structures.py
  6. 2
      template

286
engine/api.py

@ -39,7 +39,7 @@ from template.helper import flatList, EndlessGenerator
from . import items
from . import lilypond
from .tempotrack import TempoItem, TempoTrack
from .ccsubtrack import GraphItem
from .ccsubtrack import GraphItem, GraphTrackCC
from .track import Track
apiModuleSelfReference = sys.modules[__name__]
@ -143,22 +143,24 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for extra block view or background colors."""
#For simplistic reasons we just update all of this CC in all tracks.
#Makes things much simple with content links across tracks.
#Makes things much more simple with content links across tracks.
#We will see how that turns out performance wise.
#TODO: This reeks of O[n^n]!!!
#We keep the trId parameter but don't use it.
for track in session.data.tracks:
if cc in track.ccGraphTracks:
graphTrackCC = track.ccGraphTracks[cc]
trId = id(track)
ex = graphTrackCC.staticRepresentation()
for func in self.updateGraphTrackCC:
func(trId, cc, ex)
#for track in session.data.tracks:
track = session.data.trackById(trId)
if cc in track.ccGraphTracks:
graphTrackCC = track.ccGraphTracks[cc]
trId = id(track)
ex = graphTrackCC.staticRepresentation()
for func in self.updateGraphTrackCC:
func(trId, cc, ex)
ex = graphTrackCC.staticGraphBlocksRepresentation()
for func in self.updateGraphBlockTrack:
func(trId, cc, ex)
ex = graphTrackCC.staticGraphBlocksRepresentation()
for func in self.updateGraphBlockTrack:
func(trId, cc, ex)
def _updateSingleTrackAllCC(self, trId):
"""Used on CC-Channels change. No own callback list."""
@ -167,7 +169,6 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for func in self.updateGraphTrackCC:
func(trId, cc, ex)
def _graphCCTracksChanged(self, trId):
"""CC graphs of a track deleted or added"""
#TODO: moved?
@ -286,16 +287,22 @@ def startEngine(nsmClient):
#Send initial Data etc.
session.data.tempoMap.isTransportMaster = True #always true for Laborejo.
callbacks._tracksChanged() # This creates the frontend/GUI tracks with access through track ids. From now on we can send the GUI a trId and it knows which track needs change.
for track in session.data.hiddenTracks: #After loading a file some tracks could be hidden. We want midi for them as well.
track.staticRepresentation() #that generates the calfbox data as a side effect. discard the other data.
#because we do a simplicifaction in _updateGraphTrackCC that calls all tracks for one CC (for content links) we need to build the structure first, only during file load.
for trId in session.data.listOfTrackIds():
callbacks._graphCCTracksChanged(trId) #create structure: all CC graphs, accessed by CC number (0-127)
for trId in session.data.listOfTrackIds():
callbacks._updateTrack(trId) #create content: music items
callbacks._updateTempoTrack() # does everything at once
callbacks._graphCCTracksChanged(trId) #create structure: all CC graphs, accessed by CC number (0-127)
#done above. Special situation at file load. callbacks._graphCCTracksChanged(trId) #create structure: all CC graphs, accessed by CC number (0-127)
for cc in session.data.trackById(trId).listOfCCsInThisTrack():
callbacks._updateGraphTrackCC(trId, cc) #create content: CC points. user points and interpolated points.
for track in session.data.hiddenTracks: #After loading a file some tracks could be hidden. We want midi for them as well.
track.staticRepresentation() #that generates the calfbox data as a side effect. discard the other data.
setMetronome(session.data.currentMetronomeTrack.asMetronomeData, label=session.data.currentMetronomeTrack.name) #track.asMetronomeData is generated in staticRepresentation #template api. has callbacks
callbacks._setCursor()
@ -1805,14 +1812,35 @@ def getListOfGraphInterpolationTypesAsStrings():
return ["linear", "standalone"]
#CC user points
#TODO: no undo here for various reasons. Many functions auto-delete hidden or overlapping cc-graph items with no way of currently remembering them.
#history.py say "Better no history than a wrong history." so we clear it.
#Control Change Subtracks
def _lazyCCUndoRedo(trId:int, cc:int, data, description:str): #For items and blocks, not for tracks.
"""
Lazy Undo for CCs deals with one CC in one Track at a time. No blocks are involved.
The CC tracks are not full of data. We can afford it to use the save/load system to
store complete states instead of incremental changes.
"""
track = session.data.trackById(trId)
oldData = track.ccGraphTracks[cc].serialize()
track.ccGraphTracks[cc] = GraphTrackCC.instanceFromSerializedData(data, parentTrack=track)
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._historyChanged()
callbacks._updateGraphTrackCC(trId, cc)
def _CCUndoCreater(trId:int, cc:int, description:str=""): #For items and blocks, not for tracks.
"""Can be used whenever saving the old state and actually registering undo does not depend
on the success of a function. In other words: if you could write all undo related lines
in a row, use this instead."""
track = session.data.trackById(trId)
oldData = track.ccGraphTracks[cc].serialize()
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._historyChanged()
##CC Tracks
def newGraphTrackCC(trId, cc):
"""add a new CC Path for CC to the given Track.
Do nothing if already existent."""
Do nothing if already existent."""
track = session.data.trackById(trId)
track.newGraphTrackCC(cc)
session.history.clear()
@ -1833,76 +1861,47 @@ def deleteGraphTrackCC(trId, cc):
session.history.clear()
callbacks._graphCCTracksChanged(trId) #this should delete any GUI cc track
def addGraphItem(blockId, positionInTicksRelativeToBlock, newCCValue):
"""blockId includes the track as well as the CC"""
graphItem = GraphItem(newCCValue)
_addExistingGraphItem(blockId, positionInTicksRelativeToBlock, graphItem)
def _addExistingGraphItem(blockId, positionInTicksRelativeToBlock, graphItem):
"""blockId includes the track as well as the CC"""
trId, cc, graphBlock = session.data.graphBlockById(blockId)
session.history.clear()
graphBlock.insert(graphItem, positionInTicksRelativeToBlock)
callbacks._updateGraphTrackCC(trId, cc)
def removeGraphItem(graphItemId):
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
tickPositionRelativeToBlockStart = graphBlock.find(graphItem)
graphBlock.remove(tickPositionRelativeToBlockStart)
session.history.clear()
callbacks._updateGraphTrackCC(trId, cc)
def changeGraphItem(graphItemId, moveInTicks, newCCValue):
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
session.history.clear()
currentTickPositionRelativeToBlock = graphBlock.find(graphItem)
graphBlock.move(currentTickPositionRelativeToBlock, currentTickPositionRelativeToBlock + moveInTicks)
graphItem.ccStart = newCCValue
callbacks._updateGraphTrackCC(trId, cc)
def changeGraphItemInterpolation(graphItemId, graphType):
"""graphType is "linear" or "standalone" """
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
session.history.clear()
graphItem.graphType = graphType
callbacks._updateGraphTrackCC(trId, cc)
## CC Blocks
def changeGraphBlockDuration(graphBlockId, newDurationInTicks):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
session.history.clear()
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
_CCUndoCreater(trId, cc, "CC Block Duration")
graphBlock.duration = newDurationInTicks
callbacks._updateGraphTrackCC(trId, cc)
def appendGraphBlock(trId, cc):
"""Append a small duration block to the current cc track"""
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
newBlock = ccTrack.appendGraphBlock()
session.history.clear()
_CCUndoCreater(trId, cc, "Append CC Block")
newBlock = ccTrack.appendGraphBlock()
callbacks._updateGraphTrackCC(trId, cc)
def rearrangeCCBlocks(trId, cc, listOfBlockIds):
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
session.history.clear()
_CCUndoCreater(trId, cc, "Move CC Block")
ccTrack.rearrangeBlocks(listOfBlockIds)
callbacks._updateGraphTrackCC(trId, cc)
def moveCCBlockToOtherTrack(graphBlockId, newTrackId, listOfBlockIdsForNewTrack):
"""Modified copy of api.moveBlockToOtherTrack.
This only moves to the same CC value. not from volume to modwheel."""
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
oldccTrack = session.data.trackById(trId).ccGraphTracks[cc]
newGraphTrack = session.data.trackById(newTrackId).ccGraphTracks[cc] #we assume this track has this CC already activated.
if len(oldccTrack.blocks) == 1:
return False #it is not possible to move the last block.
return False #it is not possible to move the only block.
with session.history.sequence("Move CC to other track"):
_CCUndoCreater(trId, cc, "MoveToOther Old")
_CCUndoCreater(newTrackId, cc, "MoveToOther New")
callbacks._historyChanged()
newGraphTrack.appendExistingGraphBlock(graphBlock)
newGraphTrack.rearrangeBlocks(listOfBlockIdsForNewTrack)
oldccTrack.deleteBlock(graphBlock) #It is important that we delete the block at exactly this point in time, not ealier. Otherwise the reference for undo will go away.
graphBlock.parentGraphTrack = newGraphTrack
session.history.clear()
callbacks._updateGraphTrackCC(trId, cc)
callbacks._updateGraphTrackCC(newTrackId, cc)
@ -1978,15 +1977,63 @@ def mergeWithNextGraphBlock(graphBlockId):
session.history.clear() #TODO: merging deletes hidden items in the first block without remembering them for undo
callbacks._updateGraphTrackCC(trId, cc)
##CC Blocks and User Points
def addGraphItem(blockId, positionInTicksRelativeToBlock, newCCValue):
"""blockId includes the track as well as the CC"""
trId, cc, graphBlock = session.data.graphBlockById(blockId)
graphItem = GraphItem(newCCValue)
_addExistingGraphItem(blockId, positionInTicksRelativeToBlock, graphItem)
def _addExistingGraphItem(blockId, positionInTicksRelativeToBlock, graphItem):
"""blockId includes the track as well as the CC"""
trId, cc, graphBlock = session.data.graphBlockById(blockId)
_CCUndoCreater(trId, cc, "Add CC Point")
graphBlock.insert(graphItem, positionInTicksRelativeToBlock)
callbacks._updateGraphTrackCC(trId, cc)
def removeGraphItem(graphItemId):
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
_CCUndoCreater(trId, cc, "Remove CC Point")
tickPositionRelativeToBlockStart = graphBlock.find(graphItem)
graphBlock.remove(tickPositionRelativeToBlockStart)
callbacks._updateGraphTrackCC(trId, cc)
def changeGraphItem(graphItemId, moveInTicks, newCCValue):
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
_CCUndoCreater(trId, cc, "Change CC Point")
currentTickPositionRelativeToBlock = graphBlock.find(graphItem)
graphBlock.move(currentTickPositionRelativeToBlock, currentTickPositionRelativeToBlock + moveInTicks)
graphItem.ccStart = newCCValue
callbacks._updateGraphTrackCC(trId, cc)
def changeGraphItemInterpolation(graphItemId, graphType):
"""graphType is "linear" or "standalone" """
trId, cc, graphBlock, graphItem = session.data.graphItemById(graphItemId)
_CCUndoCreater(trId, cc, "CC Point Interpolation")
graphItem.graphType = graphType
callbacks._updateGraphTrackCC(trId, cc)
#Tempo Track
def _changeTempoTrackBlockAndItemOrder(listWithBlocksData):
"""A helper function for that makes it possible to undo/redo properly. It registers
itself with complementary data as undo/redo."""
orderBeforeInsert = session.data.tempoTrack.putBlockAndItemOrder(listWithBlocksData)
session.history.register(lambda o=orderBeforeInsert: _changeTempoTrackBlockAndItemOrder(o), descriptionString="Tempo Change")
def _lazyTempoTrackUndoRedo(new, description:str):
"""The tempo track is not full of data. We can afford it to use the save/load system to
store complete states instead of incremental changes."""
old = session.data.tempoTrack.serialize()
session.data.tempoTrack = TempoTrack.instanceFromSerializedData(new, parentData=session.data)
session.history.register(lambda d=old: _lazyTempoTrackUndoRedo(d, description), descriptionString=description)
callbacks._historyChanged()
callbacks._updateTempoTrack()
callbacks._historyChanged()
def _tempoTrackUndoCreater(description:str):
"""Can be used whenever saving the old state and actually registering undo does not depend
on the success of a function. In other words: if you could write all undo related lines
in a row, use this instead."""
old = session.data.tempoTrack.serialize()
session.history.register(lambda d=old: _lazyTempoTrackUndoRedo(d, description), descriptionString=description)
callbacks._historyChanged()
def addTempoItem(blockId, positionInTicksRelativeToBlock, unitsPerMinute, referenceTicks, graphType = "standalone"):
"""blockId includes the track as well as the CC"""
@ -1996,23 +2043,18 @@ def addTempoItem(blockId, positionInTicksRelativeToBlock, unitsPerMinute, refere
return tempoItem
def _addExistingTempoItem(blockId, positionInTicksRelativeToBlock, tempoItem):
orderBeforeInsert = session.data.tempoTrack.getBlockAndItemOrder()
_tempoTrackUndoCreater("Add Tempo Change")
tempoBlock = session.data.tempoTrack.tempoBlockById(blockId)
tempoBlock.insert(tempoItem, positionInTicksRelativeToBlock)
session.history.register(lambda o=orderBeforeInsert: _changeTempoTrackBlockAndItemOrder(o), descriptionString="Tempo Change")
callbacks._updateTempoTrack()
callbacks._historyChanged()
tempoBlock.insert(tempoItem, positionInTicksRelativeToBlock)
callbacks._updateTempoTrack()
def removeTempoItem(tempoItemId):
orderBeforeInsert = session.data.tempoTrack.getBlockAndItemOrder()
_tempoTrackUndoCreater("Delete Tempo Change")
tempoBlock, tempoItem = session.data.tempoTrack.tempoItemById(tempoItemId)
tickPositionRelativeToBlockStart = tempoBlock.find(tempoItem)
tempoBlock.remove(tickPositionRelativeToBlockStart)
session.history.register(lambda o=orderBeforeInsert: _changeTempoTrackBlockAndItemOrder(o), descriptionString="Delete Tempo Change")
tempoBlock.remove(tickPositionRelativeToBlockStart)
callbacks._updateTempoTrack()
callbacks._historyChanged()
def moveTempoItem(tempoItemId, tickPositionAbsolute):
"""Figures out the target block automatically"""
@ -2028,122 +2070,80 @@ def removeCurrentTempoItem():
removeTempoItem(id(tempoItem)) #undo and callback
def changeTempoBlockDuration(tempoBlockId, newDurationInTicks):
"""Can do circular undo/redo"""
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
oldDuration = tempoBlock.duration
session.history.register(lambda i=tempoBlockId, o=oldDuration: changeTempoBlockDuration(i, o), descriptionString="Tempo Block Duration")
callbacks._historyChanged()
_tempoTrackUndoCreater("Tempo Block Duration")
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
tempoBlock.duration = newDurationInTicks
callbacks._updateTempoTrack()
def rearrangeTempoBlocks(listOfBlockIds):
"""Can do circular undo/redo. Is used by appendTempoBlock and others"""
oldOrder = session.data.tempoTrack.asListOfBlockIds()
session.history.register(lambda o=oldOrder: rearrangeTempoBlocks(o), descriptionString="Move Tempo Block")
callbacks._historyChanged()
_tempoTrackUndoCreater("Move Tempo Block")
session.data.tempoTrack.rearrangeBlocks(listOfBlockIds)
callbacks._updateTempoTrack()
def appendTempoBlock():
oldOrder = session.data.tempoTrack.asListOfBlockIds()
session.history.register(lambda o=oldOrder: rearrangeTempoBlocks(o), descriptionString="Append Tempo Block")
callbacks._historyChanged()
_tempoTrackUndoCreater("Append Tempo Block")
newBlock = session.data.tempoTrack.appendTempoBlock()
callbacks._updateTempoTrack()
def duplicateTempoBlock(tempoBlockId, times = 1):
oldOrder = session.data.tempoTrack.asListOfBlockIds()
session.history.register(lambda o=oldOrder: rearrangeTempoBlocks(o), descriptionString="Duplicate Tempo Block")
callbacks._historyChanged()
_tempoTrackUndoCreater("Duplicate Tempo Block")
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
for i in range(times):
session.data.tempoTrack.duplicateBlock(tempoBlock)
callbacks._updateTempoTrack()
def duplicateContentLinkTempoBlock(tempoBlockId, times = 1):
"""This is also create content link"""
oldOrder = session.data.tempoTrack.asListOfBlockIds()
session.history.register(lambda o=oldOrder: rearrangeTempoBlocks(o), descriptionString="Duplicate Content Link Tempo Block")
callbacks._historyChanged()
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
"""This is also create content link"""
_tempoTrackUndoCreater("Content Link Tempo Block")
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
for i in range(times):
session.data.tempoTrack.duplicateContentLinkBlock(tempoBlock)
callbacks._updateTempoTrack()
def changeTempoBlock(tempoBlockId, newParametersDict):
"""Can do circular undo/redo"""
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
oldParameters = tempoBlock.getDataAsDict()
session.history.register(lambda i=tempoBlockId, o=oldParameters: changeTempoBlock(i, o), descriptionString="Change Tempo Block")
callbacks._historyChanged()
_tempoTrackUndoCreater("Change Tempo Block")
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
tempoBlock.putDataFromDict(newParametersDict)
callbacks._updateTempoTrack()
def unlinkTempoBlock(tempoBlockId):
def unlinkTempoBlock(tempoBlockId):
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
if len(tempoBlock.linkedContentBlocks) == 1:
return #This is not a content link block
newData, newLinkedContentBlocks, newDuration = tempoBlock.getUnlinkedData() #does not set itself, just returns
_exchangeTempoBlockData(tempoBlock, newData, newLinkedContentBlocks, newDuration) #handles undo and callbacks
def _exchangeTempoBlockData(tempoBlock, newData, newLinkedContentBlocks, newDuration):
oldData = tempoBlock.data.copy() #shallow copy. otherwise we can't mix unlink with putBlockAndItemOrder
oldLinkedContentBlocks = tempoBlock.linkedContentBlocks
oldDuration = tempoBlock._duration[:] #shallow copy.
_tempoTrackUndoCreater("Unlink Tempo Block")
undofunction = lambda: _exchangeTempoBlockData(tempoBlock, oldData, oldLinkedContentBlocks, oldDuration) #flipped around
session.history.register(undofunction, descriptionString="Unlink Tempo Block")
newData, newLinkedContentBlocks, newDuration = tempoBlock.getUnlinkedData() #does not set itself, just returns
tempoBlock.data = newData
tempoBlock.linkedContentBlocks.remove(tempoBlock) #the block still exists, so WeakRef will not remove it.
tempoBlock.linkedContentBlocks = newLinkedContentBlocks
tempoBlock._duration = newDuration #mutable list of length 1
for bl in tempoBlock.linkedContentBlocks:
bl.data = newData
bl.linkedContentBlocks = newLinkedContentBlocks
bl._duration = newDuration #mutable list of length 1
callbacks._historyChanged()
callbacks._updateTempoTrack()
def _lazyTempoTrackUndoRedo(new):
old = session.data.tempoTrack.serialize()
session.data.tempoTrack = TempoTrack.instanceFromSerializedData(new, parentData=session.data)
session.history.register(lambda d=old: _lazyTempoTrackUndoRedo(d), descriptionString="Change Tempo Track")
callbacks._historyChanged()
callbacks._updateTempoTrack()
def splitTempoBlock(tempoBlockId, positionInTicksRelativeToBlock:int):
"""tick position is relative to block start"""
old = session.data.tempoTrack.serialize()
success = session.data.tempoTrack.splitTempoBlock(tempoBlockId, positionInTicksRelativeToBlock)
if success:
session.history.register(lambda d=old: _lazyTempoTrackUndoRedo(d), descriptionString="Split Tempo Block")
session.history.register(lambda d=old, desc="Split Tempo Block": _lazyTempoTrackUndoRedo(d, desc), descriptionString="Split Tempo Block")
callbacks._updateTempoTrack()
def mergeWithNextTempoBlock(tempoBlockId):
old = session.data.tempoTrack.serialize()
positionForSplit = session.data.tempoTrack.mergeWithNextTempoBlock(tempoBlockId)
if positionForSplit:
session.history.register(lambda d=old: _lazyTempoTrackUndoRedo(d), descriptionString="Join Tempo Block")
session.history.register(lambda d=old, desc="Join Tempo Block": _lazyTempoTrackUndoRedo(d, desc), descriptionString="Join Tempo Block")
callbacks._historyChanged()
callbacks._updateTempoTrack()
def deleteTempoBlock(tempoBlockId):
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
oldLayout = session.data.tempoTrack.asListOfBlockIds()
old = session.data.tempoTrack.serialize()
tempoBlock = session.data.tempoTrack.tempoBlockById(tempoBlockId)
deletedBlock = session.data.tempoTrack.deleteBlock(tempoBlock)
if deletedBlock:
session.history.clear()
session.history.register(lambda d=old, desc="Delete Tempo Block": _lazyTempoTrackUndoRedo(d, desc), descriptionString="Delete Tempo Block")
callbacks._updateTempoTrack()
def insertTempoItemAtAbsolutePosition(tickPositionAbsolute, unitsPerMinute, referenceTicks, graphType):
@ -2177,9 +2177,7 @@ def insertTempoChangeDuringDuration(percentageUnitsPerMinuteAsFloat):
#originalUnitsPerMinute, originalReferenceTicks = session.data.tempoTrack.tempoAtTickPosition(startTick)
newUnitsPerMinute = tempoItem.unitsPerMinute * percentageUnitsPerMinuteAsFloat
insertTempoItemAtAbsolutePosition(startTick, newUnitsPerMinute, tempoItem.referenceTicks, graphType = "standalone")
insertTempoItemAtAbsolutePosition(endTick, tempoItem.unitsPerMinute, tempoItem.referenceTicks, graphType = "standalone")
session.history.clear() #TODO: This registers two times undo
insertTempoItemAtAbsolutePosition(endTick, tempoItem.unitsPerMinute, tempoItem.referenceTicks, graphType = "standalone")
def currentTempoScalingFactor():
return session.data.tempoTrack.factor

4
engine/ccsubtrack.py

@ -376,13 +376,13 @@ class GraphTrackCC(object):
self.parentTrack = parentTrack
@classmethod
def instanceFromSerializedData(cls, serializedObject, parentObject):
def instanceFromSerializedData(cls, serializedObject, parentTrack):
"""see Score.instanceFromSerializedData"""
assert cls.__name__ == serializedObject["class"]
self = cls.__new__(cls)
self.cc = int(serializedObject["cc"])
self.blocks = [GraphBlock.instanceFromSerializedData(block, parentObject = self) for block in serializedObject["blocks"]]
self._secondInit(parentTrack = parentObject)
self._secondInit(parentTrack = parentTrack)
return self
def serialize(self):

43
engine/tempotrack.py

@ -349,7 +349,7 @@ class TempoTrack(GraphTrackCC):
Hidden items (after the current duration value) of the original block
will be deleted. Hidden items of the follow-up block will be merged, but stay hidden.
"""
self.cleanBlockEdges() #only one tempoItem per tick position, even if they are in different blocks. #TODO: remember this when creating undo for merge!
self.cleanBlockEdges() #only one tempoItem per tick position, even if they are in different blocks.
block = self.tempoBlockById(tempoBlockId)
@ -670,46 +670,7 @@ class TempoTrack(GraphTrackCC):
result["minimumAbsoluteTempoValue"] = mini
result["maximumAbsoluteTempoValue"] = maxi
return result
def getBlockAndItemOrder(self)->list:
"""see main.getBlockAndItemOrder
We save the blocks data, not the blocks. Later in putBlockAndItemOrder we put the
data into the .data attribute.
This is primarily meant for undo, both functions assume that they restore only one unit of
change distance. In particular this function will not restore block duration/size changes.
Items get values only on creation. That means an edit will remove the old one and replace
with a new one. If we therefore create a shallow copy of the block dicts we can always
restore the deleted or modified states.
"""
result = []
seen = {} # originalDataId : shallow copy. Takes care of content linked blocks data.
for block in self.blocks:
blockDataId = id(block.data)
if not blockDataId in seen:
seen[blockDataId] = block.data.copy() #shallow dict copy. copied dict has different id than original.
result.append(seen[blockDataId])
return result
def putBlockAndItemOrder(self, listWithBlocksData:list):
"""opposite of getBlockAndItemOrder. See there"""
"""see getBlockAndItemOrder for documentation.
Returns its own undo data"""
assert len(self.blocks) == len(listWithBlocksData)
orderBefore = self.getBlockAndItemOrder()
for block, parameterBlockData in zip(self.blocks, listWithBlocksData):
block.data = parameterBlockData
return orderBefore
def lilypond(self):
"""Based on the static export"""

2
engine/track.py

@ -859,7 +859,7 @@ class Track(object):
self.initialMidiProgram = serializedData["initialMidiProgram"]
self.initialMidiBankMsb = serializedData["initialMidiBankMsb"]
self.initialMidiBankLsb = serializedData["initialMidiBankLsb"]
self.ccGraphTracks = {int(ccNumber):GraphTrackCC.instanceFromSerializedData(graphTrackCC, parentObject = self) for ccNumber, graphTrackCC in serializedData["ccGraphTracks"].items()}
self.ccGraphTracks = {int(ccNumber):GraphTrackCC.instanceFromSerializedData(graphTrackCC, parentTrack = self) for ccNumber, graphTrackCC in serializedData["ccGraphTracks"].items()}
self.ccChannels = serializedData["ccChannels"]
self.midiTranspose = serializedData["midiTranspose"]
self.initialInstrumentName = serializedData["initialInstrumentName"]

11
qtgui/structures.py

@ -610,9 +610,11 @@ class GuiScore(QtWidgets.QGraphicsScene):
#hidden track.
def updateGraphTrackCC(self, trackId, ccNumber, staticRepresentationList):
"""TrackId is a real notation track which has a dict of CCs"""
"""TrackId is a real notation track which has a dict of CCs"""
if trackId in self.tracks:
self.tracks[trackId].ccPaths[ccNumber].createGraphicItemsFromData(staticRepresentationList)
track = self.tracks[trackId]
ccPath = track.ccPaths[ccNumber]
ccPath.createGraphicItemsFromData(staticRepresentationList)
def updateGraphBlockTrack(self, trackId, ccNumber, staticRepresentationList):
"""TrackId is a real notation track which has a dict of CCs"""
@ -632,7 +634,6 @@ class GuiScore(QtWidgets.QGraphicsScene):
only here.
"""
#TODO: and moved from one CC value to another?
guiCCs = set(self.tracks[trackId].ccPaths.keys())
for backendCC in listOfCCsInThisTrack:
@ -650,7 +651,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
for cc in guiCCs:
self.removeWhenIdle(self.tracks[trackId].ccPaths[cc])
del self.tracks[trackId].ccPaths[cc]
def redraw(self, listOfStaticTrackRepresentations):
"""The order of guiTracks depends on the backend index.
@ -796,7 +797,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
When in blockmode pressing the middle button combined with either Alt or Shift moves tracks and blocks.
"""
if event.button() == 4 and self.parentView.mode() == "block": # Middle Button
if event.button() == 4 and self.parentView.mode() in ("block", "cc"): # Middle Button
modifiers = QtWidgets.QApplication.keyboardModifiers()
if modifiers == QtCore.Qt.ShiftModifier: #block move
block = self.blockAt(event.scenePos())

2
template

@ -1 +1 @@
Subproject commit c9775716ea50d062415edc81cfee71911f67b3ef
Subproject commit 193735f51b252be93df0bcee450164ce44c354b9
Loading…
Cancel
Save