1

Je me suis fait pwnd en voulant faire une classe "Image" qui alloue et libère automatiquement la bitmap associée de façon complètement transparente pour l'utilisateur.
Mais j'ai remarqué que le destructeur était appelé de multiples fois dans certains cas, comme ici:
void fonction()
{
    Image img("test.png");
    img = Image("autre.png");
}

Le destructeur de l'image "test.png" est appelé une fois après que j'assigne une nouvelle image "autre.png" mais aussi une fois une fois que je quitte 'fonction'. En gros le destructeur de image sera appelé ici 3 fois, une fois pour l'image "autre.png", deux fois pour "test.png". Du coup j'ai immanquablement un plantage en faisant ce code.
Alors je me demandais, est-ce qu'il y a des trucs à faire attention avec les string C++, du coup, et autres? (notamment l'opérateur +) Ou alors il y a moyen de contourner ce problème?
Merci d'avance smile
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

2

C'est normal que 3 appels de destructeurs soient effectués, puisqu'il y a 3 créations d'objet :

- Le premier objet : img ("test.png")
- Le second, temporaire : Image ("autre.png")
- Le troisième, créé par copie : img = ...

En revanche, le problème est peut-être que ta classe contient des pointeurs, et que tu n'as pas redéfini d'opérateur de copie ou de constructeur par copie. Du coup les pointeurs sont simplement copiés (comportement par défaut), et tu as plusieurs appels à free sur la même zone mémoire lors de la destruction de tous ces objets clonés alors qu'il faudrait soit avoir un mécanisme de pointeurs "intelligents" (qui compte le nombre de références) soit dupliquer les zones mémoire pointées.

Les classes std::string et autres n'ont jamais ce problème puisque c'est tout simplement un bug (assez vicieux certes, mais courant en C++). Pour éviter d'avoir des surprises, le plus sûr est de toujours déclarer un constructeur par copie et un opérateur "=" vides quand tu crées une classe qui contient des pointeurs, comme ça si un jour ils sont utilisés par ton code (explicitement ou implicitement), tu auras une erreur puisqu'ils ne seront pas définis.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

3

Huhu cool merci bcp smile
Donc c'est un bug... trop con sad je vois un peu plus l'intérêt des "GC" now...
Sinon je me demandais aussi un truc, quand je fais un constructeur par copie, en admettant que je ne fais rien de particulier en dehors de gérer le fait que c'est une référence (par exemple), est-ce que je suis obligé de retaper tous mes membres à la main? Genre:
Texture::Texture(const Texture& t)
{
    this->width = t.width;
    this->height = t.height;
    ...
    this->isCopy = true;
}

Au risque d'en oublier si j'étend la classe (sad). Là actu ce que je fais c'est une horreur du style:
Texture::Texture(const Texture& t)
{
    memcpy(this, &t, sizeof(*this));
    this->isCopy = true;
}

Et dans celles qui en héritent, encore pire:
Image::Image(const Image& img) : Texture(img)
{
    memcpy((u8*)this + sizeof(Texture), &img, sizeof(*this) - sizeof(Texture));
    this->isCopy = true;
}

Je fais l'assomption complètement hasardeuse (mais qui semble fonctionner) qu'une classe étendue d'une autre a une base commune + le reste des propriétés à la suite.
C'est le seul moyen? sorry
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

4

Brunni (./3) :
Au risque d'en oublier si j'étend la classe (sad). Là actu ce que je fais c'est une horreur du style:
Texture::Texture(const Texture& t)
{
    memcpy(this, &t, sizeof(*this));
    this->isCopy = true;
}

