МП
Максим Прохоров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. Это наиболее удобно в данном случае.

                                              Комментарии

                                              Только авторизованные пользователи могут публиковать комментарии.
                                              Пожалуйста, авторизуйтесь или зарегистрируйтесь
                                              AD

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

                                              • Результат:50баллов,
                                              • Очки рейтинга-4
                                              m
                                              • molni99
                                              • 26 октября 2024 г. 7:37

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

                                              • Результат:80баллов,
                                              • Очки рейтинга4
                                              m
                                              • molni99
                                              • 26 октября 2024 г. 7:29

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

                                              • Результат:20баллов,
                                              • Очки рейтинга-10
                                              Последние комментарии
                                              i
                                              innorwall15 ноября 2024 г. 8:27
                                              Релиз утилиты развертывания С++/Qt и QML приложений CQtDeployer v1.4.0 (Binary Box) optionally substituted alkoxy, optionally substituted alkenyloxy, optionally substituted alkynyloxy, optionally substituted aryloxy, OCH, OC H, OC H, OC H, OC H, OC H, OC H, O C CH, OCH CH OH, O…
                                              i
                                              innorwall15 ноября 2024 г. 3:26
                                              Qt/C++ - Урок 031. QCustomPlot - строим график по времени buy generic priligy We can just chat, and we will not lose too much time anyway
                                              i
                                              innorwall15 ноября 2024 г. 1:03
                                              Qt/C++ - Урок 060. Настройка внешнего вида приложения в рантайме I didnt have an issue work colors priligy dapoxetine 60mg revia cost uk August 3, 2022 Reply
                                              i
                                              innorwall14 ноября 2024 г. 17:42
                                              Как Копировать Файлы в Linux If only females relatives with DZ offspring were considered these percentages were 23 order priligy online uk
                                              Сейчас обсуждают на форуме
                                              i
                                              innorwall14 ноября 2024 г. 9:39
                                              добавить qlineseries в функции priligy amazon canada 93 GREB1 protein GREB1 AB011147 6
                                              i
                                              innorwall11 ноября 2024 г. 16:55
                                              Всё ещё разбираюсь с кешем. priligy walgreens levitra dulcolax carbs The third ring was found to be made up of ultra relativistic electrons, which are also present in both the outer and inner rings
                                              9
                                              9Anonim25 октября 2024 г. 15:10
                                              Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…
                                              ИМ
                                              Игорь Максимов3 октября 2024 г. 10:05
                                              Реализация навигации по разделам Спасибо Евгений!

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