Les membres ayant 30 points peuvent parler sur les canaux annonces, projets et hs du chat.

Forum Casio - Vos tutoriels et astuces


Index du Forum » Vos tutoriels et astuces » Interface Graphique Textuel pour Graph 35+ et 90
Farhi Hors ligne Membre Points: 596 Défis: 0 Message

Interface Graphique Textuel pour Graph 35+ et 90

Posté le 24/07/2021 19:56

Bonjour les amis !
Je créé ce nouveau Topic pour proposer une partie précise des sources de mon projet que j'ai soumis pour le Jam Python #1

Comme certains l'ont remarqué python (que ça soit sur Graph 35 ou 90) n'est pas le langage le plus adapté pour créer des jeux sous calculatrice.
Malgré tout, j'ai tenté de créer une "Moteur Graphique Textuel" conçu pour python. Il sera donc utilisable normalement pour Graph 35 ou 90.

Je vais donc détailler son fonctionnement ici.

Voici tout d'abord le code au complet :


import math
dim = [30,8]
def Clear():
    print("""
"""*(dim[1]+1))

class Curseur:
    def __init__(self):
        self.LstBoutons=[]
        self.index = 0
class Bouton:
    def __init__(self,curseur,Txt):
        curseur.LstBoutons.append(self)
        self.curseur = curseur
        self.text = Txt
    def GetString(self):
        if self.curseur.LstBoutons[self.curseur.index]==self:
            return self.text
        else:
            return self.text.replace("["," ").replace("]"," ")
class Image:
    def __init__(self,data):
        self.data=data
def GetWidth(name):
    w=0
    for i in name.split("\n"):
        if len(i)>w:
            w=len(i)
    return w
def GetHeight(name):
    return len(name.split("\n"))
def GetImage(name):
    if name[0]=="\n":
        name=name[1:]
    w,h=GetWidth(name),GetHeight(name)
    Lst = name.split("\n")
    Screen=[]
    for i in range(h):
        temp=[]
        for o in range(w):
            temp.append(" ")
        Screen.append(temp)
    for Y,i in enumerate(Lst):
        for X,o in enumerate(i):
            (Screen[Y])[X]=o
    return Screen

class StringVar:
    def __init__(self,data):
        self.data=data
class Interface:
    def __init__(self):
        self.curseur = Curseur()
        self.Widgets = []
        self._lastCmd=""

    def FindIndexOfWidget(self,adress):
        for i in range(len(self.Widgets)):
            if (self.Widgets[i])[0]==adress:
                return i
    def Clear(self):
        self.Widgets = []
        self._lastCmd=""
        self.curseur.index = 0
        self.curseur.LstBoutons=[]
    def Print(self):
        Screen=[]
        for i in range(dim[1]):
            temp=[]
            for o in range(dim[0]):
                temp.append(" ")
            Screen.append(temp)

        for i in self.Widgets:
            Text = ""
            try:
                if type(i[0])==Image:
                    x,y=i[3],i[2]
                    for indY,y1 in enumerate(i[0].data):
                        for indX,x1 in enumerate(y1):
                            try:
                                (Screen[indY+y])[indX+x]=x1
                            except:pass
                else:
                    if type(i[0])==str:
                        Text=i[0]
                    elif type(i[0])==StringVar:
                        Text=str(i[0].data)
                    elif type(i[0])==Bouton:
                        Text=i[0].GetString()
                    if i[1]=="PlaceCenter":
                        x=int((dim[0]/2)-math.ceil(len(Text)/2))
                        for x1,lettre in enumerate(Text):
                            (Screen[i[2]])[x+x1]=lettre
                    elif i[1]=="Locate":
                        x=i[3]
                        for x1,lettre in enumerate(Text):
                            (Screen[i[2]])[x+x1]=lettre
            except:
                pass
        for i in Screen:
            line = ""
            for o in i:
                line+=o
            print(line)
    def Draw(self):
        self.Print()
        cmd = input()
        self._lastCmd = cmd
        if cmd=="":
            return self.curseur.index,cmd,self.curseur.LstBoutons[self.curseur.index]
        else:
            return None,cmd,None
    def RemoveWidget(self,adress):
        if type(adress)==Bouton:
            self.curseur.LstBoutons.remove(adress)
            self.curseur.index=0
        for i in range(len(self.Widgets)):
            if (self.Widgets[i])[0]==adress:
                del self.Widgets[i]
                return
    def PlaceCenter(self,obj,Ln):
        self.Widgets.append([obj,"PlaceCenter",Ln])
    def Locate(self,obj,Ln,Col):
        self.Widgets.append([obj,"Locate",Ln,Col])
    def UpdateCursor(self):
        if self._lastCmd in ["8","4","6","2"]:
            x,y=0,0
            for i in self.Widgets:
                if type(i[0])!=Bouton:
                    continue
                Text = i[0].GetString()
                if i[0]==self.curseur.LstBoutons[self.curseur.index]:
                    if i[1]=="PlaceCenter":
                        x=int((dim[0]/2)-math.ceil(len(Text)/2))
                        y=i[2]
                    elif i[1]=="Locate":
                        x=i[3]
                        y=i[2]
            direction=[None,100]
            for i in self.Widgets:
                if type(i[0])!=Bouton:
                    continue
                dis = 100
                x1,y1=0,0
                Text = i[0].GetString()
                if i[1]=="PlaceCenter":
                    x1,y1= int((dim[0]/2)-math.ceil(len(Text)/2)),i[2]
                elif i[1]=="Locate" :
                    x1,y1= i[3],i[2]
                dis = math.sqrt(((y1-y)**2)+ (x1-x)**2)
                
                if self._lastCmd=="8":
                    if y1<y and dis<direction[1]:
                        direction=[i[0],dis]
                if self._lastCmd=="2":
                    if y1>y and dis<direction[1]:
                        direction=[i[0],dis]
                if self._lastCmd=="4":
                    if x1<x and dis<direction[1]:
                        direction=[i[0],dis]
                if self._lastCmd=="6":
                    if x1>x and dis<direction[1]:
                        direction=[i[0],dis]
            if direction[0]!=None:
                self.curseur.index = self.curseur.LstBoutons.index(direction[0])
    def WaitBouttonInput(self):
        retour = None
        while retour == None:
            retour , _ , _ = self.Draw()
            if retour == None:
                self.UpdateCursor()
        return retour
