Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Как древовидно визуализировать ArrayOfRecord в VT


Автор: Sheleh 21.4.2011, 18:38
Здравствуйте, помогите пожалуйста визуализировать линейную структуру Objects: array of Tobjects;: 

Код

  Tobjects = record
    ID, ParentID, Name, Num, Time: string;
    items: array[1..16] of string;


В этой структуре иерархия объектов задается полями ID (собственный индекс) и ParentID (индекс родителя, которому принадлежит объект). Например:
Код

ID;ParentID;Name;Num;Time
1;0;Radius;R001;40648.1033536806
2;1;Tr;0116;40648.5782788079
3;1;Tr;0000;40648.5796839699
4;3;Dev;C;40648.5796839699
5;3;Dev;1;40648.5764067014
6;5;Sh;1;40648.5733965394
7;1;Tr;0091;40648.5737310995


На данный момент я заполняю VT по примеру обычным списком без иерархии
Код

Процедура заполнения;
var
  NewNode: PVirtualNode;
  NewObject: Pobjects;
begin
     for i:=0 to ObjectsCount do
      begin
         NewNode := VT.AddChild(VT.FocusedNode);
         NewObject := VT.GetNodeData(NewNode);
         NewObject^:=Objects[index];
      end;
end;

//Процедура отображения
procedure TMainForm.VTGetText(Sender: TBaseVirtualTree; Node: PVirtualNode;
  Column: TColumnIndex; TextType: TVSTTextType; var CellText: WideString);
var
  ObjectNode: PObjects;
begin
  ObjectNode := VT.GetNodeData(Node);
  if Assigned(ObjectNode) then
    case Column of
      0: CellText := ObjectNode^.ID;
      1: CellText := ObjectNode^.ParentID;
      2: CellText := ObjectNode^.Name;
      3: CellText := ObjectNode^.Num;
      4: CellText := ObjectNode^.Time;
    end;
end;


А как при заполнении зная индекс родителя ParentID сфокусировать родительский node по ID, что бы вставить от него Child Node. Надеюсь я ясно выразился ) 

За ранее спасибо!

Автор: cat512 21.4.2011, 21:37
Пожалуйста, заранее.
Цитата

сфокусировать родительский node по ID

Цитата

Надеюсь я ясно выразился 

Абсолютно не ясно.  smile  Ты фотографией увлекаешься?

Автор: Sheleh 22.4.2011, 08:33
Цитата
Абсолютно не ясно.  Ты фотографией увлекаешься?
 нет, я садовод  smile Я не про оптический фокус, а про VT.AddChild(VT.FocusedNode)

Ладно, попробую еще раз по порядку. 

Есть массив записей - Objects: array of Tobjects;
Запись содержит текстовые поля: Tobjects = record
ID, ParentID, Name, Num, Time: string; с примерно следующим содержанием:
1;0;Radius;R001;40648.1033536806
2;1;Tr;0116;40648.5782788079
3;1;Tr;0000;40648.5796839699
4;3;Dev;C;40648.5796839699
5;3;Dev;1;40648.5764067014
6;5;Sh;1;40648.5733965394
7;1;Tr;0091;40648.5737310995

Казалось бы простой масиив строк, но здесь есть ирархия. Каждая запись имеет собственный ID и ParentID - это ID родителя, к которому она относится. Для наглядности вот те же самые данные в древовидной форме.
Код

0 - root
        |-----1;0;Radius;R001;40648.1033536806
                |
                |-----2;1;Tr;0116;40648.5782788079
                |
                |-----3;1;Tr;0000;40648.5796839699
                |       |
                |       |----4;3;Dev;C;40648.5796839699
                |       |
                |       |----5;3;Dev;1;40648.5764067014
                |              |
                |              |------6;5;Sh;1;40648.5733965394
                |
                |-----7;1;Tr;0091;40648.5737310995



как это можно древовидно отобразить в VT?

Автор: MetalFan 22.4.2011, 11:38
Предлагаю сделать парсер с загрузкой данных в объектную структуру, а затем уже сделать отображение структуры в VT - как два байта переслать.
Хотя конечно можно и сразу в VT грузить.
Примерный алгоритм:
1. Читаем запись, разбираем поля, смотрим ParentID, 
2. ищем нод в дереве с ID=ParentID
3. Если нашли, то добавляем ему ребенка с данными из п.1
4. Если не нашли, то или пропускаем, или добавляем в корень.
Но лучше сделать загрузку объектной модели, которую затем отражать в VT используя все его прелести с OnInitNode/OnInitChildNodes

