Remplissage récursif d'un treeview.

Résolu
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007 - 6 oct. 2006 à 12:20
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007 - 6 oct. 2006 à 19:32
Bonjour,c'est encore moi et mon treeview ;)
Alors j'ai une table qui se présente de la sorte:(exemple)


+-----------------------+
|champ1|champ2|champ3|
+-----------------------+
|valeur1 |valeur1 |valeur1|
|valeur1 |valeur1 |valeur2|
|valeur1 |valeur1 |valeur3|
|valeur1 |valeur2 |valeur1|
|valeur1 |valeur2 |valeur2|
|valeur1 |valeur2 |valeur3|
|valeur2 |valeur1 |valeur1|
|valeur2 |valeur1 |valeur2|
|valeur2 |valeur1 |valeur3|
|valeur2 |valeur2 |valeur1|
|valeur2 |valeur2 |valeur2|
|valeur2 |valeur2 |valeur3|
...
J'aimerai donc a partir de cette table génerer un treeview de la sorte:


root
|__valeur1
|    |__valeur1
|    |     |___valeur1
|    |     |___valeur2
|    |     |___valeur3
|    |
|    |__valeur2
|         |___valeur1
|         |___valeur2
|         |___valeur3
|
|__valeur2
|    |__valeur1
|    |     |___valeur1
|    |     |___valeur2
|    |     |___valeur3
|    |
|    |__valeur2
|         |___valeur1
|         |___valeur2
|         |___valeur3


Bon j'espère que c'est assez clair: J'ai fait une fonction récursive, dont je vous donne le code:

procedure TForm1.loaddb();
Const
pref_geo='GEO$';
var i : integer;
Paysli, Oli :TTreeNode;
fields:tstringlist;
fieldsG:tstringlist;
pkf:string;
begin
treefields.Items.Clear;
treecust.Items.Clear;
fieldsyears:='';
fields:=tstringlist.Create;
fieldsG:=tstringlist.Create;



if length(cmbtables.Text) <1 then exit;



 pkf:=getpk(cmbtables.Text); //clef primaire de la table
paysli:=treefields.Items.Add(nil,'Pays');
Oli:=treefields.Items.Add(nil,'Paramètres');



  UIBQuery.SQL.Clear;
  UIBQuery.SQL.Add('SELECT * FROM "' + cmbtables.Text + '";');
  UIBQuery.Open;





  for i := 0 to UIBQuery.Fields.FieldCount - 1 do
  begin
        if(lowercase(pkf) <> lowercase(UIBQuery.Fields.SqlName[i])) then
   begin
   if(not isnumeric(UIBQuery.Fields.SqlName[i])) then
         if(leftstr(UPPERCase(UIBQuery.Fields.SqlName[i]),length(pref_geo))=pref_geo) then  //je veux séparer les champs commençant par pref_geo a part...
            fieldsG.add(UIBQuery.Fields.SqlName[i])
                else
                        fields.add(UIBQuery.Fields.SqlName[i])
   else begin
          fieldsyears:=fieldsyears+ ',SUM("' + UIBQuery.Fields.SqlName[i] +'") as "' + UIBQuery.Fields.SqlName[i] +'"';
             fieldsyears2:=fieldsyears2+ ',"' + UIBQuery.Fields.SqlName[i] +'"';
   end;
        end;
  end;



UIBQUERY.close();
treefields.items.BeginUpdate;
recfields(fieldsG,paysli);
recfields(fields,Oli);
treefields.items.endUpdate;



end;




Et la fonction récursive:

procedure tform1.recfields(fields : tstringlist;  item : TTreeNode);
var
titem : TTreeNode;
tf:tstringlist;
i:integer;
begin
if not (fields.Count>0) then exit;





UIBQuery.SQL.Clear;
UIBQuery.SQL.Add('SELECT DISTINCT "' + fields[0] + '" FROM "' + cmbtables.Text + '";');
UIBQuery.Open;
tf:=TStringList.Create;
fields.Delete(0);
while not UIBQuery.EOF do
begin
    tf.Add(UIBQuery.Fields.AsString[0]);
    UIBQuery.Next;