def BoiteConfirmValide(txt):

    interface = Interface()
    txt = txt.split(" ")
    interface.Locate("#"*dim[0],0,0)
    y=1
    while len(txt)>0:
        msg=""
        while len(txt)>0 and len(msg)+len(txt[0])<dim[0]-3:
            msg+=" "+txt.pop(0)
        interface.Locate(msg,y,0)
        y+=1
    interface.Locate("#"*dim[0],dim[1]-1,0)
    interface.Locate(Bouton(interface.curseur," [OUI] "),dim[1]-1,int((dim[0]/4)-math.ceil(7/2))+1)
    interface.Locate(Bouton(interface.curseur," [NON] "),dim[1]-1,int((3*dim[0]/4)-math.ceil(7/2)))
    return interface.WaitBouttonInput() == 0

def BoiteConfirmOK(txt):
    interface = Interface()
    txt = txt.split(" ")
    interface.Locate("#"*dim[0],0,0)
    y=1
    while len(txt)>0:
        msg=""
        while len(txt)>0 and len(msg)+len(txt[0])<dim[0]-3:
            msg+=" "+txt.pop(0)
        interface.Locate(msg,y,0)
        y+=1
    interface.Locate("#"*dim[0],dim[1]-1,0)
    interface.PlaceCenter(Bouton(interface.curseur," [OK] "),dim[1]-1)
    interface.Draw()
    return True

def MainMenu(Titre):
    Clear()
    interface = Interface()
    interface.PlaceCenter((len(Titre)+2)*"=",0)
    interface.PlaceCenter(" "+Titre+" ",1)
    interface.PlaceCenter((len(Titre)+2)*"=",2)
    interface.PlaceCenter(Bouton(interface.curseur,"[Commencer une partie]"),4)
    interface.PlaceCenter(Bouton(interface.curseur,"[Entrer un code]      "),5)
    interface.PlaceCenter(Bouton(interface.curseur,"[Quitter]             "),7)
    return interface.WaitBouttonInput()


La variable Dim est très importante, comme vos aurez pus le deviner c'est les dimensions de l'écran. (pour Graph 35 les dimensions sont 30x8)

