QML boilerplates
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
- 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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersimport 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

- 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:
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") } } } }