|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
||||
|
||||
Ошибка при частой Repaint
Есть главная форма, на ней ListBox в котором вручную (onDrawItem) отрисовываются итемы, используя какую-то информацию для заполнения.
Есть функция, назовём её RefreshInform, которая собирает эту информацию, и в конце запускает ListBox.repaint, чтобы onDrawItem нарисовал итемы. При запуске главной формы, через отдельный юнит, запускается бесконечный поток, суть которого - через каждые 100 мс запускать Synchronize(MainForm.RefreshInform). И вот тут меня ждала проблема. Через 5-10 минут работы, всё зависает и падает, а отладчик пишет "out of system resources" несколько раз, а затем "Stack overflow" и стек забит WM_PAINT. Ну логично - забило, только вот почему? Сделал у функции RefreshInform некий глобальный флаг, который в начале устанавливается в False, а по окончанию в True, и, соответсвенно, в потоке перед вызовом RefreshInform стоит проверка этого флага. - Всё-равно падает. Начал читать гугл, нашёл некую информацию, что repaint - виртуальный метод или что-то типа этого, мол используйте refresh он сделан для совместимости. Ок, сделал, запускаю - не помогло, такая же проблема. Читаю дальше, нахожу, что repaint и refresh делают принудительную перерисовку, а чтобы перерисовка была в порядке очереди, - используйте Invalidate, ок делаем так. Теперь проблема уменьшилась до одного сообщения "out of system resources", но так же всё зависает сначала, потом ошибка и всё падает. Знаю что проблема именно в потоке из-за запуска функции RefreshInform в которой запускается выполнение перерисовки, - получается эдакая постоянная перерисовка. А если поставить условие, что например если в listbox изменился count, тогда запускать RefreshInform - все работает стабильно. Нужна помощь. Суть всей этой балды, что бы в ListBox всегда была актуальная информация из-за постоянного обновления в потоке. Использовать банальное ListBox.String[i]:='Бла-бла-бла' - не получится, т.к. ListBox у меня является не более чем "контейнер" для строк, а содержимое строк рисуется из массива, поэтому реальные строки ListBox остаются пустыми, не содержат текста. Програмистами не рождаются, ими становятся! Последний раз редактировалось SCrat.ORS, 11.06.2015 в 18:41. |
#2
|
||||
|
||||
Зачем поток? Почему не обычный таймер?
— Как тебя понимать? — Понимать меня не обязательно. Обязательно меня любить и кормить вовремя. На Delphi, увы, больше не программирую. Рекомендуемая литература по программированию |
#3
|
||||
|
||||
Ну там такой фокус, что если количество итемов увеличелось, то выводится сообщение. Всё это нужно чтобы управление главной формой не останавливалось при показе сообщения. Да и таймер как-то слишком скучно, да и лишняя приблуда на форме.
__________________________________________ А вообще, ты прав. Суть ошибки заключается в том, что как-бы оно не было, поток кидает сообщение на перерисовку, а потом уже главная форма смотрит флаги и запускает перерисовку, но очередь сообщений из-за потока продолжает увеличиваться... Поэтому ставь флаги или не ставь, - сообщение от потока в очередь один хрен уходит, и по этому стек заполняется хочешь ты того или нет. И что бы вся эта шляпа заработала, единственный выход - это ставить на главную форму таймер, и посылать сообщения через него, причем в начале процедуры таймера - его выключать, после выполнения - включать, А поскольку сообщения кидает таймер - он зависит от главной формы, и в нём уже те самые флаги можно смотреть сразу, выставлять условие - посылать сообщение на перерисовку или нет... И всё стало работать. Програмистами не рождаются, ими становятся! Последний раз редактировалось SCrat.ORS, 11.06.2015 в 21:57. |
#4
|
|||
|
|||
Посмотри, можно ли переписать так, что бы обновление вызывалось только тогда, когда добавляется новый итем. Кстати, если форма с ListBox нормальная (т.е. созданная в нормальном потоке со ссылкой на Application), то при добавлении итема он сам перерисуется. Только делать это надо правильно. Добавлять итем надо из главного потока (VCL), вызывая соотв. код из вторичных потоков через Synchronize. Если у тебя несколько потоков добавляют итимы, то этот код надо вызывать в критической секции.
|
#5
|
||||
|
||||
С добавлением проблем как-раз нету, дело в том, что содержимое итема может меняться "из вне". Поэтому и стоит постоянная перерисовка.
Програмистами не рождаются, ими становятся! |
#6
|
||||
|
||||
не нужно постоянно перерисовывать
Пишу программы за еду. __________________ |
#7
|
||||
|
||||
Нашел вариант, эти repaint и прочие вообще не нужны. Достаточно просто смотреть изменились ли данные итема, и если изменились, то просто выполнить string[]:='' на этот итем, и получается перерисовка только этого итема. И все работает, никаких лагов.
Програмистами не рождаются, ими становятся! |
#8
|
|||
|
|||
Цитата:
Ну, значит, перерисовывать только в случае изменения. Более того, еще в отрисовщике проверять - видим ли итем, который надо перерисовать. |
#9
|
||||
|
||||
Поскольку я отказался от методов repaint появилась маленькая дилема.
Когда в listbox много итемов и появляется скролл - получается такая хрень: Эта черная полоска исчезает при перерисовке... но стоит мотнуть скролл - опять появляется. Как бы от нее избавится? как её зарисовать. Програмистами не рождаются, ими становятся! |
#10
|
|||
|
|||
Возможно, где-то неправильно считается высота итемов, вот и остается неиспользованное место. У меня, вроде, такого не встречалось, хотя ручную отрисовку итемов ListBox'а я использую достаточно часто.
|
#11
|
||||
|
||||
Цитата:
Понять, что хочет заказчик - бесценно, ведь он платит MasterCard |
Этот пользователь сказал Спасибо cotseec за это полезное сообщение: | ||
SCrat.ORS (13.06.2015)
|
#12
|
||||
|
||||
cotseec, Спасибо. Ты прав. Отключил буферизацию, и полоса пропала. А вот обрабатывать WM_ERASEBKGND в моём случае будет не совсем к месту.
Но при отключении буферизации, немного мерцать начинать иногда.. Эта чёрная полоса - это background заднего буфера. Фокус в том, что везде пишут - в начале отрисовки ставится FillRect(ClientRect), т.е. закрашивают весь контрол фоном, а потом рисуют все итемы, а это кощунство. я не закрашиваю всю область фоном, а просто рисую итем. Таким образом, если сделать FillRect(ClientRect), то весь контрол будет белым, а нарисован только выделенный итем. Вообщем придумал костыль =D Код:
Var ... backclear:integer; Sinf:TScrollInfo; maxPos:Integer; begin Sinf.cbSize:= SizeOf(ScrollInfo); Sinf.fMask := SIF_POS + SIF_RANGE + SIF_PAGE; GetScrollInfo(Control.Handle, SB_VERT, Sinf); MaxPos:=Sinf.nMax - Round(Sinf.nPage) + 1; if Sinf.nPos = MaxPos then with (Control as TListBox).Canvas do begin Brush.Color:=ClWindow; BackRect:=ClientRect; backclear:=(Control as TListBox).Height mod (Control as TListBox).ItemHeight; BackRect.Top:=(Control as TListBox).Height-backclear; FillRect(BackRect); end; ... Програмистами не рождаются, ими становятся! Последний раз редактировалось SCrat.ORS, 13.06.2015 в 15:08. |