end;
UIBQuery.Close();
for i := 0 to tf.Count - 1 do
begin
    titem:=treefields.Items.AddChild(item,tf.Strings[i]);
    recfields(fields,titem);
end;
tf.free;
end;





Seulement, ça ne marche pas, j'ai que le premier champ qui est rempli :s
Je vais chercher de mon coté, mais si quelqu'un pouvait m'aider, je galère un peu avec les treeviews...
Merci d'avance!

10 réponses

retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007
6 oct. 2006 à 14:10
En fait cela venait du passage de stringlist en paramètre, il était vide, j'aurai peut être du faire avec des pointeurs, quoi qu'il en soit j'ai utilisé un tableau. Voici le code corrigé pour ceux que ça intéresse:

procedure TForm1.loaddb();
Const
pref_geo='GEO$';
var i : integer;
Paysli, Oli :TTreeNode;
fields, fieldsG: array of string;
pkf:string;
begin
treefields.Items.Clear;
treecust.Items.Clear;
fieldsyears:='';
setlength(fields,0);
setlength(fieldsG,0);



if length(cmbtables.Text) <1 then exit;



 pkf:=getpk(cmbtables.Text);
paysli:=treefields.Items.Add(nil,'Zone');
Oli:=treefields.Items.Add(nil,'Segments');



  UIBQuery.SQL.Clear;
  UIBQuery.SQL.Add('SELECT * FROM "' + cmbtables.Text + '";');
  UIBQuery.Open;





  for i := 0 to UIBQuery.Fields.FieldCount - 1 do
  begin
        if(lowercase(pkf) <> lowercase(UIBQuery.Fields.SqlName[i])) then
   begin
   if(not isnumeric(UIBQuery.Fields.SqlName[i])) then
         if(leftstr(UPPERCase(UIBQuery.Fields.SqlName[i]),length(pref_geo))=pref_geo) then
                begin
                        setlength(fieldsG, length(fieldsG)+1);
            fieldsG[length(fieldsG)-1]:=UIBQuery.Fields.SqlName[i]
                end
                else
                begin
                        setlength(fields, length(fields)+1);
            fields[length(fields)-1]:=UIBQuery.Fields.SqlName[i]
                end
   else begin
          fieldsyears:=fieldsyears+ ',SUM("' + UIBQuery.Fields.SqlName[i] +'") as "' + UIBQuery.Fields.SqlName[i] +'"';
             fieldsyears2:=fieldsyears2+ ',"' + UIBQuery.Fields.SqlName[i] +'"';
   end;
        end;
  end;



UIBQUERY.close();
treefields.items.BeginUpdate;
recfields(fieldsG,paysli);
recfields(fields,Oli);
treefields.items.endUpdate;



end;

procedure tform1.recfields(fields :array of string;  item : TTreeNode);
var
titem : TTreeNode;
tf:tstringlist;
tfields : array of string;
i:integer;
begin



if not (length(fields)>0) then exit;



UIBQuery.SQL.Clear;
UIBQuery.SQL.Add('SELECT DISTINCT "' + fields[0] + '" FROM "' + cmbtables.Text + '";');
UIBQuery.Open;
tf:=TStringList.Create;



setlength(tfields,length(fields)-1);
for i := 1 to length(fields)-1 do
       tfields[i-1]:=fields[i];  //on enlève le premier
 



while not UIBQuery.EOF do
begin
    tf.Add(UIBQuery.Fields.AsString[0]);
    UIBQuery.Next;
end;
UIBQuery.Close();
for i := 0 to tf.Count - 1 do
begin
    titem:=treefields.Items.AddChild(item,tf.Strings[i]);
    recfields(tfields,titem);
end;
tf.free;
setlength(tfields,0);
end;
3
Utilisateur anonyme
6 oct. 2006 à 14:07
Salut,

Deux remarques :