Attention, c'est très risqué ça. J'imagine que dans ton destructeur tu fais qqchose du genre "if (!isCopy) free (...)" ? Si oui, assure toi que les copies seront toujours détruites avant les originaux (à ta place j'éviterais absolument de faire ça, c'est vraiment un coup à se faire piéger quand tu n'auras plus cette contrainte en tête).

Sinon comme tu es en train de déclarer des constructeurs, tu peux utiliser cette syntaxe un peu moins lourde, même si ça reste moche quand tu as vraiment plein d'attributs (mais tu devrais peut-être faire des sous-structures dans ce cas) :

Texture::Texture(const Texture& t) :
    width (t.width),
    height (t.height),
    ...
{ 
}

Pour ta supposition à propos de l'héritage, on en a parlé il n'y a pas longtemps dans un autre topic, et il me semble que même si ça a l'air d'être toujours le cas ça reste risqué de faire cette supposition.
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

5

La bonne solution, c'est d'avoir un compteur de références au lieu de isCopy, et si tu veux que ce soit threadsafe, il faudra utiliser des incrémentations et décrémentations atomiques sur ce compteur de références (ou alors un mutex, mais beurk!).
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

6

Ok merci. Par contre j'ai de la peine à voir comment je peux implémenter un compteur de références.
En imginant que je mette sur ma texture:
int refCount;
Texture *refObject;
Un objet unique aura refCount = 0 et refObject = NULL. Quand je fais:
Texture tex1("image.bmp");
Texture tex2 = tex1;
tex2 aura refObject = tex1 et j'augmenterai le compteur refCount de tex1 (le refCount de tex2 n'est donc pas utilisé). Ensuite si je détruis tex2 je décrémente tex2->refObject->refCount (donc le refCount de tex1).
Problème: si je veux détruire tex1, ça va causer souci dans tous les cas puisque l'objet tex1 référencé par tex2 n'existe plus...
Est-ce que je suis obligé de faire une classe "MemBlock" qui remplace mon malloc, et en interne manipuler mon bloc avec des:
MemBlock->getCopy() ou un copy constructor qui incrémente le compteur de références et renvoie une référence vers l'objet
MemBlock->free() qui décrémente le compteur et détruit l'objet s'il atteint 0
Mais en tous cas le problème c'est que je ne peux pas utiliser "delete" sur ce bloc. C'est pour ça que je suis obligé de le wrapper parce que j'aimerais que l'utilisateur puisse effacer sa texture sans se poser de questions...
Désolé si je suis pas trop clair sad
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

7

Il y a deux possibilités :

- Soit tu dupliques la zone mémoire pointée quand tu copies un objet de type "Texture", ça coute cher en mémoire mais c'est la solution la plus simple.
- Soit tu utilises un système de pointeurs partagés entre toutes les instances comme ce que tu as essayé de faire, mais à ce moment là il faut te débrouiller pour ne plus avoir d'objet "maitre" sinon quand tu le supprimes ça invalide tous les autres.

Une solution parmi d'autres : (modulo les erreurs éventuelles, je n'ai pas de quoi compiler pour tester)

[edit] tiens j'en profite pour utiliser les super features de mirari que personne n'essaie jamais : http://www.mirari.fr/Ca5b grin

class Texture
{
    public:
        Texture (const char* path)
        {
             this->refCount = malloc (sizeof (*this->refCount)); // compteur partagé du nombre de références sur cette textures
             *this->refCount = 1;

             this->imageData = new char [...];
             // charger l'image "path" dans this->image_data
        }

        Texture (const Texture& texture)
        {
            this->refCount = texture.refCount; // on récupère le compteur partagé pour compter une nouvelle référence
            ++*this->refCount;

            this->imageData = texture.imageData;
        }

        ~Texture ()
        {
            if (--*this->refCount == 0) // si le compteur arrive à 0, alors plus aucun objet ne référence cette texture,
            {
                delete [] this->imageData; // on peut donc l'effacer pour de bon
                free (this->refCount); // sans oublier de supprimer également le compteur
            }
        }

    private:
        char* imageData;
        int* refCount;
}
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

8

Sauf que ton compteur de références n'est pas du tout threadsafe là, il faut utiliser des incrémentations et décrémentations atomiques, genre __sync_fetch_and_add (g++), q_atomic_increment (Qt), InterlockedIncrement (Win32) etc.
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

9

