How dynamically creates Popup in QML - qt

When I try to dynamically creates Popup with Qt.createQmlObject(...) or Qt.createComponent(...), I got exception:
QML Popup: cannot find any window to open popup in.
Here is my code:
var popup1 = Qt.createQmlObject('import QtQuick 2.8; import QtQuick.Controls 2.1; Popup { id: popup; x: 100; y: 100; width: 200; height: 300; modal: true; focus: true; closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent; visible: false }',
window,
"DynamicPopup");
popup1.open()
var popupComponent = Qt.createComponent("qrc:/TestPopup.qml")
var popup2 = popupComponent.createObject(window);
popup2.open()
TestPopup.qml:
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
Popup {
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
visible: false
}

Popup is not inheriting QQuickItem, and by default it is parented by QML Window, which is not instantiated if you are using QQuickWidget. Thus passing parent should be done as follows:
var popupComponent = Qt.createComponent("qrc:/TestPopup.qml")
var popup2 = popupComponent.createObject(window, {"parent" : window});
popup2.open()

The parent must be an element that inherits from QQuickItem
Example:
import QtQuick 2.6
import QtQuick.Window 2.2
import QtQuick.Controls 2.1
Window {
id: win
visible: true
width: 640
height: 480
title: qsTr("Hello World")
Row{
Button{
id: item1
text: "btn1"
onClicked: {
var popup1 = Qt.createQmlObject('import QtQuick 2.8; import QtQuick.Controls 2.1; Popup { id: popup; x: 100; y: 100; width: 200; height: 300; modal: true; focus: true; closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent; visible: false }',
item1,
"DynamicPopup");
popup1.open()
}
}
Button{
id: item2
text: "btn2"
onClicked: {
var popupComponent = Qt.createComponent("qrc:/TestPopup.qml")
var popup2 = popupComponent.createObject(item2);
popup2.open()
}
}
}
}
method 1:
method 2:

A Popup needs to be be parented to an Item, window isn't one.
You should use window.contentItem instead.

A good approach to dynamically load a popup with a Loader:
Loader {
id: popupLoader
active: false
source: "qrc:/TestPopup.qml"
onLoaded: item.open()
}
function openMyPopup() {
if( popupLoader.active )
popupLoader.item.open()
else
popupLoader.active = true
}

Related

How to set objectName for PopupItem from QML?

QML Popup and derived controls are creating a PopupItem object which is a visual representation of it, but Popup itself is parented to the contentData of the application window. objectName specified for Popup is not applied to PopupItem. For example, the following application:
import QtQuick 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Popup Test")
Button {
text: "Open"
onClicked: dummyPopup.open()
}
Popup {
id: dummyPopup
objectName: "dummyPopup"
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
}
}
creates PopupItem with empty objectName
Is there a way to set objectName for PopupItem from QML?
Set the objectName of its contentItem upon completion:
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Popup Test")
Button {
text: "Open"
onClicked: dummyPopup.open()
}
Popup {
id: dummyPopup
objectName: "dummyPopup"
x: 100
y: 100
width: 200
height: 300
modal: true
focus: true
Component.onCompleted: {
contentItem.objectName = "foo"
print(contentItem)
}
}
}
By the way, if this is for auto tests, I have a hack in C++ that avoids the need to give an objectName to the contentItem:
QObject *TestHelper::findPopupFromTypeName(const QString &typeName) const
{
QObject *popup = nullptr;
foreach (QQuickItem *child, overlay->childItems()) {
if (QString::fromLatin1(child->metaObject()->className()) == "QQuickPopupItem") {
if (QString::fromLatin1(child->parent()->metaObject()->className()).contains(typeName)) {
popup = child->parent();
break;
}
}
}
return popup;
}
You can then use that function like this in your test:
const QObject *newProjectPopup = findPopupFromTypeName("NewProjectPopup");
QVERIFY(newProjectPopup);
QTRY_VERIFY(newProjectPopup->property("opened").toBool());

Why can't QML key events be intercepted?

