Privacy policyContactsAbout siteOpinionsGitHubDonate
© EVILEG 2015-2018
Recommend hosting
TIMEWEB
МП
Jan. 22, 2019, 8:38 a.m.

Загрузка тяжелого файла через StackView

Добрый день! При загрузке файла qml через StackView, приложение зависает и через некоторое время переходит на нужную страницу. Поставил BusyIndicator и попробовал сделать через Loader. Все работает: нажимаешь, появляется BusyIndicator, затем переходит на следующую страницу:

main.qml:

ApplicationWindow {
    id: window
    visible: true
    width: 640
    height: 480     
    Loader {
        anchors.fill: parent
        id: loader
        active: true
        asynchronous: true
       }
    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent        
        running: loader.status == Loader.Loading ? true : false
       }

Так же есть две кнопки:

Button {  
    id:btn1
    text: qsTr("Назад")
    onClicked: stackView.pop()
}
Button {   
    id:btn2
    text: qsTr("В начало")
    onClicked: stackView.push(homePage)
}

и сама страница:

HomePageForm {
TitleRect.onAppClicked: {
        stackView.push(loader)
        loader.source = "8/HomePage.qml"
        }
}

После нажатия кнопки btn2 из любой части программы, переходишь на homePage. Нажимаешь повторно TitleRect для перехода к файлу qml, пишет : QML StackView: push: nothing to push

Не пойму, что я делаю не так.

Virtual hosting with 10 percent discount
Virtual hosting with 10 percent discount
EVILEG offers reliable hosting with a 10% discount for virtual hosting and 5% for VPS
22

Добрый день.

Проблема в том, что StackView при выполнении метода pop уничтожает тот объект, который был в него добавлен через метод push() . В документации этот момент прописан.

Вам нужно создавать объект homePage каждый раз перед тем, как вы будете добавлять его в StackView через push.

0
МП

Я немного не понял. При нажатии кнопки btn2 я попадаю на homePage, с этим проблем нет. Однако, на homePage расположена кнопка для перехода в папку 8, на страницу HomePage.qml Так вот при нажатии на эту кнопку выдает ошибку QML StackView: push: nothing to push

0

А где находится в коде stackView относительно loader?

0
МП

Они оба в main.qml

0

Может всё-таки более подробный код покажите?

0
МП

main.qml

ApplicationWindow {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("111")

    Loader {
        anchors.fill: parent
        id: loader
        active: true
        asynchronous: true
//        visible: status == Loader.Ready
       }
    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent        
        running: loader.status == Loader.Loading ? true : false
       }

    header: ToolBar {
        Label {
            color: "#a0f649"
            text: stackView.currentItem.title
            font.pointSize: 10
            font.bold: true
            font.capitalization: Font.AllUppercase
            wrapMode: Text.WordWrap
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.fill: parent
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
        }
    }




    StackView {
        id: stackView
        initialItem: p0
        anchors.fill: parent        


        Keys.onReleased: if (event.key === Qt.Key_Back && stackView.depth > 1) {
                             stackView.pop();
                             event.accepted = true;
                         }

        onCurrentItemChanged:  {

            if(stackView.currentItem.title==="000")
            {
                backButton.visible=false
            }
            else if(stackView.currentItem.title==="222")
            {
                homeButton.visible=false
                backButton.visible=false
            }
            else
            {
                homeButton.visible=true
                backButton.visible=true
            }
        }
    }

    footer: ToolBar{
        id: toolBar
        position: ToolBar.Footer
        anchors.horizontalCenter: parent.horizontalCenter


        RowLayout{
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            anchors.top: parent.top

            HomeButton{
                id:homeButton
            }

            BackButton{
                id:backButton
            }
        }
    }


    Component
    {
        id:p0
        Page0{
        }
    }

    Component
    {
        id:homePage
        HomePage{
        }
    }
}
0

Кажется знаю, в чём проблема. StackView по умолчанию должен содержать минимум один объект, если объектов больше 1, то метод pop удаляет последний компонент, если только один то он не удаляется. В вашем случае вы уже добавили loader, поэтому метод push не имеет эффекта, поскольку объект уже в стеке. При чём видимо в самом начале.

В общем напишите код так, чтобы homePage был в стеке в самом начала на уровне 1, а остальное просто добавляйте или удаляйте. Если у вам есть ещё тяжёлые объекты, то добавьте ещё один свой loader для тех страниц и перезагружайте через тот, что уже есть. Нужно с чем-то одним определиться вам.

Вот код, демонстрирующий проблему.

import QtQuick 2.11
import QtQuick.Controls 2.4
import QtQuick.Window 2.11

Window {
    visible: true
    width: 50
    height: 50
    title: qsTr("Hello World")

    StackView {
        id: stackView
        anchors.fill: parent
    }

    Component {
        id: greenRect

        Rectangle {
            color: "green"
        }
    }

    Component {
        id: blueRect

        Rectangle {
            color: "blue"

        }
    }

    Rectangle {
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        width: 10
        height: 10

        MouseArea {
            anchors.fill: parent

            onClicked: {
                console.log(stackView.currentItem)
                // Если поменять на 0, то добавленный при инициализации компонент blueRect не бует удаляться, то есть pop работать не будет
                // Если будет 1, то будет добавлять и удаляться greenRect
                if (stackView.depth > 1)
                {
                    console.log(stackView.pop())
                }
                else
                {
                    console.log(stackView.push(greenRect))
                }
            }
        }
    }

    Component.onCompleted: {
        stackView.push(blueRect)
    }
}

