Bonjours a tous je suis à la recherche d'idées ou de subjections pour faire un tampon d'annulation ( undo ) sur un Tstringgrid !!
Merci d'avance
Bonjours a tous je suis à la recherche d'idées ou de subjections pour faire un tampon d'annulation ( undo ) sur un Tstringgrid !!
Merci d'avance
Tu cherches à annuler quoi, exactement ? Des saisies utilisateur "uniquement", ou tu peux avoir des opérations automatiques dessus ?
J'avais utilisé un composant TStringGrid à un moment, et chaque modification de cellule devait être "vue" : j'avais utilisé les évènements suivants :
- OnGetEditMask,
- OnGetEditText,
- OnSelectCell.
Après, le principe est assez bête : lors de la sélection d'une cellule, mémoriser son contenu ainsi que ses coordonnées. Sur sortie du composant/cellule, comparer la valeur mémorisée avec la valeur actuelle.
Si la cellule a changé, alors stocker sur une pile (LIFO, donc) les coordonnées de la cellule et son ancienne valeur.
L'annulation se fait en dépilant le dernier élément du buffer d'annulation : on récupère les coordonnées de cellule, on remplace "bourrin" par la valeur sauvegardée.
RAS/TVB ?
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
Oui j'avais aussi penser a faire comme cela !
mais j'aurai aimer avoir si il y avait pas plus ' propre ' .
Pour mes "retours en arriere" je dois gerer la saisie , mais aussi les copier,coller, couper , sup... des drag drop.... en fait toutes modifications intervenant sur le tabeau.
Je vais tenter la methode donc tu me parle en esperant quelle ne soit pas trop lourde , ou lente quand le decale mes elementd de ma pile qui peuvent parfois etre important !
merci
A ma connaissance, non. C'est le principe de base d'un undo "pas trop gourmand", car sinon, la méthode de buffle consistant à sérialiser (dans un TStream servant de buffer d'annulation) l'intégralité du composant à chaque action de modification peut s'appliquer... Mais je ne te dis pas la taille mémoire consommée !!!Envoyé par petitcoucou31
Un "Coller" est vu comme si c'était une modification manuelle : donc, tu les verras. Pareil pour les suppressions et les drag & drop (du moins, les D&D de texte, pour les composants eux-même, c'est un peu différent).Envoyé par petitcoucou31
Pour les "Copier", j'avoue ne pas comprendre : pourquoi monitorer ça, étant donné que ça ne modifie pas le composant ??
Si jamais ça marche "mal", il faut et il suffit de stocker, en plus des 3 éléments que je t'ai indiqués plus haut, le "type" de modification effectuée en fonction du gestionnaire d'évènement qui a "vu" la modif (j'sais pas si je suis super clair, là...:-P)
Gniii ???? Non, c'est une pile, il n'y a pas de décalages à faire, voyons !!Envoyé par petitcoucou31
Pour la lenteur, il suffit d'implémenter la pile proprement, en n'empilant que le pointeur vers une structure d'annulation qui sera créée par le gestionnaire d'évènements "détecteur". C'est très rapide à faire, mais pense à bien allouer la mémoire (utilise GetMem, ou un tas privé Windows si tu sais faire).
Pour la consommation mémoire (je pense que c'est ça que tu veux dire par "lourdeur"), une solution courante est de refuser d'empiler deux modifications de même nature, consécutives et sur la même cellule : c'est un principe courant, qui ne choquera personne (vérifie sur Excel ou Word, tu comprendras ce que je veux dire).
Ta structure d'annulation "maximale" devrait contenir :
- Un TPoint pour les coordonnées,
- Un type de modification (énumération par exemple),
- Un pointeur non-typé vers les données d'origine si ce ne sont pas toujours des String.
Ca, c'est pour un TStringGrid. Si tu dois monitorer plus de composants, c'est légèrement plus complexe, mais largement faisable quand même et sans se casser la tête : tu empiles en même temps un Sender, et tu n'as plus qu'à implémenter des méthodes d'annulation pour chaque type de composant présent sur tes fiches. Ca demande par contre à utiliser intensivement les enregistrement à partie variable (les Record avec des "case" dedans).
De rien.Envoyé par petitcoucou31
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
OUI !! tu as entierement raison pour le "copier" çà sert a rien et comme tu dis je veux juste stocker ce qui modifie le composant !
Par contre pour la pile : je ne te suis pas trop !!
M oi j'allais creer une "pseudo pile " avec un array dymanique , un Stringlist ou un reccord ( je ne sais pas encore ) et limiter le nombre de undo a 100 pour ne pas etre gourmand en ressource.
J"allais donc aussi gerer cette pile , lorsque j'aurai ete au max des "undo" possible j'allais faire un decalage ( supprimer le plus ancien pour laisser de la place au dernier ..)
Mais vu la taille des infos a sauvegarder peut etre que la limitation est inutile , donc la gestion aussi ..surtout que je ne compte pas faire de "undo" pendant la saisie de la cellule , mais sur les modification globale des cellules .
bon je vais tenter cela merci
Bravo, tu viens de comprendre 99% de la "complexité" d'un undo... Comme souvent, on se pose de "faux problèmes", tu ne trouves pas ? ;-)Envoyé par petitcoucou31
C'est la gestion d'une pile "vraie" qui te pose problème ?Envoyé par petitcoucou31
En fait, tu as deux manières "efficaces" de coder une pile : par tableau, ou par liste chaînée. Le tableau peut être dynamique (pas de stack overflow possible), ou fixe (risque de stack overflow au bout d'un moment). Je considère que tu ne veux pas de limite, donc tableau dynamique.Envoyé par petitcoucou31
Ben justement, ce n'est pas nécessaire avec une pile : je te rappelle que c'est une gestion LIFO (Last In, First Out). En clair :Envoyé par petitcoucou31
- En tableau dynamique, tu insères en fin de tableau, et tu retires en fin de tableau aussi. Le "compactage" si tu limites la taille ne décale que des pointeurs (=> faire un MoveMemory est le plus rapide).
- En liste chaînée, tu insères en tête et tu retires en tête aussi => pour "compacter", il te suffit de ... couper les derniers éléments ! ;-)
Effectivement, la limitation n'est pas réellement nécessaire : la quantité de RAM des PC actuels n'est plus un facteur aussi limitant qu'avant.Envoyé par petitcoucou31
Je peux aussi te proposer une variante, quasi illimitée en capacité :
- Implémenter une liste chaînée d'undo en RAM, limitée à (par exemple) une dizaine d'undo, pas plus.
- Lorsque tu "coupes" ta chaîne, tu vas en fait les écrire dans un fichier temporaire, sur le disque dur au lieu de simplement libérer la RAM. Le fichier, lui, est organisé comme un tableau dynamique (=> pas besoin de compactage en plus, on considère sa taille "illimitée").
Avec ce principe, tu peux gérer un million de niveaux d'annulation si ça peut te faire plaisir : ça va te prendre une centaine de ko de RAM au maximum, et peut-être une vingtaine de mégas sur le dur (remplis au fur et à mesure, ça n'impacte même pas les perfs).
Cool, non ? :-D
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
OUi je suis dessus en ce moment ! donc cool je te le dirai plus tard![]()
par contre juste un detail , vu que je gere en meme tant que le undo le "redo" ! on ne peux pas effacer de suite les derniers elements restaurés.
Donc pas tout a fait une vrai pile 'lifo' ou l'element actif serai toujours le dernier du tableau . Car je fois conserver les elements pour justement le "redo" .
Je doit donc gerer un indice tableau sur le undo qui vient d'etre restauré
( en remontant dans le tableau ) et effacer les element superieur au undo courant quand les action sur le undo sont terminer ( j'espere etre clair !!!lol
Par contre sur un tableau limité a 10 undo par exemple , le decale reste a faire ! quand je suis en fin de tableau , je stocke toujours mon dernier element saisie en fin de tableau , je dois donc decaler tout les elements vers le bas pour faire la place a la fin du tableau .
Merci pour tes reponses qui me disent que je suis sur la bonne voix .
Ca roule.Envoyé par petitcoucou31
Ca, c'est ta faute : relis ton premier post, tu parlais seulement d'un "undo"... Ca mérite le fouet, ça... ;-)Envoyé par petitcoucou31
En effet, ce n'est pas une "véritable" pile, car tu vas devoir gérer un pointeur de sommet de pile en plus de sa taille globale... Comme une pile de processeur, en fait !Envoyé par petitcoucou31
C'est bien ça : la taille de la pile, ne serait-ce que pour savoir s'il y a (ou pas !) des undos/redos à faire, et le pointeur courant. Cependant, je te rappelle qu'en cas de modifications, les "redos" en cours doivent être détruits !Envoyé par petitcoucou31
Bon, si tu tiens absolument à faire un tableau comme ça, la solution miracle passe par un buffer semi-circulaire (ça chie à l'oreille, hein ?Envoyé par petitcoucou31
)
J'explique : on utilise les propriétés des nombres binaires pour accélérer le traitement, en prenant une taille de tableau qui soit un multiple de deux. Dans ton cas, si tu en veux au moins 10, ça sera 15.
On utilise une numérotation à base zéro : tu vas donc de 0 à 15.
Tu mémorises 3 éléments : premier, dernier, et courant. Pour ajouter un élément au tableau, tu l'ajoutes en fin, tu incrémentes et tu appliques un AND binaire dessus (valeur : indice maximal).
Ca donne :Ca va te faire une opération de type modulo, mais extrêmement rapide.
Code : Sélectionner tout - Visualiser dans une fenêtre à part Indice:=(Indice+1) And 15;
Ensuite, il te suffit de vérifier la cohérence de tes indices, et donc penser à incrémenter l'indice du premier (= "décalage") si le dernier vient "buter" sur le premier, justement.
En gros : au début, c'est un tableau "normal", mais une fois plein, ça devient une file circulaire.
Si tu sais lire le C, j'ai en stock un code de gestion "Page précédente"/"Page suivante" d'un navigateur hypertexte, qui marche sur le principe des buffers semi-circulaires.
J'ai été suffisamment clair, ou tu es en train de prendre des tranquillisants ?![]()
De nada.Envoyé par petitcoucou31
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
ça va j'en suis pas encore au transquillisant lol on est cool dans mon pays! merci a toi je pense m'en sortir pour la "pile " !
Oui oui de ma faute pour le redo !!![]()
Pour l'instant j'en suis a determiner toutes les actions effectuer sur mon stringGrid ou je dois "sauver" les cellules dans ma liste de undo .
- cellule qui change entre son activation et la sortie de la cellule( je gere pas le undo en mode edition de la cellule.
- les supression , insertion ( simple et multiselection )...
merci a toi !
Pas vrai : j'te signale qu'on est de la même ville, et j'suis pas un mec cool...Envoyé par petitcoucou31
Effectivement, recenser les actions "modifiantes" est un prérequis !!Envoyé par petitcoucou31
Bon courage pour la suite, n'hésites pas à reposter.
Mac LAK.
___________________________________________________
Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.
Sources et composants Delphi sur mon site, L'antre du Lak.
Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.
Rejoignez-nous sur : ► Serveur de fichiers [NAS] ► Le Tableau de bord projets ► Le groupe de travail ICMO
Bonjour tout le monde ..
Je voudrais savoir si il existe une methode pour savoir , Quand on rentre dans une cellule et quand on en la quitte !
l'equivalent des evements " OnEnter" et "OnExit" mais pas sur le composant , mais sur la cellule !
merci d'avance .
_________________
• Sujets fusionnés par Sub0
Pour l'entrée tu as OnSelectCell, pour la sortie il faudrait mixer avec le OnSelectCell d'une autre cellule et le OnExit de la grille.
C'est deja cela que je fais ! je voulais savoir si il existait une autre solution !
le but de la manip etant de sauver le contenu de la cellule quand j'y rentre et quand j'en sort . pour voir si il y a eux des moditifications et gerer un undo...
Je vais essayer de voir si je peux pas gerer cela sans le "ondrawcel" , qui me permetrait aussi de gerer les copier. coller , ou les insersion dans mon tableau sans passer par le "OnSelectCell" .
On idée est elle absurde ?
oui , en surcharge winproc de stringgrid pour détecté la création de zone d'edition
puis en surcharge le winproc de cette zone de texte
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97 unit Les_Aveces; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Grids, ExtCtrls, Spin, ComCtrls,Modul2, Buttons, Menus; type TFichAvences = class(TForm) Grid1: TStringGrid; procedure FormCreate(Sender: TObject); procedure Grid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); private { Déclarations privées } Winda:TWndMethod; Procedure Monmessage(var msg:TMessage); Procedure MyProcedure(var msg:TMessage); public { Déclarations publiques } end; var FichAvences: TFichAvences; implementation {$R *.DFM} procedure TFichAvences.FormCreate(Sender: TObject); begin Winda:=grid1.WindowProc ; grid1.WindowProc :=monmessage; end; procedure TFichAvences.Grid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var x,y:integer; s:string; h:TSize; begin s:=grid1.cells[acol,arow]; h:=Grid1.Canvas.TextExtent(s); if arow=0 then begin x:=1;y:=1; end else begin x:=titr[acol].Align div 3;//pour colone y:=titr[acol].Align mod 3;//pour ligne end; case x of 0:x:=rect.Left+1; 1:x:=(rect.Right+rect.Left-h.cx) div 2; 2:x:=rect.Right-h.cx -1; end; case y of 0:y:=rect.Top +1; 1:y:=(rect.Bottom+rect.Top-h.cy) div 2; 2:y:=rect.Bottom-h.cy-1; end; Grid1.Canvas.TextRect(rect,x,y,s); end; procedure TFichAvences.Grid1SelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); begin if acol=5 then grid1.Options :=grid1.Options +[goEditing] else grid1.Options :=grid1.Options -[goEditing]; end; procedure TFichAvences.Monmessage ; begin if (msg.Msg =WM_PARENTNOTIFY) and (msg.WParamLo =1) then begin grid1.WindowProc:=winda; grid1.WindowProc(msg); winda:=grid1.Controls[0].WindowProc; grid1.Controls[0].WindowProc:=myprocedure; end else Winda(msg); end; procedure TFichAvences.MyProcedure ; begin if (msg.Msg =8) then begin mot:=TEdit(grid1.Controls[0]).text; postmessage(FichAvences.Handle,wm_lecture,0,0); end ; winda(msg); end; end.
Pour détecter lorsqu'on change de cellule (avec la position de la souris), je pense que j'utiliserai l'évènement OnMouseMove et la fonction MouseToCell :Utilise les propriétes Col et Row du StringGrid pour connaître la cellule actuellement sélectionnée...
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 Var CurCol, CurRow: Integer; Procedure TForm1.StringGrid1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); Begin StringGrid1.MouseToCell(X, Y, X, Y); If (CurCol <> X) Or (CurRow <> Y) Then Begin // <-- Détection ici --> // Beep; CurCol := X; CurRow := Y; End; End;
"edam" il me manque ton unité modul2 pour essayer ton code .
" Sub0 " ta solution marche que pour la souris .
En fait je suis en train de bosser sur un Redo/Undo d'un tableau , donc je veux sauvegarder le contenu d'une cellule avant qu'elle ne soit modifié
et seulement si elle est modifié.
Mon tableau gere la saisie , les copier/coller , drag drop ....
En ce moment j'ai ecris une procedure pour chaque fonction
par exemple pour le drag ou les coller je sais ou je vais ecrire , donc je sauve le contenue de la cellule avant d'ecrire a l'interieur.
Pour la saisie manuelle je vais ecrire une autre procedure en passant le "OnselecCell" et essayer de determiner si un cellule a changer de valeur ..
et ainsi de suite pour toute les action possible sur le tableau...
je voudrais ecrire une procedure(s) qui me permette de savoir si une cellule change de valeur que je soit en edition ou pas . cela me permettrai de couvrir toute mes actions en une seule fois .
mais le "OnselecCell" ne marche pas par exemple quand on ecris comme si dessous
donc je cherche le moyen de determiner le changement de valeur d'une cellule , voila le pourkoi de ma question du debut
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 Tgris.cells[10,10]:='bonjours';
mon code n'est pas a copier totalement c'est , pour aider et a toi de manipuléa vous maintenant de tout modifié selon vos besoin
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49 unit Les_Aveces; type TFichAvences = class(TForm) Grid1: TStringGrid; procedure FormCreate(Sender: TObject); private { Déclarations privées } Winda:TWndMethod; // Procedure Monmessage(var msg:TMessage); Procedure MyProcedure(var msg:TMessage); public { Déclarations publiques } end; var FichAvences: TFichAvences; implementation {$R *.DFM} procedure TFichAvences.FormCreate(Sender: TObject); begin Winda:=grid1.WindowProc ; // enregistrement de winproc de stringrid pour que les messages continue grid1.WindowProc :=monmessage; end; procedure TFichAvences.Monmessage ; begin if (msg.Msg =WM_PARENTNOTIFY) // il y as un composant qui est crée et son parent c'est stringgrid and (msg.WParamLo =1) then le premié composnt enfant de stringgrid est toujour la zone d'edition begin grid1.WindowProc:=winda; // remétre winproc de stringgrid a sa plasse grid1.WindowProc(msg); winda:=grid1.Controls[0].WindowProc; //et enregistrement winproc du nouveau control crée grid1.Controls[0].WindowProc:=myprocedure; end else Winda(msg); end; procedure TFichAvences.MyProcedure ; begin if (msg.Msg =8) then //msg.msg=8 lostfocus du zone de d'édition begin mot:=TEdit(grid1.Controls[0]).text; postmessage(FichAvences.Handle,wm_lecture,0,0); //le poste message pour envoyé vers un message personalisé car aprés des essait on peut pas applé directement une procedure le message enoyé vers la zone d'éditon doit etre terminé avant tout autre trétement ... pourqoi je sais pas vraiment end ; winda(msg); end; end.
à un époque j'avait besoin de recalculé les donnée de tout une colone d'une grid aprés que l'utlisateur a changé une sellule dans cette colone
http://www.developpez.net/forums/viewtopic.php?t=336444
Pourquoi ne pas avoir continuer de poster dans ton 1er sujet ?
surtout que le tag résolu n' a pas été ajouté...![]()
Pour repondre a "SUB0" ,
En effet au final les deux problemes sont liées au 1er post "undo/redo".
mais les questions sont deux questions bien differentes
- Dans un premier post , ont debattaient d'un Undo/redo et de sa gestion.
- Dans un second post ma question etait plus precide et etait sur la gestion d'une cellule de TstringGrid .
Pour la clarté des recherches ensuite sur le forum , je pensais bien faire , car a mon avis faire une recherche sur le forum sur un probleme de cellule de Tgrids dans une rubrique de "Undo/redo" me semble pas tres judicieux , car ma seconde question a un champs d'application bcp plus vaste.
Quand au resolu de mon premier post , je suis toujours interresé par des sujections de d'autre personne qui en l'aurait pas lu .
Donc si mon choix de deux post n'est pas correct j'en suis dsl , mais c'etait de bonne fois( mon smeley est quand meme plus sympa ! non ?)
Partager