МП
Максим ПрохоровJan. 22, 2019, 3: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

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

We recommend hosting TIMEWEB
We recommend hosting TIMEWEB
Stable hosting, on which the social network EVILEG is located. For projects on Django we recommend VDS hosting.

Do you like it? Share on social networks!

22
Evgenii Legotckoi
  • Jan. 22, 2019, 3:45 a.m.

Добрый день.

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

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

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

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

        Они оба в main.qml

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

            МП
            • Jan. 22, 2019, 5:32 a.m.

            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{
                    }
                }
            }
            
              Evgenii Legotckoi
              • Jan. 22, 2019, 6:39 a.m.
              • (edited)

              Кажется знаю, в чём проблема. 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)
                  }
              }
              
              

                Положил 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"
                        }
                }    
                

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

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

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

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

                      Evgenii Legotckoi
                      • Jan. 22, 2019, 8:02 a.m.

                      Вот кусок документации на 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
                              }
                          }
                      }
                      
                        МП
                        • Jan. 22, 2019, 8:15 a.m.

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

                          МП
                          • Jan. 23, 2019, 6:51 a.m.
                          • (edited)

                          Добрый день! Еще один вопрос по работе 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. Пробовать через него?

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

                              МП
                              • Jan. 23, 2019, 10:21 a.m.
                              • (edited)
                              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)
                                          }
                                          ...
                              

                                ну понятно, в случае лоадера вы не получите доступ к 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
                                    }
                                }
                                
                                  МП
                                  • Jan. 23, 2019, 11:15 a.m.

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

                                  import QtQuick 2.0
                                  
                                  Loader{
                                      property alias title: item.title
                                  }
                                  
                                  
                                    Evgenii Legotckoi
                                    • Jan. 23, 2019, 3:36 p.m.

                                    мда.. я как-то не подумал, что 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 объекта.

                                      МП
                                      • Jan. 24, 2019, 2:15 a.m.

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

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

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

                                        Evgenii Legotckoi
                                        • Jan. 24, 2019, 3:35 a.m.
                                        • (edited)

                                        По моему опыту без лоадера чисто на 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"
                                            }
                                        }
                                        
                                          МП
                                          • Jan. 25, 2019, 1:53 a.m.

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

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

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

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

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

                                              Comments

                                              Only authorized users can post comments.
                                              Please, Log in or Sign up
                                              AD

                                              C ++ - Test 004. Pointers, Arrays and Loops

                                              • Result:50points,
                                              • Rating points-4
                                              m

                                              C ++ - Test 004. Pointers, Arrays and Loops

                                              • Result:80points,
                                              • Rating points4
                                              m

                                              C ++ - Test 004. Pointers, Arrays and Loops

                                              • Result:20points,
                                              • Rating points-10
                                              Last comments
                                              ИМ
                                              Игорь МаксимовNov. 22, 2024, 11:51 a.m.
                                              Django - Tutorial 017. Customize the login page to Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                                              Evgenii Legotckoi
                                              Evgenii LegotckoiOct. 31, 2024, 2:37 p.m.
                                              Django - Lesson 064. How to write a Python Markdown extension Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                                              A
                                              ALO1ZEOct. 19, 2024, 8:19 a.m.
                                              Fb3 file reader on Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                                              ИМ
                                              Игорь МаксимовOct. 5, 2024, 7:51 a.m.
                                              Django - Lesson 064. How to write a Python Markdown extension Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                                              d
                                              dblas5July 5, 2024, 11:02 a.m.
                                              QML - Lesson 016. SQLite database and the working with it in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                                              Now discuss on the forum
                                              Evgenii Legotckoi
                                              Evgenii LegotckoiJune 24, 2024, 3:11 p.m.
                                              добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
                                              t
                                              tonypeachey1Nov. 15, 2024, 6:04 a.m.
                                              google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
                                              NSProject
                                              NSProjectJune 4, 2022, 3:49 a.m.
                                              Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
                                              9
                                              9AnonimOct. 25, 2024, 9:10 a.m.
                                              Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

                                              Follow us in social networks