Автор: Sheleh 23.4.2011, 12:04
MetalFan, я в этом новичок, в какую именно объектную структуру (TObjects?) можно это загрузить, и какие это даст плюсы, кроме простоты отображения в VT.

Плюс моей структуры, как мне кажется, это простота доступа к каждому объекту по его ID в независимости в корне объект, или глубокой вложенности, безо всяких рекурсивных поисков. Да и вложенность в моем случае нужна больше для наглядности при отображении, в программе же это должно выглядеть как список объектов. 
Цитата

2. ищем нод в дереве с ID=ParentID
3. Если нашли, то добавляем ему ребенка с данными из п.1
В общем по такому алгоритму я и хотел работать, но вопрос не в алгоритме, а в его реализации. Просто не могу понять, если данные напрямую в VT не содержатся, а связаны с массивом записей только поинтерами, то как по ним выйти на нод, которому нужно добавить ребенка? Или другими словами:
2. ищем нод в дереве с ID=ParentID - а в дереве ли мы ищем? Как искать в дереве VT, если в нем только поинтеры, там же нет ID?


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

Автор: cat512 24.4.2011, 03:33
Эта задача формулируется просто - "Загрузить древовидную структуру данных в Тривью".
Реализуется она с помощью алгоритма динамически расширяемого дерева. Строится рекур. функия типа
Код

//Функция поиска нода по ключу
function TfmConfTree.FindNode(const Node: TTreeNode;
  Key: Integer): TTreeNode;
var
  I: Integer;
  Id: Integer;
begin
  Result := nil;
  if Assigned(Node) then
  begin
    Id := Integer(Node.data);
    if Id = Key then
      Result := Node
    else
      for I := 0 to Node.Count - 1 do
      begin
        Result :=
          FindNode(Node[I], Key);
        if Assigned(Result) then Break;
      end;
  end;

end;
//Функция построения дерева
procedure BuildTree;
var
  I: Integer;
  Root, Node: TTreeNode;
  Tree: TVirtualTreeView;
begin
  Root := Tree.Items.Add(nil, IntToStr(Objects[0].ID));
  for I := 1 to ObjectsCount -1 do
  begin
    Node := FindNode(Root, Objects[I].ParentId);
    if not Assigned(Node) then 
      Tree.AddObject(
        nil, 
        IntToStr(Objects[0].Id), 
        Integer(Objects[0].Id)
      );
    Node := 
      Tree.Items.AddChildObject(
        Node, 
        IntToStr(Objects[0].Id),
        Integer(Objects[0].Id)
      );
  end;
end

//КОД НЕ ПРОВЕРЯЛ, ПИСАЛ ПО ПАМЯТИ

Ну а после, уже можно оптимизировать. Надеюсь, как адаптировать код к VirtualTreeView разберёшься сам

Автор: Sheleh 27.4.2011, 03:44
Всем спасибо, в итоге получилось следующее (код ниже). Для удобства реализации я добавил в свою запись два вара - ссылка на свой нод в VT, и таг, по которому определяется, была ли запись уже отображена в дереве.

Код

type
  Pobjects = ^Tobjects;

  Tobjects = record
    ID, ParentID: integer;
    Name, Num, Time: string;
    items: array[1..16] of string;
    VTTag: Boolean;
    VTPointer : PVirtualNode;
  end;

var
  Objects: array of Tobjects;

procedure TMainForm.FormCreate(Sender: TObject);
begin
  CountObjects:=0;
  VT.NodeDataSize := SizeOf(Tobjects);
end;

procedure TMainForm.Button2Click(Sender: TObject);//процедура добавление итемов в VT
var i: integer;
  Pobject: Pobjects;
function AddVTNode(Index: integer): boolean;
begin
  if Objects[Objects[Index].ParentID].VTTag = False then AddVTNode(Objects[Index].ParentID); //Если родитель объекта не определен - рекурсивно вызываем добавление родителя
  if objects[i].VTTag = false then//после нахождения родителя добавляем ему нод
   begin
     objects[Index].VTPointer:=VT.AddChild(Objects[Objects[Index].ParentID].VTPointer);
     Pobject:= VT.GetNodeData(objects[Index].VTPointer);
     Pobject^:=objects[Index];
     objects[Index].VTTag:=True;
   end
end;

begin
  //добавляем первый нод
  objects[1].VTPointer:=VT.AddChild(nil);
  Pobject:=VT.GetNodeData(objects[1].VTPointer);
  Pobject^:=objects[1];
  Objects[1].VTTag:=True;
 //остальные автоматом
  for i:=2 to CountObjects do AddVTNode(Objects[I].ID);
end;


Из недостатков - код зацикливается, если у объекта нет родителя.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)