МП
Максим Прохоров22 января 2019 г. 14: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 г. 14:45

Добрый день.

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

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

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

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

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

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

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

        Они оба в main.qml

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

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

            МП
            • 22 января 2019 г. 16: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 г. 17: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 г. 18: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 г. 18:46

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

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

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

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

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

                      Evgenii Legotckoi
                      • 22 января 2019 г. 19: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 г. 19:15

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

                          МП
                          • 23 января 2019 г. 17: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 г. 20:15

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

                              МП
                              • 23 января 2019 г. 21: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 г. 21: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 г. 22: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
                                    • 24 января 2019 г. 2: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 г. 13:15

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

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

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

                                        Evgenii Legotckoi
                                        • 24 января 2019 г. 14: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 г. 12: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 г. 14:11

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

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

                                              Комментарии

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

                                              Qt - Тест 001. Сигналы и слоты

                                              • Результат:84баллов,
                                              • Очки рейтинга4
                                              Ua

                                              Qt - Тест 001. Сигналы и слоты

                                              • Результат:42баллов,
                                              • Очки рейтинга-8
                                              ОК

                                              Qt - Тест 001. Сигналы и слоты

                                              • Результат:47баллов,
                                              • Очки рейтинга-6
                                              Последние комментарии
                                              ИМ
                                              Игорь Максимов22 ноября 2024 г. 21:51
                                              Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
                                              Evgenii Legotckoi
                                              Evgenii Legotckoi31 октября 2024 г. 23:37
                                              Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
                                              A
                                              ALO1ZE19 октября 2024 г. 17:19
                                              Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
                                              ИМ
                                              Игорь Максимов5 октября 2024 г. 16:51
                                              Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
                                              d
                                              dblas55 июля 2024 г. 20:02
                                              QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
                                              Сейчас обсуждают на форуме
                                              f
                                              firstlunoxod15 февраля 2025 г. 13:46
                                              Рисование на QGraphicsScene при зажатой кнопке мыши Подскажите, пожалуйста! Как данный класс можно дополнить, чтобы созданные объекты можно было перемещать мышкой по сцене?
                                              Дмитрий
                                              Дмитрий3 февраля 2025 г. 16:24
                                              Создание deb-пакета. Как создать ярлык на рабочем столе после установки собственного deb-пакета? Всем привет. Сделал свой deb-пакет с программой. Всё устанавливается и работает. Ставлю по пути /usr/bin/my_application. Как для пользователя при установке пакета сразу создать ярлык на раб…
                                              NW
                                              Nayo Wai30 января 2025 г. 19:22
                                              не запускается компьютер!!! Не запускается компьютер (точнее работает блок , но сам монитор вообще жесть)В общем я ничего с интернета не скачивала в последнее время. На компе никаких левых пр…
                                              n
                                              nkly3 января 2025 г. 12:52
                                              Нужно запретить перемещение только некоторых итемов, остальные перемещать можно. Вопрос решен. Узнать QModelIndex элемента на который мы перетаскиваем другой элемент, можно с помощью функции indexAt(event->position().toPoint()) представления QTreeViev вызываемой в переопр…
                                              M
                                              Marsel17 августа 2023 г. 0:26
                                              OAuth2.0 через VK, получение email Спасибо большое за помощь и простите за то что отнял время своей невнимательностью.

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