I'm trying to create an application that is extensible with plugins. Now the plugins should be able to add a Menu dynamically in the MenuBar.
From documentation I can find a MenuBar that is provided by QtLabsPlatform. This has a method addMenu. But Windows was not in the list of supported platforms. So I cannot benefit from it.
I tried the placeholder technique suggested in Error adding a Menu in QML, but this does not work with QtQuick.Controls 2.13
In the #timday answer in the question you indicate indicates the answer but does not show an example:
...
Dynamic creation of Menus is a little harder; see Qt.createQmlObject
or Qt.createComponent docs. (It may be simpler to just declare all the
ones you need in your code, but with their visible property wired to
whatever logic is appropriate). ...
(emphasis mine)
So my answer is just to show you how to do it although I think you want to add MenuItem to a Menu dynamically, instead of a Menu to a MenuBar:
import QtQuick 2.13
import QtQuick.Controls 2.13
ApplicationWindow {
id: root
width: 640
height: 480
visible: true
menuBar: MenuBar {
Menu {
id: plugins_menu
title: qsTr("&Plugins")
}
}
function onTriggered(item){
console.log(item.text)
}
Component.onCompleted:{
var plugin_names = ["plugin1", "plugin2", "plugin3"]
for(var i in plugin_names){
var item = Qt.createQmlObject('import QtQuick 2.13; import QtQuick.Controls 2.13; MenuItem {}',
plugins_menu)
item.text = plugin_names[i]
plugins_menu.addItem(item)
var f = function(it){
it.triggered.connect(function (){ root.onTriggered(it)
})}
f(item)
}
}
}
Related
https://doc.qt.io/qt-5/qml-qtquick-controls2-menubar.html
MenuBar is supported in ApplicationWindow and not in Window.
Following throws an error "Invalid property name: MenuBar"
Window
{
visible: true
width: 280; height: 280
menuBar: MenuBar {
Menu {}
}
}
whereas following works:
ApplicationWindow
{
visible: true
width: 280; height: 280
menuBar: MenuBar {
Menu {}
}
}
In the new Qt version 5.12, the default code uses Window and not ApplicationWindow.
What is the difference between Window and ApplicationWindow? Which one should be used in which case?
The docs is very clear:
ApplicationWindow is a Window that adds convenience for positioning items, such as MenuBar, ToolBar, and StatusBar in a platform independent manner.
That is, it is an item that inherits from Window but has certain default attributes, it is similar to QMainWindow with respect to QWidget.
When you say: In the new Qt version 5.12, the default code uses Window and not ApplicationWindow I suppose you mean that QtCreator uses Window by default in creating projects, that's just because the developers wanted to and there is no science in it, just a choice.
When you should use one or the other depends on when you want to customize and if you are comfortable with the ApplicationWindow structure since as you can see the latter has a predefined structure.
Note: There are 2 items called ApplicationWindow 1, 2
There's a plugin called Qt.labs.platform. Among other things, it provides tray icon with a menu. That menu has a list of MenuItems. Any menu item may have an icon, which would be displayed on the left side of item's text.
There's two ways of setting the icon, none of which work for me:
1) Version 1.0 defined iconSource and iconName properties.
Silently does not work, just shows no icon.
2) Revision 1.1 (declared as Q_REVISION(1)) introduces icon.name, icon.source and icon.mask "sub-properties" (not sure what's the right name for it?)
Fails QML engine with a message:
"MenuItem.icon" is not available in Qt.labs.platform 1.1.
I tried both import Qt.labs.platform 1.1 and 1.0.
Am I missing something in the mechanics of QML revisions or this is a bug in Qt?
A MenuItem is declared in qquickplatformmenuitem_p.h and defined in qquickplatformmenuitem.cpp files.
I'm using ArchLinux, KDE/Plasma. Some other apps (like electron-based) do have their icons in menu showing properly.
UPD Reported as a Qt bug.
I don't know what exactly you are doing to achieve your goal, because there is no minimal reproducible example.
But take a look at here
import QtQuick 2.12
import QtQuick.Window 2.12
// import Qt.labs.platform 1.1
import QtQuick.Controls 2.12
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Rectangle {
width: 200
height: 200
anchors.centerIn: parent
MenuBar {
id: menuBar
Menu {
id: firstMenu
MenuItem {
icon.name: "download"
icon.source: "icons/cloud-down-icon.png"
}
MenuItem {
text: qsTr("Rewrap Paragraph")
onTriggered: rewrapParagraph()
}
}
}
}
}
Note: put your icon inside the icons folder in the project folder.
If you click on the menu then you should get a dropdown with two items, first one is just an icon without the text (you can include some text by yourself), second item is just a text.
I am following this tutorial on YouTube and the person sets the TextField to fill the width of the RowLayout. However, it doesn't seem to work for me. I tried using Layout.fillWidth on the CheckBox and it seems to work perfectly fine but it doesn't seem to want to work on the TextField. Here is my code:
main.qml:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow
{
visible: true;
width: 640;
height: 480;
title: qsTr("Tabs");
ToDoList
{
anchors.centerIn: parent;
}
}
ToDoList.qml:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Frame
{
ListView
{
// Using implicit width and height allows the frame to automatically scale to the size of the list view
implicitWidth: 250
implicitHeight: 250
clip: true
model: 100
delegate: RowLayout {
width: parent.width
CheckBox {}
TextField
{
Layout.fillWidth: true
}
}
}
}
Here is a screenshot of what mine looks like
What did I do wrong?
I don't know if this has anything to do with it but I made a "Qt Quick Application - Swipe" instead of "Qt Quick Controls 2 Application" as that option wasn't available to me. Thanks in advance for any help.
Edit: I have written step by step instructions to replicate the issue below.
File > New File or Project
From the new window make sure "Application" is selected then click "Qt Quick Application - Swipe" and press "Choose"
Set any name for the project and click "Next"
Set the build system to "qmake" and click "Next"
Set the minimal required Qt version to "Qt 5.9" and the Qt quick controls style to "Material Dark" and click "Next"
Select the "Desktop Qt 5.12.0 MSVC2017 64bit" as the kit and click "Next"
Set the options to have no version control and click "Finish"
Delete "Page1Form.ui.qml" and "Page2Form.ui.qml" from the "Projects" pane
Replace the contents of "main.qml" with:
import QtQuick 2.9
import QtQuick.Controls 2.2
ApplicationWindow
{
visible: true;
width: 640;
height: 480;
title: qsTr("Tabs");
ToDoList
{
anchors.centerIn: parent;
}
}
Right click on the root project file and click "Add New"
From the new window make sure "Qt" is selected then click "QML File (Qt Quick 2)" and press "Choose"
Name the file "ToDoList" and click "Next"
Add to project "qml.qrc Prefix: /" then set the options to have no version control and click "Finish"
Replace the contents of "ToDoList.qml" with:
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
Frame
{
ListView
{
// Using implicit width and height allows the frame to automatically scale to the size of the list view
implicitWidth: 250
implicitHeight: 250
clip: true
model: 100
delegate: RowLayout {
width: parent.width
CheckBox {}
TextField
{
Layout.fillWidth: true
}
}
}
}
Run the project
The width is set properly. The problem is with TextField style. You may check it by setting background like
TextField
{
Layout.fillWidth: true
background: Rectangle {
color: "red"
}
}
Or just start typing into those fields with and without Layout.fillWidth: true
changing the style of a component, seems to replace all the features of the default style. is there a way to change only one feature?
For example, suppose i want a red button;
import QtQuick 2.7
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
ApplicationWindow
{
visible: true
width: 640
height: 480
Button
{
height: 200
width: 200
text: "Press me"
style: ButtonStyle
{
// changes background but also throws away everything else
// in standard button style
background: Rectangle { color: "red" }
}
}
}
re-defining ButtonStyle with a background works fine for changing the color of the button, but then everything else within the system default ButtonStyle is gone. For example, the border and the click highlight.
How to just change one feature and keep the rest?
Sorry if this has been asked before.
thanks,
Update
The above question was for Controls 1, but the same problem exists for Controls 2. Here's the same example code for Controls 2.
import QtQuick 2.7
import QtQuick.Controls 2.1
ApplicationWindow
{
visible: true
width: 640
height: 480
Button
{
height: 200
width: 200
text: "Press me"
// changes background but also throws away everything else
// in standard button style
background: Rectangle { color: "red" }
}
}
Preface
The purpose of QtQuick.Controls 1.4 was, to offer controls with native look and feel. If you want to have easier adjustable controls, and don't need the native look, you should consider the newer and faster QtQuick.Controls 2.0.
Main
What you desire is - afaik - impossible, as the default style consists out of two Rectangles and one Image where the Image seems to be the most important. You can find the images in the mingw-package at this location:
Qt\Qt5.7.0\5.7\mingw53_32\qml\QtQuick\Controls\Styles\Base\images
To access the objects of the control, you find them here:
Button {
id: but2
x: 50
onClicked: console.log(this.children[1].item.children[0].item.children[0], this.children[1].item.children[0].item.children[1], this.children[1].item.children[0].item.children[2])
}
So, the easiest solution, to pop into my mind is to use Colorize
import QtQuick 2.0
import QtQuick.Window 2.0
import QtQuick.Controls 1.4
import QtQuick.Controls.Styles 1.4
import QtGraphicalEffects 1.0
Window {
width: 1024
height: 800
visible: true
Button {
id: but
text: 'test'
}
Colorize {
source: but
anchors.fill: but
// values from the documentation example.
hue: 0.0
saturation: 0.5
lightness: -0.2
}
}
Or to be more general: To just adjust the styling, go for shaders.
QtQuick.Controls 2.0
There you have another case: the background is a Rectangle not just the Component. But if you do not assign one your self, it is only created after the Button.
Button {
id: but1
background.color: 'red'
}
is impossible, as the background is not instantiated when you try to assign the color.
You could use the Component.onCompleted handler to do this:
Button {
id: but1
Component.onCompleted: background.color = 'red'
}
But of course you overwrite the Binding of the original style, that handles the color-change while the Button is beeing pressed
Button {
id: but1
Component.onCompleted: background.color = Qt.binding(function() { return (but1.pressed ? 'red' : 'green') }
}
would enable the color-change again, but you won't have the original color.
You might retrive the orignal color by trying it out:
Button {
id: but1
onPressed: console.log(this.background.color)
onReleased: console.log(this.background.color)
}
This will output you the colors of the two states for pressed. But maybe there are more! So the easiest solution is to use a conditional Binding as this:
Button {
id: but1
}
Binding {
when: but1.pressed
target: but1
property: 'background.color'
value: 'red'
}
All QC2 types that derive from a Control have a member called palette. (See here) For some reason they don't mention this in the docs when they're describing how to customize the different controls. But you can look in the source code to see which palette colors you need to modify for your object. For instance, to change a Button's background to be red, you just need this:
Button
{
id: btn1
palette.button: "red"
}
You can change all buttons in your application by changing the palette in your ApplicationWindow, like this:
ApplicationWindow
{
id: mainWin
// Change default button background to be red
palette.button: "red"
Button
{
id: btn1
// Background will be red
}
Button
{
id: btn2
// You can of course override the default
palette.button: "green"
}
}
I am trying to wrap GridLayout inside an Item and exposing the GridLayout's data property as the default property of the item. But this results in two problems.
I get a crash when exiting the application.
This may in fact be a bug in Qt itself and it might also already been fixed, if not I will report it after fixing 2. I am only able to test on Windows 7 using Qt 5.7.0 MSVC2015 32.bit at the moment.
How to use the attached Layout property? Take look in the following example, which results in the error:
Non-existent attached object
on line
"Layout.alignment: Qt.AlignBottom | Qt.AlignRight".
Example:
//MyCustomLayout.qml
import QtQuick 2.7
import QtQuick.Layouts 1.3
Item {
default property alias data: layout.data
//Some other QML components not to be within GridView here.
GridLayout {
id: layout
anchors.fill: parent
}
//Some other QML components not to be within GridView here.
}
//main.qml
import QtQuick.Controls 2.0
ApplicationWindow {
id: root
visible: true
height: 1024
width: 768
MyCustomLayout {
anchors.fill: parent
Button {
Layout.alignment: Qt.AlignBottom | Qt.AlignRight
}
}
}