Эффективное обновление QTableView на высокой скорости

Я использую QTableView с подклассом QItemDelegate для управления внешним видом ячеек таблицы.

Каждая ячейка отображает имя и статус подключенного извне устройства, и одновременно может быть подключено до 100 устройств.

Название и тип каждого устройства в основном статичны, обновляются очень редко (возможно, раз в час), но каждая ячейка должна отображать значение входа устройства в реальном времени, которое я в настоящее время опрашиваю каждые 50 миллисекунд. Это значение отображается в виде базовой гистограммы, нарисованной художником, предоставленным методу Delegate :: paint () в TableView.

Проблема с обновлением моей модели 20 раз в секунду заключается в том, что вся таблица перерисовывается каждый раз, что крайне неэффективно. Ограничение метода рисования только до рисования гистограммы показывает, что большая часть процессорного времени отводится для рисования имени, статуса и связанного изображения в каждой ячейке, а не на графике.

Мне нужно найти способ регулярно обновлять график для каждой ячейки, не перерисовывая ячейку, но я не могу понять, как это сделать.

Каков наиболее эффективный способ достижения этого?

Изменить: изображение прилагается, чтобы помочь.

Изображение представляет 10 датчиков в QTableView. Число, Имя и Статус практически статичны и почти никогда не обновляются. Гистограмма рядом с текстом «Значение датчика» обновляется каждые 50 мс. Я только хочу нарисовать эту строку, а не текст, статус и фон ячейки. Индикаторы состояния и фон являются сложными изображениями, поэтому занимают гораздо больше процессорного времени, чем простое рисование и заполнение прямоугольника.

 György Andrasek22 сент. 2010 г., 16:14
Статус должен быть в том же виджете, что и все остальное? Моей первой мыслью было бы вставить ListView из той же модели рядом с ним.
 Dani22 сент. 2010 г., 16:48
Да, к сожалению, это так. Каждое устройство имеет ряд параметров, которые должны находиться рядом с графиком. Я действительно думал о двух видах, возможно, наложенных друг на друга, но это кажется очень грязным способом достижения того, чего я хочу, и делает изменение моделей, редактирование и так далее более сложным.

Ответы на вопрос(3)

Редко я предлагаю этот путь, а не делегатов, но, похоже, в вашей ситуации это того стоит. Я хотел бы создать свой собственный вид, который достаточно осведомлен, чтобы обновлять только те части экрана, которые необходимо обновить. Такой виджет видимости, очевидно, более специализирован, чем обычно, но если вам действительно нужна эффективность, это один из способов.

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

 UmNyobe08 мар. 2016 г., 11:47
@Dani Немного опоздал на вечеринку, но этот вопрос мог бы сильно выиграть от того, что вы узнали после такого подхода.
 Dani28 сент. 2010 г., 11:16
Это маршрут, по которому я пошел; создавая свой собственный взгляд. Я воспользовался советом Року и использовал QGLWidget для обработки рендеринга, и в результате я вижу 3% загрузки ЦП для сотен устройств, которые обновляются с еще более короткими интервалами, чем раньше.

Кэшируйте фоновое изображение (фоновое изображение ячейки, статус и имя) в модель как QPixmap. Перерисовывайте это растровое изображение только при изменении статуса или имени. В общем случае вам нужно всего лишь нарисовать кэшированную QPixmap и значение датчика поверх этого.

Редактировать:

Добавьте флаг fullRepaintNeeded в ваш класс данных. При изменении статуса или имени для fullRepaintNeeded устанавливается значение true.

Когда делегат рисует элемент, он сначала проверяет флаг элемента fullRepaintNeeded. Если fullRepaintNeeded - true, тогда создается новый QPixmap, и все отображается в этом QPixmap, который в конечном итоге отображается в табличном представлении. Затем QPixmap кэшируется в модель (что означает для вашего класса данных) с помощью функции setData модели (но dataChanged не вызывается). fullRepaintNeeded теперь имеет значение false.

Но если fullRepaintNeeded имеет значение false в функции рисования делегата, из модели запрашивается ранее кэшированное QPixmap, которое отображается в табличном представлении, и поверх него отображается окончательное значение датчика.

 Live22 сент. 2010 г., 18:54
Я ошибся в своем первом комментарии, см. Пост Року: вы либо обновляете растровое изображение, рисуете его и рисуете свой график, либо рисуете растровое изображение и рисуете график.
 Dani22 сент. 2010 г., 19:04
Я попробую и посмотрю, но я верю, что как только paint () вызывается, область просмотра сбрасывается при подготовке к рисованию.
 Dani22 сент. 2010 г., 18:45
