[MFC] Dessiner dans une dialog

Dessiner dans une dialog [MFC] - C++ - Programmation

Marsh Posté le 22-05-2002 à 14:32:02    

Salut,
Je me demande comment dessiner "proprement" dans une dialog crée avec MFC et l'assistant.
Personnelement je procède de la manière suivante mais à mon avis c pas top
 
HWND h_dialog
OnInitDialog : h_dialog = GetSafeHwnd();
 
Dans ma fonction chargée de dessiner :
void dessin()
{
HDC hdc;
hdc = GetDC(h_dialog);
Puis dessiner avec hdc
Release(hdc);
}
 
comment faire mieux ? Le dessin s'efface quand on réduit la fenêtre par exemple, donc c'est pas la bonne méthode !
merci,
   ANT

Reply

Marsh Posté le 22-05-2002 à 14:32:02   

Reply

Marsh Posté le 22-05-2002 à 14:36:17    

:hello:  mon boolay  
 
Ta méthode n'est pas top, car elle ne rafraichit pas ta fenêtre, ce qui explique qu'elle s'efface quand tu la bouges.
 
Il faut faire toutes tes opérations de dessin dans la fonction OnPaint() de te dialog, cette fonction réagit au message WM_PAINT qui est lancé chaque fois que la fenêtre doit être redessinée.
 
A+  :hello:


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 22-05-2002 à 14:40:35    

Merci Harkonnen  :hello:  
Comment récupérer le DC ? GetDC avec hwnd de la dialog ?

Reply

Marsh Posté le 22-05-2002 à 14:48:51    

Meme pas, si ta dialog hérite de CWnd (comme toutes les fenêtres), tu as une méthode membre GetDC qui te retourne un *CDC représentant le DC de ta dialog, sans aucun argument.
 
A+  :)

 

[jfdsdjhfuetppo]--Message édité par Harkonnen le 22-05-2002 à 14:49:24--[/jfdsdjhfuetppo]


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 22-05-2002 à 14:52:30    

Par contre, n'oublie pas de faire un ReleaseDC(CDC *DC) de ton DC une fois que tu auras terminé de dessiner.


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 22-05-2002 à 15:13:49    

C'est noté, merci
a+
 
PS: Faudra que tu m'expliques ta signature !

Reply

Marsh Posté le 22-05-2002 à 15:16:34    

C'est la colère en toi qui t'interpelle ?  :D  
 
En fait, c'est parce que j'ai lu récemment un topic de toi ou tu t'énerves contre un type qui a du mal à comprendre  :lol:  :lol:  
 
Je sais plus quel topic c'est par contre  :hello:


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 22-05-2002 à 15:21:29    

C'est dommage j'aurais bien voulu voir où je me suis permis de m'énerver contre un newbie alors que beaucoup de gens comme toi font preuve de beaucoup de patience avec mes questions parfois complètement  :sarcastic:  
Je vais faire une recherche...

Reply

Marsh Posté le 22-05-2002 à 19:06:10    

Au fait, comment je fais pour "animer" (un pixel qui bouge par ex) ? Je ne vois pas ou placer le code d'un sprite par exemple. Toujours dans le OnPaint() ? Je ne serai pas contre un petit bout de code... Merci.

Reply

Marsh Posté le 22-05-2002 à 21:28:05    

Normalement, un bon programme Windows doit d'un coté créer les éléments permettant de dessiner et de l'autre effectuer le dessin. Ces 2 parties doivent être indépendantes dans la mesure du possible, à savoir que la partie qui dessine n'a pas à se soucier de faire autre chose.
Je connais très peu les MFC, mais je crois que c'est le principe de l'architecture document-vue.
En clair, d'un cote ton OnPaint doit dessiner un pixel aux coordonnées x et y, et de l'autre ton coeur de programme doit se charger de changer alternativement x et y pour faire clignoter + forcer un redessinage.
Ce dernier code peut être contenu dans un timer par exemple ...


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 22-05-2002 à 21:28:05   

Reply

Marsh Posté le 23-05-2002 à 18:59:40    

D'accord donc dès que x ou y changent, je dois appeler OnPaint ?

Reply

Marsh Posté le 23-05-2002 à 19:58:01    

En fait ma question est la suivante : faut-il appeller OnPaint et donc redessiner la fenetre entierement pour juste bouger un pixel qui normalement se limite à bouger dans un petit rectangle de la dialog ?

Reply

Marsh Posté le 23-05-2002 à 23:47:34    

Citation :