J'ai pas vu où il avait exprimé la contrainte d'être threadsafe ?
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

10

Zephyr (./2) :
C'est normal que 3 appels de destructeurs soient effectués, puisqu'il y a 3 créations d'objet :

- Le premier objet : img ("test.png")
- Le second, temporaire : Image ("autre.png")- Le troisième, créé par copie : img = ...
Hum, il me semblait que la syntaxe "foo = Foo();" était strictement équivalente à "Foo foo();" et qu'il n'y avait pas de copie confus

[edit] Ça y est j'ai compris ma stupidité tritop
C'est "Foo foo = Foo();" qui est équivalent à "Foo foo();" (ce qui est différent de ce qui a été proposé dans le post initial que j'avais mal lu)
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »

11

Sasume (./10) :
C'est "Foo foo = Foo();" qui est équivalent à "Foo foo();" (ce qui est différent de ce qui a été proposé dans le post initial que j'avais mal lu)

je crois que c'est pas strictement équivalent, le compilateur a le droit de supprimer l'affectation inutile mais il peut aussi la garder smile

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

12

exact, il y a une option dans gcc pour forcer à l'executer. La norme est assez souple sur ce point.

13

Si j'ai bien compris, c'est un comptage de références non-intrusif ça, non?

PS: Pourquoi utiliser malloc() pour refCount alors qu'on utilise new[] pour les données ?
avatar
Maintenant j'ai la flemme de garder une signature à jour sur ce site. Je n'ai même plus ma chaîne Exec sous la main.

14

histoire d'utiliser les deux, c'était juste un exemple, à lui de choisir ce qu'il préfère happy
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

15

Excellent! J'avais pas pensé à allouer le refCount... c'est beau comme truc love
Merci pour ces réponses d'une rare qualité sur le net top (non je fais pas du lèche, je passe assez de temps à chercher sur le net)
Après je comprends que c'est peut être mieux de dupliquer la zone mémoire vu que le concept de références est là pour ce cas. Par contre on s'expose à pas mal de soucis de performances, c'est impressionnant.
M'enfin ma lib C++ là c'est plutôt un test qu'autre chose, je commence à voir de moins en moins l'intérêt vu que niveau performance mon ancienne lib C blast tellement plus...
[Edit] J'ai utilisé tritop au lieu de top, trop l'habitude grin
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

16

Le c++ n'est pas plus lent que le C, il faut juste savoir ce que tu veux exactement faire, et utiliser le C++ pour faire ce que tu veux faire. D'ailleurs n'a tu pas pensé que ton C++ était plus lent car il ne faisait pas là même chose?

17

