How to delete QML object - c++

I am trying to delete QML object and recreate object like this:
Rectangle{
property var obj
signal videoStopped(variant complete)
function recreate(url){
if(!obj){
console.log("createObject")
obj = videoComponet.createObject(root)
obj.stopped.connect(function(){
videoStopped(obj.status == MediaPlayer.EndOfMedia)
})
}
obj.source = url
obj.play()
}
function stop(){
obj.destroy() // obj.deleteLater()
}
Component{
id: videoComponet
Video {
anchors.fill: parent
visible: true
autoPlay: true; autoLoad: true
}
}
}
C++ side call recreate to generate an object and call stop to delete it.
recreate ⇒ console output createObject
↓
stop
↓
recreate ⇒ console no output
Both obj.destroy() and obj.deleteLater() not worked.
How to forcedly delete the dynamically created object just like delete in C++.

A minor change to S.M.Mousavi's answer:
I observed that setting sourceComponent to undefined does not work. I have to set it to null to unload the component. Another option is to set the source to empty string.
onClicked: {
loader.sourceComponent = null; //causes destroying loaded component
}
OR
onClicked: {
loader.source = ""; //causes destroying loaded component
}

Use Loader instead.
It loads an Item dynamically and you can destroy it by setting sourceComponent property to undefined:
Loader {
id: loader
}
Component {
id: myDynComp
Rectangle {
width: 40; height: 40
anchors.centerIn: parent
}
}
Row {
Button {
width: 120; height: 40
text: "Load"
onClicked: {
loader.sourceComponent = myDynComp; //load/create component dynamically
}
}
Button {
width: 120; height: 40
text: "UnLoad"
onClicked: {
loader.sourceComponent = undefined; //causes destoying loaded component
}
}
}

Related

Return from child qml to main one