*treefields.Items.Add(nil,'Pays'); : Je comprends que tu es entrain de faire du bidouillage mais l'utilisation de "nil" dans cette déclaration est à proscrire : ce n'est pas la bonne méthode.

*Le treeview permet uniquement de faire de l'affichage mais pas de traitements.

Vu l'arborescence dans ton treeview :
Valeur1
|- Valeur1
|--ect ect

Il est évident que ton soft est mal pensé au départ. Je ne sais pas trop ce que tu cherches à faire mais à mon avis, tu n'as pas adopté la bonne stratégie. Il y a surement moyen de faire plus simple, plus logique, plus propre et plus professionnel. Je l'illustre en revenant à ta question précédente : tu utilises un treeview1 pour charger des données puis tu fais un traitement du treeview1 pour l'afficher enfin de compte dans un autre treeview : Ca ne rime à rien ^^.

De plus en tant qu'utilisateur je te dirais : "un treeview ca va, deux bonjour les dégats". C'est gonflant pour un utilisateur de passer son temps à naviguer dans une arborescence, d'autant plus que dans ton cas les 2 treeviews sont identiques à la notion de trie pres, chose qui pour un utilisateur est souvent percu comme quelque chose d'élémentaire.

Pour conclure : si tu luttes autant avec le treeview je te poserai deux questions :

-Pourquoi l'utiliser alors qu'il existe surement un composant plus simple et plus approprié ??

-Pourquoi tu cherches à tester tes fonctions sur ton soft plutot que de creer une form test ?? C'est deja assez galere comme ca quand on utilise un composant qu'on maitrise peu mais si en plus tu t'amuses à combiner cela avec des BDD (chose que tu sembles aussi peu maitriser) ca complique le travail non ??

-Ensuite il existe l'aide de Delphi, Google, et DelphiFr ou il y a pleins d'exemples et d'explications accessibles et bien faits.

@+ et bon coding
0
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007
6 oct. 2006 à 14:17
Je penses que tu saisi pas bien l'idée du soft:
Je charge une base avec d'énormes données: les données sont présentée comme la table ci-dessus.
L'utilisateur doit pouvoir segmenter cette base de façon simple, le treeview est donc le plus ergonomique.
Par exemple, si j'ai une base voiture comme cela:
marque | modèle | nombre portes | chevaux ....

L'utilsateur doit pouvoir regrouper par exemple par marque et nombre de chevaux, de même que par nombre de portes et de chevaux, sans sélectionner toutes les données.

le deuxième treeview sert quand a lui a ajouter ce les colonnes ou les critères au colonne (toutes les marques, ou marque = peugeot + renault), et donc de recréer un table selon le même schéma.

J'ai donc choisi l'arborescence du treeview qui me semblait le plus approprié, maintenant si tu as une autre idée je suis preneur :).
0
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007
6 oct. 2006 à 14:33
PS: Comment puis-je ajouter un objet au root du treeview dans définir son parent a nil ?
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Utilisateur anonyme
6 oct. 2006 à 16:07
Ben non : tu utilises un treeview alors que tu devrais faire une recherche récursive directement dans ta base et afficher le résultat dans un DBGrid. De plus BDD ne veut pas dire qu'une seule page de donnée. Pour te convaincre de ces deux points, tape dans google "Access+tutorial", et tu comprendras. Après c'est à toi de savoir gérer convenablement ta base de donnée.

Pour ton histoire de Root : la touche F1 est ton ami et DelphiFr aussi. Un source de Ni69 et un lien cité par Delphiprog devrait t'en apprendre plus. Juste pour te convaincre, récupère le source VideoCreator : j'ajoute des TreeNodes dans un TreeView vide sans utiliser de Nil. N'oublie pas les propriétés (je cite de tete) Sibling et autre qui sont fondamentaux. Sans parler de l'image que tu peux associer à chaque TreeNode et qui aide bien des fois et dont son utilisation ne fait elle pas bricolage ;).

