You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
30 KiB

5 years ago
#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2022, Nils Hilbricht, Germany ( https://www.hilbricht.net )
5 years ago
5 years ago
This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),
5 years ago
5 years ago
Laborejo2 is free software: you can redistribute it and/or modify
5 years ago
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
import logging; logger = logging.getLogger(__name__); logger.info("import")
5 years ago
#Standard Library
5 years ago
from math import log
#Third party
5 years ago
from PyQt5 import QtCore, QtGui, QtWidgets
translate = QtCore.QCoreApplication.translate
5 years ago
#Template
from template.qtgui.helper import stringToColor
from template.qtgui.helper import stretchLine, stretchRect, callContextMenu, removeInstancesFromScene
#Our own files
import engine.api as api
from . import graphs
from .items import staticItem2Item, GuiTieCurveGraphicsItem, GuiClef, GuiKeySignature, GuiMetricalInstruction
5 years ago
from .constantsAndConfigs import constantsAndConfigs
from .submenus import BlockPropertiesEdit
5 years ago
cosmeticPen = QtGui.QPen()
cosmeticPen.setCosmetic(True)
sceneXOffsetForInitialSigs = -75
5 years ago
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.
This is the transparent Block handle that appears when the user uses the mouse to drag and drop
a block.
5 years ago
It is visible all the time though and can be clicked on. In opposite to the background color,
which is just a color and stays in place.
"""
def __init__(self, parent, staticExportItem, x, y, w, h):
super().__init__(x, y, w, h) #x and y are coordinates relative to its parent block. Y will always be a fixed value. At the moment of writing -14.
#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)
5 years ago
self.parent = parent #GuiTrack instance
3 years ago
self.parentGuiTrack = parent #redundant, but specifically for block movement. see ScoreScene
5 years ago
self.trans = QtGui.QColor("transparent")
self.setPen(self.trans) #activate to show outline.
5 years ago
self.setBrush(self.trans)
#self.setOpacity(0.4) #slightly fuller than background
#self.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity, True)
#self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
5 years ago
self.setParentItem(parent)
#self.setZValue(10) #This is the z value within GuiTrack
5 years ago
self.staticExportItem = staticExportItem
self._color = None # @property
5 years ago
self.posBeforeMove = None
self.cursorPosOnMoveStart = None
self.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
5 years ago
#Display Block ID
"""
self.idText = QtWidgets.QGraphicsSimpleTextItem(str(self.staticExportItem["id"]))
self.idText.setParentItem(self)
self.idText.setPos(0, constantsAndConfigs.stafflineGap)
#self.idText.setScale(4)
self.idText.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
"""
self.startLabel = QtWidgets.QGraphicsSimpleTextItem(self.staticExportItem["name"])
self.endLabel = QtWidgets.QGraphicsSimpleTextItem(self.staticExportItem["name"] + translate("musicstructures", " end "))
5 years ago
self.startLabel.setParentItem(self)
self.startLabel.setPos(0, constantsAndConfigs.stafflineGap)
self.startLabel.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
#self.startLabel.setZValue(self.zValue()+1) this doesn't matter because our value is relative to self, the rectangle. And selfs value is relative to the GuiTracks other children.
self.endLabel.setParentItem(self)
self.endLabel.setPos(self.rect().width() - self.endLabel.boundingRect().width(), constantsAndConfigs.stafflineGap)
self.endLabel.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
@property
def color(self):
assert self._color
return self._color
@color.setter
def color(self, c:QtGui.QColor):
"""QColor inserted by the creating function in GuiTrack.
Only available during mousePressEvent. Used during dragging, then reset to transparent.
This is on every change. Every note."""
self._color = c
if c.lightness() > 127: #between 0 (for black) and 255 (for white)
labelColor = QtGui.QColor("black")
else:
labelColor = QtGui.QColor("white")
self.startLabel.setBrush(labelColor)
self.endLabel.setBrush(labelColor)
5 years ago
def stretchXCoordinates(self, factor):
"""Reposition the items on the X axis.
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates.
Docstring there."""
stretchRect(self, factor)
self.endLabel.setPos(self.rect().width() - self.endLabel.boundingRect().width(), constantsAndConfigs.stafflineGap)
def blockMode(self):
self.startLabel.show()
self.endLabel.show()
def itemMode(self):
self.startLabel.hide()
self.endLabel.hide()
5 years ago
def mousePressEventCustom(self, event):
"""Not a qt-override. This is called directly by GuiScore
if you click on a block"""
5 years ago
self.posBeforeMove = self.pos()
self.cursorPosOnMoveStart = QtGui.QCursor.pos()
self.setBrush(self.color)
self.endLabel.hide()
5 years ago
super().mousePressEvent(event)
#Mouse Move Event, as in dragging the blocks around, in in scorescene.py
#because we need to drag blocks into another track.
5 years ago
def mouseReleaseEventCustom(self, event):
"""Not a qt-override. This is called directly by GuiScore
if you click-release on a block"""
5 years ago
self.setBrush(self.trans)
self.setPos(self.posBeforeMove) #In case the block was moved to a position where no track is (below the tracks) we just reset the graphics.
self.posBeforeMove = None
self.cursorPosOnMoveStart = None
self.endLabel.show()
5 years ago
super().mouseReleaseEvent(event)
def contextMenuEventCustom(self, event):
"""The original context menu was too unreliable. We now call it
directly in Track.
We do not use event parameter. This is for compatibility.
"""
if self.startLabel.isVisible(): #block mode
5 years ago
listOfLabelsAndFunctions = [
(translate("musicstructures", "edit properties"), lambda: BlockPropertiesEdit(self.scene().parentView.mainWindow, staticExportItem = self.staticExportItem)),
5 years ago
("separator", None),
#("split here", lambda: self.splitHere(event)), #Impossible because we can't see notes.
(translate("musicstructures", "duplicate"), lambda: api.duplicateBlock(self.staticExportItem["id"])),
(translate("musicstructures", "duplicate to reserved empty"), lambda: api.duplicateToReservedSpaceBlock(self.staticExportItem["id"])),
(translate("musicstructures", "create content link"), lambda: api.duplicateContentLinkBlock(self.staticExportItem["id"])),
(translate("musicstructures", "unlink"), lambda: api.unlinkBlock(self.staticExportItem["id"])),
("separator", None),
(translate("musicstructures", "move to start"), lambda: api.moveBlockToStartOfTrack(self.staticExportItem["id"])),
(translate("musicstructures", "move to end"), lambda: api.moveBlockToEndOfTrack(self.staticExportItem["id"])),
5 years ago
("separator", None),
(translate("musicstructures", "join with next block"), lambda: api.joinBlockWithNext(self.staticExportItem["id"])),
(translate("musicstructures", "delete block"), lambda: api.deleteBlock(self.staticExportItem["id"])),
5 years ago
("separator", None),
(translate("musicstructures", "append block at the end"), lambda: api.appendBlock(self.parent.staticExportItem["id"])),
5 years ago
]
callContextMenu(listOfLabelsAndFunctions)
class GuiTrack(QtWidgets.QGraphicsItem):
"""In opposite to tracks and block(backgrounds and handles) tracks never get recreated.
Init is called once on creation, not virtually on every update.
However, the track children, its parts, get deleted and recreated very often.
Order of creation:
init
stafflines
second init
update edit mode
-> then api takes over and instructs the following methods through callbacks
Typical order of method calls for updates:
barlines
beams
stafflines
items
redraw
background color
update edit mode (not on every item update, but at least on load)
"""
def __init__(self, parentScore, staticExportItem):
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.setFlag(QtWidgets.QGraphicsItem.ItemContainsChildrenInShape, True) #Do not use! If we activate this a block will not be able to cross over into another track with drag and drop
5 years ago
self.parentScore = parentScore
self.staticExportItem = staticExportItem #This is not the notes but the track meta data. The notes are called staticRepresentationList
self.items = [] #this is used for stretching and processing of the current items. scene clear is done diffently. See self.createGraphicItemsFromData
self.barLines = []
self.beams = []
self.staffLines = []
self.staticItems = [] #not deleted automatically by callbacks.
self.ItemIgnoresTransformations = [] #not literally colors. QRectItems with a color. created in self.paintBlockBackgroundColors()
self.transparentBlockHandles = [] #list of GuiBlockHandle in order. Only appear when the mouse is used to drag and drop.
self.backgroundBlockColors = [] #QGraphicsRectItems. Always visible.
self.lengthInPixel = 0 # a cached value
self.createStaffLines() #no stafflines at all are too confusing.
self.ccPaths = {} # ccNumber0-127:PathItem. Empty for a new track. We only create ccPaths with the first ccBlock. Creation and handling is done in GuiScore, starting with syncCCsToBackend.
self.setFlag(QtWidgets.QGraphicsItem.ItemIsMovable, True)
5 years ago
self.nameGraphic = self.NameGraphic("", parent = self)
5 years ago
self.staticItems.append(self.nameGraphic)
self.nameGraphic.updateName(self.staticExportItem)
self.blockModeNameGraphic = self.NameGraphic("", parent = self) #additional name graphic in block mode that is always visible
self.staticItems.append(self.blockModeNameGraphic)
self.blockModeNameGraphic.updateName(self.staticExportItem)
5 years ago
#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"))
5 years ago
self.universalCreateFirstCCBlock.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity) #toggle between edit modes not only hides stuff but the track itself gets 10% opacity. We want to avoid it for this item.
self.universalCreateFirstCCBlock.mousePressEvent = lambda mouseEvent: api.newGraphTrackCC(trId = self.staticExportItem["id"], cc = constantsAndConfigs.ccViewValue) #trigger callback to self.syncCCsToBackend
self.universalCreateFirstCCBlock.setParentItem(self)
self.universalCreateFirstCCBlock.setPos(0,0) #for now. Afterwards it gets updated by updateBlocks .
self.universalCreateFirstCCBlock.setZValue(10)
#self.secondStageInitNowThatWeHaveAScene gets called by the ScoreScene.redraw(), where new tracks get created. After it was inserted into the scene.
5 years ago
class NameGraphic(QtWidgets.QGraphicsSimpleTextItem):
"""The update callback is in scoreScene.redraw()"""
5 years ago
def __init__(self, text, parent):
super().__init__(text)
self.setFlags(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
self.parent = parent
self.setParentItem(parent)
def _editName(self):
result = QtWidgets.QInputDialog.getText(self.scene().parentView, translate("musicstructures", "Track Name"), #dialog title
translate("musicstructures", "Set Track Name for {}").format(self.parent.staticExportItem["id"]), #label
5 years ago
QtWidgets.QLineEdit.Normal,
self.parent.staticExportItem["name"]
)
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 contextMenuEvent(self, event):
listOfLabelsAndFunctions = [ (translate("musicstructures", "edit name"), self._editName), ]
5 years ago
callContextMenu(listOfLabelsAndFunctions)
event.accept()
def updateName(self, trackExportObject:dict):
self.setText(f"Track {trackExportObject['index']+1} - {trackExportObject['name']}")
5 years ago
def secondStageInitNowThatWeHaveAScene(self):
"""ScoreScene.redraw() calls this after the track was inserted into the scene and therefore
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
5 years ago
def toggleNoteheadsRectangles(self):
for item in self.items:
item.updateVisibility()
for beam in self.beams:
beam.rectangle.setVisible(constantsAndConfigs.noteHeadMode)
def redraw(self, staticRepresentationList):
"""Called very often. For example after each note insert into the track.
But not after a mode change"""
5 years ago
self.createGraphicItemsFromData(staticRepresentationList)
self.nameGraphic.setPos(30 + self.lengthInPixel, -1*constantsAndConfigs.stafflineGap) #self.lengthInPixel is now up to date
def centerBlockModeNameGraphic(self, centerSceneX):
"""Attached to the horizontal scrollbar.
Instead of calculating X in every track we do it once in parentScore.reactToHorizontalScroll()"""
self.blockModeNameGraphic.setPos(centerSceneX, (-5*constantsAndConfigs.stafflineGap)) #y is in track coordinates
5 years ago
def paintBlockBackgroundColors(self, staticBlocksRepresentation):
"""This gets not called by self.createGraphicItemsFromData but only by
a score callback for blocksChanged"""
for bg in self.backgroundBlockColors:
self.parentScore.removeWhenIdle(bg)
for th in self.transparentBlockHandles:
self.parentScore.removeWhenIdle(th)
self.backgroundBlockColors = []
self.transparentBlockHandles = []
for block in staticBlocksRepresentation:
if self.staticExportItem["double"]: #double track, twice as high
h = 10 * constantsAndConfigs.stafflineGap
else:
h = 4 * constantsAndConfigs.stafflineGap
bgItem = QtWidgets.QGraphicsRectItem(0, 0, block["completeDuration"] / constantsAndConfigs.ticksToPixelRatio, h) #x, y, w, h
bgItem.setFlag(QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)
bgItem.setPen(QtGui.QColor("transparent"))
color = stringToColor(block["name"])
5 years ago
bgItem.setBrush(color)
bgItem.setParentItem(self)
self.backgroundBlockColors.append(bgItem)
bgItem.setPos(block["tickindex"] / constantsAndConfigs.ticksToPixelRatio, -2*constantsAndConfigs.stafflineGap)
bgItem.setZValue(-10) #This is the z value within GuiTrack
bgItem.setEnabled(False)
transparentBlockHandle = GuiBlockHandle(self, block, 0, -2 * constantsAndConfigs.stafflineGap, block["completeDuration"] / constantsAndConfigs.ticksToPixelRatio, h-constantsAndConfigs.stafflineGap) #x, y, w, h
5 years ago
transparentBlockHandle.color = color
self.transparentBlockHandles.append(transparentBlockHandle)
transparentBlockHandle.setPos(block["tickindex"] / constantsAndConfigs.ticksToPixelRatio, -2*constantsAndConfigs.stafflineGap)
def createStaffLines(self, lengthInPixel = 0):
"""By default creates 5 stafflines. But it can be 10;
5 extra below the origin-staff.
This is NOT a double-system like a piano but just a staff
with more lines that happens to have the range of e.g.
treble + bass clef."""
def createLine(yOffset):
line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(0, 0, lengthInPixel+abs(sceneXOffsetForInitialSigs), 0))
5 years ago
line.setParentItem(self)
line.setPen(cosmeticPen)
self.staffLines.append(line)
line.setPos(sceneXOffsetForInitialSigs, yOffset*constantsAndConfigs.stafflineGap)
5 years ago
line.setZValue(-5) #This is the z value within GuiTrack
for l in self.staffLines:
self.parentScore.removeWhenIdle(l)
self.staffLines = []
lengthInPixel += 25 #a bonus that gives the hint that you can write after the last object.
for i in range(-2, 3): #the normal 5 line system. We have a lower and upper range/position. The middle line is at position 0
createLine(i)
if self.staticExportItem["double"]: #add more stuffs below (user-perspective. positive Qt values)
for i in range(4, 9): #i is now 3. Make a gap:
createLine(i)
def createBarlines(self, barlinesTickList):
"""and measure numbers"""
for bl in self.barLines:
self.parentScore.removeWhenIdle(bl)
self.barLines = []
if self.staticExportItem["double"]:
h = 10 * constantsAndConfigs.stafflineGap
else:
h = 4 * constantsAndConfigs.stafflineGap
#if barlinesTickList[0] == 0: #happens when there is a metrical instruction at tick 0.
# del barlinesTickList[0]
#Pen for the borders
pen = QtGui.QPen()
pen.setWidth(2)
pen.setColor(QtGui.QColor("black"))
5 years ago
last = None
offset = 0
for barnumber, barlineTick in enumerate(barlinesTickList):
if barlineTick == last:
offset += 1
continue #don't draw the double barline
last = barlineTick
line = QtWidgets.QGraphicsLineItem(QtCore.QLineF(-3, 0, -3, h)) #x1, y1, x2, y2 #give it a -3 x offset because we have a thickness of 2. Also makes block dividers easier to see.
line.setPen(pen)
5 years ago
line.setParentItem(self)
self.barLines.append(line)
line.setPos(barlineTick / constantsAndConfigs.ticksToPixelRatio, -2*constantsAndConfigs.stafflineGap)
number = QtWidgets.QGraphicsSimpleTextItem(str(barnumber+1-offset))
number.setScale(0.75)
number.setParentItem(line)
number.setPos(-2, -3*constantsAndConfigs.stafflineGap) #-2 on X for a little fine tuning.
def createBeams(self, beamList):
"""This creates the beam-rectangle above/below the stems.
The stems theselves are created in items.py"""
5 years ago
for b in self.beams:
self.parentScore.removeWhenIdle(b)
self.beams = []
for startTick, endTick, beamtype, positionAsStaffline, direction in beamList:
numberOfBeams = int(log(beamtype, 2)-2)
assert numberOfBeams == log(beamtype, 2)-2
for x in range(numberOfBeams):
if direction > 0: #stem/beam upwards
beamNumberOffset = 4*x
xOffset = 7
else:
beamNumberOffset = -4*x
xOffset = 1
#non-scalable X-Offsets are not possible via setPos. zoom and stretch go haywire.
#Instead we use one item for streching for the offset position and wrap it in an item group that is used for positioing.
5 years ago
shifterAnchor = QtWidgets.QGraphicsItemGroup()
shifterAnchor.setParentItem(self)
rectangle = QtWidgets.QGraphicsRectItem(0, 0, (endTick-startTick) / constantsAndConfigs.ticksToPixelRatio, constantsAndConfigs.beamHeight) #x, y, w, h
rectangle.setBrush(QtGui.QColor("black"))
shifterAnchor.addToGroup(rectangle)
shifterAnchor.rectangle = rectangle
rectangle.setPos(xOffset, 0)
#We need the beam no matter the note head mode.
5 years ago
if constantsAndConfigs.noteHeadMode:
rectangle.show()
else:
rectangle.hide()
self.beams.append(shifterAnchor)
x = startTick/ constantsAndConfigs.ticksToPixelRatio
y = beamNumberOffset + positionAsStaffline * constantsAndConfigs.stafflineGap / 2 - 1
shifterAnchor.setPos(x, y)
class TrackAnchor(QtWidgets.QGraphicsItemGroup):
5 years ago
"""Handling all items as individuals when deleting a track to redraw it is too much.
Better let Qt handle it all at once."""
5 years ago
def __init__(self, parent):
super().__init__()
self.parent = parent
def createInitialSignatureArea(self, metaDataDict):
"""Assumes an empty track, called only in createGraphicItemsFromData
Initial Signatures, if present. Negative X-axis values.
The stafflines draw themselves to the left by the constant sceneXOffsetForInitialSigs.
In scoreScene.mouseReleaseEvent we check if the mouse click was within our initial area
and call the track Editor."""
xPos = sceneXOffsetForInitialSigs+5
clefSVGItem = GuiClef.clefs[metaDataDict["initialClef"]]() #just the graphics
self.anchor.addToGroup(clefSVGItem)
clefSVGItem.setPos(xPos, GuiClef.cleffYOnStaff[metaDataDict["initialClef"]])
xPos += clefSVGItem.boundingRect().width()
initKeySig = GuiKeySignature(metaDataDict["initialKeySignature"])
self.anchor.addToGroup(initKeySig)
initKeySig.setPos(xPos, 0)
xPos += initKeySig.boundingRect().width()
#The metrical position is just text. We put that on top of the staff and remove the arrow indicator.
if metaDataDict["initialMetricalInstruction"]["oneMeasureInTicks"]:
initMetricalInstruction = GuiMetricalInstruction(metaDataDict["initialMetricalInstruction"], drawMarker=False)
self.anchor.addToGroup(initMetricalInstruction)
initMetricalInstruction.setPos(sceneXOffsetForInitialSigs +10, 0) #+10 because the GuiItem itself has a negative x offset
5 years ago
def createGraphicItemsFromData(self, staticRepresentationList):
"""Create staff objects including simple barlines"""
self.parentScore.cursor.clearItemHighlight() #or else the current highlight gets deleted while it is on an item
try:
self.parentScore.removeWhenIdle(self.anchor)
except AttributeError: #first round
pass
self.items = []
itemsAppend = self.items.append
self.anchor = GuiTrack.TrackAnchor(self)
self.anchor.setParentItem(self)
metaDataDict = staticRepresentationList.pop() #we want the dict, but we also want it separated from the item list. It is, by specs, the last item.
self.createInitialSignatureArea(metaDataDict)
#Real items, positive X-axis values
5 years ago
for staticItem in staticRepresentationList:
item = staticItem2Item(staticItem)
#item.setParentItem(self.anchor)
self.anchor.addToGroup(item)
5 years ago
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
self.lengthInPixel = metaDataDict["duration"] / constantsAndConfigs.ticksToPixelRatio #this gets updated in stretchXCoordinates
self.createBarlines(metaDataDict["barlines"])
self.createBeams(metaDataDict["beams"])
self.createStaffLines(self.lengthInPixel)
def stretchXCoordinates(self, factor):
"""Reposition the items on the X axis.
Call goes through all parents/children, starting from ScoreView._stretchXCoordinates.
Docstring there."""
#The rects and lines here are just a QGraphicsRectItem and have no custom subclass. So they have no stretchXCoordinates itself.
for item in self.items:
item.setX(item.pos().x() * factor)
item.stretchXCoordinates(factor)
for staticItem in self.staticItems: #labels etc.
staticItem.setX(staticItem.pos().x() * factor)
for barline in self.barLines:
barline.setX(barline.pos().x() * factor)
for staffline in self.staffLines: #stafflines start from x=0. 0*factor=0 so we omit setPos
stretchLine(staffline, factor)
for beam in self.beams: #beams are part of the track, not of the note items.
beam.setX(beam.pos().x() * factor )
stretchRect(beam.rectangle, factor)
for backgroundBlockColor in self.backgroundBlockColors:
backgroundBlockColor.setX(backgroundBlockColor.pos().x() * factor)
stretchRect(backgroundBlockColor, factor)
for transparentBlockHandle in self.transparentBlockHandles:
transparentBlockHandle.setX(transparentBlockHandle.pos().x() * factor)
transparentBlockHandle.stretchXCoordinates(factor)
for ccPath in self.ccPaths.values():
ccPath.stretchXCoordinates(factor)
self.lengthInPixel = self.lengthInPixel * factor #this also gets updated in createGraphicItemsFromData
def itemById(self, itemId):
"""The itemId is the same for backend and gui items. In fact, it is really the
backend-python-object id. But we exported it as static value."""
return next(item for item in self.items if item.staticItem["id"] == itemId)
def blockAt(self, xScenePosition):
for th in self.transparentBlockHandles:
start = th.staticExportItem["tickindex"] / constantsAndConfigs.ticksToPixelRatio
end = start + th.staticExportItem["completeDuration"] / constantsAndConfigs.ticksToPixelRatio
if start <= xScenePosition < end:
return th
return None #After the last block.
def updateMode(self, nameAsString):
"""Modes are opacity based, not show and hide.
This gives us the option that children can ignore the opacity (via qt-flag).
For example we need the block backgrounds to stay visible.
"""
assert nameAsString in constantsAndConfigs.availableEditModes
#We always hide all CC paths and reactivate only a specific one in cc mode later.
for ccPath in self.ccPaths.values():
ccPath.hide()
self.universalCreateFirstCCBlock.hide()
if nameAsString == "notation":
self.blockModeNameGraphic.hide()
self.nameGraphic.show()
5 years ago
self.setOpacity(1)
for backgroundColor in self.backgroundBlockColors: #created in self.paintBlockBackgroundColors()
backgroundColor.setOpacity(0.2)
for tbh in self.transparentBlockHandles:
tbh.itemMode()
elif nameAsString == "cc":
self.blockModeNameGraphic.hide()
self.nameGraphic.show()
5 years ago
self.setOpacity(0.1) #the notes can still be seen in the background.
if constantsAndConfigs.ccViewValue in self.ccPaths: #It is not guaranteed that all CC Values have content. On the contrary...
self.ccPaths[constantsAndConfigs.ccViewValue].show()
else:
self.universalCreateFirstCCBlock.show()
for backgroundColor in self.backgroundBlockColors: #created in self.paintBlockBackgroundColors()
backgroundColor.setOpacity(0.2)
for tbh in self.transparentBlockHandles:
tbh.itemMode()
5 years ago
elif nameAsString == "block":
self.blockModeNameGraphic.show()
self.nameGraphic.hide()
self.setOpacity(0) #this hides the notation stuff. The block mode graphics and labels are also children of ours, but they ignore parent opacity.
5 years ago
for backgroundColor in self.backgroundBlockColors: #simple QRectItems, they don't have their own updateMode function
backgroundColor.setOpacity(1)
for tbh in self.transparentBlockHandles:
tbh.blockMode()