QML boilerplates

最終更新は 1 年前

If you are editing qmllearning, please upload a file too so that if the page is overwritten accidentally your code can still be retrieved. Discuss this page at https://musescore.org/en/node/345584

parkingb

source post

  • automatic window dimension based on UI content
  • use of the SystemPalette.Active and SystemPalette.Disabled for proper color management
  • Rely on DialogButtonBox for correct button placement
  • use as much as possible native MuseScore translations.
    The latter can be tricky because translations structure differs from MS3 and MS4.
    E.g. for a "title" label:
    text: mscoreMajorVersion > 3 ? qsTranslate("project", "Title") + ":" : qsTranslate("NewWizard", "Title") + ":"
  • automatic execution of code (such as settings management) when the window is closed.

    parkingb1.PNG

    import QtQuick 2.9
    import QtQuick.Controls 2.2
    import QtQuick.Layouts 1.3
    import QtQuick.Dialogs 1.2
    import QtQuick.Window 2.3
    import Qt.labs.settings 1.0
    import MuseScore 3.0
    
    MuseScore {
        menuPath: "Plugins." + qsTr("Plugin Name")
        version: "1.1.0"
        requiresScore: false
        description: qsTr("Create a new score based on exsiting template")
        pluginType: "dialog"
        id: mainWindow // required for setting MS4 properties and closing Window
    
    
        Component.onCompleted: {
            // Specific MS4 features
            if (mscoreMajorVersion >= 4) {
                mainWindow.title = qsTr("Plugin Name");
                mainWindow.thumbnailName = "logo.png";
                // mainWindow.categoryCode = "some category";
            }
        }
    
        onRun: {
            // check MuseScore version
            if (mscoreMajorVersion < 3) {
                mainWindow.visible = false
                    versionError.open()
            }
    
        }
    
        // Compute dimension based on content
        width: mainRow.implicitWidth + extraLeft + extraRight
        height: mainRow.implicitHeight + extraTop + extraBottom
        
        property int extraMargin: mainRow.anchors.margins ? mainRow.anchors.margins : 0
        property int extraTop: mainRow.anchors.topMargin ? mainRow.anchors.topMargin : extraMargin
        property int extraBottom: mainRow.anchors.bottomMargin ? mainRow.anchors.bottomMargin : extraMargin
        property int extraLeft: mainRow.anchors.leftMargin ? mainRow.anchors.leftMargin : extraMargin
        property int extraRight: mainRow.anchors.rightMargin ? mainRow.anchors.rightMargin : extraMargin
           
        // Signal onClosing on the main Window. This code is executed when the window closed
        // Rem: this generates some warnings in the plugin editor log, but this is ok
        Connections {
                target: mainWindow.parent.Window.window
                onClosing: {
                    // do whatever is required to do when the plugin window is closing such as managing the settings
                    console.log("onClosing: Saving some date to stettings");
                }
            }
        
        // UI
        ColumnLayout {
            id: mainRow // needed for reference in size computing
            spacing: 2
            anchors.margins: 0
    
            // Plugin controls
            GridLayout {
                Layout.margins: 20
                Layout.minimumWidth: 250
                Layout.minimumHeight: 200
                columnSpacing: 5
                rowSpacing: 5
    
                // debug
                columns: 1
                Rectangle {
                    color: "yellow"
                    width: 400
                    height: 300
                }
    
            } // GridLayout
    
            // Buttons
            DialogButtonBox {
                Layout.fillWidth: true
                spacing: 5
                alignment: Qt.AlignRight
                background.opacity: 0 // hide default white background
    
                standardButtons: DialogButtonBox.Ok | DialogButtonBox.Cancel
    
                Button {
                    // some non standard button placed on the left side
                    id: special
                    enabled: true // add real enabling test
                    DialogButtonBox.buttonRole: DialogButtonBox.ResetRole
                    text: qsTr("Special")
                }
    
                onAccepted: {
                    //... do the stuff ...
    
                    // and close
                    mainWindow.parent.Window.window.close();
                }
    
                onClicked: {
                    if (button === special) {
                        //... do some other stuff ...
                    }
                }
                onRejected: mainWindow.parent.Window.window.close();
    
            } // DialogButtonBox
    
            // Status bar (delete if not needed)
            RowLayout {
                Layout.fillWidth: true
                Layout.preferredHeight: txtStatus.height
                Layout.margins: 5
                spacing: 5
    
                Text {
                    id: txtStatus
                    text: "some status"
                    wrapMode: Text.NoWrap
                    elide: Text.ElideRight
                    maximumLineCount: 1
                    Layout.fillWidth: true
                }
            } // status bar
    
        } // ColumnLayout
    
        // Plugin settings
        Settings {
            id: settings
            category: "MyPlugin"
            // use alias on whatever UI controls
            //property alias subtitle: subtitle.text
            //property alias composer: composer.text
            //property alias lyricist: lyricist.text
            //property alias copyright: copyright.text
        }
    
        // Palette for nice color management
        SystemPalette {
            id: sysActivePalette;
            colorGroup: SystemPalette.Active
        }
        SystemPalette {
            id: sysDisabledPalette;
            colorGroup: SystemPalette.Disabled
        }
    
        // Version mismatch dialog
        MessageDialog {
            id: versionError
            visible: false
            title: qsTr("Unsupported MuseScore Version")
            text: qsTr("This plugin requires MuseScore 3.6later.")
            onAccepted: {
                mainWindow.parent.Window.window.close();
            }
        }
    
    } // MuseScore

