#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2020 , Nils Hilbricht , Germany ( https : / / www . hilbricht . net )
This file is part of the Laborejo Software Suite ( https : / / www . laborejo . org ) ,
Laborejo2 is free software : you can redistribute it and / or modify
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 " )
#Standard Library
from math import log
#Third party
from PyQt5 import QtCore , QtGui , QtWidgets
translate = QtCore . QCoreApplication . translate
#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
from . constantsAndConfigs import constantsAndConfigs
from . submenus import BlockPropertiesEdit
cosmeticPen = QtGui . QPen ( )
cosmeticPen . setCosmetic ( True )
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 with shift + middle mouse button .
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 )
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 )
self . parent = parent #GuiTrack instance
self . color = None #inserted by the creating function in GuiTrack
self . trans = QtGui . QColor ( " transparent " )
self . setPen ( self . trans )
self . setBrush ( self . trans )
self . setOpacity ( 0.4 ) #slightly fuller than background
self . setFlag ( QtWidgets . QGraphicsItem . ItemIgnoresParentOpacity )
self . setParentItem ( parent )
self . setZValue ( 10 ) #This is the z value within GuiTrack
self . staticExportItem = staticExportItem
self . posBeforeMove = None
self . cursorPosOnMoveStart = None
#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 )
"""
if self . staticExportItem [ " completeDuration " ] > = api . D1 : #cosmetics
self . startLabel = QtWidgets . QGraphicsSimpleTextItem ( self . staticExportItem [ " name " ] + translate ( " musicstructures " , " start " ) )
self . startLabel . setParentItem ( self )
self . startLabel . setPos ( 0 , constantsAndConfigs . stafflineGap )
self . startLabel . setFlags ( QtWidgets . QGraphicsItem . ItemIgnoresParentOpacity )
self . endLabel = QtWidgets . QGraphicsSimpleTextItem ( self . staticExportItem [ " name " ] + translate ( " musicstructures " , " end " ) )
self . endLabel . setParentItem ( self )
self . endLabel . setPos ( self . rect ( ) . width ( ) - self . endLabel . boundingRect ( ) . width ( ) , constantsAndConfigs . stafflineGap )
self . endLabel . setFlags ( QtWidgets . QGraphicsItem . ItemIgnoresParentOpacity )
else :
self . startLabel = QtWidgets . QGraphicsSimpleTextItem ( " " )
self . endLabel = QtWidgets . QGraphicsSimpleTextItem ( " " )
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 ( )
def mouseMoveEventCustom ( self , event ) :
# All the positions below don't work. They work fine when dragging Tracks around but not this Item. I can't be bothered to figure out why.
#scenePos() results ins an item position that is translated down and right. The higher the x/y value the more the offset
#Instead we calculate our delta ourselves.
#self.setPos(self.mapToItem(self, event.scenePos()))
#self.setPos(self.mapFromScene(event.scenePos()))
#posGlobal = QtGui.QCursor.pos()
#posView = self.parent.parentScore.parentView.mapFromGlobal(posGlobal) #a widget
#posScene = self.parent.parentScore.parentView.mapToScene(posView)
#print (posGlobal, posView, posScene, event.scenePos())
"""
#Does not work with zooming.
if self . cursorPosOnMoveStart :
delta = QtGui . QCursor . pos ( ) - self . cursorPosOnMoveStart
new = self . posBeforeMove + delta
self . setPos ( new )
"""
if self . cursorPosOnMoveStart :
self . setPos ( event . scenePos ( ) )
super ( ) . mouseMoveEvent ( event )
def mousePressEventCustom ( self , event ) :
self . posBeforeMove = self . pos ( )
self . cursorPosOnMoveStart = QtGui . QCursor . pos ( )
self . setBrush ( self . color )
super ( ) . mousePressEvent ( event )
def mouseReleaseEventCustom ( self , event ) :
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
super ( ) . mouseReleaseEvent ( event )
def contextMenuEvent ( self , event ) :
if self . startLabel . isVisible ( ) :
listOfLabelsAndFunctions = [
( translate ( " musicstructures " , " edit properties " ) , lambda : BlockPropertiesEdit ( self . scene ( ) . parentView . mainWindow , staticExportItem = self . staticExportItem ) ) ,
( " separator " , None ) ,
#("split here", lambda: self.splitHere(event)),
( translate ( " musicstructures " , " duplicate " ) , lambda : api . duplicateBlock ( 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 " , " join with next block " ) , lambda : api . joinBlockWithNext ( self . staticExportItem [ " id " ] ) ) ,
( translate ( " musicstructures " , " delete block " ) , lambda : api . deleteBlock ( self . staticExportItem [ " id " ] ) ) ,
( " separator " , None ) ,
( translate ( " musicstructures " , " append block at the end " ) , lambda : api . appendBlock ( self . parent . staticExportItem [ " id " ] ) ) ,
]
callContextMenu ( listOfLabelsAndFunctions )
else :
super ( ) . contextMenuEvent ( event )
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 )
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 . nameGraphic = self . NameGraphic ( self . staticExportItem [ " name " ] , parent = self )
self . staticItems . append ( self . nameGraphic )
#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 " ) )
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.
class NameGraphic ( QtWidgets . QGraphicsSimpleTextItem ) :
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
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 ) , ]
callContextMenu ( listOfLabelsAndFunctions )
event . accept ( )
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
def toggleNoteheadsRectangles ( self ) :
for item in self . items :
item . updateVisibility ( )
for beam in self . beams :
beam . rectangle . setVisible ( constantsAndConfigs . noteHeadMode )
def redraw ( self , staticRepresentationList ) :
self . createGraphicItemsFromData ( staticRepresentationList )
self . nameGraphic . setPos ( 30 + self . lengthInPixel , - 1 * constantsAndConfigs . stafflineGap ) #self.lengthInPixel is now up to date
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 " ] )
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 + 4 * constantsAndConfigs . stafflineGap ) #x, y, w, h
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 , 0 ) )
line . setParentItem ( self )
line . setPen ( cosmeticPen )
self . staffLines . append ( line )
line . setPos ( 0 , yOffset * constantsAndConfigs . stafflineGap )
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]
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 ( 0 , 0 , 0 , h ) )
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 ) :
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 strechting and for the offset position and wrap it in an item group that is used for positioing.
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 )
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 ) :
""" Handling all items as individuals when deleting a track to redraw it is too much.
Better let Qt handle it all at once . """
def __init__ ( self , parent ) :
super ( ) . __init__ ( )
self . parent = parent
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 ( )
for staticItem in staticRepresentationList :
item = staticItem2Item ( staticItem )
#item.setParentItem(self.anchor)
self . anchor . addToGroup ( item )
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 . 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 . 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 ( )
elif nameAsString == " block " :
self . setOpacity ( 0 )
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 ( )