Bienvenue, Invité. Merci de vous connecter ou de vous inscrire.
Avez-vous perdu votre e-mail d'activation ?

Auteur Sujet: Gérer plusieurs fenêtres  (Lu 8739 fois)

0 Membres et 1 Invité sur ce sujet

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Gérer plusieurs fenêtres
« le: Mai 06, 2013, 11:48:01 am »
Bonjour,

Je me demandais si certains avait une méthode fiable pour gérer 2 (ou plus) fenêtres SFML. Vu qu'il faut une boucle testant d'ailleurs si la fenêtre est ouverte, je me suis toujours dit que ce n'été pas possible.

Hors à force d'y réfléchir, j'ai pensé à un truc : c'est le cas de l'API Win32, Cocoa pour Mac et X11 pour Linux ; pourtant cela n'empêche pas que certains programment ont plusieurs fenêtres affichées en même temps et qui sont interactive.

J'ai donc fait des tests, je peux afficher deux fenêtres, mais la seconde ne sera interactif qu'une fois la première boucle finie donc la première fenêtre fermée.


J'ai donc fait un nouveau test : chaque fenêtre est dans une fonction et j'utilise un thread par fenêtre. Les deux fenêtres sont alors réactives et fonctionnent parfaitement.

Ma question est alors la suivante : est-ce que sur un système mono-CPU, cela ne risque pas de faire perdre en performance le fait d'avoir plusieurs thread sur le programme juste pour gérer des fenêtres de rendus ?

Laurent, as-tu une meilleur solution que celle-ci ?


Merci d'avance pour la réponse.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #1 le: Mai 06, 2013, 12:01:34 pm »
Citer
Vu qu'il faut une boucle testant d'ailleurs si la fenêtre est ouverte, je me suis toujours dit que ce n'été pas possible.
C'est pas obligé, tu mets ce que tu veux comme condition d'arrêt. Par exemple, tu tournes tant que les deux fenêtres sont ouvertes.

while (window1.isOpen() && window2.isOpen())
{
    sf::Event event;
    while (window1.pollEvent(event))
    {
        ...
    }
    while (window2.pollEvent(event))
    {
        ...
    }

    window1.clear();
    window1.draw(...);
    window1.display();

    window2.clear();
    window2.draw(...);
    window2.display();
}

En ce qui concerne les threads, ça marche bien sur Windows et Linux. En ce qui concerne Mac OS X, il y a une limitation qui oblige à créer toute fenêtre et à gérer ses évènements dans le thread principal.
Laurent Gomila - SFML developer

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #2 le: Mai 06, 2013, 12:13:54 pm »
J'y ai pensé à ta solution, mais imagine un programme où depuis la fenêtre principale, tu en crée une autre avec un bouton, il faut bien une boucle séparée pour les deux.


Pour Mac, les programmes comme Gimp font comment alors ? Puisque chaque fenêtre à sa propre boucle. Si on met une boucle commune, cela veut dire que fermer une fenêtre les fermes toutes. Gimp par exemple te permet de toute les fermer sauf la fenêtre principale qui elle fermera toute les autres, donc à un moment donné il doit y avoir une séparation des boucles non ?

Après je prend Gimp comme exemple, mais c'est en fait GTK qui est concerné et c'est pareil pour Qt d'ailleurs ;)

Ou autre solution, les fenêtres en question sont créer dans le thread principal, mais les boucles sont dans des threads séparés. Tu penses que ça peut marcher ? Fournir un argument à la fonction du thread : l'instance ici de sf::Window.

Je verrais pour tester cet aprem car là je vais aller manger, sinon je testerais ce soir en arrivant à la maison. Parce que, que tout soit dans le même thread j'ai alors du mal à comprendre comment Qt/GTK font, à moins qu'ils crées la fenêtre dans le thread principal mais gère la boucle dans un thread séparé.


Je sais je me pose des questions que sans doute personne s'est posé ici mais bon, je suis fan du dessin animé CodeLyoko et j'ai toujours voulut reproduire le pupitre du SuperCalculateur ; du coup plusieurs fenêtres c'est ce qu'il faudrait. Et puis ça ouvre de nouvelles perspectives d'utilisation que de pouvoir le faire sans trop de pertes en performance.

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #3 le: Mai 06, 2013, 01:25:42 pm »
Bon je viens de faire des essais, je crée les fenêtres dans le main et les boucles sont mises dans des thread séparés. Les fenêtres s'affichent, mais elles sont incapables de répondre par contre.

Le problème est que je ne peux fournir le sf::Window en paramètre de la fonction pour le thread, si je met un &sf::Window comme argument, il m'envoi baladé en disant que ça ne va pas avec un retour void. Et en mettant les fenêtres en tan que variables globales, ça démarre mais les fenêtres plantes.