Sisi bien sûr que c'est différent. J'ai essayé de respecter un peu la philosophie objet (le but étant d'apprendre) smile
Mais bon je voulais pas faire du C++ pour faire 80% du C et 20% de C++ quand ça arrange (là ça aurait éventuellement pu être efficace je pense, quoique ça se discute parce que toutes les facilités du C++ ont en général un coût niveau vitesse).
Sinon je trouve vraient le :: du namespace trop lourd, dommage, du coup y'a pas mal de chances qu'on va faire un using pour la lisibilité et on arrive de nouveau à cette pollution habituelle sad
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

18

bof ça dépend de ton namespace, mais perso je ne fais absolument jamais de "using std;" par exemple ; au lieu de gagner 5 misérables caractères, je préfère afficher clairement à quel namespace j'accède
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

19

En tous cas chez moi ça alourdit vraiment vraiment (notamment du côté du drawFillRect par exemple):
int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPTSTR    lpCmdLine,
                     int       nCmdShow)
{
	// Variables locales
	int ordre = 1, taille = 240;
	int positionX = 0, positionY = 0;
	char lecture;
	wingraph::Font verdana("Tahoma", 13, true);

	// Initialisation de la librairie
	wingraph::initLib();
	wingraph::initWindow("Fractales de Koch", 640, 480, true);
	turtle::init();

	while (!wingraph::closed)
	{
		const int vitesseDeplacement = 20;

		// Fonctions de la librairie pour la saisie (à faire à chaque frame)
		wingraph::keyboard.readInput();

		// Lit les caractères au clavier tant qu'il y en a
		while (wingraph::keyboard.availableChar())
		{
			lecture = wingraph::keyboard.readChar();
			if (lecture >= '1' && lecture <= '9')
				ordre = lecture - '0';
			else if (lecture == '0')
				ordre = 10;
		}

		// Teste les touches de direction
		if (wingraph::keyboard.held(VK_UP))
			positionY -= vitesseDeplacement;
		if (wingraph::keyboard.held(VK_DOWN))
			positionY += vitesseDeplacement;
		if (wingraph::keyboard.held(VK_LEFT))
			positionX -= vitesseDeplacement;
		if (wingraph::keyboard.held(VK_RIGHT))
			positionX += vitesseDeplacement;

		// Et celles de zoom
		if (wingraph::keyboard.held('A'))
			// Si la taille devient trop petite, on ne peut plus la récupérer...
			taille = max(taille * 9 / 10, 10);
		if (wingraph::keyboard.held('S'))
			taille = taille * 11 / 10;

		// Et le milieu
		if (wingraph::keyboard.held('M'))
			positionX = positionY = 0;

		if (!wingraph::skipFrame)
		{
			wingraph::beginDrawing();
			// Un petit dégradé de fond
			wingraph::drawFillRect(
				wingraph::Vertex(0, 0, wingraph::Color(128, 128, 255)),
				wingraph::Vertex(wingraph::screenWidth, 0, wingraph::Color(128, 128, 255)),
				wingraph::Vertex(0, wingraph::screenHeight, wingraph::Color(255, 255, 255)),
				wingraph::Vertex(wingraph::screenWidth, wingraph::screenHeight, wingraph::Color(255, 255, 255))
			);
			// Dessin de la fractale
			curColor = wingraph::Color(0, 0, 0);
			turtle::setPosition(taille / 2 - positionX, taille / 3 - positionY);
			koch(taille, ordre);
			// Affiche l'ordre par-dessus
			std:: ostringstream titre;
			titre << "Ordre: " << ordre;
			wingraph::setColor(wingraph::Color(0, 0, 0));
			verdana.drawText(0, 0, titre.str().c_str());
			// Ainsi que des informations supplémentaires
			verdana.drawText(0, 13, "0-9: définir l'ordre");
			verdana.drawText(0, 26, "a/s: modifier la taille");
			verdana.drawText(0, 39, "m: revenir au milieu");
			wingraph::endDrawing();
		}

		// On fait tourner à 10 fps pour ne pas trop surcharger le GPU =)
		wingraph::syncFrame(10, 8);
	}
}

En plus ça doit être moi mais ça me fait bizarre de faire image.draw() quand tout ce que tu fais avant est du genre wingraph::qqch(). En fait je dessine une image mais je ne sais pas que je le fais avec wingraph au coup d'oeil, ça me paraît illogique.
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

20

C'est vrai que le drawFillRect est un peu long mais ça reste lisible je trouve. D'autant plus que tu peux te permettre localement des "using wingraph" quand tu es dans un fichier qui n'utilise que ce namespace puisqu'il n'y a pas de confusion ou de conflits possibles. Par contre c'est très "C style" tout ça, peut-être parceque ton équivalent C++ n'était pas aussi rapide comme tu disais, mais du coup je trouve que ça ne vaut plus tellement le coup de programmer en C++ :/
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

21

Ben en C++ pur je verrais pas trop comment faire une lib graphique comme ça (et que ça soit si possible plus simple à utiliser).
J'ai pas mal cherché, mais je cogne toujours quelque part, donc j'ai opté pour un truc séquenciel mais avec des TDA grâce aux objets, par exemple les Color qui supportent directement les opérateurs d'addition, multiplication, etc.
Mais la modélisation reste un problème: par exemple si je fais image.draw, finalement sur quoi ça va dessiner? Est-ce que c'est plus logique de faire image.draw(wingraphCurrentContext) ou encore drawBuffer.draw(image) ou autre variante? Difficile à dire... mais comme je l'ai dit dans le post plus haut j'ai vraiment l'impression que ce verdana.drawText est perdu au milieu du reste mais j'ai pas d'idée pour améliorer...
Si tu as des suggestions générales je suis volontiers preneur smile
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