@+
0
Utilisateur anonyme
6 oct. 2006 à 16:17
Enfin en résumé : étudie, recherche et exploite les possibilités monstrueuses (je parle en quantité) offertes par les BDD. Ton projet là me fait penser à un mec qui répare le pot d'échappement de sa 2CV avec du chewing gum, plutot que de faire une bonne vieille soudure pour boucher le trou

@+
0
Cirec Messages postés 3833 Date d'inscription vendredi 23 juillet 2004 Statut Modérateur Dernière intervention 18 septembre 2022 50
6 oct. 2006 à 16:25
Change de suite de pot c'est plus simple

 
@+
Cirec

0
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007
6 oct. 2006 à 16:31
La solution serait peut être d'utiliser un start with dans SQL, mais il faut que je regarde si sous firebird cela existe :s.
J'ai regardé ton exemple, lorsque tu ajoute un élément a la racine d'un treeview,

Procedure AddFileInTreeView(AListFile : TStrings);
Var
  CNode:TTreeNode;
  NmFile : LongInt;
Begin
With Main.ImageFiles_TreeView do
  Begin
  {On définir le TreeNode en cas de TreeView vide, sinon si pas vide}
  IF selected=nil then  CNode:=selected Else
    Begin
 {Si la sélection n'est pas un fichier on sort}
    If Selected.ImageIndex<>0 then Exit 
 {Sinon le TreeNode est le prochain TreeNode "frère"}
    Else CNode:=selected.getNextSibling;
    End;
 {On fait une boucle avec tout les fichiers sélectionnés (rappel on est en multisélection)}
    For NmFile:=0 to(AListFile.Count-1) do
      Begin
   {On définit l'icone}
      Images:=Main.ImageList1;
   {On insert le nom du fichier dans le TreeNode}
      Items.insert(CNode, AListFile[NmFile]);
      End;
    End;
End;

Cnode peut donc avoir la valeur nil, ça n'est pas fondamentalement différent si ?
0
Utilisateur anonyme
6 oct. 2006 à 19:06
Pour répondre à tes deux interrogations :

-Au niveau de ta base SQL, tu peux parfaitement faire une nouvelle page de données à partir de ta première ou alors faire directement un traitement sur celle que tu as. Ton bidouillage avec le Treeview a plusieurs inconveniants :

°Déjà au niveau de l'utilisation plusieurs treeviews : berkkk

°On ne s'attend pas à utiliser un treeview dans un logiciel de base de données. Classiquement le treeview est plutot utilisé pour tout ce qui est dossiers et fichiers.

°Si ta base contient un nombre important de données, ca va ramer méchant (du reste il me semble que tu avais utilisé un process.messages) comparé à un traitement direct sur ta BDD.

°Ensuite ca limite beaucoup les possibilités : imagine un soft ou tu peux faire 50 classements différens. Ca fait 50 treeviews ? Aie aie aie.

Pourquoi ne pas utiliser des radiobuttons et un simple bouton "lancer"? Suivant le radiobutton checké, tu déclenches une procédure donnée qui présente tes données comme tu le souhaites.

Attention on ne parle pas de la meme chose : moi je parle quand tu additionnes un TreeNode. Dans ce cas c'est différent. Du reste même mon code n'est pas optimisé : un assigned aurait été mieux. De plus si je me rappelle bien de ton code tu faisais une boucle en utilisant un Add(nil,'.....') : autrement dit tu n'utilisais pas le nil uniquement pour le premier TreeNode de ton treeview.

Pour conclure, je te conseil d'abandonner des treeviews : tu te prends la tete pour un truc dont le résultat risque d'etre vraiment médiocre ;).
0
retaks666 Messages postés 286 Date d'inscription jeudi 2 janvier 2003 Statut Membre Dernière intervention 16 juillet 2007
6 oct. 2006 à 19:32
Bah j'ai testé avec une base de 1000 lignes, ça passe nickel, après je vais pas travailler avec des millions de lignes... Enfin je te remercie pour tout tes conseils, mais étant donnés que j'ai besoin d'une organisation en hiérarchie, le treeview fait très bien ce que je veux ;). Mais mon soft est quelque peu particulier...
0
Rejoignez-nous