МП
Максим Прохоров22 января 2019 г. 3:38

Загрузка тяжелого файла через 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

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

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

22
Evgenii Legotckoi
  • 22 января 2019 г. 3:45

Добрый день.

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

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

    МП
    • 22 января 2019 г. 3:52

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

      Evgenii Legotckoi
      • 22 января 2019 г. 4:14

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

        МП
        • 22 января 2019 г. 5:28

        Они оба в main.qml

          Evgenii Legotckoi
          • 22 января 2019 г. 5:29

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

            МП
            • 22 января 2019 г. 5:32

            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
              • 22 января 2019 г. 6:39
              • (ред.)

              Кажется знаю, в чём проблема. 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)
                  }
              }
              
              
                МП
                • 22 января 2019 г. 7:34

                Положил 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"
                        }
                }    
                
                  Evgenii Legotckoi
                  • 22 января 2019 г. 7:46

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

                    МП
                    • 22 января 2019 г. 7:58

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

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

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

                      Evgenii Legotckoi
                      • 22 января 2019 г. 8:02

                      Вот кусок документации на 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
                              }
                          }
                      }
                      
                        МП
                        • 22 января 2019 г. 8:15

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

                          МП
                          • 23 января 2019 г. 6:51
                          • (ред.)

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

                            Evgenii Legotckoi
                            • 23 января 2019 г. 9:15

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

                              МП
                              • 23 января 2019 г. 10:21
                              • (ред.)
                              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)
                                          }
                                          ...
                              
                                Evgenii Legotckoi
                                • 23 января 2019 г. 10:51

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

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

                                  import QtQuick 2.0
                                  
                                  Loader{
                                      property alias title: item.title
                                  }
                                  
                                  
                                    Evgenii Legotckoi
                                    • 23 января 2019 г. 15:36

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

                                      МП
                                      • 24 января 2019 г. 2:15

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

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

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

                                        Evgenii Legotckoi
                                        • 24 января 2019 г. 3:35
                                        • (ред.)

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

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

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

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

                                            Evgenii Legotckoi
                                            • 25 января 2019 г. 3:11

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

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

                                              Комментарии

                                              Только авторизованные пользователи могут публиковать комментарии.
                                              Пожалуйста, авторизуйтесь или зарегистрируйтесь
                                              г
                                              • ги
                                              • 23 апреля 2024 г. 12:51

                                              C++ - Тест 005. Структуры и Классы

                                              • Результат:41баллов,
                                              • Очки рейтинга-8
                                              l
                                              • laei
                                              • 23 апреля 2024 г. 6:19

                                              C++ - Тест 004. Указатели, Массивы и Циклы

                                              • Результат:10баллов,
                                              • Очки рейтинга-10
                                              l
                                              • laei
                                              • 23 апреля 2024 г. 6:17

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

                                              • Результат:50баллов,
                                              • Очки рейтинга-4
                                              Последние комментарии
                                              k
                                              kmssr8 февраля 2024 г. 15:43
                                              Qt Linux - Урок 001. Автозапуск Qt приложения под Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
                                              АК
                                              Анатолий Кононенко4 февраля 2024 г. 22:50
                                              Qt WinAPI - Урок 007. Работаем с ICMP Ping в Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
                                              EVA
                                              EVA25 декабря 2023 г. 7:30
                                              Boost - статическая линковка в CMake проекте под Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
                                              J
                                              JonnyJo25 декабря 2023 г. 5:38
                                              Boost - статическая линковка в CMake проекте под Windows Сделал всё по-как у вас, но выдаёт ошибку [build] LINK : fatal error LNK1104: не удается открыть файл "libboost_locale-vc142-mt-gd-x64-1_74.lib" Хоть убей, не могу понять в чём дел…
                                              G
                                              Gvozdik18 декабря 2023 г. 18:01
                                              Qt/C++ - Урок 056. Подключение библиотеки Boost в Qt для компиляторов MinGW и MSVC Для решения твой проблемы добавь в файл .pro строчку "LIBS += -lws2_32" она решит проблему , лично мне помогло.
                                              Сейчас обсуждают на форуме
                                              G
                                              Gar22 апреля 2024 г. 2:46
                                              Clipboard Как скопировать окно целиком в clipb?
                                              DA
                                              Dr Gangil Academics20 апреля 2024 г. 4:45
                                              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_vlasov14 апреля 2024 г. 3:41
                                              Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Евгений, добрый день! Такой вопрос. Верно ли следующее утверждение: Любое Android-приложение, написанное на Java/Kotlin чисто теоретически (пусть и с большими трудностями) можно написать и на C+…
                                              Павел Дорофеев
                                              Павел Дорофеев13 апреля 2024 г. 23:35
                                              QTableWidget с 2 заголовками Вот тут есть кастомный QTableView с многорядностью проект поддерживается, обращайтесь
                                              f
                                              fastrex4 апреля 2024 г. 1:47
                                              Вернуть старое поведение QComboBox, не менять индекс при resetModel Добрый день! У нас много проектов в которых используется QComboBox, в версии 5.5.1, когда модель испускает сигнал resetModel, currentIndex не менялся. В версии 5.15 при resetModel происходит try…

                                              Следите за нами в социальных сетях