Donc je ne vois pas comment faire autrement que faire la création au sein du même thread que celui qui gère la boucle de la fenêtre en question.

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #4 le: Mai 06, 2013, 01:26:11 pm »
Citer
J'y ai pensé à ta solution, mais imagine un programme où depuis la fenêtre principale, tu en crée une autre avec un bouton, il faut bien une boucle séparée pour les deux.
Non pas forcément. Tu peux dans tous les cas garder une seule boucle principale qui gère toutes les fenêtres. Là j'ai mis un exemple avec deux "window1" et "window2", mais ça marcherait aussi avec un std::vector<sf::RenderWindow*> auquel tu pourrais ajouter dynamiquement des nouvelles fenêtres. Le tout c'est de toujours retomber dans la boucle principale, il ne faut jamais démarrer une autre boucle similaire toute seule dans son coin, qui bloquerait la boucle principale.

Citer
Pour Mac, les programmes comme Gimp font comment alors ? Puisque chaque fenêtre à sa propre boucle.
Ah bon ? Comment tu sais ça ? Tu as regardé le code source de Gimp ? :P

Citer
Ou autre solution, les fenêtres en question sont créer dans le thread principal, mais les boucles sont dans des threads séparés. Tu penses que ça peut marcher ?
Non. Comme je te l'ai dit (et comme expliqué dans le tutoriel sur les fenêtres), il faut gérer les évènements dans le même thread qui a créé la fenêtre.

Citer
Je verrais pour tester cet aprem car là je vais aller manger, sinon je testerais ce soir en arrivant à la maison. Parce que, que tout soit dans le même thread j'ai alors du mal à comprendre comment Qt/GTK font, à moins qu'ils crées la fenêtre dans le thread principal mais gère la boucle dans un thread séparé.
Tu cherches vraiment compliqué alors qu'il n'y a aucun problème. Essaye de mieux comprendre le truc (et par "truc", j'entends "ce que tu penses être le problème") avant de te lancer dans des tests compliqués et inutiles ;)

Ce serait peut-être plus simple si tu me disais précisément ce qui te bloque avec le fait de tout gérer dans une même boucle, qu'est-ce qui te paraît impossible ? Tu vois bien que c'est très facile avec deux fenêtres, et que ce même code fonctionnerait avec n'importe quel autre nombre.

Je pense que ton bloquage vient de là :
Citer
Si on met une boucle commune, cela veut dire que fermer une fenêtre les fermes toutes.
Pourquoi ? Tant que ton propre code ne dit pas de fermer les autres lorsqu'une est fermée, cela ne se produira pas. Je te l'ai dit, la condition qui stoppe la boucle principale, tu y mets ce que tu veux. Si c'est "boucler tant que la fenêtre principale est ouverte" plutôt que "bloquer tant que toutes les fenêtres sont ouvertes", et bien pas de souci, tu peux le faire aussi... C'est ton programme, tu codes ce que tu veux.
Laurent Gomila - SFML developer

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #5 le: Mai 06, 2013, 06:08:21 pm »
Citer
Ah bon ? Comment tu sais ça ? Tu as regardé le code source de Gimp ?

Gimp utilise GTK, je doute qu'il modifie à la volée son propre code pour inclure dans la même boucle les différentes fenêtres, hors comme pour Qt je doute qu'ils puissent prévoir le nombre de fenêtres à afficher. Qt a résolu d'ailleurs le soucis en créant leur propre système d'évènement avec les Qt::Connect.


Si je gère tout dans la même boucle en prenant ton exemple, si je ferme une des deux fenêtres la boucle ne s'exécute plus puisqu'un des membres de test de la dite boucle ne renvoi plus true mais false. Il faudrait dans ce cas là une boucle while sans fin, mais alors comment l'interrompre pour quitter le programme ? Avec un break ou un return en plein milieu ? Le problème de la boucle infinie c'est que le programme sera toujours à 100 % non ?


Citer
Pourquoi ? Tant que ton propre code ne dit pas de fermer les autres lorsqu'une est fermée, cela ne se produira pas. Je te l'ai dit, la condition qui stoppe la boucle principale, tu y mets ce que tu veux. Si c'est "boucler tant que la fenêtre principale est ouverte" plutôt que "bloquer tant que toutes les fenêtres sont ouvertes", et bien pas de souci, tu peux le faire aussi... C'est ton programme, tu codes ce que tu veux.

Je ne l'avais pas compris comme cela lol, dans ce cas je peux éventuellement mettre une simple variable comme condition au lieu de l'habituel isOpen, variable qui passe à false si les conditions de fermeture du programme sont réunis.

Je vais voir ça de suite.


Oui je crois que je me pose beaucoup trop de question lol.

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #6 le: Mai 06, 2013, 06:40:12 pm »
Bon en effet ça fonctionne. Par contre quelle est l'utilité de WaitEvent par rapport à PollEvent ? WaitEvent attend un événement, donc utile si on a rien à rafraîchir, rien du CPU n'est utilisé. Mais dans ce cas là, cela ne permet pas de gérer plusieurs fenêtres si une seule et unique boucle est utilisée. Dans quel cas se révèle-t-il alors utile ?


Sinon pour revenir sur le sujet, c'est bien comme tu me dis si c'est codé en dur. Mais si je crée un programme permettant avec un simple bouton de créer une fenêtre (par exemple), comment l'inclure dans la boucle alors pour qu'elle puisse réagir à des événements (ou tout simplement quelle puisse afficher quelque chose en particulier) ? Parce que codé en dur c'est simple, mais de façon dynamique y doit bien y avoir un moyen non ?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Messages: 32498
    • Voir le profil
    • SFML's website
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #7 le: Mai 06, 2013, 06:51:56 pm »
Citer
Par contre quelle est l'utilité de WaitEvent par rapport à PollEvent ?
C'est principalement utilisé lorsque tu ne gardes que la gestion d'évènements dans le thread principal, et mets le rendu et la logique dans un autre thread. Du coup grace à WaitEvent, tu es certain que ton thread principal ne fait rien lorsqu'il n'a rien à faire ;)
Et évidemment, ça ne fonctionne que lorsque tu n'as qu'une seule fenêtre.

