МП
Максим Прохоров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
                                              SH

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

                                              • Result:33points,
                                              • Rating points-10
                                              г
                                              • ги
                                              • April 24, 2024, 1:51 a.m.

                                              C++ - Test 005. Structures and Classes

                                              • Result:41points,
                                              • Rating points-8
                                              l
                                              • laei
                                              • April 23, 2024, 7:19 p.m.

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

                                              • Result:10points,
                                              • Rating points-10
                                              Last comments
                                              k
                                              kmssrFeb. 9, 2024, 5:43 a.m.
                                              Qt Linux - Lesson 001. Autorun Qt application under Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                              Qt WinAPI - Lesson 007. Working with ICMP Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                                              EVA
                                              EVADec. 25, 2023, 9:30 p.m.
                                              Boost - static linking in CMake project under Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                                              J
                                              JonnyJoDec. 25, 2023, 7:38 p.m.
                                              Boost - static linking in CMake project under Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                                              G
                                              GvozdikDec. 19, 2023, 8:01 a.m.
                                              Qt/C++ - Lesson 056. Connecting the Boost library in Qt for MinGW and MSVC compilers Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                                              Now discuss on the forum
                                              G
                                              GarApril 22, 2024, 3:46 p.m.
                                              Clipboard Как скопировать окно целиком в clipb?
                                              DA
                                              Dr Gangil AcademicsApril 20, 2024, 5:45 p.m.
                                              Unlock Your Aesthetic Potential: Explore MSC in Facial Aesthetics and Cosmetology in India Embark on a transformative journey with an msc in facial aesthetics and cosmetology in india . Delve into the intricate world of beauty and rejuvenation, guided by expert faculty and …
                                              a
                                              a_vlasovApril 14, 2024, 4:41 p.m.
                                              Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
                                              Павел Дорофеев
                                              Павел ДорофеевApril 14, 2024, 12:35 p.m.
                                              QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                                              f
                                              fastrexApril 4, 2024, 2:47 p.m.
                                              Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                                              Follow us in social networks