TreeView странность.

Вопросы программирования и использования среды Lazarus.

Модератор: Модераторы

TreeView странность.

Сообщение wofs » 31.08.2017 00:28:58

Добрый день! Прошу помощи.
Имеем дерево. Слево - дерево. Справа - лог из процедуры заполнения.
Screenshot_1.png

Процедура заполнения:
Код: Выделить всё
procedure FillTree();
var
   i:integer;
begin
  // Корневой узел (Root), должен быть первым в выборке Query
if not Connect1.Connected then Connect1.Open;
with Query1 do
begin
    Tree1.BeginUpdate;
    Close;
    SQL.Clear;
    SQL.Text:='SELECT * FROM '+TableName+' ORDER BY '+idParent+', '+cNodeName+';'; // запрос на выборку
    Open;
    First;
    Tree1.Items.Clear;
    Tree1.Items.AddObject(nil, FieldByName(cNodeName).AsString,
      Pointer(FieldByName(idNode).asInteger)); // добавляем корневой объект
    Next;
    while not Eof do // перебираем в цикле все записи в Query
    begin
      i := 0;
      while i < Tree1.Items.Count do
        if Tree1.Items.Item[i].Data = Pointer(FieldByName(idParent).asInteger)
          then
        begin
          Form1.Log('i= '+IntToStr(i)+' Node= '+FieldByName(idNode).AsString+' Name= '+FieldByName(cNodeName).AsString); //ЗАПИСЬ В ЛОГ
          Tree1.Items.AddChildObject(Tree1.Items.Item[i],
            FieldByName(cNodeName).AsString,
            Pointer(FieldByName(idNode).asInteger));      // добавляем дочерние объекты
          break;
        end
        else
          Inc(i);
      Next;
    end;
  end;
  Tree1.EndUpdate;
end;


-----
Изменяем один из узлов: событие onEdited, обработчик:
Код: Выделить всё
procedure TForm1.EditItemName(Node: TTreeNode; var S: ansistring);
var
   i:integer;
begin
   SQLTransaction1.Active:=False;

   if not Connect1.Connected then Connect1.Open;
     SQLTransaction1.Active:=true;

  with Query1 do
    begin
       Close;
       SQL.Clear;
       SQL.Text:='UPDATE '+TableName+' SET '+cNodeName+'=:NameText WHERE '+idNode+' = :idNodeWhereValue ;'; // запрос на выборку

       Params.ParamByName('NameText').Value:=S;
       Params.ParamByName('idNodeWhereValue').Value:=IntToStr(Integer(Node.Data));
       execSQL;

         SQLTransaction1.Commit;
         SQLTransaction1.Active:=false;
         Clear();
         FillTree();
    end;

end;   


Далее чистим дерево процедурой:
Код: Выделить всё
procedure Clear();
var
   i:integer;
begin
with Tree1 do
begin
   for  i:=Items.Count-1 downto 0 do
   begin
        Items[i].Data:=nil;
        Items[i].Free;
   end;
end;   
end; 

И заново заполняем
Код: Выделить всё
FillTree()
.

И получаем странный результат, а именно Node7 (см лог) имеет имя, которое мы указали при изменении Node14. Слево - дерево. Справа - лог из процедуры заполнения.
Screenshot_2.png

Не могу понять отчего так происходит. Притом, если кинуть кнопку и назначить процедуру очистки и заполнения на нее, то все обновляется ок.
Накидал текстовый проект (~460кб) скачать можно здесь (YandexDisk):
https://yadi.sk/d/t_7oRPBs3MTyow
-------------------
Вариант без БД.
Изначальный вариант с ошибкой. Пробуем править узел.
https://yadi.sk/d/uo3W0J-g3MYqXY

Вариант, предложенный zub.
https://yadi.sk/d/6ZYVCZQC3MYqaV

Проверил компиляцию Win/Mac - норм.
У вас нет необходимых прав для просмотра вложений в этом сообщении.
Последний раз редактировалось wofs 03.09.2017 00:19:04, всего редактировалось 2 раз(а).
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение vitaly_l » 31.08.2017 07:00:11

wofs писал(а):procedure Clear();
var
   i:integer;
begin
with Tree1 do
begin
   for  i:=Items.Count-1 downto 0 do
   begin
        Items[i].Data:=nil;
        Items[i].Free;
   end;
end;   
end; 

Код: Выделить всё
        Items[i].Free;
        Items[i].Data:=nil;

?


.
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3070
Зарегистрирован: 31.01.2012 16:41:41

Re: TreeView странность.

Сообщение olegy123 » 31.08.2017 08:11:58

wofs писал(а):Items[i].Data:=nil;

просто Items[i].Data:=0;

wofs писал(а):Items[i].Free;

