oneRectToReturnThemAll=QtCore.QRectF(0,0,0,0)#prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection.
_zValuesRelativeToConductor={#Only use for objects added directly to the Conductor, not their children.
_zValuesRelativeToConductor={#Only use for objects added directly to the Conductor, not their children.
"line":0,
"startItem":1,
"block":2,
"item":4,
"handle":5,
"startItem":1,
"block":2,
"item":4,
"handle":5,
}
classConductor(QtWidgets.QGraphicsItem):
@ -52,6 +52,8 @@ class Conductor(QtWidgets.QGraphicsItem):
self.staticBlocks=None#Cached Block Data list
self.staticMeta=None#Cached track meta data dict.
self.setFlag(QtWidgets.QGraphicsItem.ItemHasNoContents,True)#only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
super().__init__(-3,-2,3,parent.rect().height())#x, y, w, h
super().__init__(-3,-2,3,parent.rect().height())#x, y, w, h
self.setFlags(QtWidgets.QGraphicsItem.ItemIsMovable|QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable|QtWidgets.QGraphicsItem.ItemIgnoresParentOpacity)#QtWidgets.QGraphicsItem.ItemClipsToShape puts the item behind the parent rect and only receives event inside its own shape.
self.setAcceptHoverEvents(True)
self.setCursor(QtCore.Qt.SizeHorCursor)
self.setZValue(_zValuesRelativeToConductor["handle"])#The handle does not compete with the transparent block but with its contents! Therefore the startItem is set to have a custom lower priority than the handle
#Too irritating. And confuses with handle movement. self.setCursor(QtCore.Qt.SizeHorCursor) #this sets the cursor while the mouse is over the item. It is independent of AcceptHoverEvents
else:
else:
self.setZValue(0)#avoid hovering conflict with block handle
self.ungrabMouse=api.nothing#to surpress a warning from the context menu
self.ungrabMouse=api.nothing#to surpress a warning from the context menu
self.note=QtWidgets.QGraphicsTextItem("")
self.note.setParentItem(self)
@ -465,7 +468,7 @@ class TempoPoint(QtWidgets.QGraphicsItem):
fornin(self.note,self.number,self.arrow):
ifn:n.setDefaultTextColor(QtGui.QColor("black"))
self.wheelEventChangedValue=0#resetted in hoverEnterEvent. But we still need it for new items that appear directly under the mouse cursor
self.wheelEventChangedValue=0#resetted in hoverEnterEvent. But we still need it for new items that appear directly under the mouse cursor
defpaint(self,painter,options,widget=None):
#painter.drawRect(self.boundingRect()) #uncomment to show the bounding rect
@ -491,21 +494,21 @@ class TempoPoint(QtWidgets.QGraphicsItem):
#after delete the item and tempo tracks gets recreated so we need to reactivate the shortcut now. It will work without these two lines, but that is only implicit behaviour.
self.parentTempoTrack.parentView.mainWindow.ui.actionDelete.setEnabled(True)#Delete key collides with our hover-delete.
self.ungrabKeyboard()
api.removeTempoItem(self.staticExportItem["id"])
self.ungrabKeyboard()
api.removeTempoItem(self.staticExportItem["id"])
else:
returnsuper().keyPressEvent(event)
@ -581,15 +583,16 @@ class TimeLine(QtWidgets.QGraphicsItem):
oneRectToReturnThemAll=QtCore.QRectF(0,0,0,0)#prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection.
classPitchCursor(QtWidgets.QGraphicsRectItem):
def__init__(self):
"""Does not need the actual dotOnLine.
@ -42,6 +44,8 @@ class PitchCursor(QtWidgets.QGraphicsRectItem):
oneRectToReturnThemAll=QtCore.QRectF(0,0,0,0)#prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection.
api.changeGraphItem(self.staticExportItem["id"],self.getXDifferenceAsBackendValue(),self.getYAsBackendValue())#send update to the backend, don't wait for callback.
oneRectToReturnThemAll=QtCore.QRectF(0,0,0,0)#prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection.
@ -60,7 +60,7 @@ class MainWindow(TemplateMainWindow):
#Make the first three words matter!
#Do not start them all with "You can..." or "...that you can", in response to the Did you know? title.
#We use injection into the class and not a parameter because this dialog gets shown by creating an object. We can't give the parameters when this is shown via the mainWindow menu.
About.didYouKnow=[
About.didYouKnow=[
translate("About","<p>Most commands work in the appending position (last position in a track) and apply to the item before it.</p><p>Use it to apply dots, sharps and flats on the item you just inserted without moving the cursor back and forth.</p>"),
translate("About","<p>Learn the keyboard shortcuts! Laborejo is designed to work with the keyboard alone and with midi instruments for full speed.</p>Everytime you grab your mouse you loose concentration, precision and time."),
translate("About","<p>Spread/shrink the space between notes with Ctrl+Shift+Mousewheel or Ctrl+Shift with Plus and Minus.</p>"),
@ -74,19 +74,19 @@ class MainWindow(TemplateMainWindow):
translate("About","<p>There is no key-rebinding except numpad-shortcuts.</p>"),
translate("About","<p>Hidden tracks still output sound.</p>"),
translate("About","<p>Non-audible tracks still output instrument changes and CCs so that they can be switched on again in the middle of playback.</p>"),
]+About.didYouKnow
]+About.didYouKnow
super().__init__()
#New menu entries and template-menu overrides
self.menu.addMenuEntry("menuDebug","actionRedrawAllTracks","Redraw all Tracks")
self.menu.connectMenuEntry("actionSave",api.save)
self.menu.hideSubmenu("menuFile")
self.menu.hideSubmenu("menuGeneric")
api.callbacks.setCursor.append(self.updateStatusBar)#returns a dict. This get's called after loading the file so the status bar is filled on self.show
#New menu entries and template-menu overrides
self.menu.addMenuEntry("menuDebug","actionRedrawAllTracks","Redraw all Tracks")
self.menu.connectMenuEntry("actionSave",api.save)
self.menu.hideSubmenu("menuFile")
self.menu.hideSubmenu("menuGeneric")
api.callbacks.setCursor.append(self.updateStatusBar)#returns a dict. This get's called after loading the file so the status bar is filled on self.show
#Create the Main Widgets in the Stacked Widget
self.scoreView=ScoreView(self)
self.ui.mainStackWidget.addWidget(self.scoreView)
@ -99,8 +99,8 @@ class MainWindow(TemplateMainWindow):
self.scoreView.setFocus()#So the user can start typing from moment 0.
self.scoreView.setFocus()#So the user can start typing from moment 0.
self.start()#Inherited from template main window #This shows the GUI, or not, depends on the NSM gui save setting. We need to call that after the menu, otherwise the about dialog will block and then we get new menu entries, which looks strange.
stepMidiInput.start()#imported directly. Handles everything else internally, we just need to start it after the engine somehow. Which is here.
stepMidiInput.start()#imported directly. Handles everything else internally, we just need to start it after the engine somehow. Which is here.
#Populate the left toolbar. The upper toolbar is created in menu.py
#Populate the left toolbar. The upper toolbar is created in menu.py
self.ui.leftToolBar.addWidget(LeftToolBarPrevailingDuration(self))#needs stepmidiinput started
#Now all tracks and items from a loaded backend-file are created. We can setup the initial editMode and viewPort.
#Now all tracks and items from a loaded backend-file are created. We can setup the initial editMode and viewPort.
self.scoreView.updateMode()#hide CCs at program start and other stuff
self.scoreView.scoreScene.grid.redrawTickGrid()#Init the grid only after everything got loaded and drawn to prevent a gap in the display. #TODO: which might be a bug. but this here works fine.
#There is so much going on in the engine, we never reach a save status on load.
#Here is the crowbar-method.
self.nsmClient.announceSaveStatus(isClean=True)
self.nsmClient.announceSaveStatus(isClean=True)
defzoom(self,scaleFactor:float):
"""Scale factor is absolute"""
"""Scale factor is absolute"""
self.scoreView.zoom(scaleFactor)
defstretchXCoordinates(self,factor:float):
self.scoreView.stretchXCoordinates(factor)
defstretchXCoordinates(self,factor:float):
self.scoreView.stretchXCoordinates(factor)
defupdateStatusBar(self,exportCursorDict):
"""Every cursor movement updates the statusBar message"""
c=exportCursorDict
try:
try:
i=c["item"]
except:
print(c)
@ -182,7 +182,7 @@ class LeftToolBarPrevailingDuration(QtWidgets.QLabel):
self.mainWindow.ui.actionInstrument_Change:SecondaryProgramChangeMenu(self.mainWindow),#no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.
self.mainWindow.ui.actionChannel_Change:SecondaryChannelChangeMenu(self.mainWindow),#no lambda for submenus. They get created here once and have a __call__ option that executes them. There is no internal state in these menus.
#Lilypond
#Print and Export is in self.actions
self.mainWindow.ui.actionLyBarline:ChooseOne(self.mainWindow,translate("menu","Choose a Barline"),api.getLilypondBarlineList()),
self.mainWindow.ui.actionLyRepeat:ChooseOne(self.mainWindow,translate("menu","Choose a Repeat"),api.getLilypondRepeatList()),
self.mainWindow.ui.actionLyRepeat:ChooseOne(self.mainWindow,translate("menu","Choose a Repeat"),api.getLilypondRepeatList()),
@ -43,6 +43,8 @@ from .submenus import BlockPropertiesEdit
cosmeticPen=QtGui.QPen()
cosmeticPen.setCosmetic(True)
oneRectToReturnThemAll=QtCore.QRectF(0,0,0,0)#prevent the annoying "NotImplementError" from Qt for boundingRect. For items that don't need any collision detection.
classGuiBlockHandle(QtWidgets.QGraphicsRectItem):
"""A simplified version of a Block. Since we don't use blocks in the GUI, only in the backend
westillneedthemsometimesasmacrostrutures,wherewedon't care about the content.
@ -54,7 +56,7 @@ class GuiBlockHandle(QtWidgets.QGraphicsRectItem):
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.ItemHasNoContents,True)#only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
@ -188,9 +192,9 @@ class GuiTrack(QtWidgets.QGraphicsItem):
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.ItemHasNoContents,True)#only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
@ -237,6 +243,8 @@ class GuiTrack(QtWidgets.QGraphicsItem):
ifresult[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
@ -392,13 +400,15 @@ class GuiTrack(QtWidgets.QGraphicsItem):
classTrackAnchor(QtWidgets.QGraphicsItem):
"""Handling all items as individuals when deleting a track to redraw it is too much.
BetterletQthandleitallatonce."""
def__init__(self,parent):
super().__init__()
self.parent=parent
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.ItemHasNoContents,True)#only child items. Without this we get notImplementedError: QGraphicsItem.paint() is abstract and must be overridden
self.tracks={}#trackId:guiTrack, #if we don't save the instances here in Python space Qt will loose them and they will not be displayed without any error message.
@ -69,7 +69,7 @@ class GuiScore(QtWidgets.QGraphicsScene):
self.backColor.setNamedColor("#fdfdff")
self.setBackgroundBrush(self.backColor)
self.grid=GuiGrid(parent=self)
self.grid=Grid(parentScene=self)
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.setZValue(-50)
@ -129,9 +129,9 @@ class GuiScore(QtWidgets.QGraphicsScene):
@ -83,9 +83,9 @@ class ScoreView(QtWidgets.QGraphicsView):
self.setStyleSheet(style)
defwheelEvent(self,event):
ifQtWidgets.QApplication.keyboardModifiers()in(QtCore.Qt.ControlModifier,QtCore.Qt.ControlModifier|QtCore.Qt.ShiftModifier):#a workaround for a qt bug. see score.wheelEvent docstring.
ifQtWidgets.QApplication.keyboardModifiers()in(QtCore.Qt.ControlModifier,QtCore.Qt.ControlModifier|QtCore.Qt.ShiftModifier):#a workaround for a qt bug. see score.wheelEvent docstring.
event.ignore()#do not send to scene, but tell the mainWindow to use it.
else:
else:
super().wheelEvent(event)#send to scene
defcenterOnCursor(self,cursorExportObject):
@ -101,10 +101,10 @@ class ScoreView(QtWidgets.QGraphicsView):
defstretchXCoordinates(self,factor):
self.scoreScene.stretchXCoordinates(factor)
self.centerOnCursor(None)
defzoom(self,scaleFactor:float):
"""Scale factor is absolute"""
self.resetTransform()
"""Scale factor is absolute"""
self.resetTransform()
self.scale(scaleFactor,scaleFactor)
deftoggleNoteheadsRectangles(self):
@ -144,7 +144,7 @@ class ScoreView(QtWidgets.QGraphicsView):