Citer
Sinon pour revenir sur le sujet, c'est bien comme tu me dis si c'est codé en dur. Mais si je crée un programme permettant avec un simple bouton de créer une fenêtre (par exemple), comment l'inclure dans la boucle alors pour qu'elle puisse réagir à des événements (ou tout simplement quelle puisse afficher quelque chose en particulier) ? Parce que codé en dur c'est simple, mais de façon dynamique y doit bien y avoir un moyen non ?
Tu peux par exemple utiliser un tableau dynamique (std::vector) de fenêtres, comme je l'ai mentionné précédemment.
Laurent Gomila - SFML developer

christophedlr

  • Full Member
  • ***
  • Messages: 153
    • Voir le profil
    • E-mail
Re : Gérer plusieurs fenêtres
« Réponse #8 le: Mai 06, 2013, 07:00:03 pm »
OK merci pour tes réponses, ça me donne des tas d'idées, mais chut c'est secret défense (tan que j'ai pas le temps de mettre en pratique mes idées lol).

GG_A_SsaSsIbs

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Gérer plusieurs fenêtres
« Réponse #9 le: Février 01, 2014, 02:57:35 pm »
Bonjour Laurent, bonjour à tous,

Tout d'abord, toutes mes excuses pour ce déterrage de topic mais les différentes réponses s'y trouvant sont pour moi au cœur de ma question. Si jamais cela ne se fait pas, aucun problème je recréerai un autre topic, suffit de me le signaler.

Laurent, quand tu dis :

Citer
Pourquoi ? Tant que ton propre code ne dit pas de fermer les autres lorsqu'une est fermée, cela ne se produira pas. Je te l'ai dit, la condition qui stoppe la boucle principale, tu y mets ce que tu veux. Si c'est "boucler tant que la fenêtre principale est ouverte" plutôt que "bloquer tant que toutes les fenêtres sont ouvertes", et bien pas de souci, tu peux le faire aussi... C'est ton programme, tu codes ce que tu veux.

et

Citer
Tu peux par exemple utiliser un tableau dynamique (std::vector) de fenêtres, comme je l'ai mentionné précédemment.

Je souhaite moi aussi pouvoir créer plusieurs fenêtre en même temps. Pour faire court, j'ai des class spécifique qui ont besoin de se créer des fenêtres indépendantes de la principale mais qui n'ont pas forcément besoin d'être sur un autre thread.

Dans la phase de développement et pour tester, j'appel le constructeur de ma class qui gère le fenêtrage. ( Cette class contient donc un vector pour ranger dynamiquement mes fenêtres ). Mais je n'arrive pas bien à conceptualisé dans ma tête la suite à comprendre :

Pour la boucle principale, j'ai bien comprit que j'arrête un peu comme et quand je veux par exemple, javais idée dans la condition de ma boucle principale que tant que mon vector n'est pas vide, c'est qu'il y a encore des fenêtres active. ( En gros chaque fois que je ferme une fenêtre je dois aussi l'éliminer dans le vector ). La ou je pêche en revanche, c'est pour réaliser les boucles d'écoutes des events à chaque création dynamique d'une fenêtre. ( ne pas faire une boucle en dur pour chaque fenêtre créée sinon, l’intérêt de la création dynamique est absurde ).