Я только что попробовал это. Процедура рисования работает точно так, как вы описали, результат: когда флаг установлен, ячейка очищается, а затем график рисуется. При обновлении других значений (или изменении выбора) остается только максимум 50 мс до следующего обновления графика, который затем очищает ячейки и снова окрашивает только столбец.
 Live22 сент. 2010 г., 18:10
Хорошая идея тоже, не думал об этом.
 Dani28 сент. 2010 г., 11:13
Исчерпав все остальные возможности, я воспользовался вашим советом и начал воспроизводить QTableView, использующий рендеринг OpenGL. На самом деле это заняло намного меньше времени, чем я думал, и будет более гибким в долгосрочной перспективе, так как я буду полностью контролировать то, как он выглядит и работает.
 Dani22 сент. 2010 г., 18:56
Roku and Live: проблема с этим ответом заключается в том, что мне все еще приходится рисовать растровое изображение на экране 20 раз в секунду, что неэффективно. Мне нужен способ оставить нетронутым растровое изображение, если оно не обновлено, и просто рисовать графики каждые 50 мс.
 Live22 сент. 2010 г., 18:59
Разве вы не можете определить область рисования, меньшую, чем размер делегата, и оставить остальную часть нетронутой?
 user36263822 сент. 2010 г., 19:09
Вам необходимо использовать QTableView? Если вам действительно нужно быстро и с низкой загрузкой процессора, нарисуйте все с помощью OpenGL, используя QGLWidget. Вы можете легко рисовать каждые 20 раз в секунду с очень низкой загрузкой процессора. Но это займет намного больше кода, чем использование QTableView.
 Dani22 сент. 2010 г., 18:20
Это то, что я хочу сделать, но как мне это сделать? Есть только одна процедура рисования и только один слот dataChanged (). В идеале мне нужно два, но какой объект QPainter я использую для дополнительного? Я начал реализовывать это, используя метод флага Live, посмотрю, как это будет.

так как ваш QTableView наследует QWidget, вы можете вызвать на нем следующее:

setUpdatesEnabled(false);
changeAllYourData();
setUpdatesEnabled(true);

Когда setUpdatesEnabled имеет значение false, любой вызов paint () или update () не имеет никакого эффекта. Таким образом, вы можете остановить его обновление, изменить все ваши данные и затем снова включить его, возможно, вручную вызвав paint () или update () вручную, я не уверен в этой части.

Вот документация для метода setUpdatesEnabled.

QWidget updatesEnabled

Надеюсь это поможет.

РЕДАКТИРОВАТЬ после комментария от пользователя:

Вы можете реализовать свой собственный setUpdatesEnabled (bool) для своего подкласса QItemDelegate (так как он не наследует QWidget и не имеет его), протестировав флаг перед выполнением вашего исходного paint () или update (). После этого вы можете указать для каждой ячейки (или строки, или столбца) вашего QTableView, должны ли они быть обновлены или перекрашены.

Сделав это, вы можете помешать перекрашиванию других ваших ячеек (делегатов), если вы не измените флаг setUpdatesEnabled, который вы создали вручную, но сохраните обновления в ваших ячейках, содержащих график.

Я должен сказать, что никогда не проверял это или что-то подобное, поэтому я надеюсь, что это работает так, как я думаю.

Удачи

РЕДАКТИРОВАТЬ после редактирования от пользователя:

Следуя моему предыдущему комментарию, вместо установки флага для каждой ячейки (я думал, что ваш график находится в отдельной ячейке), вы можете установить флаг для каждого делегата, чтобы рисовать только ваш график или все изображение.

Надеюсь это поможет,

,

РЕДАКТИРОВАТЬ:

Я наткнулся на новую функцию в Qt 4.7 (я не знаю, возможно ли вам ее использовать, но она может решить некоторые ваши проблемы.) Эта функция - QStaticText. Это класс, который позволяет вам кэшировать текст (шрифт и эффекты) и рисовать их быстрее. Смотрите ссылкуВот.

Надеюсь, что это может решить вашу проблему.

 Live22 сент. 2010 г., 16:48
Является ли график, который вы хотите написать один в ячейке?
 Dani22 сент. 2010 г., 17:02
Нет, это включено с другой информацией. Я приложил изображение к исходному вопросу, чтобы помочь визуализировать графический интерфейс.
 Live22 сент. 2010 г., 17:05
Я отредактировал свой пост, посмотрите, может ли он работать сейчас (надеюсь).
 Dani22 сент. 2010 г., 16:45
Я на самом деле уже делаю это, так что я только обновляю модель только 20 раз в секунду, а не 20 x numberOfDevices, но это не решает проблему, так же как и графики, я рисую фон изображение, строка имени, строка состояния и различные другие дисплеи, которые сами обновляются только с гораздо меньшей скоростью.

Ваш ответ на вопрос