0
МП

Положил loader в homePage. Все работает как надо, спасибо. Теперь проблема с busyIndicator. Положил его вместе с loader. Теперь при загрузке большого файла просто темный экран. В чем может быть проблема?

HomePageForm {

    Loader {
        anchors.fill: parent
        id: loader
        active: true
        asynchronous: true
    }
    BusyIndicator {
        id: busyIndicator
        running: loader.status == Loader.Loading ? true : false
        anchors.centerIn: parent
    }
    TitleRect.onAppClicked: {
        stackView.push(loader)
        loader.source = "8/HomePage.qml"
        }
}    
0

Я бы этот индикатор оставил снаружи в окне приложения и запускал его через обработку сигнала о загрузке от лоадера.

0
МП

Да, я примерно так и сделал. Разместил его в main.qml:

ApplicationWindow {   

    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent
        running: loader.status == Loader.Loading
    }
}    

Получается он запускается и работает постоянно. Или я ни от того сигнала запускаю loader?

0

Вот кусок документации на Loader

Loader {
    id: loader
    onStatusChanged: if (loader.status == Loader.Ready) console.log('Loaded')
}

Я думаю, что Вы можете внутри компонента делать проверку на статус лоадера и по изменению его статуса переопределять состояние индикатора, который находится снаружи компонента.

Пожалуй, это должно выглядеть примерно так

Loader {
    id: loader
    onStatusChanged: {
        if (loader.status == Loader.Loading) {
            busyIndicator.running = true
        } else {
            busyIndicator.running = false
        }
    }
}
0
МП

Евгений, огромное спасибо, все работает!

1
МП

Добрый день! Еще один вопрос по работе louder. stackView.currentItem.title показывает в заголовке title страницы.

header: ToolBar {
        Label {
            color: "#a0f649"
            text: stackView.currentItem.title
            ....
            }
        }

Переходя на страницу через loader:

HomePageForm {   
    TitleRect.onAppClicked: {
        stackView.push(loader)
        loader.source = "8/HomePage.qml"
        }
}    

title HomePage.qml не показывает, выдает ошибку: Unable to assign [undefined] to QString У лоудера есть свойство SetSourse. Пробовать через него?

0

Добрый день! Содержимое HomePage.qml покажите. Подозреваю, что у него или нет title или нужно alias на этот title делать.

0
МП
import QtQuick 2.10
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.0