Это что такое?
Может правильнее сделать Items.Delete(i)
Items[i] имеет предка TObject, у которого есть свойство Free. При этом сам Items в удалении объекта не участвует, поэтому сохраняет запись на объект по внутреннем списке FList => которые есть Items.
olegy123
энтузиаст
 
Сообщения: 724
Зарегистрирован: 25.02.2016 12:10:20

Re: TreeView странность.

Сообщение wofs » 31.08.2017 09:42:21

vitaly_l писал(а):Items[i].Free;
Items[i].Data:=nil;

Без изменений.

olegy123 писал(а):просто

Код: Выделить всё
Items[i].Data:=0;

Ошибка типов.

Код: Выделить всё
Items.Delete(i);

Ошибка типов Int и TNode.
Попробовал так:
Код: Выделить всё
Items.Delete(Items.Item[i]);

Без ошибок, но результата нет.

p.s. перезалил проект - была зависимость от rx. Теперь только стандартные компоненты.
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение olegy123 » 31.08.2017 10:13:51

wofs писал(а):Ошибка типов Int и TNode.

Да. Перепутал с TListBox.

Код: Выделить всё
procedure TTreeNodes.Delete(Node: TTreeNode);
begin
  Node.Delete;
  if (FUpdateCount=0) and (Owner<>nil) then
    Owner.Invalidate;
end;

Можно и Node.Delete; и Node.Free;

Но,
wofs писал(а): for i:=Items.Count-1 downto 0 do
begin
Items[i].Data:=nil;
Items[i].Free;
end;

не проще Items[i].DeleteChildren();
а раз общий Items, то Items.Clear();

Добавлено спустя 8 минут 27 секунд:
http://promsite.us/TTreeView1_p3.html
olegy123
энтузиаст
 
Сообщения: 724
Зарегистрирован: 25.02.2016 12:10:20

Re: TreeView странность.

Сообщение wofs » 31.08.2017 10:40:00

olegy123 писал(а):procedure TTreeNodes.Delete(Node: TTreeNode);
begin
Node.Delete;
if (FUpdateCount=0) and (Owner<>nil) then
Owner.Invalidate;
end;


Не сообразил что-то я...
olegy123 писал(а):не проще...

Код: Выделить всё
Items[i].DeleteChildren();

Результат аналогичен.
Код: Выделить всё
а раз общий Items, то Items.Clear();

А это был мой первый вариант.
olegy123 писал(а):http://promsite.us/TTreeView1_p3.html

Спасибо, почитаю.

Добавлено спустя 8 минут 49 секунд:
Самое интересное, что если процедуры
Код: Выделить всё
Clear();
FillTree();

Вызвать кнопкой, то все работает. А вот, если вызываю сразу после обновления - получаем такой вот результат.

Добавлено спустя 35 минут 31 секунду:
В общем, если поставить таймер на 100мс и обновлять дерево по таймеру (после обновления, с задержкой 100мс), то все норм.

Собственно вопрос - почему так происходит?
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение vitaly_l » 31.08.2017 11:52:14

wofs писал(а):почему так происходит?

Посмотрите по ссылке на функцию new для pointer. И перед присваиванием data и перед созданием объекта, делайте вначале new(somePointer) и только потом присваивайте созданный somePointer data и объекту, даже когда делаете это в цикле. И вначале free, а потом nil.
Вот описание для new() http://wiki.freepascal.org/Pointer
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3070
Зарегистрирован: 31.01.2012 16:41:41

Re: TreeView странность.

Сообщение wofs » 31.08.2017 13:31:26

vitaly_l писал(а):И вначале free, а потом nil.

Согласен, но даже в таком случае, если вызывать очистку и заполнение из процедуры изменения наименования узла, то происходит описанный выше казус. А вот если очистку и заполнение вызывать из таймера 100мс, то все ок.

Меня и интересует - кому требуется "отдых" и почему?
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение Лекс Айрин » 31.08.2017 14:16:30

wofs писал(а):Меня и интересует - кому требуется "отдых" и почему?


Видимо, сборщику мусора. А возможно, что удаление идет параллельно вызвавшему ее коду.
Аватара пользователя
Лекс Айрин
долгожитель
 
Сообщения: 4097
Зарегистрирован: 19.02.2013 16:54:51

Re: TreeView странность.

Сообщение zub » 31.08.2017 15:27:40

>>Меня и интересует - кому требуется "отдых" и почему?
Отдых требуется тебе и всем тут советующим.

Есть простая истина: ненадо удалять класс из егоже метода, особенно если этот класс писал не ты и еще более особенно если этот метод вызывал не ты))
vangamode:=on {ибо нефиг оставлять sql в "простом" примере};
вижу такое:
Код: Выделить всё
procedure TForm1.EditItemName([b]Node: TTreeNode[/b]; var S: ansistring);
....
         Clear();
....
end;

то что вызвало это непотребство передало ему Node: TTreeNode, ты взял и порубил всё дерево, в котором этот Node сидит. А вдруг по возвращению из .EditItemName вызывающая сторона захочет какиенибудь манипуляции с Node произвести?

