# -*- coding: ISO-8859-1 -*- """ capellaScript -- Bernd Jungmann >>> Capella-Datei anhängen. Vor dem Anhängen werden die Mustersysteme der Basispartitur und der anzuhängenden Datei in einer Dialogbox einander gegenübergestellt und können interaktiv aufeinander abgestimmt werden.| | Version 4.1: B. Jungmann 11.2.14| <<< Append capella score

Before appending, the systemTemplates of both scores are shown in a dialog box, and may be adjusted interactively.

Capella-Datei anhängen

Vor dem Anhängen werden die Mustersysteme der Basispartitur und der anzuhängenden Datei in einer Dialogbox einander gegenübergestellt und können interaktiv aufeinander abgestimmt werden.

Version 4.1: B. Jungmann 11.2.14

Version 4.1: Bernd Jungmann 11.2.2014: Korrektur für Zeichen " und & in Textknoten. Version 4: Bernd Jungmann 2.3.12: Umlaute korrekt ab capella 7.1-08 Unterstützung für CapXML 2.0. Kann auch in capella 2008 verwendet werden. Kann auch in capella 7 CapXML 1-Dateien anhängen. Kann auch in capella 2008 CapXML 2-Dateien anhängen. Sprachunterstützung für Englisch Version 3: Bernd Jungmann 29.4.05 Unterstützung für Nicht-ASCII-Zeichen (äöüßé...) in der Mustersystem-Beschreibung Version 2: Bernd Jungmann 20.4.05: Unterstützung für metafile-Objekte Version 1: Bernd Jungmann 18.4.05| """ german = { 'title2' : ' anhängen: Mustersysteme kombinieren', 'title1':'capella-Datei anhängen', 'title':'Datei anhängen', 'capellaFiles':'capella-Dateien (*.cap, *.capx)', 'allFiles':'alle Dateien (*.*)', 'noCapxFile':'keine capx-Datei \"', 'dontWork':'\" - geht noch nicht.', 'onlyCapella':'\" - Nur für Capella-Dateien!', 'sysTempCombined':'Mustersysteme kombiniert', 'base':'Basis', 'toAppend':'anzuhängen', 'renamed':'automatisch umbenannt ', 'notUnique':'Name aus anzuhängender Datei nicht eindeutig', 'notUniqueManual':'Manuell geänderter Name nicht eindeutig', 'help':'Stimmen Sie Position und Beschreibung der Mustersystemzeilen in beiden Dateien aufeinander ab!\n\ Folgende Attribute der Mustersystemzeilen werden angezeigt:\n\ [ : Zeile hat eckige Klammer\n\ { : Zeile hat geschweifte Klammer\n\ G : Zeile hat Violinschlüssel als Standardschlüssel\n\ F : Zeile hat Bassschlüssel als Standardschlüssel\n\ G8 : Zeile hat Violinschlüssel mit Oktavierung nach unten als Standardschlüssel\n\ C3 : Zeile hat C-Schlüssel (Alt) als Standardschlüssel\n\ C4 : Zeile hat C-Schlüssel (Tenor) als Standardschlüssel\n\ \n\ Ok zeigt die Zeilen nach Umordnung in der neuen Position.\n\ Weiteres Ok hängt die anzuhängende Datei in der angezeigten Weise an.\n' } english = { 'title2' : ' is to be appended: Combine systemTemplate', 'title1':'Append capella Score', 'title':'Append Score', 'capellaFiles':'capella files (*.cap, *.capx)', 'allFiles':'alle Dateien (*.*)', 'noCapxFile':'no capx file \"', 'dontWork':'\" - not yet working.', 'onlyCapella':'\" - only for capella files!', 'sysTempCombined':'systemTemplates combined', 'base':'Base', 'toAppend':'to be appended', 'renamed':'renamed automatcally ', 'notUnique':'Name from score to append is not unique', 'notUniqueManual':'Changed name is not unique', 'help':'Adjust position and description of systemTemplate staves in both scores!\n\ The following attributes of the systemTemplate staves are shown:\n\ [ : stave has bracket\n\ { : stave has brace\n\ G : stave has treble clef as standard clef\n\ F : stave has bass clef as standard clef\n\ G8 : stave has treble clef with octave shift downwards as standard clef\n\ C3 : stave has alto clef (viola) as standard clef\n\ C4 : stave has tenor clef as standard clef\n\ \n\ Ok will show the staves after reordering in the new position.\n\ Next Ok will append the chosen score in specified way.' } try: setStringTable( ("en", english), ("de", german)) except: def tr(s): return german[s] import zipfile, tempfile, os, xml.dom.minidom # Die Basisdatei File1 ist mit activeScore().write() erzeugt. Die ist ab capella 7 CapXML 2 in utf-8. g_ver, g_sub, g_sr = capVersion() def encode(u): u = '&'.join([x.replace('"', '"').replace('<','<') for x in u.split('&')]) if g_ver >= 7: return u.encode('utf-8') else: try: return u.encode('Latin-1') except: return u.encode('unicode_escape') def decode(u): if g_ver >= 7: return u.decode('utf-8') else: return u.decode('Latin-1') def getFirstChildElement(el, childTag): for n in el.childNodes: if n.nodeType == n.ELEMENT_NODE and n.tagName == childTag: return n return None def mkNameUnique(staves, desc1, ItemNo0, dlgList1): StaffLayouts = staves.getElementsByTagName('staffLayout') doagain = True changed = False while doagain: # zunächst die bisher angelegten StaffLayouts überprüfen # (die können von der Wunschliste abweichen, weil schon Namensänderungen # vorgenommen werden mussten) doagain = False for StaffLayout in StaffLayouts: descr = StaffLayout.getAttribute('description') if descr == desc1[0]: desc1[0] = desc1[0] + "1" doagain = True changed = True break # dann die verbliebenen Items in der Namenswunschliste überprüfen ItemMax = len(dlgList1) ItemNo = ItemNo0 while ItemNo < ItemMax: hbox = dlgList1[ItemNo] ItemNo += 1 if hbox != ' ': # descriptor-String für Basisdatei (ggfls. geändert!) ed,lab, edDesc = hbox['content'] desc = decode(edDesc['value']) if desc == desc1[0]: desc1[0] += "1" doagain = True changed = True break return changed # ändere das Mustersystem entsprechend den Vorgaben in NewLayoutlist def UpdateLayout(text, Layoutlist, dlgList1, text2, dlgList2, mapDescr1, mapDescr2): layout = getFirstChildElement(text, 'layout') layout2 = getFirstChildElement(text2, 'layout') if layout != None: staves = getFirstChildElement(layout, 'staves') staffLayouts = staves.getElementsByTagName('staffLayout') mapIndex1 = len(staffLayouts)*[-1] staves2 = getFirstChildElement(layout2, 'staves') staffLayouts2 = staves2.getElementsByTagName('staffLayout') mapIndex2 = len(staffLayouts2)*[-1] newstaves = text.parentNode.createElement('staves') # dlgList1 durchgehen ItemNo = 0 ItemCount = len(dlgList1) newindex = 0 while ItemNo < ItemCount: # ' ' war das letzte Element der Liste, damit sie ins Fenster passt! hbox = dlgList1[ItemNo] if hbox != ' ': # descriptor-String für Basisdatei (ggfls. geändert!) ed,lab, edDesc = hbox['content'] edval = int(ed.value()) desc1 = decode(edDesc['value']) # descriptor-String für anzuhängende Datei hbox2 = dlgList2[ItemNo] ed2,lab2,edDesc2 = hbox2['content'] descstr2 = decode(lab2['text']) desc2 = descstr2[descstr2.rfind('|')+2:] oldindexstr2 = descstr2[0:descstr2.find('.')] if desc1 != "--": # das staffLayout für diese Zeile von der Basis-Datei übernehmen, ggfls. Namen ändern oldindexstr = lab['text'] oldindex = int(oldindexstr[0:oldindexstr.find('.')]) staffLayoutIndex = oldindex-1 newStaffLayout = staffLayouts[staffLayoutIndex].cloneNode(True) descr = newStaffLayout.getAttribute('description') if descr != desc1: # Eindeutigkeit des geänderten descriptor-Strings sicherstellen! arr = [desc1] if mkNameUnique(newstaves, arr, len(dlgList1), dlgList1): messageBox(tr('notUniqueManual'),tr('renamed')+desc1+" -> "+arr[0]) desc1 = arr[0] newStaffLayout.setAttribute('description',desc1) newstaves.appendChild(newStaffLayout) mapIndex1[staffLayoutIndex] = newindex mapDescr1[descr] = desc1 if descstr2 != "---": mapDescr2[desc2] = desc1 staffLayoutIndex2 = int(oldindexstr2)-1 mapIndex2[staffLayoutIndex2] = newindex newindex += 1 elif descstr2 != "---": # das staffLayout für diese Zeile von der anzuhängenden Datei übernehmen staffLayoutIndex2 = int(oldindexstr2)-1 newStaffLayout = staffLayouts2[staffLayoutIndex2].cloneNode(True) arr = [desc2] if mkNameUnique(newstaves, arr, ItemNo, dlgList1): # der Name muss modifiziert werden messageBox(tr('notUnique'),tr('renamed')+desc2+" -> "+arr[0]) desc1 = arr[0] newStaffLayout.setAttribute('description', desc1) else: # der Name kann unverändert übernommen werden desc1 = desc2 mapDescr2[desc2] = desc1 newstaves.appendChild(newStaffLayout) mapIndex2[staffLayoutIndex2] = newindex newindex += 1 else: # in beiden Dateien gibt's keinen Eintrag für diese Zeile. # Also nicht ins Mustersystem übernehmen. pass ItemNo += 1 layout.replaceChild(newstaves, staves) staves.unlink() # Falls Klammern existieren, Indices den ggfls. verschobenen Zeilennummern anpassen brackets2 = layout2.getElementsByTagName('bracket') brackets = layout.getElementsByTagName('bracket') if len(brackets2) > 0: # die anzuhängende Datei hat Klammern. bracketsNode2 = getFirstChildElement(layout2, 'brackets') if len(brackets) == 0: # Die Basispartitur hat keine Klammern # Klone das brackets-Element newbracketsNode = bracketsNode2.cloneNode(True) # newbracketsNode soll hinter staves reingehängt werden. # es gibt aber nur appendChild (hängt ans Ende) oder insertBefore # deshalb hänge ich's vor spacing rein. spacing = getFirstChildElement(layout, 'spacing') layout.insertBefore(newbracketsNode, spacing) brackets = layout.getElementsByTagName('bracket') # jetzt noch für die Klammern der Basispartitur die Indices anpassen for bracket in brackets: old = int(bracket.getAttribute('from')) if old < len(mapIndex2): bracket.setAttribute('from',str(mapIndex2[old])) old = int(bracket.getAttribute('to')) if old < len(mapIndex2): bracket.setAttribute('to',str(mapIndex2[old])) return else: # wir versuchen, die Klammern der anzuhängenden Datei in der # Basispartitur wiederzufinden. Wenn das nicht gelingt, muss # eine neue Klammer angelegt werden. bracketsNode = getFirstChildElement(layout, 'brackets') for bracket2 in brackets2: oldfrom2 = int(bracket2.getAttribute('from')) oldto2 = int(bracket2.getAttribute('to')) oldcurly2 = bracket2.getAttribute('curly') found = False for brack in brackets: oldfrom = int(brack.getAttribute('from')) oldto = int(brack.getAttribute('to')) oldcurly = brack.getAttribute('curly') if mapIndex2[oldfrom2] == mapIndex1[oldfrom]\ and mapIndex2[oldto2] == mapIndex1[oldto]\ and oldcurly2 == oldcurly: found = True if not found: newbracket = bracket2.cloneNode(True) newbracket.setAttribute('from',str(mapIndex2[oldfrom2])) newbracket.setAttribute('to',str(mapIndex2[oldto2])) bracketsNode.appendChild(newbracket) # jetzt noch für die Klammern der Basispartitur die Indices anpassen for bracket in brackets: old = int(bracket.getAttribute('from')) if old < len(mapIndex1): bracket.setAttribute('from',str(mapIndex1[old])) old = int(bracket.getAttribute('to')) if old < len(mapIndex1): bracket.setAttribute('to',str(mapIndex1[old])) # passe die Verweise in den Zeilen an die geänderten Layoutdeskriptornamen an def UpdateStaffLayoutDescr(system, mapDescr): staves = system.getElementsByTagName('staff') for staff in staves: descr = staff.getAttribute("layout") if descr in mapDescr: mappedDescr = mapDescr[descr] if mappedDescr != descr: staff.setAttribute("layout", mappedDescr) # hänge die Systeme der anzuhängenden Datei an die Basisdatei an - das Mustersystem passt schon! def AppendSystems(text1, text2, mapDescr1, mapDescr2, mapOtherFiles): systems1 = getFirstChildElement(text1, 'systems') # layout-Name der einzelnen Zeilen in Basis-Datei ggfls. anpassen for sys1 in systems1.childNodes: if sys1.nodeType == sys1.ELEMENT_NODE: UpdateStaffLayoutDescr(sys1, mapDescr1) systems2 = getFirstChildElement(text2, 'systems') for system2 in systems2.childNodes: system1 = system2.cloneNode(True) if system1.nodeType == system1.ELEMENT_NODE: # layout-Name in angehängter Datei ggfls. anpassen ## messageBox('layout-Name in angehängter Datei ggfls. anpassen',\ ## str(mapDescr2)) UpdateStaffLayoutDescr(system1, mapDescr2) # Namen der RTF- und WMF- Dateien ggfls. anpassen RTFobjs = system1.getElementsByTagName('richText') for RTFobj in RTFobjs: oldfilnam = RTFobj.getAttribute('file') RTFobj.setAttribute('file',mapOtherFiles[oldfilnam]) WMFobjs = system1.getElementsByTagName('metafile') for WMFobj in WMFobjs: oldfilnam = WMFobj.getAttribute('file') WMFobj.setAttribute('file',mapOtherFiles[oldfilnam]) systems1.appendChild(system1) def ClefToString(clef): if clef == 'treble': return 'G ' if clef == 'bass': return 'F ' if clef == 'alto': return 'C3' if clef == 'tenor': return 'C4' if clef == 'G2-': return 'G8' return str(clef) # Vergleichsrelevante Daten aus dem Mustersystem holen # Liste mit z.B. folgendem Inhalt: # G Singstimme Konzertflügel,80 # { G Piano r.H. Konzertflügel,80 # { F Piano l.H. Konzertflügel,80 def getLayoutDescriptor(text): layout = getFirstChildElement(text, 'layout') if layout != None: List = [] staves = getFirstChildElement(layout, 'staves') staffLayouts = staves.getElementsByTagName('staffLayout') for staffLayout in staffLayouts: FullDescriptor = '' descr = staffLayout.getAttribute('description') notation = getFirstChildElement(staffLayout, 'notation') defClef = notation.getAttribute('defaultClef') notelines = str(notation.getAttribute('notelines')) if notelines != '': FullDescriptor += notelines + ' | ' else: FullDescriptor += ' ' FullDescriptor += ClefToString(defClef) + ' | ' FullDescriptor += descr List.append(FullDescriptor) brackets = layout.getElementsByTagName('bracket') ListLength = len(List) for bracket in brackets: indexFrom = int(bracket.getAttribute('from')) indexTo = int(bracket.getAttribute('to')) Klammerform = '' if bracket.getAttribute('curly'): Klammerform = '{ ' else: Klammerform = '[ ' index = 0 while index < ListLength: if index >= indexFrom and index <= indexTo: List[index] = Klammerform + List[index] else: List[index] = ' ' + List[index] index += 1 index = 0 while index < ListLength: List[index] = str(index+1) + '. ' + ' ' + List[index] index += 1 return List # dialog-Hilfsprogramm def mkLayoutColumn(layoutlist, descEditable): LabelList = [] ItemNo = 0 maxItemNo = len(layoutlist) for item in layoutlist: indexdesc = item.rfind('|')+1 strdesc = item[indexdesc + 1:] editdesc = Edit(strdesc, width=10) label = Label(item[0:indexdesc-1], width=6) if not descEditable: label = Label(item, width=10) editdesc = Label(' ') ItemNo += 1 edit = Edit(str(ItemNo), min=1,max=maxItemNo,width = 4) LabelList.append(HBox([edit,label,editdesc], padding=10)) # LabelList.append(' ') return LabelList def reorderLayoutColumn(dlgList, NewLayoutlist): ItemNo = 0 ItemCount = len(dlgList) changed = False while ItemNo < ItemCount: # ' ' war das letzte Element der Liste, damit sie ins Fenster passt! hbox = dlgList[ItemNo] ItemNo += 1 if hbox != ' ': ed,lab, edDesc = hbox['content'] edval = int(ed.value()) if edval != ItemNo: changed = True # falls die Zielposition keine geänderte Positionsnummer hat, soll der # dort wegbewegte Eintrag die Positionsnummer seiner neuen Stelle übernehmen hboxDest = dlgList[edval-1] NewLayoutItem = NewLayoutlist[edval-1] edDest, labDest, edDescDest = hboxDest['content'] if int(edDest.value()) == edval: edDest['value'] = str(ItemNo) hboxDest['content'] = [edDest, labDest, edDescDest] dlgList[ItemNo-1] = dlgList[edval-1] NewLayoutlist[ItemNo-1] = NewLayoutlist[edval-1] dlgList[edval-1] = hbox NewLayoutlist[edval-1] = NewLayoutItem return changed def SaveChanges(el, file): file.write('<' + el.tagName) if el.hasAttributes(): a = el.attributes for i in range(a.length): file.write(' %s="%s"' % (encode(a.item(i).name), encode(el.getAttribute(a.item(i).name)))) if el.hasChildNodes() or el.tagName == 'textarea': file.write('>') # Start-Tag schließen for n in el.childNodes: if n.nodeType == n.TEXT_NODE: file.write(encode(n.data)) elif n.nodeType == n.ELEMENT_NODE: SaveChanges(n, file) file.write('') else: file.write('/>') # Element ohne Inhalt # transferiere die RichText-Dateien aus File nach FileDest. # Achte auf Namensgleichheiten! def CopyOtherFiles(File1, File2, zw, MapOtherFiles): CopyOtherFilesSimple(File1, zw) CopyOtherFilesCheck(File2, zw, MapOtherFiles) def CopyOtherFilesSimple(File, zw): zr = zipfile.ZipFile(File,'r') for name in zr.namelist(): if name != 'score.xml': t = zr.read(name) info = zipfile.ZipInfo(name) info.compress_type = zipfile.ZIP_DEFLATED zw.writestr(info, t) def CopyOtherFilesCheck(File, zw, MapOtherFiles): zr = zipfile.ZipFile(File,'r') newnamelist = zw.namelist() for name in zr.namelist(): if name != 'score.xml': strExt = name[name.rfind('.'):] newname = name while newname in newnamelist: newname = newname[:newname.rfind('.')]+"1"+strExt t = zr.read(name) info = zipfile.ZipInfo(newname) info.compress_type = zipfile.ZIP_DEFLATED zw.writestr(info, t) MapOtherFiles[name] = newname newnamelist.append(newname) # hole aus dem ZipFile den CapXML-Text def getText(file): zr = zipfile.ZipFile(file,'r') for name in zr.namelist(): t = zr.read(name) if name == 'score.xml': doc = xml.dom.minidom.parseString(t) return doc.documentElement # hänge capx-Datei File2 an capx-Datei File1 an def capxAppend(File1, File2, FileDest, zw): text1 = getText(File1) text2 = getText(File2) # Kurzdarstellung der Mustersysteme produzieren Layoutlist1 = getLayoutDescriptor(text1) Layoutlist2 = getLayoutDescriptor(text2) # Passende Zeilen aufeinander abbilden Len1 = len(Layoutlist1) Len2 = len(Layoutlist2) NewLayoutlist1 = [] NewLayoutlist2 = [] index1 = 0 index2 = 0 i1lastfound = 0 while index2 < Len2: index1 = i1lastfound found = False while index1 < Len1: if Layoutlist1[index1] == Layoutlist2[index2]: found = True while i1lastfound < index1: NewLayoutlist1.append(Layoutlist1[i1lastfound]) NewLayoutlist2.append('---') i1lastfound += 1 NewLayoutlist1.append(Layoutlist1[index1]) NewLayoutlist2.append(Layoutlist2[index2]) i1lastfound = index1 + 1 index1 += 1 if not found: NewLayoutlist1.append('---') NewLayoutlist2.append(Layoutlist2[index2]) index2 += 1 while i1lastfound < Len1: NewLayoutlist1.append(Layoutlist1[i1lastfound]) NewLayoutlist2.append('---') i1lastfound += 1 dlgList1 = mkLayoutColumn(NewLayoutlist1, True) vBox1 = VBox(dlgList1, padding = 10, text=tr('base')) dlgList2 = mkLayoutColumn(NewLayoutlist2, False) vBox2 = VBox(dlgList2, padding = 10, text=tr('toAppend')) hBox = HBox([vBox1,vBox2], padding = 30) helptext = tr('help') ## "Stimmen Sie Position und Beschreibung der Mustersystemzeilen in beiden Dateien aufeinander ab!\n\ ##Folgende Attribute der Mustersystemzeilen werden angezeigt:\n\ ##[ : Zeile hat eckige Klammer\n\ ##{ : Zeile hat geschweifte Klammer\n\ ##G : Zeile hat Violinschlüssel als Standardschlüssel\n\ ##F : Zeile hat Bassschlüssel als Standardschlüssel\n\ ##G8 : Zeile hat Violinschlüssel mit Oktavierung nach unten als Standardschlüssel\n\ ##C3 : Zeile hat C-Schlüssel (Alt) als Standardschlüssel\n\ ##C4 : Zeile hat C-Schlüssel (Tenor) als Standardschlüssel\n\ ##\n\ ##Ok zeigt die Zeilen nach Umordnung in der neuen Position.\n\ ##Weiteres Ok hängt die anzuhängende Datei in der angezeigten Weise an." dlg = Dialog(File2[appendfile.rfind('\\')+1:]+tr('title2'), hBox, helptext) changed = True doit = False dlgList1Save = str(dlgList1) while changed and dlg.run(): doit = True changed = False while reorderLayoutColumn(dlgList1, NewLayoutlist1): changed = True while reorderLayoutColumn(dlgList2, NewLayoutlist1): changed = True if changed: dlg = Dialog(tr('sysTempCombined'), hBox, helptext) doit = False dlgList1Save = str(dlgList1) if doit: activeScore().registerUndo(tr('title')) mapDescr1 = {} mapDescr2 = {} mapOtherFiles = {} CopyOtherFiles(File1, File2, zw, mapOtherFiles) UpdateLayout(text1, Layoutlist1, dlgList1, text2, dlgList2, mapDescr1, mapDescr2) AppendSystems(text1, text2, mapDescr1, mapDescr2, mapOtherFiles) SaveChanges(text1, FileDest) return True return False # Hauptprogramm as = activeScore() if as: # Name der anzuhängenden Datei erfragen fdlg = FileDialog() fdlg.setDefaultExt("capx") fdlg.addFilter(tr('capellaFiles'),"*.cap;*.capx") fdlg.addFilter(tr('allFiles'),"*.*") fdlg.setTitle(tr('title1')) if fdlg.run(): appendfile = fdlg.filePath() if appendfile.endswith(".capx"): tempFile1 = tempfile.mktemp('.capx') tempFile2 = tempFile1[:tempFile1.rfind('.')] + '~.capx' zw = zipfile.ZipFile(tempFile2, 'w', zipfile.ZIP_DEFLATED) tempFile = tempfile.mktemp('.xml') fd = file(tempFile, 'wt') if g_ver >= 7: fd.write('\n') else: fd.write('\n') as.write(tempFile1) if capxAppend(tempFile1, appendfile, fd, zw): fd.close() zw.write(tempFile, 'score.xml') zw.close() as.read(tempFile2) else: fd.close() zw.close() os.remove(tempFile) os.remove(tempFile1) os.remove(tempFile2) elif appendfile.endswith(".cap"): messageBox(tr('title'), tr('noCapxFile')+appendfile[appendfile.rfind('\\')+1:]+tr('dontWork')) pass else: messageBox(tr('title'), tr('noCapxFile')+appendfile[appendfile.rfind('\\')+1:]+tr('onlyCapella'))