I have several QML files, main one is the one that opens the ApplicationWindow when ever I try to get back from child QML to main, new window is opened again!
how can I prevent of doing this?
I thought that enabling a flag in child QML, but there may be other ways!
I tried stackview in QML
Is there any way to prevent from opening new page when I get back to main QML?
create a loader in main window and call every page into that loader when ever you need to change page just change loader's source
Window {
Loader{
id:myLoader
anchors.fill: parent
source: "LoginPage.qml"
}
Connections{
target: myLoader.item
onBack_clicked:{
loginid = ""
myLoader.source = "LoginPage.qml"
}
onSetting_clicked:{
myLoader.source = "Setting.qml"
}
}
}
and for child qml files : (for me Setting.qml)
Item {
signal back_clicked()
Button {
id: button1
anchors.right: parent.right
anchors.rightMargin: 15
onClicked: {
back_clicked()
}
}
}
but if you want to not destroy old page use SwipeView Or StackView:
SwipeView {
id: swipeView
clip: true
currentIndex: 0
Item{
id:firstPage
clip:true
//your page
}
Item{
id:secondPage
clip:true
//your page
}
}
and to change pages just change currentIndex
swipeView.currentIndex = 1
UPDATE:
StackView {
id: stackView
initialItem: one
}
Component {
id: one
Item{//your first page }
}
Component {
id: two
Item{//your second page }
}
and to push your pages :
stackView.push({ item: two})
//or
stackView.push("MySecondView.qml")
to get back to old or main page just pop it :
stackView.pop()

QML TreeView display nodes by levels or custom delegate

I have a tree model derived from a QAbstractItemModel. And I can display the data in a tree like way.
What I want is to display teh data by the layers. To display only one level of a layer at a time AND put each layer on a stack and navigate backwards by poping the layer from the stack.
I guess I have to implement a custom delegate? Any advice would be highly appreciated. Thank you.
I recently implemented something similar, based on a QFileSystemModel, set as a qml contextProperty, named treeModel in the example below.
The idea was to keep track of the current QModelIndex, and to use the data() & rowCount() functions of the QAbstractItemModel to get the actual model data, and to use a recursive stack view for the navigation
General layout
ApplicationWindow {
id: main
visible: true
width: 640
height: 480
ColumnLayout
{
anchors.fill: parent
// Breadcrumb
SEE BELOW
// View
StackView
{
id: stackView
Layout.fillHeight: true
Layout.fillWidth: true
initialItem: TreeSlide {}
}
}
}
TreeSlide
The view itself is pretty simple. I didn't used anything fancy here, and it displays only one role, but you could extend it without trouble. Note that the view's model is NOT your treeModel, but instead just the rowCount for the rootIndex.
ListView
{
Layout.fillHeight: true
Layout.fillWidth: true
model: treeModel.rowCount(rootIndex)
clip: true
snapMode: ListView.SnapToItem
property var rootIndex
// I used a QFileSytemModel in my example, so I had to manually
// fetch data when the rootIndex changed. You may not need this though.
onRootIndexChanged: {
if(treeModel.canFetchMore(rootIndex))
treeModel.fetchMore(rootIndex)
}
Connections {
target: treeModel
onRowsInserted: {
rootIndexChanged()
}
}
delegate: ItemDelegate {
property var modelIndex: treeModel.index(index,0, rootIndex)
property bool hasChildren: treeModel.hasChildren(modelIndex)
width: parent.width
text: treeModel.data(modelIndex)
onClicked: {
if(hasChildren)
{
// Recursively add another TreeSlide, with a new rootIndex
stackView.push("TreeSlide.qml", {rootIndex: modelIndex})
}
}
}
}
Breadcrumb
To navigate the model, instead of a simple back button, I used a kind of dynamic breadcrumb
// Breadcrumb
RowLayout
{
Repeater
{
id: repeat
model: {
var res = []
var temp = stackView.currentItem.rootIndex
while(treeModel.data(temp) != undefined)
{
res.unshift(treeModel.data(temp))
temp = temp.parent
}
res.unshift('.')
return res
}
ItemDelegate
{
text : modelData
onClicked: {
goUp(repeat.count - index-1)
}
}
}
}
the goUp function simply goes up the stack by poping items
function goUp(n)
{
for(var i=0; i<n; i++)
stackView.pop()
}
To to do it completely by guides we should use DelegateModel and DelegateModel.rootIndex
DelegateModel {
id: delegateSupportPropConfigModel
model: supportModel
delegate: SupportPropConfigListItem {
id: currentItem
width: scrollRect2.width - 60
fieldName: model.fieldName
fieldValue: model.value
onClick:{
delegateSupportPropConfigModel.rootIndex = supportPropConfigModel.index(0, 0, supportPropConfigModel)
}
}
}
Column {
id: columnSettings
spacing: 2
Repeater {
model: delegateSupportPropConfigModel
}
}

Signal emit Issue - Listview is not showing full list

I am binding a ListView with values passed from the cpp.
Issue: Listview displays only one row, mean first value, The rest of the rows are not appeared.
Checked:
I created an ListModel/ListElement in main.qml as test and bind with ListView, Now the Listview just working fine, display all values
I suspect after the signal emit, the error occurs.
Code snippet:
main.qml
ListView {
id: idListView
anchors {
left: parent.left
leftMargin: 10 * scaleFactor
right: parent.right
rightMargin: 10 * scaleFactor
top: rectangleToolBar.bottom
topMargin: 10 * scaleFactor
bottom: rectangleStatusBar.top
bottomMargin: 10 * scaleFactor
}
// model: objHomeController.detailsModel // Display only one row
//model: idListmodel //Working fine
delegate: comsearchDelegate
spacing: 10 * scaleFactor
clip: true
highlight: Rectangle {
color: 'grey'
Text {
anchors.centerIn: parent
color: 'white'
}
}
focus: true
}
Component {
id: comsearchDelegate
Row {
spacing: 10 * scaleFactor
Column {
Layout.alignment: Qt.AlignTop
Text { text: title; font { pixelSize: 14 * scaleFactor; bold: true } }
Text { text: description; font { pixelSize: 14 * scaleFactor; bold: true } }
}
}
}
ListModel {
id: idListModel
ListElement{
title : "sdfsdf";
description:"sdfsdfs";
}
ListElement {
title : "sdfsdf";
description:"sdfsdfs";
}
ListElement {
title : "sdfsdf";
description:"sdfsdfs";
}
ListElement {
title : "sdfsdf";
description:"sdfsdfs";
}
}
HomeController.h
Q_PROPERTY(Model* detailsModel READ get_detailsModel WRITE set_detailsModel NOTIFY detailsModelChanged )
HomeController.cpp
void HomeController::set_detailsModel(Model* value)
{
m_detailsModel = value;
//value has correct values - checked.
emit detailsModelChanged(value);
}
Model* HomeController::get_detailsModel(void)
{
return m_detailsModel;
}
void HomeController::getAllData()
{
m_detailsModel->clear();
m_detailsModel->updateModel(eveReadXML());
set_detailsModel(m_detailsModel);
}
Model.cpp
void Model::updateModel(const QList<Details> & details)
{
if(this->rowCount() > 0) {
this->clear();
}
beginInsertRows(QModelIndex(),rowCount(),rowCount());
m_modelData.append(details);
endInsertRows();
}
Since I came from .Net background, I would like to understand binding a Listview/GridView to a DataTable or an XML. Here I followed, Created class called Details [Details.h] and created Model.h/Model.cpp and fetching the value from there and binding to ListView. Am I doing right, Or do we have other flow. Any tutorial/Codesnippet/Link for projects highly appreciated.
To define ListModel from c++, you need to subclass QAbstractListModel
https://doc.qt.io/qt-5/qabstractlistmodel.html
You can take example on QQmlObjectListModel in this project : http://gitlab.unique-conception.org/qt-qml-tricks/qt-qml-models
Or clone it and use it in your project as follow :
Q_PROPERTY(QQmlObjectListModel<Details>* detailsModel READ get_detailsModel WRITE set_detailsModel NOTIFY detailsModelChanged)

How to prevent recreation of page in a loader?

I am developing a Qt app on Win/Android. My question is very simple.
When my app starts, first a login page welcomes you.
If you want to configure server settings, ServerInfo.qml is opened in a loader. The login page and ServerInfo are loaded in the same loader.
My problem is that when I close ServerInfo.qml, then load loginpage.qml to loader, the loader creates a new instance of loginpage.qml. I don't want the page to be created again.
Here is my Qml code :
ApplicationWindow {
id:mainwindow
visible: true
width: 600
height: 800
x: Screen.width / 2 - width / 2
y: Screen.height / 2 - height / 2
menuBar:MenuBar{
Menu {
title:"Edit"
MenuItem {
text:"Sunucu Ayarları"
onTriggered: {
loader.source="ServerConfig.qml"
loader.anchors.centerIn=main
}
}
MenuItem {
text:"Çıkış"
onTriggered: {
Qt.quit();
}
}
}
}
Connections {
ignoreUnknownSignals: true
target: process
onProcessstart: {
busyrec.visible=true;
busyloader.item.busytext=text;
busyloader.item.busyrunnig=true;
}
onProcessstop: {
busyloader.item.busytext=text;
busyloader.item.busyrunnig=false;
busyloader.item.busytextcolor="blue"
}
Component.onCompleted: {
// process.onSuccesLogin();
//TaskResultm.taskresult.Malzemeler.push
console.log(TaskResultm.taskresult.serilaze());
}
}
Column {
anchors.fill: parent
Rectangle {
id:busyrec
width: parent.width
height: (parent.height/10)
visible:true
color:"green"
Loader {
id:busyloader
source:"BusyIndicator.qml"
anchors.fill: parent
}
Connections {
ignoreUnknownSignals: true
}
}
Rectangle {
id: main
// anchors.fill: parent
width: parent.width
height: (parent.height/10)*9
Loader {
id:loader
source: "LoginPage.qml"
anchors.centerIn:parent
focus:true
property bool valid: item !== null
}
Connections {
ignoreUnknownSignals: true
target: loader.valid? loader.item : null
onDirecttomainpage:{
// process.getWorkOrderList();
busyloader.item.switenabled=true;
busyloader.item.switopacity=1;
loader.anchors.fill=main;
loader.source="TaskNavigationMainScreen.qml";
}
onServerinfopageclose: {
loader.source="LoginPage.qml";
loader.anchors.centerIn=main;
}
}
}
}
onClosing: {
if(Qt.platform.os=="android") {
if(loader.item!==null)
{
if(loader.item.objectName==="tasknavigationmain")
if(loader.item.zemin===0)
close.accepted=true;
else
close.accepted=false;
}
}
else if (Qt.platform.os=="windows")
{
Qt.quit();
//if(loader.item!==null)
// if(loader.item.objectName==="tasknavigationmain")
// console.log(loader.item.stackViewItem.depth);
}
}
}
Just use a StackView instead of a Loader, it will keep previous "forms" alive as you push new ones on top, and you can always go back and forth.
A loader will load a single element, if you load another, the old one will be destroyed, there is no way around that.

Implementing the back button on bb10

I have one navigation pane and I disabled the back button in the main.qml, however I want to display the back button again to some part of the app. How do i implement this? here's my cpp
pane = qml->createRootObject<NavigationPane>();
// Set created root object as the application scene
app->setScene(pane);
qml->setContextProperty("cppObj", this);
}
void ApplicationUI::onLoginClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///projects.qml").parent(this);
qml->setContextProperty("cppObj", this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
void ApplicationUI::onAddClicked() {
// create scene document from buttonclicked.qml asset
// set parent to created document to ensure it exists for the whole application lifetime
QmlDocument *qml = QmlDocument::create("asset:///addprojects.qml").parent(this);
Page* root = qml->createRootObject<Page>();
pane->push(root);
}
and here's my main where I disabled the back button
NavigationPane {
backButtonsVisible: false }
How do i make the back button active to some section of the app?
Let's say you have the following code:
StartPage
import bb.cascades 1.0
NavigationPane {
id: navPane
Page {
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Next Page"
onClicked: {
var nextPage = page.createObject();
navPane.push(nextPage);
}
}
}
}
attachedObjects: [
ComponentDefinition {
id: page
source: "NextPage.qml"
}
]
}
NextPage:
import bb.cascades 1.0
Page {
// disables backButton (not peeking)
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
// [0]
Container {
horizontalAlignment: HorizontalAlignment.Fill
verticalAlignment: VerticalAlignment.Fill
layout: DockLayout {
}
Button {
horizontalAlignment: HorizontalAlignment.Center
verticalAlignment: VerticalAlignment.Center
text: "Prev page"
onClicked: {
var page = navPane.pop();
if (page)
page.destroy();
}
}
} // [0]
// hides navigation bar
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
}
Then you can completely disable backButton with this code in NextPage.qml
onCreationCompleted: {
navPane.backButtonsVisible = false;
}
Alternatively, if you want to have navigation pane visible but just Back Button disabled, use following in the same NextPage.qml file:
paneProperties: NavigationPaneProperties {
backButton: ActionItem {
enabled: false
}
}
Also, don't forget about peekEnabled property. Using peek BB10 feature user can move between sibling pages using sliding finger movement not touching any buttons on the screen.
It could be done in similar way:
onCreationCompleted: {
navPane.peekEnabled = false;
}
Hope it helps.