Boilerplates, snippets, use cases and QML notes

Updated 6 days ago

    To help beginners kickstart their plugin project, this section gathers boilerplates ,tiny reusable code snippets or parts which are too trivial to be used as an individual plugin, along with several specific use cases and notes that demonstrate how to achieve particular tasks in plugin code.

    What goes in here? and why?

    This place centralizes clever code bits which would otherwise be left hidden under waves of forum posts. Anyone can edit. Feel free to rearrange, correct or add anything, provide a source if possible. Only share code that is taken from other's plugin after you make sure you have permission. Musicians who wish to learn to create plugins usually visit here first, it'd be counter-productive to fragmentize MuseScore resource on the web, plus sharing here saves you the trouble of learning git commands and signing up for github. Codebase large or functional enough to be used as an individual plugin could be posted as a project on musescore.org without github knowledge. Tips on QML are also put in here and as subpage, notes on API write in community notes on API

    Knock knock, who's there?

    Wong. Spot a typo? Found codes need fixing? Want to share yours? Please go ahead. Click the ⠇ to the right of the title, choose "Edit".
    Not a musescore.org member yet? Register an account, it's free. Start editing right away, you really do not need to report and wait for permission. In fact, paid employee from MuseScore BVBA tends to concentrate on improving the main program and providing forum support, it is up to fellow passionate musicians like yourself to share findings and correct errors in the plugin section. Discussion about this page itself visit this forum thread.
    capture_001_29082022_145502.jpg

    Marked Default are copied from plugins bundled with MuseScore 3 installation, open them to learn more.

    use case: Add Fingering Text

    use case: Changing an Existing Note's Pitch

    use case: Element Explorer

    boilerplates and snippets:

    run this script once. then upon clicking on a element, print all its properties and func on logger

    also try the buildin debugger
    Object Explorer plugin exhaustively print out element info
    see also: use case: Element Explorer

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug20"
        onScoreStateChanged: {
          if (state.selectionChanged && curScore){
            var es=curScore.selection.elements
            for (var i = 0; i < es.length; i++) {
              console.log('\n'+es[i].name)
              for(var p in es[i]){
                console.log(es[i].name+"."+p+" : "+es[i][p])
              }
            }
         }
      }
    }

    run this script once to sync/bind the time position of the Cursor to first note of current selection

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug2"
      id:pluginscope
      property var c
      onRun: {
        pluginscope.c=curScore.newCursor()
        pluginscope.c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
      }
      onScoreStateChanged: {
        if (state.selectionChanged && curScore) console.log( pluginscope.c.tick )
      }
    }

    select everything on the score, then filter by element type

    Note: selectRange() parameter endTick and endStaff are excluded from selection

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug3"
      onRun: {
        curScore.startCmd()
        curScore.selection.selectRange(0,curScore.lastSegment.tick + 1,0,curScore.nstaves);    //cmd("select-all")
        curScore.endCmd()
        var es=curScore.selection.elements
        for (var i = 0; i < es.length; i++) {
          if(es[i].type==Element.NOTE) console.log(es[i].pitch)
        }
      }
    }

    move the Cursor to the beginning of score

    more info: move cursor to start or end of your current selection ,or tick
    see also Default walk.qml

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
          menuPath: "Plugins.debug5"
          onRun: {
            var c= curScore.newCursor();
            c.voice    = 0;
            c.staffIdx = 0;
            c.rewind(Cursor.SCORE_START);
            console.log(c.tick)    
        }
    }

    log selected note's pitch and next note's pitch

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug4"
      onRun: {
        var e=curScore.selection.elements[0]
        if(e.type==Element.NOTE){
          console.log( "This pitch "+e.pitch )
          var track=e.track
          var seg=e.parent
          while(seg&&seg.type!=Element.SEGMENT){ seg=seg.parent }
          if(seg) var tick=seg.tick
          var c=curScore.newCursor()
          c.track=track  //set track first, setting track will reset tick
          c.rewindToTick(tick)
          var segnext=c.segment.next
          if(segnext
            &&segnext.elementAt(track).type==Element.CHORD  // 1 or more notes
            &&segnext.elementAt(track).notes
          ) console.log("Next pitch "+ segnext.elementAt(track).notes[0].pitch )  //0=first entered note
        }
      }
    }

    log selected chord's pitches

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug41"
      onRun: {
        var e=curScore.selection.elements[0]
        var track=e.track
        var seg=e.parent
        while(seg&&seg.type!=Element.SEGMENT){ seg=seg.parent }
        if(seg) var tick=seg.tick
        var c=curScore.newCursor()
        c.track=track  //set track first, setting track will reset tick
        c.rewindToTick(tick)
        if(c.segment.elementAt(track).type==Element.CHORD){
          var notesarray=c.segment.elementAt(track).notes
          if(notesarray&&notesarray.length>0){
            for (var i = 0; i < notesarray.length; i++){ 
              console.log('note # '+i+' pitch '+notesarray[i].pitch) 
            }
          }
        }
      } 
    }

    create new note, assign pitch

    see also the update pitch snippet below

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug7"
      onRun:{
        var c=curScore.newCursor()
        c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
        curScore.startCmd()
        c.addNote(60)
        curScore.endCmd()
      }
    }

    update note's pitch

    see also the create note snippet above
    see also use case: Changing an Existing Note's Pitch
    Two scenarios
    * in Fretted eg guitar TAB: must set 5 values: .pitch, .string, .fret, .tpc1, .tpc2
    * in Fretless eg piano: must set 3 values: .pitch, .tpc1, .tpc2
    source notes

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug8"
     //WIP
    }

    add lyrics at selected element and subsequent non rest elements

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug9"
      onRun: {
        var e=curScore.selection.elements[0]
        var seg=e.parent
        while(seg&&seg.type!=Element.SEGMENT){ seg=seg.parent }
        if(seg) var tick=seg.tick
        var c=curScore.newCursor()
        c.rewindToTick(tick)
        c.track=e.track
        var lyr = newElement(Element.LYRICS)
        lyr.text = "muse"
        lyr.verse = 0
        curScore.startCmd()
        c.add(lyr)
        curScore.endCmd()
     
        var lyricsarray=['score','is','awesome']
        c.next()
        while(c&&c.element&&lyricsarray.length>0){
          if(
            c.element.type==Element.CHORD  // 1 or more notes
            // &&c.element.type!=Element.REST  //redundant
          ){
            var lyr = newElement(Element.LYRICS)
            lyr.text = lyricsarray[0]
            lyr.verse = 0
            curScore.startCmd()
            c.add(lyr)
            curScore.endCmd()
            lyricsarray.shift()
          }
          c.next()
        }
      } 
    }

    log the shown key signature at selected position

    see notes on which methods seem not working
    Note: Musescore 3 interpret key signature's shape and flat symbols to provide tonality logic, it does not understand tonality really. The following code returns the key signature shown on screen, it does not always mean the real key in musical sense . Caution when using an open/atonal keysig and/or on transposing instruments; transposing instruments' return value also varies with user's current "Concert Pitch" display status.
    key signature enum are not exposed as API yet, use this hardcoded schema, ref:
    Num of Sharps = positive number
    No symbols = 0
    Num of Flats = negative number

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug29" 
      onRun:{
        var  c=curScore.newCursor()
        c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
         console.log(c.keySignature)
      }
    }

    get to the title, composer etc frame at start of page

    Did you find a way get to its existing content text eg read title, composer text string ? src ref
    also see metatag snippet which contains score properties set as data field only , which may not be the current visual text

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug12"
      onRun: {
        var c=curScore.newCursor()
        c.rewind(Cursor.SCORE_START)
        curScore.selection.select(c) //tackle weird range select problem
        cmd('prev-element')
        while( curScore.selection.elements[0].type!=Element.VBOX ){
          cmd('prev-element')
        }
        var e=curScore.selection.elements[0]
        console.log(e.name)
      }
    }

    log time signature then create new one at selected position

    some values won't work, always check actual timesig : right click > measure prop see handbook
    See Time Signature Enforcer for a plugin that uses the cmd interface to manipulate the timesigActual of a measure.
    see notes for methods currently not working

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug28"
      onRun: {
        var c=curScore.newCursor()
        c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
        //
        var m=c.measure.timesigActual  //replace with timesigNominal for displayed symbol
        console.log("timesigActual : "+m.numerator)
        console.log("timesigActual : "+m.denominator)
        //
        var ts=newElement(Element.TIMESIG)
        ts.timesig=fraction(12,8) //some values won't work, always check timesigActual
        curScore.startCmd()
        c.add(ts)
        curScore.endCmd()
      }
    }

    log score information entered in File>Properties or new score wizard

    'name' of default score properties

    import MuseScore 3.0
    MuseScore {
      menuPath: "Plugins.debug31"
      onRun:{
        console.log(curScore.metaTag("workTitle")) //https://github.com/fp22june/MuseScoreTag362/blob/master/share/templates/My_First_Score.mscx#L19-L29
      }
    }

    icon and symbols. add text, symbols to selected note

    see also TempoChanges Plugin
    sym, SymId enum: see Master palatte > symbols or lookup
    MS3 fontfamily
    lookup codepoints
    everything in musescore.qrc
    only icon image
    source post

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug30"
      pluginType:"dock"  
      onRun: {
        var c=curScore.newCursor()
        c.inputStateMode=Cursor.INPUT_STATE_SYNC_WITH_SCORE
        //
        var s = newElement(Element.ARTICULATION); 
        s.symbol = SymId.articAccentAbove // SymId has long loading time at first run
        curScore.startCmd()
        c.add(s)
        curScore.endCmd()
        //
        var t = newElement(Element.STAFF_TEXT); 
        t.fontSize= 20
        t.text = "<sym>miscEyeglasses</sym>" 
        curScore.startCmd()
        c.add(t)
        curScore.endCmd()
      }
      Column{
        Text{
          font.family: 'MScore Text' //also  Bravura  Leland  //https://github.com/musescore/MuseScore/tree/master/fonts
          font.pointSize: 20
          text:"\uE4E5" //https://github.com/w3c/smufl/blob/gh-pages/metadata/glyphnames.json
        }
        Image{
          source:"qrc:///data/icons/note-longa.svg" //https://github.com/musescore/MuseScore/blob/3.x/mscore/icons.cpp#L40
        }
      }
    }

    Save custom data

    The following snippet saves a string as tag property in a score, to save object use JSON.stringify() and parse()
    Also:
    Save data to a time position: add invisible Staff Text, more info study the MuseScore Navigation plugin
    Save data across sessions, use Settings { }, study the Daily Log plugin

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    MuseScore { 
      menuPath: "Plugins.debug24"
      pluginType: "dock"
      anchors.fill: parent
      onRun:{
        curScore.setMetaTag('customdataname', 'initemptyvalue')
      }
      Column{
        Button{
          text:"load"
          onClicked: console.log( curScore.metaTag('customdataname') )
        }
        Button{
          text:"save"
          onClicked: curScore.setMetaTag('customdataname', 'customdatavalue')
        }
      }
    }

    workaround to gate onScoreStateChanged's cpu-heavy code to the last plugin instance

    Following limits cpu-heavy, blocking synchronous code to last plugin instance (because every onScoreStateChanged continues to run even after its invocation plugin window closed)
    Remember to use unique string in metaTag('PluginLastInstance')
    To test, comment out the comparison line then reopen plugin few times

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug43"
      property string instanceStamp:Date.now()
      Component.onCompleted: curScore.setMetaTag('PluginLastInstance', instanceStamp) //save to score, overwritten by latest plugin
      function blockingSynchronousCode(){
        var x=0
        for(var i=0;i<1e7;i++) x+=i //edit 1e7 according to cpu
      }
      onScoreStateChanged:{
        if(curScore.metaTag('PluginLastInstance')==instanceStamp){   //filter out old instances
          blockingSynchronousCode()
        }
      }
    }

    workaround to relink onScoreStateChanged to current score only

    Using the gate method above, if the plugin window stays open, switching to another score will invalidate onScoreStateChanged because instanceStamp doesn't exist or doesn't equal to plugin's stored value. Click button to reapply stamp so that plugin is affected by current score's onScoreStateChanged
    Remember to use unique string in metaTag('PluginLastInstance')

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    MuseScore {
      menuPath: "Plugins.debug44"
      pluginType: "dock"
      anchors.fill:parent
      property string instanceStamp:Date.now()
      function maincode(){
        txt.text=instanceStamp+' score called '+Date.now()
      }
      function ownInstance(){
        instanceStamp=Date.now()
        curScore.setMetaTag('PluginLastInstance', instanceStamp)
        maincode()
      }
      Component.onCompleted: ownInstance()
      onScoreStateChanged:{
        if(curScore.metaTag('PluginLastInstance')==instanceStamp){ 
          maincode()
        }
      }
      Column{
        Button{
          text:"relink to current score"
          onClicked:ownInstance()
        }
        Text{
          id: txt
          anchors.fill:parent
        }
      }
    }

    Listview with unidirectional dataflow

    qt ref

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    MuseScore {
      menuPath: "Plugins.debug22"
      pluginType: "dock"
      anchors.fill: parent
      id:pluginscope
      property var db:[
          { nm : "name1" , btn : true }
        , { nm : "name2" , btn : false }
        , { nm : "name3" , btn : false }
        , { nm : "name4" , btn : true }
      ]
      function gen(payload){
        if(payload && payload.deletenm){
          for (var i=0; i<pluginscope.db.length; i++){
            if(pluginscope.db[i].nm==payload.deletenm){
              pluginscope.db[i].removed=true
            }
          }
        }
        if(payload && payload.renewall){
          pluginscope.db=pluginscope.db.map(function _(r){
            return ({ nm: r.nm, btn: r.btn, removed: false})
          })
        }
        md1.clear()
        pluginscope.db.map(function _(r){
          if(!r.removed) md1.append(r)
        })
        console.log("gen")
      }
      Item{
        anchors.fill: parent
        ListModel { id: md1 }    //ref from delegate use getview1.ListView.view.model
        ListView {
          anchors.fill: parent
          id: listview1         //ref from delegate use getview1.ListView.view
          model: md1  
          delegate: Component {
            Row{
              id: getview1
              width: parent.width
              Text{
                clip:true
                text: nm
              }
              Button { 
                text: btn ? "logmynm" : "deleteme"
                onClicked:{ 
                  if(btn){ 
                    console.log(nm)
                  } else {
                    gen({ deletenm : nm })
                  }
                }
              }
            }
          }
        }
      }
      Button { 
        anchors.bottom: parent.bottom
        text: "renewall"
        onClicked: gen({ renewall : true })
      }
    }

    QML keyboard handler

    more info see this QML notes
    Keys enum list
    focus:true may not mean want you think

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore { 
      menuPath: "Plugins.debug10"
      pluginType: "dock"
      Item {
        focus:true
        Keys.onPressed: {
          console.log("event.key: "+event.key)
          if (event.key == Qt.Key_Space) cmd("play")
        }
      }
    }

    Button color, mouse over color

    more prop like disabled, qmlssed, checked, checkable, focused, highlighted, flat, mirrored, hovered etc
    for more mouse control like detect button, QML use MouseArea , see this QML notes

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    MuseScore { 
      menuPath: "Plugins.debug6"
      pluginType: "dock"
      Button{ 
        text:"clickme"
        onClicked: onbtn1()
        background: Rectangle {
        color: parent.hovered ? "#aaa": "#ddd"
        } 
      }
      function onbtn1(){
        console.log("clicked")
      }
    }

    mouse button, mouse down, mouse up

    Mouse button enum list
    also see this QML notes
    for complex mouse handler logic, you may want to use standard js mouseevent as in the WebEngine snippet, instead of QML mouseevent shown here

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2 
    MuseScore { 
      menuPath: "Plugins.debug90"
      pluginType: "dock"
      anchors.fill: parent
      Row{
        Rectangle { 
          width:  50;
          height: 50;
          color: "red"
          MouseArea{
            id:ma
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton | Qt.RightButton
            onPressed: {
              console.log('MouseArea.pressedButtons: '+ pressedButtons) //pressedButtons is also available ma.pressedButtons
              console.log('mouse.button: '+mouse.button)                //mouse is mouseEvent exist inside onPressed(){} 
            }
          }
        }
        Rectangle { 
          width:  50;
          height: 50;
          color: "blue"
          MouseArea{
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton
            z:2
            onPressed:{
              mouse.accepted=false
              console.log('pressed')
            }
          }
          MouseArea{
            anchors.fill: parent
            acceptedButtons: Qt.LeftButton
            z:-1
            onReleased:{
              console.log('released')
            }
          }
        }
      }
    }

    WebEngine = fetch online or local page, or write html, run javascript

    Windows only, see QML notes
    save the following two snippet in the same directory, name the html debug21.html
    also see the other advanced boilerplate below

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    import QtWebEngine 1.5
    MuseScore { 
      menuPath: "Plugins.debug21"
      pluginType: "dock"
      anchors.fill: parent
      Column{
        anchors.fill: parent
        Button{
          id:b
          height:30
          text:"clickme"
          onClicked:{
            v2.runJavaScript('approot.style.background="red"')
            v3.loadHtml('<div style="background:green;width:100%;height:100%"></div>')
          }
        }
        WebEngineView {
          width: parent.width
          height: (parent.height-b.height)/3
          url: "https://example.com"
        }
        WebEngineView {
          id:v2
          width: parent.width
          height: (parent.height-b.height)/3
          url: "debug21.html"
        }
        WebEngineView {
          id:v3
          width: parent.width
          height: (parent.height-b.height)/3
        }
      }
    }

    debug21.html

    <div id="approot" style="width:100%;height:100%"></div>

    WebEngine + WebChannel = hybrid UI html css js

    Windows only, see QML notes
    save the following two snippet in the same directory, name the html debug19.html
    advice: if large project, avoid direct mutations will help prevent nightmare
    also see the other simpler boilerplate above

    import MuseScore 3.0
    import QtQuick 2.9
    import QtQuick.Controls 2.2
    import QtWebEngine 1.5
    import QtWebChannel 1.0
    MuseScore{
      menuPath: "Plugins.debug19"
      pluginType: "dock"
      anchors.fill: parent
      QtObject {
        id: backendobj
        WebChannel.id: "backendid"
        property string backp: "nothing"
        signal post(string payload)
        function backf(payload){
          backconsole.text = "From front f: " + payload +'\n'+ backconsole.text
          return (payload+110)
        }
      }
      Column{
        anchors.top: wv.bottom
        spacing: 6
        Button {
          height: 12
          text: "Cast signal backendobj.post"
          onClicked: { backendobj.post(201319) }
        }
        Button {
          height: 12
          text: "Direct javascript"
          onClicked:{wv.runJavaScript(""
            + "   var t=document.querySelector('textarea').value;    "
            + "   document.querySelector('textarea').value = 'Direct javascript: 394' +'\\n'+ t;   "
          )}
        }
        Button {
          height: 12
          text: "Read backendobj.backp"
          onClicked: { backconsole.text = backendobj.backp +'\n'+ backconsole.text }
        }
        Text {
          id: backconsole
          text: "BackConsole"
          //onTextChanged: backendobj.post("BackConsoleCB")
        }
      }
      WebChannel {
        id: channel
        registeredObjects: [backendobj]
      }
      WebEngineView {
        id: wv
        width: parent.width
        height: parent.height/2
        url: "debug19.html"
        webChannel: channel
        onJavaScriptConsoleMessage: function(w,s,l,i){ console.log('Web console line '+l+' : '+s) } //pipe console
      }
    }

    debug19.html

    <button onclick="frontf()">Front f</button>
    <button onclick="backendobjread()">Direct backendobj.back read</button>
    <button onclick="backendobjwrite()">Direct backendobj.back write</button>
    <textarea id="debug" style="width:100%;height:60%"></textarea>
    <script src="qrc:///qtwebchannel/qwebchannel.js"></script>
    <script>
        var backend
        window.onload = function _(){
            new QWebChannel(qt.webChannelTransport, channel=>{
                backend = channel.objects.backendid;
                backend.post.connect(payload=>{
                    debug.value = ("Signal from backendobj.post: "+payload) +'\n'+ debug.value
                })
            })
        }
        // async backend.func and callback
        var frontf = _=>{ 
          backend.backf( 721 , r=>{
            debug.value = ("Back f CB: "+r) +'\n'+ debug.value 
          })
        }
        // direct read/write backend.prop 
        var backendobjread  = _=> debug.value = backend.backp +'\n'+ debug.value
        var backendobjwrite = _=> backend.backp = new Date() 
    </script>

    loop thru all channel, mute all

    see also this note

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      menuPath: "Plugins.debug64"
      function iterCh(cb){
        var parts = curScore.parts
        for (var i = 0; i < parts.length; i++) {
            var instrs = parts[i].instruments
            for (var j = 0; j < instrs.length; j++) {
                var channels = instrs[j].channels
                for (var k = 0; k < channels.length; k++) {
                    cb(parts[i],instrs[j],channels[k], i,j,k)
                }
            }
        }
      }
      onRun:{
        iterCh(function _(p,i,c, n,m,k){ 
          c.mute=true
        })
      }
    }

    log OS name, start another program commandline

    source the BandInMuseScore plugin
    also see receiving / passing MuseScore command line parameter

    import MuseScore 3.0
    import QtQuick 2.9
    MuseScore {
      anchors.fill: parent
      menuPath: "Plugins.debug42"
      QProcess {
        id: proc
      }
      onRun:{
        console.log("OS : "+Qt.platform.os)
        proc.start("cmd.exe /c calc");
        var val = proc.waitForFinished(30000);
        if (val) console.log("stdout : "+proc.readAllStandardOutput())
      }
    }

    setup keyboard shortcut in MuseScore from QML

    better use Plugin Manager's "Define Shortcut" instead, because running the following code more than once will bug out that key until restart
    source post

    import MuseScore 3.0
    import QtQuick 2.6
    import QtQuick.Window 2.2
    MuseScore {
        id: plugin
        pluginType: "dock"
        readonly property var window: Window.window
        Item { 
          id: someItem 
          focus:true
        }
        Shortcut {
            sequence: "Ctrl+D"
            context: Qt.ApplicationShortcut
            onActivated: {
                plugin.window.requestActivate();
                someItem.forceActiveFocus();
            }
        }
    }

    External links

    Element Analyser plugin which aims to provide a reusable library

    License and credits for this page only

    License info are for legal purpose of this webpage only.
    These are copied from default plugins and MuseScore files. Some contributor info are missing, please add credits wherever due.
    abc_import.qml
    Based on ABC Import by Nicolas Froment (lasconic)
    Copyright (2013) Stephane Groleau (vgstef)
    colornotes.qml
    Copyright (C) 2012 Werner Schweer
    Copyright (C) 2013-2017 Nicolas Froment, Joachim Schmitz
    Copyright (C) 2014 Jörn Eichler
    notenames-interactive.qml
    Copyright (C) 2012 Werner Schweer
    Copyright (C) 2013 - 2019 Joachim Schmitz
    Copyright (C) 2014 Jörn Eichler
    Copyright (C) 2020 MuseScore BVBA
    notenames.qml
    Copyright (C) 2012 Werner Schweer
    Copyright (C) 2013 - 2020 Joachim Schmitz
    Copyright (C) 2014 Jörn Eichler
    Copyright (C) 2020 Johan Temmerman
    run.qml
    Copyright (C)2012 Werner Schweer and others
    walk.qml
    jeetee
    Copyright (C) 2012-2017 Werner Schweer
    MuseScore
    Music Composition & Notation
    This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2as published by the Free Software Foundation. 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    These are for the WebEngine WebChannel boilerplate:
    msfp
    decovar Declaration of VAR
    source
    GPLv3