En fait ma question est la suivante : faut-il appeller OnPaint et donc redessiner la fenetre entierement pour juste bouger un pixel qui normalement se limite à bouger dans un petit rectangle de la dialog ?


 
On appelle pas OnPaint ... enfin, je connais pas MFC, mais je pense que appeller OnPaint, c'est _mal_.
On provoque son appel via un InvalidateRect qui va provoquer l'appel de OnPaint.
 
Maintenant juste bouger un pixel ... je sais pas trop ce qui est bien. Je pense qu'InvalidateRect() approprié (un tout petit rectangle qui englobe ton pixel à effacer et redessiner) est plus convenable que se bidouiller une variable qui mémorise la couleur qui va effacer le pixel et réafficher celui-ci ...
Ca te semble plutot boeuf comme méthode ?
Ben, un exemple. Ton programme intéresse quelqu'un. Il décide de le modifier. Il modifie OnPaint pour afficher tout autre chose ...
Et la, surprise. Au milieu de son truc il a un pixel qui clignote ... il cherche dans Onpaint et il trouve pas d'ou ca vient ... "Oh putain il va falloir que je me coltine tout pour trouver où ce ***** a fouttu ces 2 lignes qui me font chier"
bon, encore la, c'est pas trop grave, il va trouver vite. Mais une ligne par ci, une autre par là, ca devient très vite très lourd.


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 24-05-2002 à 07:16:30    

La nuit porte conseil ...
 
Ce qui est sûr, c'est qu'il faut respecter la règle "regrouper le plus possible le code du dessin".
J'ai ainsi vu un code qui ne gère pas OnPaint ... C'est un code d'animation (faire bouger un sprite) et comme cette animation est très rapide, l'auteur n'a pas géré WM_PAINT.
Son code est bien dans une seule fonction, mais celle-ci n'est pas appelée par OnPaint mais appelée de manière automatisée via timeSetEvent.
Sur la MSDN, il est dit à propos du dessin en dehors de WM_PAINT d'être prudent vis à vis des conflits avec ce dernier. Ils parlent en fait de code de dessin qui fonctionne en paralèlle du code principal.
Comme exemples ils citent un éditeur de texte qui surligne du texte. Ce code travail en concurrence avec celui de WM_PAINT car on a 2 portions de modification de la fenêtre dans le programme et ça c'est à éviter.
Ils disent que c'est surtout valable dans des cas où on ne modifie pas vraiment le dessin (on inverse couleurs ... c'est aussi le cas lors de la selection d'un object par exemple) et que celui-ci peut être très facilement restauré.
Pour animer un pixel, j'avais à l'esprit un clignotelent lent. Mais si celui-ci est rapide faire des InvalidateRect est débile :D
Si celui-ci est rapide, il n'a pas à figurer dans WM_PAINT, sauf si on peut suspendre le clignotement.
Automatiser via un timer me semble une bonne idée.
 
Tu peux donc créer une fonction qui sera appelée dans OnPaint et via un timer ... et pas appeler OnPaint via le timer.
OnPaint est appelée quand il y a besoin de repeindre, et c'est tout.
Une fonction On**** ne doit pas être appelée par toi afin de rester cohérent dans la structure du programme.
Si tu as besoin ailleur du code contenu dans une fonction On****, tu le déplaces dans une nouvelle fonction à part.
Imagines la maintenabilité d'un code où les On**** s'appellent mutuellements ... c'est plus des On**** mais tous des OnNimporteQuand :)
 
Apparement y'a 2 cas principaux :
- les objets à dessiner ne bougent pas
- il y a au moins un objet qui bouge
 
Pour un jeu. Par exemple PacMan : un fond qui reste tout le temps le même et quelques sprites à déplacer.
Je pense que là le mieux est d'avoir 2 fonctions :
- une qui dessine dans un bitmap en mémoire la carte, les bonhomme (sprites), les scores ...
- une qui affiche ce bitmap
 
Les 2 fonctions seront appélées lors du timer, et la seconde seulement dans OnPaint.
On a bien une seule portion de code à modifier pour changer le dessin. Mission accomplie.
Le InvalidateRect est donc bien là en quelque sorte ... puisqu'on efface tout a chaque animation.
 
Maintenant afficher 2 lignes de texte et faire clignoter une troisième.
- une fonction qui dessine le TOUT le texte non clignotant
- une fonction qui affiche le TOUT le texte clignotant
 