22

Ah bah ça, ça s'appelle la conception et c'est difficile de donner une réponse simple à ton problème ; pour moi c'est quelque chose de bien plus complexe que d'écrire du code par exemple happy

Entre image.draw (surface) et surface.draw (image) j'aurais tendance à préférer la 2eme solution en général (pour plein de raisons que je peux prendre le temps d'expliquer mais pas ce soir ^^), mais ça dépend du contexte et la 1ere peut tout à fait se défendre dans certains cas. En revanche le verdana.drawText me semble difficilement justifié ("verdana" si c'est une police est un attribut ou "entité", et non un contrôleur, et devrait donc logiquement être un paramètre plutôt qu'un objet possédant des méthodes d'affichage)... enfin ça demande vraiment tout un roman pour justifier ces différents choix, pour le coup happy
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

23

wingraph::Font verdana("Tahoma", 13, true);

trilove

« The biggest civil liberty of all is not to be killed by a terrorist. » (Geoff Hoon, ministre des transports anglais)

24

(ah tiens j'avais zappé cette ligne... grin)
avatar
All right. Keep doing whatever it is you think you're doing.
------------------------------------------
Besoin d'aide sur le site ? Essayez par ici :)

25

Quelle idée aussi d'utiliser du Win32 directement (WinMain, VK_UP etc.), ce n'est pas du tout portable, ton truc! sick Pourquoi pas utiliser quelque chose de portable et en C++ propre plutôt que de faire ton propre wrapper C++ de l'API Win32?
avatar
Mes news pour calculatrices TI: Ti-Gen
Mes projets PC pour calculatrices TI: TIGCC, CalcForge (CalcForgeLP, Emu-TIGCC)
Mes chans IRC: #tigcc et #inspired sur irc.freequest.net (UTF-8)

Liberté, Égalité, Fraternité

26

Peut-être parce qu'il veut faire un wrapper de l'API Win32 à des fins de test et qu'il n'a pas envie d'apprendre à utiliser Qt s'il s'en branle complètement de Qt ?
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

27

Pollux (./23) :
wingraph::Font verdana("Tahoma", 13, true);

trilove

C'est ça quand on met à jour le code par parties magic
Kevin Kofler (./25) :
Quelle idée aussi d'utiliser du Win32 directement (WinMain, VK_UP etc.), ce n'est pas du tout portable, ton truc! sick Pourquoi pas utiliser quelque chose de portable et en C++ propre plutôt que de faire ton propre wrapper C++ de l'API Win32?

Désolé mais je n'en ai pas envie. Peut être dans un prochain projet: c'est clair que c'est quelque chose d'important. En plus c'est une des raisons pour lesquelles je code en C++. Pas forcément le portage sous nux, mais les consoles de jeu par exemple.
Les VK_UP et cie seront wrappés dans une classe 'wingraph::Key', mais j'y réfléchis encore.
Là par contre j'ai hésité quand j'ai dû choisir de laisser le point d'entrée (WinMain) au programme utilisateur. J'aurais aussi pu laisser le démarrage maître à ma librairie, et elle créerait un thread hôte pour l'utilisateur qui démarrerait dans une fonction qu'il devrait définir, nommée 'main'. Ainsi on pourrait porter ça sur d'autres plate-formes sans problème puisque tous les détails spécifiques sont cachés (à la manière d'une application console, quoi). L'avantage du WinMain c'est que l'utilisateur n'est pas obligé d'utiliser tout le temps ma librairie, mais on perd la portabilité.
Je vais peut être faire un truc intermédiaire: une fonction WinMain implémentée en inline dans un .h de la librairie et qui appelle un main utilisateur, mais j'y réfléchis encore parce que je trouve pas ça propre.
Zephyr (./22) :
Ah bah ça, ça s'appelle la conception et c'est difficile de donner une réponse simple à ton problème ; pour moi c'est quelque chose de bien plus complexe que d'écrire du code par exemple happy

T'inquiète je vois bien tongue
Zephyr (./22) :
Entre image.draw (surface) et surface.draw (image) j'aurais tendance à préférer la 2eme solution en général (pour plein de raisons que je peux prendre le temps d'expliquer mais pas ce soir ^^)