I have the following code:
MyTest.qml
import QtQuick 2.0
FocusScope {
anchors.fill: parent
Keys.onReturnPressed: {
console.log("++++++++++")
}
}
main.qml
import QtQuick 2.9
import QtQuick.Window 2.2
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
MyTest {
focus: true
Keys.onReturnPressed: {
console.log("==========")
event.accepted = true
}
}
}
The output is:
++++++++++
==========
What is event.accepted = true invalid?
I want to intercept keystroke events in Window and process the event on ly in Window (only output "=========="). How to do that?
You can't disconnect a method when you use onReturnPressed: {} definition.
You have to use Connections for that.
A quick example:
import QtQuick 2.9
import QtQuick.Controls 1.4
Item {
id: myItem
anchors.fill: parent
property bool acceptEvents: true
signal returnPressed()
Keys.onReturnPressed: returnPressed()
Connections {
id: connection
target: myItem
enabled: acceptEvents
onReturnPressed: {
console.log("++++++++++")
}
}
}
Window {
visible: true
width: 640
height: 480
title: qsTr("Hello World")
VolumeSlider {
id: obj
anchors.fill: parent
focus: true
Keys.onReturnPressed: {
console.log("==========")
event.accepted = true
}
Component.onCompleted: {
obj.acceptEvents = false; // Remove connections
}
}
}

How can I assign an ordering number to dynamically created component in qml?

My code (actually an official example) can draw markers and polylines on the point which I clicked. And I want that every marker has their own Text which represents its order. Text "1" for the first marker, and Text "2" for the second marker, for example. But markerCount(declared in componentCreation.js) for the Text does not increase, so all of the Text of the marker is "1" which is a default.
In the code, Rectangle which is MapQuickItem's child represents a marker, and it is dynamically created by createElements() (componentCreation.js). markerCount++ is implemented in Component.onCompleted.
The code is:
componentCreation.js
var arrayLines = []
var lineComplete = false
var markerCount = 1
function createElements(point) {
console.log(markerCount)
var componentMarker = Qt.createComponent("Marker.qml");
if (componentMarker.status === Component.Ready) {
var markerFirstCorner = componentMarker.createObject(map);
markerFirstCorner.coordinate = map.toCoordinate(point)
map.addMapItem(markerFirstCorner)
} else {
console.log("Marker not created")
}
var theLine
if (arrayLines.length === 0) {
createLine(point)
} else {
theLine = arrayLines[arrayLines.length-1]
theLine.mainPolyline.addCoordinate(map.toCoordinate(point))
}
}
function createLine(point){
var componentLine = Qt.createComponent("Line.qml")
if (componentLine.status === Component.Ready) {
var lineFirstCorner = componentLine.createObject(map);
lineFirstCorner.mainPolyline.addCoordinate(map.toCoordinate(point))
map.addMapItem(lineFirstCorner)
arrayLines.push(lineFirstCorner)
} else {
console.log("Line not created")
}
}
main.qml
import QtQuick 2.11
import QtQuick.Window 2.11
import QtLocation 5.11
import QtPositioning 5.8
import QtQuick.Controls 2.1
import "componentCreation.js" as MyScript
ApplicationWindow {
id: applicationWindow
visible: true
width: 640
height: 480
Plugin {
id: mapPlugin
name: "googlemaps"
}
Map {
id: map
anchors.fill: parent
zoomLevel: 12
plugin: mapPlugin
center: QtPositioning.coordinate(35.8926195, 128.6000172)
MouseArea{
id: mouseArea
anchors.fill: parent
z: 1
onClicked: {
console.log("Before creation : " + MyScript.markerCount)
var point = Qt.point(mouse.x, mouse.y)
console.log()
console.log("You clicked : " + map.toCoordinate(point))
MyScript.createElements(Qt.point(mouse.x,mouse.y))
}
}
}
}
Marker.qml
import QtQuick 2.0
import QtLocation 5.11
import "componentCreation.js" as MyScript
MapQuickItem {
property alias marker: marker
id: marker
sourceItem: Rectangle {
width: 50
height: 50
color: "transparent"
Image {
anchors.fill: parent
source: "images/drone.svg" // Ignore warnings from this
sourceSize: Qt.size(parent.width, parent.height)
}
Text {
anchors.fill: parent
text: { MyScript.markerCount }
}
Component.onCompleted: {
MyScript.markerCount++
console.log("markerCount: " + MyScript.markerCount)
}
}
opacity: 1.0
anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
}
Line.qml
import QtQuick 2.0
import QtLocation 5.8
MapPolyline {
property alias mainPolyline: mainPolyline
id: mainPolyline
line.width: 3
line.color: 'black'
}
I'm new to Qt and Qml. I don't know why markerCount does not increase. Please tell me why or give me another way to order the markers.
Thank you for your help.
You are complicating yourself too much, in case you want to store a lot of information the correct thing is to use a model, in this case ListModel, and a view, in this case MapItemView, that has as a delegate the Marker, then use a property to save the index that it is obtained by using the count property of the model:
Marker.qml
import QtQuick 2.0
import QtLocation 5.11
MapQuickItem {
id: marker
property alias text: txt.text
sourceItem: Rectangle {
width: 50
height: 50
color: "transparent"
Image {
anchors.fill: parent
source: "images/drone.svg" // Ignore warnings from this
sourceSize: Qt.size(parent.width, parent.height)
}
Text {
id: txt
anchors.fill: parent
}
}
opacity: 1.0
anchorPoint: Qt.point(sourceItem.width/2, sourceItem.height/2)
}
main.qml
import QtQuick 2.11
import QtQuick.Window 2.11
import QtLocation 5.11
import QtPositioning 5.8
import QtQuick.Controls 2.1
ApplicationWindow {
id: applicationWindow
visible: true
width: 640
height: 480
Plugin {
id: mapPlugin
name: "googlemaps"
}
ListModel{
id: md
}
Map {
id: map
anchors.fill: parent
zoomLevel: 12
plugin: mapPlugin
center: QtPositioning.coordinate(35.8926195, 128.6000172)
MapItemView{
model: md
delegate: Marker{
text: title
coordinate: QtPositioning.coordinate(coords.latitude, coords.longitude)
}
}
Line{
id: li
}
MouseArea{
id: mouseArea
anchors.fill: parent
z: 1
onClicked: {
var point = Qt.point(mouse.x, mouse.y)
var coord = map.toCoordinate(point);
var text = md.count + 1;
md.append({"coords": coord, "title": text})
li.addCoordinate(coord)
}
}
}
}
Line.qml
import QtQuick 2.0
import QtLocation 5.8
MapPolyline {
id: mainPolyline
line.width: 3
line.color: 'black'
}