La première n'est appelée que dans OnPaint, la seconde dans OnPaint et via le timer.
Cette fois on a 2 fonctions :( ... mais ca me semble meilleur ainsi (lors d'un clignotement on ne réaffiche pas le texte non clignotant).
 
Une meilleur idée ?


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 24-05-2002 à 11:49:33    

Je ne crois pas qu'une de mes idées puisse rivaliser avec les tiennes ! Je te remercie grandement d'avoir donné une réponse si bien détaillée. Je vais essayer de coder un peu tout ça et si j'ai un problème je referais probablement appel à tes connaissances ! En tout cas merci et a+
 ANT

Reply

Marsh Posté le 24-05-2002 à 12:06:07    

Voila mon code mais vu que je n'ai rien mis dans OnPaint, dès que je réduis la fenetre les points sont automatiquements effacés, juste les nouveaux apparaissent.
Pourrais tu me dire ce que je dois mettre dans OnPaint car je ne vois pas trop.
Code :
 
Dans InitDialog : m_nTimer = SetTimer(1,500,0);
 
void CVirtualWorldDlg::OnTimer(UINT nIDEvent)  
{
 CDC *dc = GetDC();
 
 x++;
 y++;
 dc->SetPixelV(x,y,0x00000000);
 
 ReleaseDC(dc);
 
 CDialog::OnTimer(nIDEvent);
}
Merci

Reply

Marsh Posté le 24-05-2002 à 12:34:46    

:hello:  
 
Attention ! OnPaint doit contenir le code qui te permettra de rafraichir ta fenêtre !
 
En l'occurence, si tu te contentes de mettre tes opérations de dessin dans une fonction externe, elle ne sera dessinée que quand tu l'appelleras ou quand ton timer expirera, à condition que ta fenêtre reste immobile. si tu resize ta fenêtre, elle ne sera pas mise à jour car ce dessin n'est pas contenu dans OnPaint
 
Ce qu'HelloWorld te conseille c'est de calculer dans une fonction externe les coordonnées de ton pixel, puis de l'afficher dans OnPaint


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 24-05-2002 à 13:18:03    

Donc il faut qu'en plus du code ci-dessus je rajoute ça dans OnPaint :
CDC *dc = GetDC();
dc->SetPixelV(x,y,0x00000000);  
 
ReleaseDC(dc);  
 
est-ce que c'est ça ?
 
 :hello:

Reply

Marsh Posté le 24-05-2002 à 14:53:55    

oui. tu calcules x et y (qui doivent être des variables membres de ta classe) dans une fonction membre, puis tu les utilises dans OnPaint()


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 24-05-2002 à 15:00:32    

Peux tu m'expliquer pourquoi il faut que les variables et la fonction de calcul des points soient membres de la classe de ma dialog ?

Reply

Marsh Posté le 24-05-2002 à 15:05:47    

car si tu les utilises dans une fonction membre, puis dans OnPaint, leur valeur doit être conservée. si tu les déclares à l'intérieur de ta fonction, elles n'existeront plus en dehors de la fonction
tu dois donc les déclarer en variables membres afin que leur valeur soit conservé au fur et à mesure des différents appels


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 24-05-2002 à 15:09:11    

Je me contentais jusqu'alors de les déclarer en vairables globales...
Avec les MFC je suis un peu perdu, je sais pas rtop où elle est déclarée la classe avec var & fonctions

Reply

Marsh Posté le 24-05-2002 à 15:20:19    

1ere chose à savoir, valable en programmation générale : éviter tant que tu le peux les variables globales !
 
pourquoi ? car vu qu'elles ne sont pas allouées sur la pile, elles consomment de la mémoire, et leur accès est beaucoup plus lent. il vaut mieux passer des variables en argument à des fonctions si tu souhaites conserver des valeurs, ou alors les déclarer en static (au sein d'une même fonction)
 
et en plus, pour peu que ton projet soit imposant, tu finis vite par perdre le fil de tes variables globales à force, surtout si tu en a un paquet


---------------
J'ai un string dans l'array (Paris Hilton)
Reply

Marsh Posté le 24-05-2002 à 17:35:51    

Le fait de rajouter ce code n'empêche pas l'effacement des anciens points tracés :
 
 CDC *dc = GetDC();
 dc->SetPixelV(x,y,0x00000000);
 ReleaseDC(dc);
 
Comment faut-il faire alors pour ne pas perdre les anciens points tracés ? Faut-il Sauvegarder le DC avant de cacher la fenetre puis le restaurer une fois celle-ci revenu au 1er plan ?
 
Sinon merci pour le conseil d'éviter les variables globales, désormais je tacherais de tout déclarer dans la classe. Mais par exemple, int x,y; je le met en public ou private ?

Reply

Marsh Posté le 24-05-2002 à 17:55:42    

Citation :

1ere chose à savoir, valable en programmation générale : éviter tant que tu le peux les variables globales !  
 
pourquoi ? car vu qu'elles ne sont pas allouées sur la pile, elles consomment de la mémoire, et leur accès est beaucoup plus lent. il vaut mieux passer des variables en argument à des fonctions si tu souhaites conserver des valeurs, ou alors les déclarer en static (au sein d'une même fonction)  
 