La fonction Clear permet de "nettoyer" l'écran. Pour être précis, l'écran n'est jamais nettoyé, il défile plutôt, c'est-à-dire que si vous appuyez sur la flèche du haut, vous vos apercevrez qu'il y a les anciens affichages.
La Class Curseur sert à pointer un Bouton (c'est comme une souris d'ordinateur)
La Class Bouton est comme son nom l'indique permet de créer un bouton, on lui donne un curseur et son texte en entrée.
Il est obligatoire de mettre entre crochets le texte que vous souhaitez (exemple : "[Continuer]")

Exemple de bouton :
curseur = Curseur()
Bouton(curseur,"[Commencer une partie]")


Mais jusqu'à présent tout ça ne sert a rien sans la Class "Interface".
Cette Class permet de manipuler facilement les éléments présentés ci-dessus.
C'est à dire qu'elle permet de se balader facilement sur un menu par exemple et se charge de l'affichage ainsi que l'ordre de déplacement du curseur sur les Boutons.
Pour placer un élément on peut utiliser les fonctions PlaceCenter et Locate.
PlaceCenter permet de placer au centre d'une ligne l'élément voulu. (PlaceCenter(élément,ligne)
Exemple :
interface = Interface()
interface.PlaceCenter("Tire du jeu",0)
interface.PlaceCenter(Bouton(interface.curseur,"[Commencer une partie]"),4)


Quant à Locate, cette fonction permet de placer un élément à des coordonnées exactes. (Locate(élément,ligne,colonne)
Exemple :
interface = Interface()
interface.Locate("Coucou",0,0)#place "coucou" sur le coin supérieur gauche
interface.Locate("Coucou",dim[1]-1,0)#place "coucou" sur le coin inférieur gauche


Maintenant qu'on a placé les éléments, on peut les afficher, et il existe 3 manières de le faire.
-Print
-Draw
-WaitBouttonInput

Print affiche juste l'interface. (cette fonction renvoie None)
Quant à Draw, cette fonction effectue un Print puis attend une entrée de l'utilisateur. (cette fonction renvoie l'index du curseur,l'entrée brut et le bouton qui est sélèctionné par le curseur.
Pour finir, la fonction WaitBouttonInput (qui est la plus conseillé) attent que l'utilisateur valide sont choix (renvoie l'index du bouton sélèctionner).
Exemple:
def Confirm():
    interface = Interface()
    interface.PlaceCenter("Voulez-vous quittez ?",0)
    interface.PlaceCenter(Bouton(interface.curseur," [OUI] "),3)#index = 0 car initialisé en premier
    interface.PlaceCenter(Bouton(interface.curseur," [NON] "),4)#index = 1 car initialisé en second etc...

    return interface.WaitBouttonInput() == 0#renvoie True si [OUI] est sélectionné


Comme python est capricieux, il n'existe pas de GetKey, il a fallu créer un système pour contrôler l'interface.
Tout d'abord, oubliez la croix directionnelle de votre calculatrice !
La croix directionnelle est remplacée par les touches 2,4,6,8.
8 : Haut
4 : gauche
6 : droite
2 : bas
Chaque action doit être validée par la touche EXE
(par exemple si vous voulez aller en haut appuyez sur 8 puis EXE)
Autrement dit, pour effectuer une action vous devez appuyer sur au moins deux touches.

Vous pouvez aussi supprimer les éléments d'une interface en utilisant RemoveWidget.
Exemple :
interface = Interface()
interface.PlaceCenter("coucou",0)
interface.RemoveWidget("coucou")

Exemple 2 :
interface = Interface()
btn=Bouton(interface.curseur,"[Commencer une partie]")
interface.PlaceCenter(btn,4)
interface.RemoveWidget(btn)


Maintenant je vais vous présenter d'autre type qui existe : Image et StringVar
Oui c'est possible d'afficher des images faites en caractère ascii
Exemple : un coffre
  __________
/\____;;___\
| /         /
./_________/
|\         \
| |---------|
\ |    ))   |
\|_________|


Exemple 2 : Un soldat
|
_|_ o __
I /|\)_)
   / \


Le double slash est indispensable donc les images ci-dessus devrai être :
player1="""
|
_|_ o __
I /|\\)_)
   / \\
"""
chest="""
  __________
/\\____;;___\\
| /         /
./_________/
|\\         \\
| |---------|
\\ |    ))   |
\\|_________|
"""


Ensuite, il faut transformer cette chaine de caractère en Image donc pour faire ça voici le code :
player1_Image=Image(GetImage(player1))