Такие вещи нужно делать асинхронно (с передышкой), чтобы гарантировано никто не пытался лезть к уже удаленным данным
zub
долгожитель
 
Сообщения: 2259
Зарегистрирован: 14.11.2005 23:51:26

Re: TreeView странность.

Сообщение wofs » 31.08.2017 16:26:57

zub писал(а):Есть простая истина: ненадо удалять класс из егоже метода, особенно если этот класс писал не ты и еще более особенно если этот метод вызывал не ты))

Записал на полях...
zub писал(а):то что вызвало это непотребство передало ему Node: TTreeNode, ты взял и порубил всё дерево, в котором этот Node сидит. А вдруг по возвращению из .EditItemName вызывающая сторона захочет какиенибудь манипуляции с Node произвести?

А судя по всему так и есть!
zub писал(а):Такие вещи нужно делать асинхронно (с передышкой), чтобы гарантировано никто не пытался лезть к уже удаленным данным

А вот за это спасибо - запомню. Пишу программки для собственных нужд и крайне редко - многого не знаю.

Всем спасибо, вопрос можно считать закрытым.
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение vitaly_l » 31.08.2017 16:37:15

zub писал(а):Отдых требуется тебе и всем тут советующим.
vangamode:=on

Zub-ик! А кто отключать vangamode будет?
После того, как станет понятно что, на работу с БД требуется время,
нужно обязательно сделать vangamode:=off иначе ванга-модуль перегреется, т.к. вы даёте pointer ссылки куда-то туда, неизвестно куда и как оно вообще работает непонятно. Кстати функцию Clear(); можете отключить, т.к. БД - сама за собой всё почистит.

wofs писал(а):А вот если очистку и заполнение вызывать из таймера 100мс, то все ок.

А вообще, Вам - нужно посмотреть в вики, как правильно: создавать, заполнять и удалять Node-узлы в Tree, т.к. перед присваиванием pointer-ов для Data - нужно создавать их функцией new() и потом удалять, соответственно правилам удаления [после использования new()].

На этом по совету, Zub-ика - ухожу на заслуженный отдых. :roll: Главное: вангинатору - не забывайте присваивать off;


.
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3070
Зарегистрирован: 31.01.2012 16:41:41

Re: TreeView странность.

Сообщение wofs » 31.08.2017 17:03:30

vitaly_l писал(а):После того, как станет понятно что, на работу с БД требуется время,

БД успевает отработать и это видно по логу - там нет никаких проблем. Проблемы именно в TreeView,а не при чтении данных для заполнения.
vitaly_l писал(а):Кстати функцию Clear(); можете отключить, т.к. БД - сама за собой всё почистит.

А тут опять не понял. Причем здесь Clear для TreeView и БД.
vitaly_l писал(а): Вам - нужно посмотреть в вики, как правильно: создавать, заполнять и удалять Node-узлы в Tree, т.к. перед присваиванием pointer-ов для Data - нужно создавать их функцией new() и потом удалять, соответственно правилам удаления [после использования new()].

Пойду почитаю... Отчего-то была мысль, что если есть узел, то есть и дата к нему, осталось его только заполнить.
Аватара пользователя
wofs
постоялец
 
Сообщения: 165
Зарегистрирован: 05.10.2009 10:16:55
Откуда: Астрахань

Re: TreeView странность.

Сообщение Alex2013 » 31.08.2017 17:17:42

В TreeView вообще много странного и загадочного ...
(В суть твоей проблемы пока не вник но сам сталкивался с чем-то похожим )
:arrow: viewtopic.php?f=5&t=24995&start=30
Alex2013
энтузиаст
 
Сообщения: 697
Зарегистрирован: 03.04.2013 11:59:44

Re: TreeView странность.

Сообщение vitaly_l » 31.08.2017 17:20:09

wofs писал(а):Причем здесь Clear для TreeView и БД

Вы даёте в TreeView Node.Data - адреса, которые возможно временно создал SQL. И когда SQL автоматом подчищает за собой, у вас начинает всё работать, через 100 мс. Там не должно быть никакой задержки, если всё правильно сделать.

В смысле, я полагаю что, вот так делать неправильно:
node.data := Pointer(FieldByName(idNode).asInteger));
Tree1.Items.AddChildObject(Tree1.Items.Item[i], FieldByName(cNodeName).AsString, Pointer(FieldByName(idNode).asInteger));

Я могу ошибаться, тогда сейчас меня поправят и объяснят суть.
Аватара пользователя
vitaly_l
долгожитель
 
Сообщения: 3070
Зарегистрирован: 31.01.2012 16:41:41

След.

Вернуться в Lazarus

Кто сейчас на конференции

Сейчас этот форум просматривают: Google Adsense [Bot] и гости: 8

Рейтинг@Mail.ru