Ok, en fait c'est clair que ce qui a influencé mon choix c'est le fait qu'on ne peut pas changer de drawbuffer comme on veut. D'ailleurs pour l'instant c'est même pas possible du tout, je dois passer à une version d'openGL > 1.1, qui n'est pas disponible par défaut sous Windows et cause donc des soucis vu que tout le monde ne pourra pas l'exécuter sad (je peux me permettre ça pour du jeu, mais pas pour des applis de démo d'algorithmes graphiques par exemple. A vrai dire j'hésitais déjà entre utiliser GL ou faire du soft...)
avatar
Highway Runners, mon jeu de racing à la Outrun qu'il est sorti le 14 décembre 2016 ! N'hésitez pas à me soutenir :)

https://itunes.apple.com/us/app/highway-runners/id964932741

28

Utilise direct3d trigic.
Enfin sans rire, quasiment tout le monde a un OpenGL relativement récent à partir du moment où il met ses drivers à jour. Et pour ce dont ce n'est pas le cas tu peux toujours envisager plusieurs modes de fonctionnement (par exemple crée plusieurs implémentations de ta classe de dessin, et décide de celle à utiliser à l'éxécution wink )
Sinon si tu veux des exemples de librairie graphique en C++ je ne peux que te conseiller de regarder GDI+. Y'a peut-être quelques endroit où c'est un peu pourri, mais c'est quand même vachement bien conçu, et ça devrait te donner des idées de comment ça peut fonctionner tongue
avatar
Le scénario de notre univers a été rédigée par un bataillon de singes savants. Tout s'explique enfin.
T'as un problème ? Tu veux un bonbon ?
[CrystalMPQ] C# MPQ Library/Tools - [CrystalBoy] C# GB Emulator - [Monoxide] C# OSX library - M68k Opcodes

29

[nosmile]faut pas déconner, faire ça en QT, ça m'a pris 2 heures sans rien connaitre de Qt au préalable (et j'aurais pu faire plus vite)

class MainForm : public QMainWindow
{
public:
	MainForm(QWidget *parent = 0);

private:
	virtual void resizeEvent(QResizeEvent *e);
	virtual void paintEvent(QPaintEvent *e);
	virtual void mouseMoveEvent(QMouseEvent * e);
	virtual void mousePressEvent(QMouseEvent * e);
	virtual void mouseReleaseEvent(QMouseEvent *e);
	virtual void wheelEvent(QWheelEvent *e);
	virtual void keyPressEvent(QKeyEvent *e);
..........
};

int main(int argc, char* argv[])
{
    QApplication app(argc, argv);
    MainForm mf(cf);
    mf.resize(640,480);
    mf.show();
    return app.exec();
}

void MainForm::paintEvent(QPaintEvent *e)
{
	QPainter painter(this);
	painter.setFont(QFont("Arial", 8));
	painter.setPen(Qt::blue);
	painter.drawText(rect(), Qt::AlignLeft | Qt::AlignTop, "plop");
	e->accept(); //et encore ça marche sans
}

void MainForm::keyPressEvent(QKeyEvent *e)
{
	if(e->key()==Qt::Key_Space) {
	/* blabla*/
	e->accept();
}
love

30

Tu programmes quoi ?
avatar
« Quand le dernier arbre sera abattu, la dernière rivière empoisonnée, le dernier poisson capturé, alors vous découvrirez que l'argent ne se mange pas. »