Russian Qt Forum

Qt => Qt Quick => Тема начата: Гурман от Апрель 17, 2019, 23:07



Название: [РЕШЕНО] Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 17, 2019, 23:07
Нужно сделать скроллируемый декорированный список строк с чекбоксами, и обязательно внутри QGraphicsScene - чтобы легко масштабировать под разные размеры экрана в Android. Всегда всё делал на виджетах. Но QListView и QListWidget с добавленными к ним виджетами не работают в QGraphicsScene. На эту тему уже есть баг репорт. Заранее просьба - не предлагать реализовать через делегатов для QListView. Я пробовал, получается такая мешанина, что это в только самом крайнем случае. В реальной задаче в элементе списка должны быть 12 текстовых полей с разными размерами шрифтов, и два чекбокса. Количество элементов списка в реальности до 150, но размер каждого элемента должен быть 200 пикселов, чтобы все поля поместились. И должны быть три разных варианта отображения с разными расположениями и размерами текстов. На QML это сделать было бы милое дело...

Поэтому попытался сделать на QML с помощью ListView и делегата, засунув их в QQuickWidget, а его уже в QGraphicsScene. При этом в QML я как свинья в апельсинах, то есть почти никак. Сначала вроде с помощью Интернет всё получилось - и список легко декорирован, и чекбокс без проблем, и текст на строках можно расположить где угодно и разного размера. То, что мне надо. Но потом... когда увеличил количество элементов в списке, вдруг обнаружил что вместо него в окне чёрное поле с какими-то графическими искажениями. И в консоли появились сообщения об ошибке:

Цитировать
QOpenGLFramebufferObject: Framebuffer incomplete attachment.
QOpenGLFramebufferObject: Framebuffer incomplete attachment.
QOpenGLFramebufferObject: Framebuffer incomplete attachment.
QOpenGLFramebufferObject: Framebuffer incomplete attachment.
QOpenGLFramebufferObject: Framebuffer incomplete, missing attachment.

С меньшим числом элементов списка их не было.

Экспериментально установил, что всё это начинается если вертикальный размер изображения списка превышает 4096 пикселов. До 4096 всё работает, но с 4097 уже нет. Пытаюсь найти соответствующее описание где-нибудь - ничего не нахожу. Нашёл только близкую по теме проблему у одного разработчика на stackoverflow (https://stackoverflow.com/questions/46032180/qml-take-screenshot-of-the-whole-listviewincluding-the-clipped-part). Больше нигде никаких упоминаний о каких-либо ограничениях.

Прикладываю здесь исходник тестового приложения, компилировал и проверял в десктопном Linux с Qt 5.9. Изначально всё работает. Но если в начале файла testlist.cpp закомментировать первую строчку с #define works - после пересборки вместо красивого скроллирующего списка в окне будет какая-то хрень. Это увеличивает вертикальный размер элемента списка с 40 пикселов до 50, весь список получается 5000 пикселов вместо 4000, и оба-на.

Кто-нибудь слышал что-нибудь об этой проблеме? Есть какие-нибудь пути её обхода?


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: kambala от Апрель 18, 2019, 01:08
Qt 5.12.2, macOS 10.13.6 — никаких проблем что с works, что без.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 03:01
О. Вот это надо проверить с Qt 5.12. ОС тут вряд ли имеет значение. Хотя... для рендеринга используется OpenGL, могут быть нюансы.

А если число элементов задать 150, а высоту каждого 200? Это соответственно const int num в методе TestList::FillList() и число в открытой ветке #ifdef works ниже.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: kambala от Апрель 18, 2019, 03:24
в этом случае наблюдаю ту же проблему, что и у тебя. но при количестве 150 и высоте 50 проблем нет.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: qate от Апрель 18, 2019, 09:14
Надо запилить багрепорт - даже пример есть !

Я решил еще раз попробовать qml т.к. может придется сделать простое приложение, но чтобы работало и на андроиде и на десктопе.
Кроме Qml решения не вижу чтобы дважды не писать
И очень мало кто использует qml по сравнению в виджетами - чтото найти сложно, а сразу много непонятно )

Например при сборке и установке на андроит qt либы всегда помещаются в apk - что тормозит и сборку и запуск, как убрать непонятно.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 11:09
в этом случае наблюдаю ту же проблему, что и у тебя. но при количестве 150 и высоте 50 проблем нет.
Можете установить "границу" - при каком числе вертикальных пикселов ломается? У меня при 4096, это не случайное число - 2^12. Устанавливать приходится методом ловли льва в пустыне - то есть, подбором с половинным делением. Если это тоже степень двойки, то граница будет быстро заметна.

Для этого нужно:
1. в файле testlist.cpp заменить строку:
    setResizeMode(QQuickWidget::SizeRootObjectToView);
на
    setResizeMode(QQuickWidget::SizeViewToRootObject);