Flickable {
    contentHeight: page.contentHeight+10
    property string title: page.title
    Page {
        id:page
        anchors.fill: parent
        title: qsTr("Текст")

        ColumnLayout {
            anchors.fill: parent

            MainButton{
                text: qsTr("1")
                onClicked: stackView.push(p1)
            }
            ...
0

ну понятно, в случае лоадера вы не получите доступ к title так, как делаете. Лоадер выступает в качестве промежутоного звена, тем более, что вы его пушите в StackView, чтобы достучаться до той property, нужно писать так

header: ToolBar {
    Label {
      color: "#a0f649"
      text: stackView.currentItem.item.title
    }
}

Как вариант, можно попробовать немного кастомизировать Loader, чтобы он отвечал вашему изначальному коду. То есть создаёте новый QML файл - CustomLoader.qml

CustomLoader.qml

А в нём уже сделать property alias и заменить таким кастомным лоадером стандартный.

Loader {
    property alias title: item.title
}

Тогда по идее Loader будет реализовывать ваш обобщённый интерфейс и следующий код должен будет работать.

header: ToolBar {
    Label {
      color: "#a0f649"
      text: stackView.currentItem.title
    }
}
0
МП

Ошибка: qrc:/CustomLoader.qml:4 Invalid alias reference. Unable to find id "item"

import QtQuick 2.0

Loader{
    property alias title: item.title
}

0

мда.. я как-то не подумал, что QML делает alias только по id, хотя логично.. а item это не id, а property.

Ну тогда нужно слушать изменение currentItem у StackView и по его изменению реализовывать специфичную логику. Выглядеть может так.

    Connections {
        target: stackView   
        onCurrentItemChanged: {    
            labelTitle.text = stackView.currentItem.getTitle())
        }
    }

Прокомментирую код выше.

  • labelTitle - это id вашего лейбла, в который вы устанавливали текст так
  header: ToolBar {
    Label {
      id: labelTitle
      color: "#a0f649"
    }
  }
  • stackView.currentItem.getTitle() - это уже поинтереснее, в каждой странице, которую вы добавляете в stackView, нужно будет определить функцию которая будет возвращать title
  Loader {
    id: root

    function getTitle()
    {
      return root.item.title;
    }
  }

Ну и для каждой страницы нужно реализовывать эту функцию соответсвующим образом, чтобы был единообразный интерфейс QML объекта.

0
МП

Интрересно. А если сделать вообще без лоудера, через stackView загружать страницу:

TitleRect.onAppClicked: {
        stackView.push("8/HomePage.qml")
    }

Только непонятно к какому свойству stackView привязать busyIndicator, что бы он запускался во время загрузки тяжелого файла? В этом случае, проблемы с заголовками нет.

0

По моему опыту без лоадера чисто на StackView либо не получится, либо будет с очень большими костылями. StackView служит для организации структуры контента, но не для асинхронной загрузки.

Но есть немного иной вариант, вы довольно близко находитесь со своей идеей в этом последнем коде

TitleRect.onAppClicked: {
    stackView.push("8/HomePage.qml")
}

Сделайте так, но в HomePage поместите loader и busyIndicator. Внутрь лоадера поместите тяжёлую часть файла. Таким образом при помещении файла HomePage.qml внутрь StackView, часть контента, которая лёгкая, отобразится сразу, а тяжёлую часть начните загружать через лоадер в обработчике Component.onCompleted

HomePage.qml

Item {
    id: root
    title: "Home"

    Loader {
        anchors.fill: parent
        id: loader
        active: true
        asynchronous: true
    }

    BusyIndicator {
        id: busyIndicator
        anchors.centerIn: parent        
        running: loader.status == Loader.Loading ? true : false
    }

    Component.onCompleted: {
        loader.source = "8/HomePageHardPart.qml"
    }
}
0
МП

Попробуем идти в другую сторону.

HomePageForm {   
    TitleRect.onAppClicked: {
        stackView.push(loader)
        loader.source = "8/HomePage.qml"
        }
}    

Возможно не засовывать в stackView loader, а при нажатии на TitleRect сразу обращаться к лоудеру? Файл в папке 8, около 800 страниц. Получается при нажатии сначала все эти 800 страниц скачиваются в кеш, а только потом осуществляется переход на HomePage. Затем переходы занимают 1-2 сек и внутри, и с главной страницы приложения. Конечно это неудобно. На слабых устройствах загрузка занимает порядка 2 минут.

0

какие 800 страниц? я допустим понятия не имею, что вы туда загружаете.

Что касается лоадера и stackView. Сделать-то это можно, только не понятно какой вы результат ожидаете. Если вам нужны странички в стиле как у Андроида, то нужен StackView. Это наиболее удобно в данном случае.

1

Comments

Only authorized users can post comments.
Please, Log in or Sign up
d
April 26, 2019, 2:45 a.m.
djanaibekova_0301@mail.ru

C++ - Тест 003. Условия и циклы

  • Result:42points,
  • Rating points-8
Z
April 25, 2019, 8:02 p.m.
ZadvornyAlexey

C++ - Test 001. The first program and data types

  • Result:60points,
  • Rating points-1
m
April 24, 2019, 2:30 p.m.
maksImkа

C++ - Тест 003. Условия и циклы

  • Result:50points,
  • Rating points-4
Last comments
April 21, 2019, 4:22 p.m.
Евгений Легоцкой

Через метод setIcon table.horizontalHeaderItem(0).setIcon("qrc://path/to/icon.png")
April 21, 2019, 3:48 p.m.
Евгений Легоцкой

Добрый день! Спасибо за комментарий. Там действительно лучше будет сделать с инициализацией по умолчанию.
U
April 18, 2019, 3:37 p.m.
Unreal_man

А как иконку в хедер задать?
u
April 18, 2019, 2:15 a.m.
uaa

доброго времени,большое спасибо за пример для начинающего)при адаптации к своему проекту столкнулся с таким ньансом:в vepolyline.h в 47 строке нужна инициализация по умолчанию: int m_pointF...
E
April 11, 2019, 12:49 p.m.
Evgeny

Спасибо за ответ) У меня компоновщик на нее ругался просто. Оказалось, просто забыл Q_OBJECT в начале класса указать.
Now discuss on the forum
April 25, 2019, 10:51 a.m.
Ruslan Polupan

Изменил функциютеперь работает. bool ModelTerminals::setData(const QModelIndex &index, const QVariant &value, int role){ Q_UNUSED(role) if(!index.isValid()) {return false;...
April 24, 2019, 6:20 a.m.
Ruslan Polupan

я так понимаю надо инфорация об устройствах.Я бы пробовал так rust@suse:~> lsblk -PNAME="sda" MAJ:MIN="8:0" RM="0" SIZE="111,8G" RO="0" TYPE="disk" MOUNTPOINT=""NAME="sda1" MAJ:MIN="8...
April 21, 2019, 4:16 p.m.
Евгений Легоцкой

Приветствую Нужно сохранять где-то выбранное значение, а потом восстанавливать его. Или использовать QSettings или добавить метод open(), в который передавать начальные значения для того...
R
April 19, 2019, 9:55 a.m.
RED_Spider

мені важко це зараз навіть перевірити, тому що знайшов коміт, це ще було в 2016 році, і цей код не буде працювати коректно зараз, єдине скажу що це були QThread
Join us in social networks

For registered users on the site there is a minimum amount of advertising