qmllearning Dialog

Demo version 20230227A

20230227A.gif

  • automatic window dimension show all content
  • option to set fixed size instead
  • scrollable contentarea
  • does not require UI building pixel by pixel
  • does not use pluginType:Dialog
  • does not use quit or Qt.quit()
  • wrap and reflow Column{ Flow{ Item{} Item{}...}} (need to set a wrap width)

TODO

  • study parkingb's mainWindow.parent.Window.window.close();
  • Settings{}

Download: musescore_qmllearning_20230301A.zip

Overview version 20230227A:
20230227A_overview.PNG

View Raw:

import MuseScore 3.0
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2
MuseScore {
  menuPath: "Plugins.QMLlearning_Dialog"
  version: "20230301A"
  id: pluginscope
  property real wrapperwidth: 600 // use Screen if you need ratio based ui
  property bool fixsize:false
  property int  pagemargintb:10; property int pagemarginlr:20 // use Screen if you need ratio based ui
  property real customwidth: Screen.desktopAvailableWidth*.2
  property real customheight: Screen.desktopAvailableHeight*.2
  onRun:{
    if(fixsize){
      plugindialog.width =Math.min(customwidth  ,Screen.desktopAvailableWidth)
      plugindialog.height=Math.min(customheight ,Screen.desktopAvailableWidth)
    }else{
      // plugindialog doesn't auto expand by contentItem : qml bug ?
      plugindialog.width =Math.min( Math.max(wrapperwidth, plugincontent.width) ,Screen.desktopAvailableWidth)
      // height is tricky
      setTimeout.createObject(pluginscope,{ interval:1 ,running:true  }).triggered.connect(function(){ 
        plugindialog.height=Math.min(  grid.height+plugincontent.header.height+plugincontent.footer.height ,Screen.desktopAvailableHeight)
      })
    }
  }
  Component { id: setTimeout ; Timer {  } }
  Dialog { id:plugindialog
    title: "PluginDialog"
    visible:true
    contentItem:Page{ id:plugincontent
      header: ToolBar {  // ToolBar, TabBar, or DialogButtonBox 
        background: Rectangle { color: "skyblue"}
        RowLayout{
          anchors.fill: parent
          Label{
            horizontalAlignment: Qt.AlignHCenter
            verticalAlignment: Qt.AlignVCenter
            Layout.fillWidth: true
            text: "header"
            color: "navy"
          }
          ToolButton {
            text: 'btn1'
            onClicked: console.log('btn1')
          }
          ToolButton {
            text: 'btn2'
            onClicked: { console.log('btn2') }
          }
          ToolButton {
            text: 'btn3'
            onClicked: function(){ console.log('btn3') }
          }
        }
      }
      contentItem:Rectangle{ id:pagecontent
        color: "beige" 
        Flickable{ 
          anchors.fill:parent;
          contentWidth: grid.width; contentHeight: grid.height
          GridLayout{ id:grid
            columns: 1
            Column{ id:allcontent
              Layout.preferredWidth: pagecontent.width-Layout.leftMargin-Layout.rightMargin 
              Layout.leftMargin : pagemarginlr; Layout.rightMargin  :pagemarginlr
              Layout.topMargin  : pagemargintb; Layout.bottomMargin :pagemargintb
              Column{ id:innercontent
                width: parent.width
                //
                // Contents here
                Rectangle{ color: "red"         ;height: 20   ;width:parent.width }
                Rectangle{ color: "green"       ;height: 20   ;width:parent.width /2 }
                Rectangle{ color: "blue"        ;height: 20   ;width:parent.width /2 }
                Flow{
                  width: parent.width
                  Rectangle{ color: "cyan"      ;height:50  ;width:50 } //dont' assign width with Flow's width, it will cause infinite loop
                  Rectangle{ color: "yellow"    ;height:50  ;width:50 }
                  Rectangle{ color: "magenta"   ;height:50  ;width:50 }
                  Rectangle{ color: "black"     ;height:50  ;width:50 }
                  Rectangle{ color: "cyan"      ;height:50  ;width:50 }
                  Rectangle{ color: "yellow"    ;height:50  ;width:50 }
                  Rectangle{ color: "magenta"   ;height:50  ;width:50 }
                  Rectangle{ color: "black"     ;height:50  ;width:50 }
                  Rectangle{ color: "cyan"      ;height:20  ;width:20 }
                  Rectangle{ color: "yellow"    ;height:20  ;width:20 }
                  Rectangle{ color: "magenta"   ;height:20  ;width:20 }
                  Rectangle{ color: "black"     ;height:20  ;width:20 }
                  Rectangle{ color: "cyan"      ;height:20  ;width:20 }
                  Rectangle{ color: "yellow"    ;height:20  ;width:20 }
                  Rectangle{ color: "magenta"   ;height:20  ;width:20 }
                  Rectangle{ color: "black"     ;height:20  ;width:20 }
                  Rectangle{
                    color: "purple"
                    height:50; width:parent.parent.width/2  //safe to assign width with id=allcontent's width
                    Text {
                      anchors.fill:parent
                      verticalAlignment: Qt.AlignVCenter
                      text: 'VCenter'
                    }
                  }
                  Rectangle{
                    color: "pink"
                    height:50; width:parent.parent.width/4
                    Text {
                      anchors.centerIn:parent
                      text: 'Centered'
                    }
                  }
                  Button {
                    text: 'btnC'
                    onClicked: function(){ console.log('btnC') }
                  }
                  Text {
                    width:parent.parent.width
                    wrapMode: Text.WordWrap
                    text: "   Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Arcu cursus euismod quis viverra nibh cras. Vulputate enim nulla aliquet porttitor lacus. Sed cras ornare arcu dui. Dui vivamus arcu felis bibendum. Porttitor massa id neque aliquam vestibulum morbi blandit cursus risus. Nullam ac tortor vitae purus faucibus ornare suspendisse sed. Amet porttitor eget dolor morbi. Leo a diam sollicitudin tempor id eu. Vitae semper quis lectus nulla at. Quam adipiscing vitae proin sagittis nisl rhoncus. A erat nam at lectus. Pretium vulputate sapien nec sagittis aliquam malesuada. Rhoncus dolor purus non enim praesent elementum facilisis leo vel. Vestibulum sed arcu non odio euismod lacinia at quis risus. Turpis cursus in hac habitasse platea. Ut consequat semper viverra nam libero justo laoreet sit amet.\n   Hendrerit gravida rutrum quisque non tellus orci ac auctor. Scelerisque purus semper eget duis at tellus at. Suspendisse potenti nullam ac tortor. Adipiscing elit ut aliquam purus sit amet luctus. Massa sed elementum tempus egestas sed sed risus pretium. Risus quis varius quam quisque id. Ut sem nulla pharetra diam sit amet. Fames ac turpis egestas sed tempus. Elit eget gravida cum sociis. Adipiscing at in tellus integer feugiat. Lectus mauris ultrices eros in cursus turpis. Vel fringilla est ullamcorper eget nulla facilisi. At varius vel pharetra vel. Erat velit scelerisque in dictum non consectetur a. Lorem sed risus ultricies tristique nulla aliquet enim tortor at."
                  }
                }
                Rectangle{ color: "red"         ;height: 20   ;width:parent.width }
                Rectangle{ color: "green"       ;height: 20   ;width:parent.width /2 }
                Rectangle{ color: "blue"        ;height: 20   ;width:parent.width /2 }
              }
            }
          }
        }
      }
      footer: DialogButtonBox { //  ToolBar, TabBar, or DialogButtonBox  // ms3 Page footer is not bugged
        background: Rectangle { color: "darkgrey"}
        Button {
          text: 'OK'
          DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
        }
        Button {
          text: 'Cancel'
          DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
        }
        onAccepted: { console.log("ok") }
        onRejected: { console.log("cancel"); plugindialog.visible=false }
      }
    }
    // footer: Item{} // ms3 Dialog bug : Cannot assign to non-existent property "footer"
  }
}

