Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > Программа и интерфейс
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 11.06.2015, 18:20
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию Ошибка при частой 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  
Старый 11.06.2015, 19:05
Аватар для M.A.D.M.A.N.
M.A.D.M.A.N. M.A.D.M.A.N. вне форума
Sir Richard Abramson
 
Регистрация: 05.04.2008
Сообщения: 5,505
Версия Delphi: XE10
Репутация: выкл
По умолчанию

Зачем поток? Почему не обычный таймер?
__________________
— Как тебя понимать?
— Понимать меня не обязательно. Обязательно меня любить и кормить вовремя.


На Delphi, увы, больше не программирую.
Рекомендуемая литература по программированию
Ответить с цитированием
  #3  
Старый 11.06.2015, 19:48
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию

Ну там такой фокус, что если количество итемов увеличелось, то выводится сообщение. Всё это нужно чтобы управление главной формой не останавливалось при показе сообщения. Да и таймер как-то слишком скучно, да и лишняя приблуда на форме.
__________________________________________
А вообще, ты прав. Суть ошибки заключается в том, что как-бы оно не было, поток кидает сообщение на перерисовку, а потом уже главная форма смотрит флаги и запускает перерисовку, но очередь сообщений из-за потока продолжает увеличиваться... Поэтому ставь флаги или не ставь, - сообщение от потока в очередь один хрен уходит, и по этому стек заполняется хочешь ты того или нет. И что бы вся эта шляпа заработала, единственный выход - это ставить на главную форму таймер, и посылать сообщения через него, причем в начале процедуры таймера - его выключать, после выполнения - включать, А поскольку сообщения кидает таймер - он зависит от главной формы, и в нём уже те самые флаги можно смотреть сразу, выставлять условие - посылать сообщение на перерисовку или нет... И всё стало работать.
__________________
Програмистами не рождаются, ими становятся!

Последний раз редактировалось SCrat.ORS, 11.06.2015 в 21:57.
Ответить с цитированием
  #4  
Старый 11.06.2015, 22:49
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,015
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Посмотри, можно ли переписать так, что бы обновление вызывалось только тогда, когда добавляется новый итем. Кстати, если форма с ListBox нормальная (т.е. созданная в нормальном потоке со ссылкой на Application), то при добавлении итема он сам перерисуется. Только делать это надо правильно. Добавлять итем надо из главного потока (VCL), вызывая соотв. код из вторичных потоков через Synchronize. Если у тебя несколько потоков добавляют итимы, то этот код надо вызывать в критической секции.
Ответить с цитированием
  #5  
Старый 12.06.2015, 15:19
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию

С добавлением проблем как-раз нету, дело в том, что содержимое итема может меняться "из вне". Поэтому и стоит постоянная перерисовка.
__________________
Програмистами не рождаются, ими становятся!
Ответить с цитированием
  #6  
Старый 12.06.2015, 17:33
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

не нужно постоянно перерисовывать
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
  #7  
Старый 12.06.2015, 18:17
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию

Нашел вариант, эти repaint и прочие вообще не нужны. Достаточно просто смотреть изменились ли данные итема, и если изменились, то просто выполнить string[]:='' на этот итем, и получается перерисовка только этого итема. И все работает, никаких лагов.
__________________
Програмистами не рождаются, ими становятся!
Ответить с цитированием
  #8  
Старый 12.06.2015, 23:58
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,015
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Цитата:
Сообщение от SCrat.ORS
С добавлением проблем как-раз нету, дело в том, что содержимое итема может меняться "из вне". Поэтому и стоит постоянная перерисовка.

Ну, значит, перерисовывать только в случае изменения. Более того, еще в отрисовщике проверять - видим ли итем, который надо перерисовать.
Ответить с цитированием
  #9  
Старый 13.06.2015, 01:23
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию

Поскольку я отказался от методов repaint появилась маленькая дилема.
Когда в listbox много итемов и появляется скролл - получается такая хрень:

Эта черная полоска исчезает при перерисовке... но стоит мотнуть скролл - опять появляется. Как бы от нее избавится? как её зарисовать.
__________________
Програмистами не рождаются, ими становятся!
Ответить с цитированием
  #10  
Старый 13.06.2015, 02:22
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,015
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Возможно, где-то неправильно считается высота итемов, вот и остается неиспользованное место. У меня, вроде, такого не встречалось, хотя ручную отрисовку итемов ListBox'а я использую достаточно часто.
Ответить с цитированием
  #11  
Старый 13.06.2015, 12:37
Аватар для cotseec
cotseec cotseec вне форума
Активный
 
Регистрация: 16.07.2008
Сообщения: 353
Версия Delphi: D7,TDE06,RAD09
Репутация: 1443
По умолчанию

Цитата:
Сообщение от SCrat.ORS
Эта черная полоска исчезает при перерисовке... но стоит мотнуть скролл - опять появляется. Как бы от нее избавится? как её зарисовать.
или убрать двойную буферизацию у листбокса (логическое свойство DoubleBuffered) или обрабатывать WM_ERASEBKGND
__________________
Понять, что хочет заказчик - бесценно, ведь он платит MasterCard
Ответить с цитированием
Этот пользователь сказал Спасибо cotseec за это полезное сообщение:
SCrat.ORS (13.06.2015)
  #12  
Старый 13.06.2015, 12:46
Аватар для SCrat.ORS
SCrat.ORS SCrat.ORS вне форума
Активный
 
Регистрация: 20.02.2007
Адрес: Мой адрес не дом и не улица, мой адрес 0x7С00
Сообщения: 208
Версия Delphi: 2006
Репутация: 884
По умолчанию

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;
...
Извлекаем информацию от скролла листбокса, если он находится внизу, - это сигнализирует о том, что там может нарисоваться эта черная полоса заднего буфера, Поэтому берём всю клиентскую область, смотрим сколько итемов может влезти, и забираем остаток области, и закрашиваем её =D
__________________
Програмистами не рождаются, ими становятся!

Последний раз редактировалось SCrat.ORS, 13.06.2015 в 15:08.
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 12:42.


 

Сайт

Форум

FAQ

RSS лента

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2023

ВКонтакте   Facebook   Twitter