En résumé je ne suis pas bien sur de partir dans la bonne direction ( même avec mon histoire d'éliminer chaque fenêtre dans le vector des leur fermeture ) et je souhaiterais avoir ton avis sur la question voir même si tu as la possibilité de me donner un exemple concret en utilisant un tableau de vector selon l'idée que tu exposes dans tes anciens posts ?

Merci par avance. :)
« Modifié: Février 01, 2014, 03:03:08 pm par GG_A_SsaSsIbs »

GG_A_SsaSsIbs

  • Newbie
  • *
  • Messages: 15
    • Voir le profil
Re : Gérer plusieurs fenêtres
« Réponse #10 le: Février 03, 2014, 08:32:18 pm »
Bon finalement, j'ai pu bossé sur l'idée et je suis arrivé à quelque chose. :)

Je vais donc proposer un début de solution ici concernant l'ouverture dynamique simple de plusieurs fenêtre SFML.

Une class qui gère le fenêtrage, tout d'abord son prototype pas grand chose à dire :

Attributs :
- Un conteneur vector

Methodes :
- un constructeur par défaut
- la méthode addFrame qui permet d'ajouter dans le conteneur dynamiquement autant de fenêtre que vous le souhaitez une fois la class instanciée.
- la méthode getWindowListSize qui retourne à la boucle principale le nombre de case restant dans le conteneur. ( et donc le nombre de fenêtre encore ouverte ).
- la méthode poolEvent qui s'occupe d'écouter les événements dans chaque fenêtre ouverte en utilisant bien sur les fonctions et méthodes de la SFML pour les gérer. Cette méthode se charge également de supprimer du conteneur la fenêtre dont l'event de fermeture a été appelé en utilisant un itérateur ( principe de l'idiom erase-remove pour gérer correctement l'itérateur ).

#include <string>
#include <vector>
#include <algorithm>
#include "make_unique_ptr_temp.h"
#include <SFML/Graphics.hpp>


class graphic_engine
{
    public:
        graphic_engine();
        void addFrame(unsigned int width, unsigned int height, std::string title);
        int getWindowListSize();
        void poolEvent(const int val, sf::Event& e);
        virtual ~graphic_engine();
    protected:
    private:
        std::vector<std::unique_ptr<sf::RenderWindow>> windowList;

};

et la définition ( je pense que le code parle de lui même mais ne pas hésiter à me demander si besoin de détail ) :

graphic_engine::graphic_engine()
{

}

void graphic_engine::addFrame(unsigned int width, unsigned int height, std::string title)
{
    windowList.push_back(make_unique<sf::RenderWindow>(sf::VideoMode(width, height), title));
}

int graphic_engine::getWindowListSize()
{
    return windowList.size();
}

void graphic_engine::poolEvent(const int val, sf::Event& e)
{
    if(windowList[val]->pollEvent(e))
    {
        if (e.type == sf::Event::Closed)
        {
            windowList[val]->close();
            windowList.erase(std::remove(windowList.begin(), windowList.end(), windowList[val]), windowList.end());
        }
    }
}


graphic_engine::~graphic_engine()
{
    //dtor
}

Et enfin dans le main :

Citer
int main()
{

    graphic_engine* gs = new graphic_engine();
    gs->addFrame(300,200,"fen1");
    gs->addFrame(300,200,"fen2");
    gs->addFrame(300,200,"fen3");

    while (gs->getWindowListSize() != 0)
        {
            for(int i = 0; gs->getWindowListSize() > i; i++)
            {
                sf::Event event;
                gs->poolEvent(i, event);
            }
        }
    return 0;
}

Une fois les fenêtres créées, on démarre la boucle principale qui ne s’arrêtera pas tant qu'il restera des fenêtres dans le conteneur. On ajoute une autre boucle qui aura pour fonction de parcourir toutes les cases du conteneur et d'écouter les events sur chaque fenêtre.

Vous noterez également que les smart pointer ont été sélectionnés, je trouvais très pratique de ne pas avoir à gérer le deleting dans le conteneur avant de supprimer une case. Cependant comme la fonction std::make_unique n'existe pas dans la librairie standard et que je n'utilise pas boost, j'ai récupéré un template proposant l'implémentation de la fonction en attendant la norme c++14 :

http://www.jsfauteux.com/blog/post/2/std::make_unique-dans-le-C++14

basé sur ce rapport :

http://herbsutter.com/gotw/_102/

Je me doute bien qu'on doit pouvoir faire mieux et que pour les pratiquants chevronnés du c++, ce n'était absolument pas difficile à réaliser. Je suis en tout cas ouvert à toute critique constructive pour améliorer le code. :)

En espérant que sa pourra servir à quelqu'un.

Bonne soirée.