diff --git a/engine/api.py b/engine/api.py index 09ab24b..ef9c53a 100644 --- a/engine/api.py +++ b/engine/api.py @@ -2494,12 +2494,32 @@ def midiRelativeChannelReset(): #Lilypond -def lilypondText(text): +def lilypondText(text:str): + """This is the generic lilypond item. It will get inserted as-is""" try: insertItem(items.LilypondText(text)) except Exception as err: logger.error(err) +def lilypondMark(text:str, aboveStaff=True): + """This is actual human-readable text, called a Mark by lilypond. + By default is will be above the staff, but it can be below as well. + + It needs to be attached to another item. Lilypond doesn't allow "just place text here". + + https://lilypond.org/doc/v2.23/Documentation/notation/writing-text + """ + + text = lilypond.lilyfy(text) + + if aboveStaff: + text = '^"' + text + '"' #add quotes "" for lilypond + else: + text = '-"' + text + '"' #add quotes "" for lilypond + + insertItem(items.LilypondText(text)) + + def exportLilypond(absoluteFilePath): save() lilypond.saveAsLilypond(session.data, absoluteFilePath) diff --git a/engine/lilypond.py b/engine/lilypond.py index 6a31771..e581ff1 100644 --- a/engine/lilypond.py +++ b/engine/lilypond.py @@ -73,6 +73,13 @@ def stringToCharsOnlyString(string): return "".join([num2wordForIterations(c) for c in string]) def lilyfy(string): + """Escape special lilypond characters like the quote " etc. + + This is extended on a as-needed basis. + """ + + string = string.replace('"', '\\"') + return string def fromTemplate(session, templateFile, data, meta, tempoStaff): @@ -102,15 +109,15 @@ def fromTemplate(session, templateFile, data, meta, tempoStaff): def findTemplate(session, templateFile:str=None)->str: """returns a path. checks for existence. - + There are two options: - Use a standard template in SHARE/lilypondTemplates - Use a template that belongs to the save file. - + The one that belongs to the save file needs to be created by hand by the user. If not we throw a FileNotFoundError""" if templateFile: - path = os.path.join(PATHS["share"], "lilypondTemplates", templateFile) + path = os.path.join(PATHS["share"], "lilypondTemplates", templateFile) else: path = os.path.join(session.sessionPrefix, "template.laborejo2.ly") #the name is always the same because the sessionPrefix is the unqiue part assert path.endswith(".ly") diff --git a/qtgui/designer/mainwindow.py b/qtgui/designer/mainwindow.py index 6dddbb5..b63053a 100644 --- a/qtgui/designer/mainwindow.py +++ b/qtgui/designer/mainwindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'mainwindow.ui' # -# Created by: PyQt5 UI code generator 5.15.2 +# Created by: PyQt5 UI code generator 5.15.6 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -664,6 +664,10 @@ class Ui_MainWindow(object): self.actionQuit.setObjectName("actionQuit") self.actionPlay_from_Block = QtWidgets.QAction(MainWindow) self.actionPlay_from_Block.setObjectName("actionPlay_from_Block") + self.actionLyMarkAbove = QtWidgets.QAction(MainWindow) + self.actionLyMarkAbove.setObjectName("actionLyMarkAbove") + self.actionLyMarkBelow = QtWidgets.QAction(MainWindow) + self.actionLyMarkBelow.setObjectName("actionLyMarkBelow") self.menuObjects.addAction(self.actionMetrical_Instruction) self.menuObjects.addAction(self.actionClef) self.menuObjects.addAction(self.actionKey_Signature) @@ -847,6 +851,8 @@ class Ui_MainWindow(object): self.menuLilypond.addAction(self.actionLyBarline) self.menuLilypond.addAction(self.actionLyRepeat) self.menuLilypond.addAction(self.actionLyFree_Instruction) + self.menuLilypond.addAction(self.actionLyMarkAbove) + self.menuLilypond.addAction(self.actionLyMarkBelow) self.menubar.addAction(self.menuView.menuAction()) self.menubar.addAction(self.menuEdit_2.menuAction()) self.menubar.addAction(self.menu.menuAction()) @@ -986,7 +992,7 @@ class Ui_MainWindow(object): self.actionRandom_in_scale_in_cursor_plus_octave.setText(_translate("MainWindow", "Random in-scale in cursor plus octave (authentic mode)")) self.actionRandom_in_scale_in_octave_around_cursor.setText(_translate("MainWindow", "Random in-scale in octave around cursor (hypo mode)")) self.actionLyBarline.setText(_translate("MainWindow", "Barline")) - self.actionLyFree_Instruction.setText(_translate("MainWindow", "Free Instruction")) + self.actionLyFree_Instruction.setText(_translate("MainWindow", "Free Ly Instruction")) self.actionLyRepeat.setText(_translate("MainWindow", "Repeat")) self.actionZoom_In_Score_View.setText(_translate("MainWindow", "Zoom In Score View")) self.actionZoom_Out_Score_View.setText(_translate("MainWindow", "Zoom Out Score View")) @@ -994,3 +1000,5 @@ class Ui_MainWindow(object): self.actionQuit.setShortcut(_translate("MainWindow", "Ctrl+Q")) self.actionPlay_from_Block.setText(_translate("MainWindow", "Play from Block")) self.actionPlay_from_Block.setShortcut(_translate("MainWindow", "Alt+Space")) + self.actionLyMarkAbove.setText(_translate("MainWindow", "Text above Staff")) + self.actionLyMarkBelow.setText(_translate("MainWindow", "Text below Staff")) diff --git a/qtgui/designer/mainwindow.ui b/qtgui/designer/mainwindow.ui index 98ebe01..2ec88da 100644 --- a/qtgui/designer/mainwindow.ui +++ b/qtgui/designer/mainwindow.ui @@ -323,6 +323,8 @@ + + @@ -1711,7 +1713,7 @@ - Free Instruction + Free Ly Instruction @@ -1751,6 +1753,16 @@ Alt+Space + + + Text above Staff + + + + + Text below Staff + + diff --git a/qtgui/menu.py b/qtgui/menu.py index 37e7d5f..84d62f9 100644 --- a/qtgui/menu.py +++ b/qtgui/menu.py @@ -307,6 +307,8 @@ class MenuActionDatabase(object): 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.actionLyFree_Instruction: lambda: forwardText(self.mainWindow, translate("menu", "Enter Instruction"), api.lilypondText), + self.mainWindow.ui.actionLyMarkAbove: lambda: forwardText(self.mainWindow, translate("menu", "Text above the Staff, will be attached to previous(!) item."), lambda tx: api.lilypondMark(tx, True)), + self.mainWindow.ui.actionLyMarkBelow: lambda: forwardText(self.mainWindow, translate("menu", "Text below the Staff, will be attached to previous(!) item."), lambda tx: api.lilypondMark(tx, False)), } self.modalActions = { #these are only available in Note Edit Mode, not in CC Edit Mode etc.