How to draw an element over following items in QML layouts

I'm working on a DatePicker control for QtQuick.Controls 2.0 I have problem on drawing popup calendar over other page items. does anyone help me on this ?
Component Screenshot
My DatePicker source code:
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Controls 1.4 as OldControls
Rectangle {
id: root
width: childrenRect.width
height: childrenRect.height
clip: true
property bool expanded: false
property bool enabled: true
property alias selectedDate: cal.selectedDate
MouseArea {
height: expanded ? txt.height + cal.height : txt.height
width: expanded ? Math.max(txt.width, cal.width) : txt.width
hoverEnabled: true
enabled: root.enabled
onHoveredChanged: {
expanded = root.enabled && containsMouse
}
TextField {
id: txt
enabled: root.enabled
text: cal.selectedDate
inputMask: "0000-00-00"
}
OldControls.Calendar {
id: cal
anchors.top: txt.bottom
anchors.left: txt.left
visible: expanded
}
}
}
and here is my DatePicker usage in page:
import QtQuick 2.0
import QtQuick.Layouts 1.0
import QtQuick.Controls 2.2
import "./Components"
Item {
anchors.fill: parent
GridLayout {
columns: 2
Text { text: qsTr("Date Filter: ") }
DatePicker {}
Text { text: qsTr("name filter: ") }
TextField {}
}
}

QML2 ApplicationWindow Keys handling

Is there any way to handle key press events in ApplicationWindow of QtQuick.Controls component? Documentation of Qt5.3 does not provide any way to do this. Also, it says that Keys is only exists in Item-objects . When I try to handle key press event it says "Could not attach Keys property to: ApplicationWindow_QMLTYPE_16(0x31ab890) is not an Item":
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Controls.Styles 1.1
import QtQuick.Window 2.1
ApplicationWindow {
id: mainWindow
visible: true
width: 720
height: 405
flags: Qt.FramelessWindowHint
title: qsTr("test")
x: (Screen.width - width) / 2
y: (Screen.height - height) / 2
TextField {
id: textField
x: 0
y: 0
width: 277
height: 27
placeholderText: qsTr("test...")
}
Keys.onEscapePressed: {
mainWindow.close()
event.accepted = true;
}
}
ApplicationWindow {
id: mainWindow
Item {
focus: true
Keys.onEscapePressed: {
mainWindow.close()
event.accepted = true;
}
TextField {}
}
}
Maybe this will help some.
Using a Shortcut doesn't require focus to be set.
ApplicationWindow {
id: mainWindow
Shortcut {
sequence: "Esc"
onActivated: mainWindow.close()
}
}
}

Resources