|
|
@@ -61,6 +61,7 @@ class Score(Data): |
|
|
|
self.tracks = [] #see docstring |
|
|
|
self.tempoMap = TempoMap(parentData = self) |
|
|
|
self._template_processAfterInit() |
|
|
|
self._tracksFailedLookup = [] |
|
|
|
|
|
|
|
def _template_processAfterInit(self): #needs a different name because there is an inherited class with the same method. |
|
|
|
"""Call this after either init or instanceFromSerializedData""" |
|
|
@@ -119,11 +120,21 @@ class Score(Data): |
|
|
|
except Exception as e: #No Jack Meta Data or Error with ports. |
|
|
|
logger.error(e) |
|
|
|
|
|
|
|
|
|
|
|
def trackById(self, trackId:int): |
|
|
|
"""Returns a track or None, if not found""" |
|
|
|
for track in self.tracks: |
|
|
|
if trackId == id(track): |
|
|
|
return track |
|
|
|
raise ValueError(f"Track {trackId} not found. Current Tracks: {[id(tr) for tr in self.tracks]}") |
|
|
|
else: |
|
|
|
#Previously this crashed with a ValueError. However, after a rare bug that a gui widget focussed out because a track was deleted and then tried to send its value to the engine we realize that this lookup can gracefully return None. |
|
|
|
#Nothing will break: Functions that are not aware yet, that None is an option will crash when they try to access None as a track object. For this case we present the following logger error: |
|
|
|
if not trackId in self._tracksFailedLookup: |
|
|
|
logger.error(f"Track {trackId} not found. Current Tracks: {[id(tr) for tr in self.tracks]}") |
|
|
|
self._tracksFailedLookup.append(trackId) #prevent multiple error messages for the same track in a row. |
|
|
|
return None |
|
|
|
#raise ValueError(f"Track {trackId} not found. Current Tracks: {[id(tr) for tr in self.tracks]}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#Save / Load / Export |