et en plus, pour peu que ton projet soit imposant, tu finis vite par perdre le fil de tes variables globales à force, surtout si tu en a un paquet


 
Une variable static n'est pas non plus allouée sur la pile, mais exactement comme une variable globale ... static permet en fait de déplacer une variable globale à l'intérieur d'une fonction.
Je sais pas si une variable globale c'est plus lent, mais ce qui est certain en tous cas c'est que c'est à éviter. Ca rend l programme plus difficile à maintenir.
 
Ce que je te disais c'est jsutement de ne pas faire ce que tu es en train de faire ... ;)
à savoir copier coller le code de dessin !!
 
Créé une fonction DrawPixel dans ce genre:
 

Code :
  1. void DrawPixen(int x, int y, CDC * dc)
  2. {
  3.     dc->SetPixelV(x,y,0x00000000);
  4. }


 
puis tu l'appelle et dans le timer et dans OnPaint ...
Si tu veux modifier ton code de dessin, tu n'as ainsi qu'une seule fonction à maintenir, au lieu de chercher partout où tu as copier coller.
 

Code :
  1. void CVirtualWorldDlg::OnTimer(UINT nIDEvent) 
  2. {
  3.     CDC *dc = GetDC();
  4.     x++;
  5.     y++;
  6.     DrawPixel(x, y, dc);
  7.     ReleaseDC(dc);
  8.     CDialog::OnTimer(nIDEvent);
  9. }
  10. void CVirtualWorldDlg::OnPaint() 
  11. {
  12.     CPaintDC dc(this);
  13.     DrawPixel(x, y, dc);
  14.     EndPaint(&ps);
  15. }


 
Bon c'est surement pas la bonne syntaxe car moi les MFC je connais pas.


---------------
FAQ fclc++ - FAQ C++ - C++ FAQ Lite
Reply

Marsh Posté le 11-06-2002 à 17:04:34    

HelloWorld a écrit a écrit :

Citation :

1ere chose à savoir, valable en programmation générale : éviter tant que tu le peux les variables globales !  
 
pourquoi ? car vu qu'elles ne sont pas allouées sur la pile, elles consomment de la mémoire, et leur accès est beaucoup plus lent. il vaut mieux passer des variables en argument à des fonctions si tu souhaites conserver des valeurs, ou alors les déclarer en static (au sein d'une même fonction)  
 
et en plus, pour peu que ton projet soit imposant, tu finis vite par perdre le fil de tes variables globales à force, surtout si tu en a un paquet


 
Une variable static n'est pas non plus allouée sur la pile, mais exactement comme une variable globale ... static permet en fait de déplacer une variable globale à l'intérieur d'une fonction.
Je sais pas si une variable globale c'est plus lent, mais ce qui est certain en tous cas c'est que c'est à éviter. Ca rend l programme plus difficile à maintenir.
 
Ce que je te disais c'est jsutement de ne pas faire ce que tu es en train de faire ... ;)
à savoir copier coller le code de dessin !!
 
Créé une fonction DrawPixel dans ce genre:
 

Code :
  1. void DrawPixen(int x, int y, CDC * dc)
  2. {
  3.     dc->SetPixelV(x,y,0x00000000);
  4. }


 
puis tu l'appelle et dans le timer et dans OnPaint ...
Si tu veux modifier ton code de dessin, tu n'as ainsi qu'une seule fonction à maintenir, au lieu de chercher partout où tu as copier coller.
 

Code :
  1. void CVirtualWorldDlg::OnTimer(UINT nIDEvent) 
  2. {
  3.     CDC *dc = GetDC();
  4.     x++;
  5.     y++;
  6.     DrawPixel(x, y, dc);
  7.     ReleaseDC(dc);
  8.     CDialog::OnTimer(nIDEvent);
  9. }
  10. void CVirtualWorldDlg::OnPaint() 
  11. {
  12.     CPaintDC dc(this);
  13.     DrawPixel(x, y, dc);
  14.     EndPaint(&ps);
  15. }


 
Bon c'est surement pas la bonne syntaxe car moi les MFC je connais pas.  




a tout hazard je veux faire un truc ressemblant (des graphiques) sous qt (multiplateforme oblige) ya pas un d'entre vous qui s'y connait ?


---------------

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed