Russian Qt Forum

Qt => Пользовательский интерфейс (GUI) => Тема начата: PinkPanther от Май 08, 2013, 07:16



Название: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 07:16
Здравствуйте.
На панели имеется виджет QTableWidget, данные в таблицу вносятся перед обработкой (она довольно простая и занимает мало времени). 5 столбцов, изменяемое количество строк (выставляется в начале цикла наполнения перед setRowCount(). Обычно количество строк не превышает 3000 (как правило их около 500).
При загрузке программы виджет заполняется данными по умолчанию (те самые 3000 строк).
Когда пользователь меняет настройки отображения данных в таблице (крутит переключатели), посылается сигнал перезаполнить таблицу. По идее, это должно происходить достаточно быстро, чтобы пользователь не заметил задержки.
В итоге при первом запуске, со всеми данными, таблица создается и рисуется на экране примерно за 0,4 секунды. Не мгновенно, но терпимо.
Но при любом следующем вызове той же функции - скачать данные из базы, обработать, заполнить табличный виджет - время создания и рисования увеличивается в 50 раз (!). Исходная таблица 5х3000 создается 20 секунд. Из них (как показали замеры скорости) на создание QTableWidgetItem-ов и их наполнение данными уходит ~0,3 секунды, данные из базы приходят за 0,002 секунды, а сам блок из setItem-ов забирает все остальное время. Естественно, при такой скорости пользоваться программой невозможно, потому что менять данные в этой таблице приходится часто.
Пробовал ->clear(), ->hide() и ->show(), ->setUpdatesEnabled(true/false) - ничего не помогло. Предварительное удаление того, что было в ячейке (removeCellWidget()) - тоже.
В чем проблема задержки при повторной загрузке данных в таблицу, и как она может быть решена?


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: GreatSnake от Май 08, 2013, 07:23
Сортировка включена?


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 07:40
Сортировка включена?

В таблице сортировка работает (значит, включена).
Выключил перед созданием и включил после всех манипуляций - результат тот же:
0.4 секунды при запуске программы и 18,5 секунд при повторной загрузке.

Вот тут http://stackoverflow.com/questions/4031168/qtableview-is-extremely-slow-even-for-only-3000-rows у человека были похожие проблемы, но он, кажется, справился (через hide и show). Мне это не помогло. Да, ее не видно во время создания, но создается все равно почти 20 секунд.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 08:42
PS Методом проб и ошибок пришел к выводу, что бешеные задержки вызваны удалением QTableWidgetItem-ов при внесении изменений в таблицу, и, вероятно, при этом вызывается сборщик мусора. Кое-как удалось заставить работать код быстро (создал двумерный массив указателей QTableWidgetItem прямо в классе), но сработало только на усечение: элементы, которые были в таблице и остались в новой версии таблицы, не удаляются! А при попытке расширить диапазон значений прога вылетает, так как эти объекты не вошли в новую таблицу и были удалены.

Напрашивается вопрос: можно ли заставить QTableWidget НЕ УДАЛЯТЬ QTableWidgetItem?


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: RedDog от Май 08, 2013, 09:33
Напрашивается вопрос: можно ли заставить QTableWidget НЕ УДАЛЯТЬ QTableWidgetItem?
А зачем создавать каждый раз новый итем? Не легче старому присвоить новое значение? QTableWidgetItem::setText(...)


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 11:26
Здравая мысль, я так и хотел сделать - создал массив ссылок Item в классе, с запасом, инициализировал их в конструкторе, там же раз и навсегда записал им свойства типа шрифта и расположения, а потом, при необходимости, записывал в них данные и менял rowCount таблицы.
Проблема в том, что если количество строк таблицы уменьшается, а потом опять увеличивается, то айтимы, которые были на этом месте раньше, уничтожаются. Поэтому этот способ работает только тогда, когда мы уменьшаем таблицу. Не менять размер таблицы нельзя: неудобно, к тому же при сортировке вверху будут появляться пустые строки.

Проблема должна быть решена как-то иначе, потому что первая заливка данных в таблицу происходит быстро - и тем же кодом, что и в следующие разы, когда возникают тормоза.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: RedDog от Май 08, 2013, 11:47
А зачем создавать отдельный массив? QTableWidget уже содержит в себе все необходимое.
Ну либо прямая дорога использовать QTableView со своей моделью.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 12:27
А зачем создавать отдельный массив? QTableWidget уже содержит в себе все необходимое.
Ну либо прямая дорога использовать QTableView со своей моделью.


Спасибо за советы. Писать в Item ячейки tableWidget->item(y,x)->setText(...) за пределы старой таблицы у меня не получилось. Программа вылетает. Этих Item после изменения размеров в таблице просто нет, они старательно и неторопливо вымараны из памяти компьютера, в чем и проблема.
В данном случае задача не в том, чтобы отказаться от существующего кода в пользу новой реализации, а в том, чтобы выяснить, почему возникают тормоза. 250 элементов в секунду - очень медленно. Может быть, кто-то знает решение и попозже откликнется. QTableView - тоже вариант, один из, но все-таки хотелось бы сначала разобраться с QTableWidget, лечится он или нет.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: GreatSnake от Май 08, 2013, 12:44
Удаление элементов QStandardItem, QTreeWidgetItem и QTableWidgetItem очень тормознутое.
Особенно при иерархии просто даже неприлично энергоёмкое и соответственно медленное :(
Тут как указывалось выше есть 2 варианта:
1. Переиспользовать существующие.
2. Задействовать QTableView со своей моделью.
Первый вариант реализуется довольно просто.
У каждой строки таблицы должен быть свой уникальный id. Перед обновлением таблицы помечаете все строки как удалённые.
Далее при добавлении новых элементов ищите существующие по id и если находите, то сбрасываете "удалённость" и обновляете элементы строки. Новые строки добавляются в конец. Далее удаляете оставщиеся помеченные как удалённые строки.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: RedDog от Май 08, 2013, 12:54
Вот только не понятно, зачем удалять каждый раз все и заново создавать?
Удалил строку и хрен с ней - забыли про нее. По оставшимся пробежались циклом и установили нужный текст на давно уже созданные.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 13:25
Вот только не понятно, зачем удалять каждый раз все и заново создавать?
Удалил строку и хрен с ней - забыли про нее. По оставшимся пробежались циклом и установили нужный текст на давно уже созданные.

Пример работы с QTableWidget есть в мануале. Их пример - "динамически создать Item - установить значения - запустить setItem" очень прост, и, если использовать его, при запуске программы выполняется за 0.15 секунды (таблица 1390 строк, 5 столбцов). При следующих запусках того же метода тот же объем данных записывается в ту же таблицу за 6,5 секунды. Я очень надеюсь. что на форуме есть человек, который знает решение этой проблемы, увидит эту ветку, и расскажет, что нужно сделать, чтобы при любом запуске метода таблица наполнялась за 0.15 секунды, а не за 6.5.  :)


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: RedDog от Май 08, 2013, 13:29
[quote author=PinkPanther link=topic=24735.msg175955#msg175955 date=1Я очень надеюсь. что на форуме есть человек, который знает решение этой проблемы, увидит эту ветку, и расскажет, что нужно сделать, чтобы при любом запуске метода таблица наполнялась за 0.15 секунды, а не за 6.5.  :)
[/quote]Уже ж раз 5 сказали, почему так происходит (удаление старых - долгий процесс).
Что надо делать тоже не раз сказали...


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 14:04
Уже ж раз 5 сказали, почему так происходит (удаление старых - долгий процесс).
Что надо делать тоже не раз сказали...

Если не знаете ответа на поставленный мною вопрос, пожалуйста, покиньте эту ветку.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: GreatSnake от Май 08, 2013, 14:09
Если не знаете ответа на поставленный мною вопрос, пожалуйста, покиньте эту ветку.
Как-то грубо... Причём RedDog абсолютно прав.
Я уверен, что никто тебе здесь не поможет, ибо проблема в Qt.
Тем более, что QTableWidget изначально рассчитан на работу с небольшим объёмом данных.

Кстати, попробуй перед clear() выключить обработку сигналов самого виджета и его модели:
Код
C++ (Qt)
tw->model()->blockSignals( true );
tw->blockSignals( true );
tw->clear();
tw->blockSignals( false );
tw->model()->blockSignals( false );
 


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: Bepec от Май 08, 2013, 14:28
1) отключить напрочь обновление.
2) отключить сортировку.
3) провести операции по изменению-вставке.
4) включить отключенное в первых двух пунктах.
5) посмотреть на результат.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 15:24
Если не знаете ответа на поставленный мною вопрос, пожалуйста, покиньте эту ветку.
Как-то грубо... Причём RedDog абсолютно прав.
Я уверен, что никто тебе здесь не поможет, ибо проблема в Qt.
Тем более, что QTableWidget изначально рассчитан на работу с небольшим объёмом данных.

Кстати, попробуй перед clear() выключить обработку сигналов самого виджета и его модели:
Код
C++ (Qt)
tw->model()->blockSignals( true );
tw->blockSignals( true );
tw->clear();
tw->blockSignals( false );
tw->model()->blockSignals( false );
 

clear() работает быстро. Сотые доли секунды. Стоит ли блокировать сигналы перед вызовом clear() и восстанавливать их после вызова clear()?
Тормозит setItem(), так что если блокировать сигналы, то, наверное, перед вызовом этого метода?

Что касается "долгого удаления", это лишь смутная догадка (притом моя же собственная). Таблица создается быстро в первый раз, ДО ее визуализации, а в следующие разы окно нарисовано и она видна; товарищу с западного форума помог метод hide(), что противоречит догадке о задержках из-за сборщика мусора. Так как пример из мануала работает плохо, нужен ответ того, кто не ограничился чтением мануала, бился лично и решил эту проблему. Или бился, но не решил, зато выяснил, лично, почему оно так работает. Сделать таблицу на модели я могу, но придется существенно менять код. Время терпит. Спасибо за ответ, посмотрим, что скажут другие.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 15:28
1) отключить напрочь обновление.
2) отключить сортировку.
3) провести операции по изменению-вставке.
4) включить отключенное в первых двух пунктах.
5) посмотреть на результат.

Если речь о setUpdatesEnabled() и setSortingEnabled(), то все равно медленно. Возможно, какой-то прирост есть, но на глаз не заметен.


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: Bepec от Май 08, 2013, 15:29
Выложите проект и я его вам поколупаю. В сотни раз быстрее, чем в телепатов играть :P


Название: Re: QTableWidget (->setItem()) работает очень медленно
Отправлено: PinkPanther от Май 08, 2013, 15:45
Выложите проект и я его вам поколупаю. В сотни раз быстрее, чем в телепатов играть :P

Поздно! Проблема только что решилась. :)
Отдельное спасибо GreatSnake за то, что навел на мысль об использовании blockSignals(). После взятия ими в клещи группы setItem() скорость загрузки данных достигла космических высот. 1300 строк грузятся за 0,14 секунды! То есть втрое быстрее, чем была скорость на старте. 500 строк грузятся в таблицу за 63 миллисекунды.
Так что у Qt все в порядке - информации, увы, недостаточно. Всем спасибо за помощь.