2. в нём же закомментировать строки с 32 по 38 включительно
3. в файле main.qml раскомментировать строки 13 и 14
4. в строке 14 менять высоту в пикселах


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 12:11
qate, в вашем сообщении по теме только замечание про баг репорт, всё остальное оффтопик. За мной не убудет, не впервой багрепорты Qt писать - надо только больше материала собрать. По поводу остального - если для Android не требуется делать скроллирующий список со сложными айтемами, которые должны собираться из разных элементов, то всё можно сделать на виджетах. И так что будет одинаково работать на всех ОС, включая Android. Правда, чтобы и выглядело одинаково, придётся все виджеты самостоятельно декорировать, то есть писать в них paintEvent() и рисовать им картинки. У меня таким способом сделано больше 20 приложений на Google Play, и да - они идентично работают в Windows и Linux. По поводу библиотек Qt - изучите Ministro. И просьба больше в этой ветке не оффтопить.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: ViTech от Апрель 18, 2019, 12:24
Можете установить "границу" - при каком числе вертикальных пикселов ломается? У меня при 4096, это не случайное число - 2^12.

Возможно дело в ограничении на размер текстуры (Maximum OpenGL FrameBuffer Object size limit? (https://stackoverflow.com/questions/6655943/maximum-opengl-framebuffer-object-size-limit)). Соответственно, может не стоит устанавливать размер QQuickWidget больше этих лимитов.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 12:55
Можете установить "границу" - при каком числе вертикальных пикселов ломается? У меня при 4096, это не случайное число - 2^12.

Возможно дело в ограничении на размер текстуры (Maximum OpenGL FrameBuffer Object size limit? (https://stackoverflow.com/questions/6655943/maximum-opengl-framebuffer-object-size-limit)). Соответственно, может не стоит устанавливать размер QQuickWidget больше этих лимитов.

Я тоже так сначала думал. Но... если в моём примере раскомментировать в файле main.qml строки, включающие использование QML типа Window (для этого надо ещё раскомментировать строку с anchors.fill parent в ListView) и выполнить шаг 1 из моего предыдущего сообщения, то это не подтверждается! Откроется отдельное окно с размерами, заданными в main.qml, и в нём будет скроллировать весь список, все 100 айтемов. Высота списка в этом случае 5000 пикселов.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: kambala от Апрель 18, 2019, 12:59
у меня предел 8192. подтвердил это, проверив максимальный размер буфера.
Код
C++ (Qt)
void TestList::showEvent(QShowEvent *e)
{
   GLint dims[2] = {0};
   glGetIntegerv(GL_MAX_VIEWPORT_DIMS, &dims[0]);
   qDebug("%d %d", dims[0], dims[1]);
}


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: ViTech от Апрель 18, 2019, 14:09
Я тоже так сначала думал. Но... если в моём примере раскомментировать в файле main.qml строки, включающие использование QML типа Window (для этого надо ещё раскомментировать строку с anchors.fill parent в ListView) и выполнить шаг 1 из моего предыдущего сообщения, то это не подтверждается! Откроется отдельное окно с размерами, заданными в main.qml, и в нём будет скроллировать весь список, все 100 айтемов. Высота списка в этом случае 5000 пикселов.

А "QML типа Window" и QQuickWidget это одно и то же? Возможно, "QML типа Window" и имеет большие размеры, но, условно говоря, "viewport" для него гораздо меньше, и имеет ограничения по размеру framebuffer. На экране это отдельное окно реально высотой 5000 пикселей? И ошибки "QOpenGLFramebufferObject: Framebuffer incomplete attachment." при этом не появляются?


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 16:30
Я тоже так сначала думал. Но... если в моём примере раскомментировать в файле main.qml строки, включающие использование QML типа Window (для этого надо ещё раскомментировать строку с anchors.fill parent в ListView) и выполнить шаг 1 из моего предыдущего сообщения, то это не подтверждается! Откроется отдельное окно с размерами, заданными в main.qml, и в нём будет скроллировать весь список, все 100 айтемов. Высота списка в этом случае 5000 пикселов.

А "QML типа Window" и QQuickWidget это одно и то же? Возможно, "QML типа Window" и имеет большие размеры, но, условно говоря, "viewport" для него гораздо меньше, и имеет ограничения по размеру framebuffer. На экране это отдельное окно реально высотой 5000 пикселей? И ошибки "QOpenGLFramebufferObject: Framebuffer incomplete attachment." при этом не появляются?


Нет, на экране это окно с тем размером, который задан в QML Window { width: height: }. Я делал его 600х300. Но в нём скроллирует всё полотно высотой 5000 пикселов, то есть весь мой QQuickWidget. Ошибки при этом не появляются. Значит он отрендерился в OpenGL полностью. Попозже сегодня попробую проверить есть ли в этом случае лимит, сейчас некогда.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 18:12
Сейчас проверил работу QQuickWidget в окне, заданном в QML, при больших размерах списка. То есть, main.qml выглядит так:
Код:
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.2

//Item {
Window {
    visible: true
    width: 600
    height: 300

    ListView {
        id:lView
        //width: 600
        //height: 4096
        anchors.fill: parent
        model: DeathList
        orientation: ListView.Vertical

        delegate: Rectangle {
            color: modelData.dead ? "darkred" : "white"

            width: lView.width
            height: 50

            Text {
                id:nameField
                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                text: modelData.name
                color: modelData.dead ? "white" : "black"
                width: contentWidth
            }

            Text {
                anchors.left: nameField.right
                anchors.leftMargin: 20
                anchors.verticalCenter: parent.verticalCenter
                text: modelData.age + qsTr("years old")
                color: modelData.dead ? "white" : "black"
                width: contentWidth
            }

            CheckBox {
                text: qsTr("dead")
                anchors.right: parent.right
                anchors.top:parent.top
                anchors.bottom: parent.bottom
                checked: modelData.dead
                onCheckedChanged: modelData.dead = checked
            }
        }
    }
}

В testlist.cpp
Код:
TestList::TestList(QWidget *p, uint _width) : QQuickWidget(p), width(_width)
{
    qsrand(QTime::currentTime().second());
    connect(this, SIGNAL(statusChanged(QQuickWidget::Status)), SLOT(changedStatus(QQuickWidget::Status)));
    setResizeMode(QQuickWidget::SizeRootObjectToView); // size in code
    //setResizeMode(QQuickWidget::SizeViewToRootObject); // size in QML
    QSurfaceFormat fmt = format();
    fmt.setSwapBehavior(QSurfaceFormat::SingleBuffer);
}

void TestList::FillList()
{
    const int num = 660;
    for( int i = 0; i < num; i++ )
        notebook.append( new NoteEntry("person_"+QString::number(i), qrand()%60+20, i%2) );
    rootContext()->setContextProperty("DeathList",QVariant::fromValue(notebook));
    setSource(QUrl("qrc:/main.qml"));
    if( ! rootContext()->isValid() )
    {
        qWarning()<<"QML is not valid";
        QApplication::quit();
    }
    resize( width, num * 50 );
}

То есть, размер полотна 33000 пикселов. И нормально оно в отдельном окне скроллирует всё. Но! В консоли сообщения те же сообщения об ошибках, и добавились ещё два:
Код:
QXcbShmImage: shmget() failed (22: ???????????? ????????) for size 120252000 (911x33000)
QXcbConnection: XCB error: 11 (BadAlloc), sequence: 591, resource id: 595, major code: 53 (CreatePixmap), minor code: 0
и... главное окно приложения (не то в котором список, а то которое MainWindow) стало чёрным. Что там внутри происходит пока сложно сказать, но список рендерится при помощи QpenGL, и рендерится без ошибок.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: ViTech от Апрель 18, 2019, 18:28
QQuickWindow и QQuickWidget - найдите 10 отличий :).


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 18:55
QQuickWindow и QQuickWidget - найдите 10 отличий :).
Во-первых, независимо от количества отличий, есть одно совпадение - их содержимое рендерится в OpenGL. Во-вторых, мой TestList - он не стал QQuickWindow. Он так и остался QQuickWidget.


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: ViTech от Апрель 18, 2019, 19:28
Во-вторых, мой TestList - он не стал QQuickWindow. Он так и остался QQuickWidget.
Вот именно.

Код
C++ (Qt)
void TestList::FillList()
{
   const int num = 1000;
   for( int i = 0; i < num; i++ )
       notebook.append( new NoteEntry("person_"+QString::number(i), qrand()%60+20, i%2) );
   rootContext()->setContextProperty("DeathList",QVariant::fromValue(notebook));
   setSource(QUrl("qrc:/main.qml"));
   if( ! rootContext()->isValid() )
   {
       qWarning()<<"QML is not valid";
       QApplication::quit();
   }
 
   resize(300, 300);
}

А для ListView можно добавить скроллбар (https://stackoverflow.com/questions/45650226/qml-attach-scrollbar-to-listview).


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 19:38
Зачем этот код? Вы его запускали? Я такой вчера делал.

Скроллбар у меня должен быть у QGraphicsScene - и он там есть, если виджет в ограничения размеров не упирается. Отдельное окно мне нафик не нужно. На сцене кроме виджета, ещё кнопки будут и поле для рекламы с AdMob. Чтобы всё это масштабировалось вызовом QGraphicsView::scale(x,y). А с отдельным окном мне делать нечего, есть там скроллбар или нет.

Стоп, я тут что-то нащупал... В main.qml надо Window заменить на Item...


Название: Re: Странное ограничение размеров QML ListView в QQuickWidget
Отправлено: Гурман от Апрель 18, 2019, 20:15
Да. Получилось. Я вчера ходил возле этого решения, но после длительной долбёжки лбом об стенку прозевал его. В реальном приложении всё будет немного не так, но принцип вроде найден. Если это ещё и не будет на Android слишком сильно тормозить с несколькими сотнями айтемов, то похоже то что надо.