Old versions:
qmllearning_20230227A.qml Add show all content, set fix size, scrollable contentarea
qmllearning_20230226A.qml
qmllearning_20230226B.qml : Add show all content (edit: revert back to A, B does not work in ms4)
qmllearning_20230226C.qml : Add optional set init size (edit: revert back to A, C does not work in ms4)

qmllearning Dockable

Same as the above dialog variant but use pluginType: "dock"
pluginscope.height not working

Download: musescore_qmllearning_20230301A.zip

View Raw:

import MuseScore 3.0
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.2
import QtQuick.Dialogs 1.2
import QtQuick.Window 2.2
MuseScore {
  menuPath: "Plugins.QMLlearning_Dockable"
  version: "20230301A"
  id: pluginscope
  pluginType: "dock"
  property real wrapperwidth: 600 // use Screen if you need ratio based ui
  property int  pagemargintb:10; property int pagemarginlr:20 // use Screen if you need ratio based ui
  property real customwidth: Screen.desktopAvailableWidth*.2
  property real customheight: Screen.desktopAvailableHeight*.2
  onRun:{
    pluginscope.height=Math.min(customheight ,Screen.desktopAvailableWidth)
  }
  Page{ id:plugincontent
    anchors.fill: parent
    header: ToolBar {  // ToolBar, TabBar, or DialogButtonBox 
      background: Rectangle { color: "skyblue"}
      RowLayout{
        anchors.fill: parent
        Label{
          horizontalAlignment: Qt.AlignHCenter
          verticalAlignment: Qt.AlignVCenter
          Layout.fillWidth: true
          text: "header"
          color: "navy"
        }
        ToolButton {
          text: 'btn1'
          onClicked: console.log('btn1')
        }
        ToolButton {
          text: 'btn2'
          onClicked: { console.log('btn2') }
        }
        ToolButton {
          text: 'btn3'
          onClicked: function(){ console.log('btn3') }
        }
      }
    }
    contentItem:Rectangle{ id:pagecontent
      color: "beige" 
      Flickable{ 
        anchors.fill:parent;
        contentWidth: grid.width; contentHeight: grid.height
        GridLayout{ id:grid
          columns: 1
          Column{ id:allcontent
            Layout.preferredWidth: pagecontent.width-Layout.leftMargin-Layout.rightMargin 
            Layout.leftMargin : pagemarginlr; Layout.rightMargin  :pagemarginlr
            Layout.topMargin  : pagemargintb; Layout.bottomMargin :pagemargintb
            Column{ id:innercontent
              width: parent.width
              //
              // Contents here
              Rectangle{ color: "red"         ;height: 20   ;width:parent.width }
              Rectangle{ color: "green"       ;height: 20   ;width:parent.width /2 }
              Rectangle{ color: "blue"        ;height: 20   ;width:parent.width /2 }
              Flow{
                width: parent.width
                Rectangle{ color: "cyan"      ;height:50  ;width:50 } //dont' assign width with Flow's width, it will cause infinite loop
                Rectangle{ color: "yellow"    ;height:50  ;width:50 }
                Rectangle{ color: "magenta"   ;height:50  ;width:50 }
                Rectangle{ color: "black"     ;height:50  ;width:50 }
                Rectangle{ color: "cyan"      ;height:50  ;width:50 }
                Rectangle{ color: "yellow"    ;height:50  ;width:50 }
                Rectangle{ color: "magenta"   ;height:50  ;width:50 }
                Rectangle{ color: "black"     ;height:50  ;width:50 }
                Rectangle{ color: "cyan"      ;height:20  ;width:20 }
                Rectangle{ color: "yellow"    ;height:20  ;width:20 }
                Rectangle{ color: "magenta"   ;height:20  ;width:20 }
                Rectangle{ color: "black"     ;height:20  ;width:20 }
                Rectangle{ color: "cyan"      ;height:20  ;width:20 }
                Rectangle{ color: "yellow"    ;height:20  ;width:20 }
                Rectangle{ color: "magenta"   ;height:20  ;width:20 }
                Rectangle{ color: "black"     ;height:20  ;width:20 }
                Rectangle{
                  color: "purple"
                  height:50; width:parent.parent.width/2  //safe to assign width with id=allcontent's width
                  Text {
                    anchors.fill:parent
                    verticalAlignment: Qt.AlignVCenter
                    text: 'VCenter'
                  }
                }
                Rectangle{
                  color: "pink"
                  height:50; width:parent.parent.width/4
                  Text {
                    anchors.centerIn:parent
                    text: 'Centered'
                  }
                }
                Button {
                  text: 'btnC'
                  onClicked: function(){ console.log('btnC') }
                }
                Text {
                  width:parent.parent.width
                  wrapMode: Text.WordWrap
                  text: "   Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Arcu cursus euismod quis viverra nibh cras. Vulputate enim nulla aliquet porttitor lacus. Sed cras ornare arcu dui. Dui vivamus arcu felis bibendum. Porttitor massa id neque aliquam vestibulum morbi blandit cursus risus. Nullam ac tortor vitae purus faucibus ornare suspendisse sed. Amet porttitor eget dolor morbi. Leo a diam sollicitudin tempor id eu. Vitae semper quis lectus nulla at. Quam adipiscing vitae proin sagittis nisl rhoncus. A erat nam at lectus. Pretium vulputate sapien nec sagittis aliquam malesuada. Rhoncus dolor purus non enim praesent elementum facilisis leo vel. Vestibulum sed arcu non odio euismod lacinia at quis risus. Turpis cursus in hac habitasse platea. Ut consequat semper viverra nam libero justo laoreet sit amet.\n   Hendrerit gravida rutrum quisque non tellus orci ac auctor. Scelerisque purus semper eget duis at tellus at. Suspendisse potenti nullam ac tortor. Adipiscing elit ut aliquam purus sit amet luctus. Massa sed elementum tempus egestas sed sed risus pretium. Risus quis varius quam quisque id. Ut sem nulla pharetra diam sit amet. Fames ac turpis egestas sed tempus. Elit eget gravida cum sociis. Adipiscing at in tellus integer feugiat. Lectus mauris ultrices eros in cursus turpis. Vel fringilla est ullamcorper eget nulla facilisi. At varius vel pharetra vel. Erat velit scelerisque in dictum non consectetur a. Lorem sed risus ultricies tristique nulla aliquet enim tortor at."
                }
              }
              Rectangle{ color: "red"         ;height: 20   ;width:parent.width }
              Rectangle{ color: "green"       ;height: 20   ;width:parent.width /2 }
              Rectangle{ color: "blue"        ;height: 20   ;width:parent.width /2 }
            }
          }
        }
      }
    }
    footer: DialogButtonBox { //  ToolBar, TabBar, or DialogButtonBox  // ms3 Page footer is not bugged
      background: Rectangle { color: "darkgrey"}
      Button {
        text: 'OK'
        DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
      }
      Button {
        text: 'Cancel'
        DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
      }
      onAccepted: { console.log("ok") }
      onRejected: { console.log("cancel") }
    }
  }
}