interact.py

Created by frablock

Created on June 15, 2023

7.16 KB

Implémentation d’élément d’UI :

  • input pour kandinsky (voir fonction keyboard)

  • affichage popup

  • menu de sélection/menu de sélection par pointeur

  • Des icônes

  • Affichage des images

Ce script est un assemblage d’élément qui me semblent utiles à la création d’interface

Si vous souhaitez gagner de la place, supprimez les commentaires

LICENCE CC4.0 BY SA NC


#Fichier contenant des fonctions utiles
from kandinsky import fill_rect, draw_string, set_pixel, get_pixel
from ion import *
import ion
from time import sleep


#configs
sleeping_time=0.1




def image(im,pal,t=4,px=0,py=0,suppr="",bs=40):
 """
 inspiré par https://my.numworks.com/python/schraf/photov2
 im : image
 pal : palette

 t : taille d'un pixel

 px : placement origine x
 py : placement origine y

 suppr : Couleur à supprimer (image transparente, couleur encodée)

 bs : nombre de pixel par ligne
 """
 r,i=0,0
 while r<len(im):
  s,n=im[r],'';r+=1
  while r<len(im)and'9'>=im[r]>='0':n+=im[r];r+=1
  nb=1 if n==''else int(n);c=pal[ord(s)-65]
  for j in range(nb):
    if str(s)!=str(suppr):fill_rect(px+t*(i%bs),py+t*(i//bs),t,t,(c[0],c[1],c[2]))
    i+=1


def base(number, base, original_base=10):
    """
    Sert à convertir des nombres de différentes bases
    Utile pour la fonction icone
    """
    if base == "bin":
        return bin(int(number, original_base))[2:]
    elif base == "hex":
        return hex(int(number, original_base))[2:]

def icone(icon="happy",x=0,y=0,t=5,col=(0,0,0)):
    """
    x,y position
    t taille
    icone : nom de l'icone, ou icone en format ("base hex",w,h)
    col couleur de l'icone
    """

    pre_icon={"up_arrow":("c4a1",6,3),"down_arrow":("2148c",6,3),"right_arrow":("4454",3,6),"left_arrow":("1511",3,6),
    "settings":("f801b03f747c7d800f0e1b3e626c8cf9b0e1e0037c7cdd901b003e0",15,15),"cross":("1151151",5,5),"check":("8a88",5,4),"menu":("1f07c1f",5,5),
    "happy":("a5022e",5,5),"sad":("a501d1",5,5),"pen":("30007003700f603f00fe03f80fe03f80fe01f806e00f803c006000",15,15),"search":("1e00ff038606061806300c6018c030c0c1c380ff80f3800380038003",15,15),"heart":("66ffff7e3c1800",8,8),
    "image":("7fff80030006020c14187233136c20c0018003fffc0000000",15,15),"folder":("3e087a018060180601ffc00",10,10),"text":("ffe0180679806798067980601807ff",10,12),"python":("3c00420042004200c307f3e80218021840184014c020c3004200420042003c0",16,16),"game":("3eab060ff",7,5)}

    a=pre_icon.get(icon,icon)
    b=base(a[0],"bin",16)
    for i in range(a[1]*a[2]-len(b)):b="0"+b
    for i in range(a[1]*a[2]):
        if int(b[i]):fill_rect(x+i%a[1]*t,y+i//a[1]*t,t,t,col)


def wait_clicks(keys):
    """
    Attends jusqu'à une pression sur le clavier pour lancer une fonction
    keys : {KEY_UP:fonction,KEY_OK:fonction}
    """
    a=1
    while a:
        for k,i in keys.items():
            if keydown(k):
                a=0
                i()


def popup(a,c="white",x=10,y=75,w=300,h=75,f=None):
    """
    a : texte à afficher
    c : couleur de la popup

    x,y : position origine

    w,h : taille et hauteur

    f : fonction associée : (fonction,texte)
    """
    assert f is None or type(f)==list or type(f)==tuple, "(fonction,texte)"
    fill_rect(x,y,w,h,(0,0,0));fill_rect(x+1,y+1,w-2,h-2,c);draw_string(a,x+2,y+2,(0,0,0),c);draw_string('[OK]' if f is None else "[OK] "+str(f[1])+"\n[UP] Annuler",x+2,y+h-20,(0,0,0),c)
    wait_clicks({KEY_OK:lambda:None} if f is None else {KEY_OK:f[0],KEY_UP:lambda:None})
def select(options):
    """
    Menu de sélection à choix multiples
    options : {"Texte affiché":fonction,"autre texte":autre fonction}
    """
    assert len(options)>=2, "manques d'option"
    p=len(options)//2
    for i,(a,b) in enumerate(sorted(options.items())):
        fill_rect(20,40+(i-p)*20,280,20,"white" if i-p!=0 else "black")
        draw_string(str(a),41,40+(i-p)*20,"black" if i-p!=0 else "white","white" if i-p!=0 else "black")
    e=0
    while not keydown(KEY_OK):
        if keydown(KEY_DOWN):
            p=p+1 if 0<=p<len(options) else 0
            e=1
        if keydown(KEY_UP):
            p=p-1 if 0<=p<len(options) else len(options)-1
            e=1
        if e:
            e=0
            for i,(a,b) in enumerate(sorted(options.items())):
                if 2>(i-p)>-2:
                    fill_rect(20,40+(i-p)*20,280,20,"white" if i-p!=0 else "black")
                    draw_string(str(a),41,40+(i-p)*20,"black" if i-p!=0 else "white","white" if i-p!=0 else "black")
                    icone("up_arrow" if i-p<0 else "down_arrow",21,40+(i-p)*25,2)
            sleep(0.5)
    sleep(1)
    return sorted(options.items())[p][1]()


def keyboard(t=""):
    """
    Petite zone d'input simpliste pour kandinsky
    t :  texte par défaut
    """
    text=t
    m_text=chr(124)
    main_mode=13
    rang=len(text)

    letters="abcdefghijklmnopqrstuvwxyz ?!"
    numbers=("exp()","log()","log10()","1j",",","**","sin()","cos()","tan()","pi","sqrt()","**2","7","8","9","(",")","4","5","6","*","/","1","2","3","+","-","0",".")
    shift=("[","]","{","}","_","->","asin()","acos()","atan()","=","<",">","μ","","Ω","()",":)",":(")

    mode={13:letters,14:numbers,12:shift}
    mode_l={12:"shift",13:"alpha",14:"12345"}

    icone("pen",300,180)

    while not keydown(KEY_OK):
        fill_rect(0,200,320,1,(0,0,0))
        fill_rect(0,201,320,30,"white")
        draw_string(m_text,0,202,(0,0,0),"white")
        draw_string(mode_l[main_mode],0,180)


        for key in range(18,49+1):
            if keydown(key):
                try:
                    l=mode[main_mode][key-18 if key<=34 else key-19 if key<=40 else key-20]
                    rang+=len(l)
                    text=text[:rang-1]+l+text[rang-1:]
                except:
                    text+=""
                sleep(sleeping_time)

        for key in (12,13,14):
            if keydown(key):
                main_mode=key
                sleep(sleeping_time)
        
        if keydown(17):
            text=text[:rang-1]+text[rang:]
            rang-=1
            sleep(sleeping_time)

        if keydown(0):
            rang=rang-1 if rang-1>0 else len(text)-1
            sleep(sleeping_time)
        if keydown(3):
            rang=rang+1 if len(text)>rang else 0
            sleep(sleeping_time)

        t1=text[rang-20:rang] if len(text)>30 else text[:rang]
        t2=text[rang:rang+20] if len(text)>30 else text[rang:]
        m_text=t1+chr(124)+t2

    sleep(0.5)
    return text

def pointer_menu(buttons:list,col=(255,0,0)):
    """
    buttons:[{"bouton1":{"co":(x,y,w,h),"f"=fonction,couleur texte, background,"v":visible)}]
    col : couleur du pointeur

    menu de séléction par pointeur
    """
    pointer=[50,50]
    scol=[]

    depl={KEY_UP:(0,-1),KEY_DOWN:(0,+1),KEY_LEFT:(-1,0),KEY_RIGHT:(+1,0)}

    for i in range(16):
                    a=get_pixel(pointer[0]-1+i%4,pointer[1]-1+i//4)
                    scol+=[a]
    while 1:
        fill_rect(pointer[0],pointer[1],2,2,col)
        for k,e in depl.items():
            if keydown(k):
                for n,i in enumerate(scol):
                    set_pixel(pointer[0]-1+n%4,pointer[1]-1+n//4,i)
                pointer[0]+=e[0] if 0<pointer[0]+e[0]<310 else 0
                pointer[1]+=e[1] if 0<pointer[1]+e[1]<210 else 0
                scol=[]
                for i in range(16):
                    a=get_pixel(pointer[0]-1+i%4,pointer[1]-1+i//4)
                    scol+=[a]
        if keydown(KEY_OK):
            for n,b in buttons.items():
                if b["co"][0]<=pointer[0]<=b["co"][0]+b["co"][2] and b["co"][1]<=pointer[1]<=b["co"][1]+b["co"][3]:
                    sleep(0.5)
                    return b["f"]()