Puis on peut l'afficher sur l'interface avec Locate (PlaceCenter ne fonctionne pas pour les Images)
Exemple :
player1_Image=Image(GetImage(player1))
interface = Interface()
interface.Locate(player1_Image,0,0)


Pour finir, la Class StringVar permet de changer le texte de l'interface.
Exemple sans StringVar:
interface = Interface()
interface.PlaceCenter("test",0)
interface.RemoveWidget("test")
interface.PlaceCenter("test 2",0)


Exemple avec StringVar:
interface = Interface()
txt = StringVar("test")
interface.PlaceCenter(txt,0)
txt.data = "test 2"


Une interface doit toujour contenir au moins un bouton même s'il est invisible
Exemple de bouton invisible :
interface = Interface()
interface.Locate(Bouton(interface.curseur,""),-1,-1)


Dans le code intégral vous trouverez trois fonctions supplémentaire : BoiteConfirmValide et BoiteConfirmOK et MainMenu
Dont je vous laisse le soin de découvrir leur utilité par vous même.

Bug que vous allez sûrement rencontrer : "Maximum recursion depth exceeded"

Ce bug arrive quand vous faites trop d'appel d'initiation de class à la suite.
Exemple où ça risque de planter :
Class A:
    def __init__(self):
         pass
Class B:
    def __init__(self):
         self.a = a()
Class C:
    def __init__(self):
         self.b = B()
Class D:
    def __init__(self):
         self.c = C()
d= D()


Donc pour palier à cela, vous pouvez transformer ce code :
class Fight:
    def __init__(self,plateau,monster):
    self.interface = Interface()
    etc...
class PlateauDeJeu:
    def __init__(self,x,y):
    self.fight = Fight(self,"Monstre1")
    etc...
pdj = PlateauDeJeu(0,0)

en :
interfaceFight=Interface()
class Fight:
    def __init__(self,plateau,monster):
    interfaceFight.Clear()
    self.interface = interfaceFight
    etc...
class PlateauDeJeu:
    def __init__(self,x,y):
    self.fight = Fight(self,"Monstre1")
    etc...
pdj = PlateauDeJeu(0,0)


Voilà, j'espère que cela vous sera utile.


Shadow15510 Hors ligne Administrateur Points: 4981 Défis: 16 Message

Citer : Posté le 26/08/2021 00:19 | #


Hey o/

Le double slash est indispensable donc les images ci-dessus devrai être

Pour éviter les doubles slash, tu peux utiliser des r-docstrings :
player1 = r"""
|
_|_ o __
I /|\)_)
   / \
"""

Voila
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque

Farhi Hors ligne Membre Points: 596 Défis: 0 Message

Citer : Posté le 08/09/2021 21:22 | #


Ha merci je savais pas
"La créativité est contagieuse faites la tourner"
Albert Einstein
Shadow15510 Hors ligne Administrateur Points: 4981 Défis: 16 Message

Citer : Posté le 08/09/2021 21:23 | #


Mais de rien
"Ce n'est pas parce que les chose sont dures que nous ne les faisons pas, c'est parce que nous ne les faisons pas qu'elles sont dures." Sénèque


LienAjouter une imageAjouter une vidéoAjouter un lien vers un profilAjouter du codeCiterAjouter un spoiler(texte affichable/masquable par un clic)Ajouter une barre de progressionItaliqueGrasSoulignéAfficher du texte barréCentréJustifiéPlus petitPlus grandPlus de smileys !
Cliquez pour épingler Cliquez pour détacher Cliquez pour fermer
Alignement de l'image: Redimensionnement de l'image (en pixel):
Afficher la liste des membres
:bow: :cool: :good: :love: ^^
:omg: :fusil: :aie: :argh: :mdr:
:boulet2: :thx: :champ: :whistle: :bounce:
valider
 :)  ;)  :D  :p
 :lol:  8)  :(  :@
 0_0  :oops:  :grr:  :E
 :O  :sry:  :mmm:  :waza:
 :'(  :here:  ^^  >:)

Σ π θ ± α β γ δ Δ σ λ
Veuillez donner la réponse en chiffre
Vous devez activer le Javascript dans votre navigateur pour pouvoir valider ce formulaire.

Si vous n'avez pas volontairement désactivé cette fonctionnalité de votre navigateur, il s'agit probablement d'un bug : contactez l'équipe de Planète Casio.

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2021 | Il y a 26 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd