Initial commit
This commit is contained in:
@ -0,0 +1,40 @@
|
||||
|
||||
def base_conversions():
|
||||
"""
|
||||
Conversions entre types via les fonctions de base de python.
|
||||
|
||||
"""
|
||||
# Convertir une chaîne en valeur entière
|
||||
print(int("26"))
|
||||
print(float("36.5"))
|
||||
# Convertir une valeur en chaîne
|
||||
print(str(36.5))
|
||||
# Convertir une valeur complexe en chaîne
|
||||
print(str([1, 2, 3, 4, 5]))
|
||||
# Convertir un entier en valeur hexadécimale
|
||||
print(hex(571))
|
||||
|
||||
# Conversions mathématiques
|
||||
print(round(2.5))
|
||||
print(round(2.45678, 1))
|
||||
print(round(2.45678, 2))
|
||||
|
||||
# Les valeurs dites fausses
|
||||
print("Conversions booléennes qui renvoient faux")
|
||||
print(bool(None))
|
||||
print(bool(0))
|
||||
print(bool(""))
|
||||
print(bool(False))
|
||||
print(bool([]))
|
||||
|
||||
# Les valeurs dites vraies (autres que les valeurs fausses)
|
||||
print("Conversions booléennes qui renvoient vrai")
|
||||
print(bool(1)) # nombre non nul
|
||||
print(bool(-1)) # nombre non nul
|
||||
print(bool("Bonjour")) # chaîne non vide
|
||||
print(bool(True))
|
||||
print(bool([1, 3, 5, 7, 9])) # liste non vide
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_conversions()
|
@ -0,0 +1,3 @@
|
||||
seq = [25, 40, 55, 19]
|
||||
for idx, number in enumerate(seq, start=1):
|
||||
print(f"Itération {idx} : {number}.")
|
@ -0,0 +1,30 @@
|
||||
|
||||
def builtin_introspection():
|
||||
"""
|
||||
Test des fonctions natives d'introspection de Python.
|
||||
|
||||
Ici on teste les fonctions accessibles sans faire d'import,
|
||||
et qui permettent d'en savoir plus sur l'état du programme ou de
|
||||
ses variables.
|
||||
|
||||
"""
|
||||
data1 = "chaîne de caractères"
|
||||
# Fonction pour vérifier le type d'une variable
|
||||
print(isinstance(data1, str))
|
||||
print(issubclass(str, object))
|
||||
print(issubclass(str, int))
|
||||
print(type(data1))
|
||||
# Fonction pour accéder aux attributs d'une variable
|
||||
print(hasattr(data1, "manger"))
|
||||
print(hasattr(data1, "upper"))
|
||||
print(getattr(data1, "upper"))
|
||||
# Fonctions pour accéder à toutes les variables locales ou globales
|
||||
print(locals())
|
||||
print(globals())
|
||||
# Fonction qui affiche le texte d'aide pour une fonction, méthode etc.
|
||||
print(help(data1))
|
||||
print(help(data1.upper))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
builtin_introspection()
|
@ -0,0 +1,34 @@
|
||||
|
||||
def base_iterable_tests():
|
||||
"""
|
||||
Fonctions de base de python utilisables sur les itérables.
|
||||
|
||||
"""
|
||||
# Tester any, all
|
||||
iterable1 = [0, 1, 0, 1, 0, 1]
|
||||
iterable2 = [0, 0, 0, 0, 0, 0]
|
||||
iterable3 = [1, 1, 1, 1, 1, 1]
|
||||
iterable4 = [0, 1, 2, 3, 4, 5]
|
||||
iterable5 = ["a", "b", "c", "d", "e", "f"]
|
||||
# Vérifier que toutes les valeurs sont "vraies"
|
||||
print(all(iterable1))
|
||||
print(all(iterable3))
|
||||
# Vérifier qu'au moins une valeur est vraie
|
||||
print(any(iterable1))
|
||||
print(any(iterable2))
|
||||
print(any(iterable4))
|
||||
# Vérifier la longueur de l'itérable
|
||||
print(len(iterable1))
|
||||
|
||||
# Calculer la somme des éléments
|
||||
print(sum(iterable1))
|
||||
print(sum(iterable4))
|
||||
# Trouver la valeur minimale/maximale des éléments
|
||||
print(min(iterable4))
|
||||
print(max(iterable4))
|
||||
print(min(iterable5))
|
||||
print(max(iterable5))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_iterable_tests()
|
@ -0,0 +1,16 @@
|
||||
"""Example module for variable declaration."""
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Define simple variables
|
||||
integer1 = 15 # integer value
|
||||
float1 = 15.0 # floating value
|
||||
text1 = "Simple text" # string value
|
||||
boolean1 = True # boolean value
|
||||
undefined1 = None # special None value
|
||||
|
||||
# Display variable contents
|
||||
print(integer1)
|
||||
print(float1)
|
||||
print(text1)
|
||||
print(boolean1)
|
||||
print(undefined1)
|
62
source/03-exceptions/exceptions/source/base.py
Normal file
62
source/03-exceptions/exceptions/source/base.py
Normal file
@ -0,0 +1,62 @@
|
||||
def base_exception_handling():
|
||||
"""Code de base pour gérer une exception."""
|
||||
try:
|
||||
15 / 0
|
||||
except ZeroDivisionError:
|
||||
print("Nous avons eu une erreur de division par zéro !")
|
||||
except Exception:
|
||||
print("Exception non prise en charge !")
|
||||
|
||||
|
||||
def exception_with_else():
|
||||
"""
|
||||
Gérer une exception, avec la clause else.
|
||||
|
||||
La clause else contient du code qui est exécuté si tout se passe
|
||||
bien.
|
||||
|
||||
"""
|
||||
try:
|
||||
15 / 1
|
||||
except ZeroDivisionError:
|
||||
print("Division par zéro !")
|
||||
else:
|
||||
print("Tout s'est bien passé !")
|
||||
|
||||
|
||||
def exception_with_finally():
|
||||
"""
|
||||
Gérer une exception, avec la clause finally.
|
||||
|
||||
Le bloc de la clause finally est toujours exécuté,
|
||||
quand une exception est levée ou que tout se passe bien.
|
||||
le bloc finally est même exécuté si vous essayez de faire un
|
||||
`return` dans votre bloc `try`, alors que pourtant la directive
|
||||
`return` interrompt normalement l'exécution de la fonction.
|
||||
Cas de figure : fermer un fichier ouvert quoi qu'il arrive.
|
||||
|
||||
"""
|
||||
try:
|
||||
15 / 0
|
||||
except ZeroDivisionError:
|
||||
print("Division par zéro !")
|
||||
return None
|
||||
finally:
|
||||
print("Pas si vite, monsieur return !")
|
||||
|
||||
|
||||
def raise_exception():
|
||||
"""
|
||||
Lever manuellement une exception simple.
|
||||
|
||||
Si vous voyez une erreur dans la console, c'est normal.
|
||||
|
||||
"""
|
||||
raise ValueError("Valeur incorrecte.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_exception_handling()
|
||||
exception_with_else()
|
||||
exception_with_finally()
|
||||
raise_exception()
|
0
source/06-extra-types/dates/__init__.py
Normal file
0
source/06-extra-types/dates/__init__.py
Normal file
56
source/06-extra-types/dates/basedate.py
Normal file
56
source/06-extra-types/dates/basedate.py
Normal file
@ -0,0 +1,56 @@
|
||||
from datetime import date, datetime, timezone
|
||||
|
||||
|
||||
def base_dates():
|
||||
"""
|
||||
Gérer des dates de base.
|
||||
|
||||
"""
|
||||
# Créer deux dates
|
||||
today = date.today()
|
||||
before = date(2018, 7, 15) # 15 juillet 2018
|
||||
# Afficher la date du jour, et aussi la formater
|
||||
print(f"Aujourd'hui nous sommes le {today}")
|
||||
print(f"Aujourd'hui nous sommes le (formaté) {today:%d/%m/%Y}")
|
||||
# Afficher la date initialisée manuellement
|
||||
print(f"La finale de Coupe du monde de football a eu lieu le {before}")
|
||||
|
||||
|
||||
def base_datetimes():
|
||||
"""
|
||||
Gérer des dates avec heure.
|
||||
|
||||
"""
|
||||
# Créer deux moments dans le temps
|
||||
now = datetime.now()
|
||||
before = datetime(2018, 7, 15, 20, 0) # 15 juillet 2018 à 20h
|
||||
# Afficher la date du jour, et aussi la formater
|
||||
print(f"Maintenant nous sommes le {now}")
|
||||
print(f"Maintenant nous sommes le (formaté) {now:%d/%m/%Y %H:%M:%S}")
|
||||
# Afficher la date initialisée manuellement
|
||||
print(f"La finale de Coupe du monde de football a eu lieu à {before}")
|
||||
|
||||
|
||||
def timezone_datetimes():
|
||||
"""
|
||||
Gérer des dates avec heure, et avec fuseau horaire.
|
||||
|
||||
"""
|
||||
now = datetime.now(timezone.utc)
|
||||
before = datetime(2018, 7, 15, 20, 0, tzinfo=timezone.utc) # 15 juillet 2018 à 20h
|
||||
# Afficher la date du jour, et aussi la formater
|
||||
# Voir https://docs.python.org/3/library/datetime.html#strftime-and-strptime-format-codes
|
||||
print(f"Maintenant nous sommes le {now}")
|
||||
print(f"Maintenant nous sommes le (formaté) {now:%d/%m/%Y %H:%M:%S (%Z)}")
|
||||
# Afficher la date initialisée manuellement
|
||||
print(f"La finale de Coupe du monde de football a eu lieu à {before}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Lancer la fonction qui teste des dates
|
||||
base_dates()
|
||||
# Lancer la fonction qui teste des dates avec heure
|
||||
base_datetimes()
|
||||
# Lancer la fonction qui teste des dates avec heure et fuseau horaire
|
||||
timezone_datetimes()
|
||||
|
21
source/06-extra-types/dates/timedeltas.py
Normal file
21
source/06-extra-types/dates/timedeltas.py
Normal file
@ -0,0 +1,21 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
def base_datetime_timedelta():
|
||||
"""
|
||||
Gérer des dates avec heure et ajouter des intervalles de temps.
|
||||
|
||||
"""
|
||||
# Créer deux moments dans le temps
|
||||
now: datetime = datetime.now()
|
||||
interval: timedelta = timedelta(hours=6)
|
||||
in_six_hours: datetime = now + interval
|
||||
# Afficher la date du jour, et aussi la formater
|
||||
print(f"Dans 6 heures on sera le (formaté) {in_six_hours:%d/%m/%Y %H:%M:%S}")
|
||||
# Afficher la représentation texte de l'intervalle
|
||||
print(f"L'intervalle de temps est {interval}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Lancer la fonction qui teste des dates
|
||||
base_datetime_timedelta()
|
0
source/07-objects/inheritance/__init__.py
Normal file
0
source/07-objects/inheritance/__init__.py
Normal file
115
source/07-objects/inheritance/aggregation.py
Normal file
115
source/07-objects/inheritance/aggregation.py
Normal file
@ -0,0 +1,115 @@
|
||||
from typing import List, Set
|
||||
|
||||
|
||||
class User(object):
|
||||
"""
|
||||
Classe représentant un utilisateur.
|
||||
|
||||
"""
|
||||
name: str = None
|
||||
first_name: str = None
|
||||
email: str = None
|
||||
password: str = None
|
||||
|
||||
def __init__(self, name, first_name, email, password):
|
||||
"""
|
||||
Initialiser un nouvel utilisateur.
|
||||
|
||||
Args:
|
||||
name: nom
|
||||
first_name: prénom
|
||||
email: email
|
||||
password: mot de passe
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.first_name = first_name
|
||||
self.email = email
|
||||
self.password = password
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Texte affiché pour l'objet quand on le passe dans `print()` ou surtout
|
||||
que sa représentation textuelle est demandée.
|
||||
|
||||
Returns:
|
||||
Le nom de l'utilisateur.
|
||||
|
||||
"""
|
||||
return self.name
|
||||
|
||||
|
||||
class Group(object):
|
||||
"""
|
||||
Classe de groupe d'utilisateurs.
|
||||
|
||||
"""
|
||||
name: str = None
|
||||
description: str = None
|
||||
# Ici on a un attribut qui est une liste d'objets d'une autre classe.
|
||||
# C'est ce qu'on appelle l'agrégation.
|
||||
# Si on supprime le groupe, les objets utilisateurs existent toujours indépendamment,
|
||||
# on a ici simplement un lien vers des utilisateurs.
|
||||
users: Set[User] = None
|
||||
|
||||
def __init__(self, name, description=None):
|
||||
"""
|
||||
Initialiser un nouveau groupe d'utilisateurs.
|
||||
|
||||
Args:
|
||||
name: nom du groupe
|
||||
description: texte descriptif, facultatif
|
||||
|
||||
"""
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
def __repr__(self):
|
||||
"""
|
||||
Texte affiché pour l'objet quand on le passe dans `print()`
|
||||
|
||||
Returns:
|
||||
Le nom du groupe.
|
||||
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def add_user(self, user: User):
|
||||
"""
|
||||
Ajouter un utilisateur au groupe.
|
||||
|
||||
Args:
|
||||
user: instance d'utilisateur à ajouter
|
||||
|
||||
"""
|
||||
self.users = self.users or set()
|
||||
self.users.add(user)
|
||||
|
||||
def get_users(self):
|
||||
"""
|
||||
Renvoie la liste des utilisateurs dans le groupe.
|
||||
|
||||
Returns:
|
||||
La liste des utilisateurs ajoutés au groupe.
|
||||
|
||||
"""
|
||||
return self.users
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Créer quelques utilisateurs
|
||||
user1 = User("Martin", "Jacques", "jmartin@example.com", "jmartin")
|
||||
user2 = User("Carletti", "Audrey", "acarletti@example.com", "acarletti")
|
||||
user3 = User("Richard", "Luc", "lrichard@example.com", "lrichard")
|
||||
user4 = User("Havel", "Vaclav", "vhavel@example.com", "vhavel")
|
||||
# Créer un groupe ou deux
|
||||
group1 = Group("Amicale des amateurs de froid")
|
||||
group2 = Group("Amicale des amateurs de chaud")
|
||||
# Ajouter des utilisateurs aux groupes
|
||||
group1.add_user(user1)
|
||||
group1.add_user(user2)
|
||||
group1.add_user(user3)
|
||||
group2.add_user(user4)
|
||||
# Afficher les utilisateurs pour chaque groupe
|
||||
print(f"Utilisateurs du groupe {group1.name} : {group1.get_users()}")
|
||||
print(f"Utilisateurs du groupe {group2.name} : {group2.get_users()}")
|
0
source/07-objects/inheritance/multiple.py
Normal file
0
source/07-objects/inheritance/multiple.py
Normal file
64
source/07-objects/inheritance/polymorphism.py
Normal file
64
source/07-objects/inheritance/polymorphism.py
Normal file
@ -0,0 +1,64 @@
|
||||
from typing import Optional
|
||||
|
||||
|
||||
class Animal(object):
|
||||
"""
|
||||
Classe de base pour tous les animaux.
|
||||
|
||||
"""
|
||||
pattes: int = None
|
||||
vertebre: Optional[bool] = None
|
||||
|
||||
def manger(self):
|
||||
"""
|
||||
Méthode de base pour que l'animal mange.
|
||||
|
||||
On ne définit aucun comportement particulier ici.
|
||||
|
||||
"""
|
||||
print("Je suis un animal et je mange.")
|
||||
|
||||
|
||||
class Chien(Animal):
|
||||
"""
|
||||
Classe pour le chien.
|
||||
|
||||
"""
|
||||
pattes: int = 4
|
||||
vertebre = True
|
||||
|
||||
def manger(self):
|
||||
"""
|
||||
Méthode pour faire manger le chien.
|
||||
|
||||
"""
|
||||
super().manger() # Exécute le code ligne 19
|
||||
print("Je dirais même plus *wouf* ! Je suis un chien qui mange.")
|
||||
|
||||
|
||||
class Escargot(Animal):
|
||||
"""
|
||||
Classe pour l'escargot.
|
||||
|
||||
"""
|
||||
pattes: int = 0
|
||||
vertebre = False
|
||||
|
||||
def manger(self):
|
||||
"""
|
||||
Méthode pour faire manger l'escargot.
|
||||
|
||||
"""
|
||||
print("Slurp *mange, mais on ne sait pas comment*")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Créer des nouvelles instances d'animaux
|
||||
escargot = Escargot()
|
||||
chien = Chien()
|
||||
# Et voir comment ça se comporte quand on appelle la méthode `manger`.
|
||||
# Le fait que plusieurs classes héritant d'une autre classe ont des comportements
|
||||
# différents pour les mêmes méthodes s'appelle du polymorphisme.
|
||||
# Les méthodes peuvent aussi avoir des arguments différents mais le même nom.
|
||||
print(escargot.manger())
|
||||
print(chien.manger())
|
92
source/07-objects/inheritance/simple.py
Normal file
92
source/07-objects/inheritance/simple.py
Normal file
@ -0,0 +1,92 @@
|
||||
|
||||
class Person(object):
|
||||
"""
|
||||
Classe représentant une personne.
|
||||
|
||||
"""
|
||||
nom: str = None
|
||||
prenom: str = None
|
||||
age: int = None
|
||||
|
||||
def __init__(self, *_args, **kwargs):
|
||||
"""
|
||||
Initialiser les valeurs d'une nouvelle personne.
|
||||
|
||||
Args:
|
||||
*_args: Liste d'arguments non positionnels
|
||||
**kwargs: Arguments nommés, utilisés pour initialiser les attributs de notre objet.
|
||||
|
||||
"""
|
||||
self.nom = kwargs.get("nom", None)
|
||||
self.prenom = kwargs.get("prenom", None)
|
||||
self.age = kwargs.get("age", None)
|
||||
|
||||
def set_nom_complet(self, nom, prenom):
|
||||
"""
|
||||
Méthode pour définir les noms et prénom de l'individu.
|
||||
|
||||
Args:
|
||||
nom: Nom de famille à attribuer
|
||||
prenom: Prénom à attribuer à l'objet
|
||||
|
||||
"""
|
||||
self.prenom = prenom
|
||||
self.nom = nom
|
||||
|
||||
def get_nom_complet(self):
|
||||
"""
|
||||
Méthode pour retrouver d'un coup le nom complet de l'individu.
|
||||
|
||||
Returns:
|
||||
Nom complet, sous la forme "<prenom> <nom>"
|
||||
|
||||
"""
|
||||
if self.prenom and self.nom:
|
||||
return f"{self.prenom} {self.nom}"
|
||||
else:
|
||||
return "Nom complet non renseigné"
|
||||
|
||||
|
||||
class Professional(Person):
|
||||
"""
|
||||
Classe représentant un professionel, héritant de `Person`.
|
||||
|
||||
"""
|
||||
telephone_pro: str = None
|
||||
job: str = None
|
||||
|
||||
def __init__(self, *_args, **kwargs):
|
||||
"""
|
||||
Initialiser les données d'un nouveau professionel.
|
||||
|
||||
On peut également initialiser le prénom, nom et âge.
|
||||
|
||||
Args:
|
||||
*_args: Liste d'arguments non positionnels
|
||||
**kwargs: Liste d'arguments nommés, utilisés pour initialiser les attributs de notre objet.
|
||||
|
||||
"""
|
||||
super().__init__(*_args, **kwargs)
|
||||
self.telephone_pro = kwargs.get("telephone_pro", None)
|
||||
self.job = kwargs.get("job", None)
|
||||
|
||||
def get_nom_complet(self):
|
||||
"""
|
||||
Méthode pour retrouver d'un coup le nom complet de l'individu.
|
||||
|
||||
Returns:
|
||||
Nom complet, sous la forme "<prenom> <nom>"
|
||||
|
||||
"""
|
||||
base_nom = super().get_nom_complet()
|
||||
return f"{base_nom} ({self.job})"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ici on se crée plusieurs objets, 2 professionnels et une personne
|
||||
pro1 = Professional(nom="Mario", prenom="Mario", age=38, telephone_pro="0799999999", job="Jardinier")
|
||||
pro2 = Professional(nom="Mario", prenom="Luigi", age=37, telephone_pro="0799999999", job="Plombier")
|
||||
mec = Person(nom="Bertrand", prenom="Julien", age=30)
|
||||
# Les deux classe Person et Professional ont des méthodes `get_nom_complet()` différentes, tester
|
||||
print(mec.get_nom_complet())
|
||||
print(pro1.get_nom_complet())
|
0
source/07-objects/introspection/__init__.py
Normal file
0
source/07-objects/introspection/__init__.py
Normal file
42
source/07-objects/introspection/base.py
Normal file
42
source/07-objects/introspection/base.py
Normal file
@ -0,0 +1,42 @@
|
||||
import pdir
|
||||
|
||||
|
||||
def use_dir():
|
||||
"""
|
||||
Découvrir l'introspection via la fonction `dir()`
|
||||
|
||||
Nécessite pdir2 pour montrer une sortie plus sympa aux étudiants.
|
||||
|
||||
"""
|
||||
# En python, tout est un objet, et a donc des attributs et des méthodes.
|
||||
# Le truc bien, c'est qu'en Python, on peut aussi manipuler et retrouver les
|
||||
# propriétés de ces attributs et méthodes.
|
||||
chaine = "Bonjour"
|
||||
# À la place de `pdir` on pouvait utiliser `dir` qui fait partie de python,
|
||||
# mais qui affiche la liste des attributs de l'objet de façon beaucoup moins
|
||||
# lisible.
|
||||
print(pdir(chaine))
|
||||
|
||||
|
||||
def check_attrs():
|
||||
"""
|
||||
Via l'introspection, accéder à des attributs d'objets programmatiquement.
|
||||
|
||||
"""
|
||||
chaine = "Bonjour"
|
||||
# Une chaîne a toujours une méthode `capitalize`, donc ça va fonctionner
|
||||
if hasattr(chaine, "capitalize"):
|
||||
print("L'objet a bien une fonction `capitalize`.")
|
||||
print(getattr(chaine, "capitalize"))
|
||||
# Mais une chaîne n'a pas d'attribut `doesnotexist`.
|
||||
if hasattr(chaine, "doesnotexist"):
|
||||
print("L'objet a un attribut `doesnotexist`.")
|
||||
else:
|
||||
print("L'objet n'a pas d'attribut `doesnotexist`.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Utilisation de la fonction `dir` :")
|
||||
use_dir()
|
||||
print("Utilisation de l'introspection d'attributs :")
|
||||
check_attrs()
|
0
source/07-objects/magics/__init__.py
Normal file
0
source/07-objects/magics/__init__.py
Normal file
11
source/07-objects/magics/deletion.py
Normal file
11
source/07-objects/magics/deletion.py
Normal file
@ -0,0 +1,11 @@
|
||||
class DelTest:
|
||||
def __del__(self):
|
||||
print("Suppression.")
|
||||
|
||||
|
||||
a = DelTest()
|
||||
b = a
|
||||
|
||||
# On s'attend à afficher Suppression avant d'afficher la référence de b.
|
||||
del a
|
||||
print(b)
|
6
source/08-text-files/csv/csvread.py
Normal file
6
source/08-text-files/csv/csvread.py
Normal file
@ -0,0 +1,6 @@
|
||||
import csv
|
||||
|
||||
with open("demo-file-985.csv", "r", encoding="utf-8") as f:
|
||||
reader = csv.reader(f)
|
||||
for row in reader: # row sera une séquence, contenant un élement par colonne de la ligne en cours
|
||||
print(row) # Affiche la ligne courante du CSV séparée dans une liste.
|
1
source/08-text-files/csv/demo-file-985.csv
Normal file
1
source/08-text-files/csv/demo-file-985.csv
Normal file
File diff suppressed because one or more lines are too long
0
source/08-text-files/fileuse/__init__.py
Normal file
0
source/08-text-files/fileuse/__init__.py
Normal file
42
source/08-text-files/fileuse/binaryfile.py
Normal file
42
source/08-text-files/fileuse/binaryfile.py
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
def write_file():
|
||||
"""
|
||||
Écrire un fichier binaire très simple.
|
||||
|
||||
En Python, on ne peut pas tout simplement écrire des chaînes dans un
|
||||
fichier dit binaire. Dans Python 3, on fait la différence entre binaire et texte.
|
||||
Quand on manipule des fichiers texte, on peut y écrire des données `str`.
|
||||
Par contre, quand on manipule des fichiers binaires, on manipule des données plus
|
||||
brutes, de type `bytes` (ou `bytearray`)
|
||||
|
||||
"""
|
||||
f = open("demo.bin", "wb")
|
||||
f.write(bytearray([32, 33, 34, 35, 36, 37, 38, 39, 40])) # (9)
|
||||
f.write(b"Bonjour les amis") # (16) ça passe car les caractères sont ASCII et leur code tient dans un byte
|
||||
f.write("Bonjour les héros, ça va bien ?".encode("utf-8")) # (33)
|
||||
f.close()
|
||||
|
||||
|
||||
def read_file():
|
||||
"""
|
||||
Lire dans un fichier binaire très simple.
|
||||
|
||||
"""
|
||||
f = open("demo.bin", "rb")
|
||||
tableau = f.read(9)
|
||||
chaine1 = f.read(16)
|
||||
chaine2 = f.read(33).decode("utf-8")
|
||||
f.seek(0)
|
||||
data = f.read()
|
||||
f.close()
|
||||
# Afficher les données lues dans le fichier
|
||||
print(tableau)
|
||||
print(chaine1)
|
||||
print(chaine2)
|
||||
# Pour tester, revenir au début du fichier et tout lire d'une traite
|
||||
print(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
write_file()
|
||||
read_file()
|
1
source/08-text-files/fileuse/demo.bin
Normal file
1
source/08-text-files/fileuse/demo.bin
Normal file
@ -0,0 +1 @@
|
||||
!"#$%&'(Bonjour les amisBonjour les héros, ça va bien ?
|
1
source/08-text-files/fileuse/demo.txt
Normal file
1
source/08-text-files/fileuse/demo.txt
Normal file
@ -0,0 +1 @@
|
||||
Contenu de notre fichier texte de démo !
|
43
source/08-text-files/fileuse/filemanager.py
Normal file
43
source/08-text-files/fileuse/filemanager.py
Normal file
@ -0,0 +1,43 @@
|
||||
|
||||
with open("demo.txt", "r", encoding="utf-8") as f:
|
||||
pass
|
||||
|
||||
# écrire un manager
|
||||
class File(object):
|
||||
def __init__(self, file_name, method):
|
||||
self.file_obj = open(file_name, method)
|
||||
def __enter__(self):
|
||||
return self.file_obj
|
||||
def __exit__(self, type, value, traceback):
|
||||
self.file_obj.close()
|
||||
|
||||
# Just by defining __enter__ and __exit__ methods we can use our new class in a with statement. Let’s try:
|
||||
#
|
||||
# with File('demo.txt', 'w') as opened_file:
|
||||
# opened_file.write('Hola!')
|
||||
#
|
||||
# Our __exit__ method accepts three arguments. They are required by every __exit__ method which is a part of a Context Manager class. Let’s talk about what happens under-the-hood.
|
||||
#
|
||||
# The with statement stores the __exit__ method of the File class.
|
||||
# It calls the __enter__ method of the File class.
|
||||
# The __enter__ method opens the file and returns it.
|
||||
# The opened file handle is passed to opened_file.
|
||||
# We write to the file using .write().
|
||||
# The with statement calls the stored __exit__ method.
|
||||
# The __exit__ method closes the file.
|
||||
|
||||
|
||||
# We did not talk about the type, value and traceback arguments of the __exit__ method. Between the 4th and 6th step, if an exception occurs, Python passes the type, value and traceback of the exception to the __exit__ method. It allows the __exit__ method to decide how to close the file and if any further steps are required. In our case we are not paying any attention to them.
|
||||
#
|
||||
# What if our file object raises an exception? We might be trying to access a method on the file object which it does not supports. For instance:
|
||||
#
|
||||
# with File('demo.txt', 'w') as opened_file:
|
||||
# opened_file.undefined_function('Hola!')
|
||||
#
|
||||
# Let’s list the steps which are taken by the with statement when an error is encountered:
|
||||
#
|
||||
# It passes the type, value and traceback of the error to the __exit__ method.
|
||||
# It allows the __exit__ method to handle the exception.
|
||||
# If __exit__ returns True then the exception was gracefully handled.
|
||||
# If anything other than True is returned by the __exit__ method then the exception is raised by the with statement.
|
||||
|
12
source/08-text-files/fileuse/filesearch.py
Normal file
12
source/08-text-files/fileuse/filesearch.py
Normal file
@ -0,0 +1,12 @@
|
||||
import os, glob
|
||||
|
||||
# traverse root directory, and list directories as dirs and files as files
|
||||
for root, dirs, files in os.walk("/home/steve/Code/python/initiation"):
|
||||
for filename in files:
|
||||
path = os.path.join(root, filename)
|
||||
print(path)
|
||||
|
||||
|
||||
# https://docs.python.org/fr/3/library/glob.html
|
||||
files = glob.glob("/home/steve/Code/python/initiation/**/*.py", recursive=True)
|
||||
print(files)
|
25
source/08-text-files/fileuse/textfile.py
Normal file
25
source/08-text-files/fileuse/textfile.py
Normal file
@ -0,0 +1,25 @@
|
||||
|
||||
def write_file():
|
||||
"""
|
||||
Écrire un fichier texte très simple.
|
||||
|
||||
"""
|
||||
f = open("demo.txt", "w")
|
||||
f.write("Contenu de notre fichier texte de démo e!")
|
||||
f.close()
|
||||
|
||||
|
||||
def read_file():
|
||||
"""
|
||||
Lire dans un fichier texte très simple.
|
||||
|
||||
"""
|
||||
f = open("demo.txt", "r")
|
||||
data = f.read()
|
||||
f.close()
|
||||
print(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
write_file()
|
||||
read_file()
|
0
source/08-text-files/json/__init__.py
Normal file
0
source/08-text-files/json/__init__.py
Normal file
25
source/08-text-files/json/demo.json
Normal file
25
source/08-text-files/json/demo.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"glossary": {
|
||||
"title": "example glossary",
|
||||
"GlossDiv": {
|
||||
"title": "S",
|
||||
"GlossList": {
|
||||
"GlossEntry": {
|
||||
"ID": "SGML",
|
||||
"SortAs": "SGML",
|
||||
"GlossTerm": "Standard Generalized Markup Language",
|
||||
"Acronym": "SGML",
|
||||
"Abbrev": "ISO 8879:1986",
|
||||
"GlossDef": {
|
||||
"para": "A meta-markup language, used to create markup languages such as DocBook.",
|
||||
"GlossSeeAlso": [
|
||||
"GML",
|
||||
"XML"
|
||||
]
|
||||
},
|
||||
"GlossSee": "markup"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
12
source/08-text-files/json/jsondecode.py
Normal file
12
source/08-text-files/json/jsondecode.py
Normal file
@ -0,0 +1,12 @@
|
||||
import json
|
||||
from os.path import dirname, join
|
||||
|
||||
# N'exécute le code que si vous avez spécifiquement exécuté ce module python
|
||||
if __name__ == "__main__":
|
||||
saisie = input("Saisissez un truc")
|
||||
current_folder = dirname(__file__)
|
||||
# Ouvre le fichier demo.json en lecture
|
||||
f = open(join(current_folder, "demo.json"), "r", encoding="utf-8")
|
||||
data = json.load(f) # Va manipuler le descripteur de fichier et renvoyer les données converties
|
||||
print(type(data)) # Vérifier que le type de la donnée n'est plus juste du texte
|
||||
f.close()
|
26
source/08-text-files/json/jsonencode.py
Normal file
26
source/08-text-files/json/jsonencode.py
Normal file
@ -0,0 +1,26 @@
|
||||
from json import dumps
|
||||
|
||||
# N'exécute le code que si vous avez spécifiquement exécuté ce module python
|
||||
if __name__ == "__main__":
|
||||
data = {
|
||||
"users": [
|
||||
{
|
||||
"name": "Jean",
|
||||
"age": 25
|
||||
},
|
||||
{
|
||||
"name": "Denis",
|
||||
"age": 30
|
||||
},
|
||||
{
|
||||
"name": "Alice",
|
||||
"age": 35
|
||||
},
|
||||
{
|
||||
"name": "Achour",
|
||||
"age": 40
|
||||
}
|
||||
]
|
||||
}
|
||||
chaine = dumps(data)
|
||||
print(type(chaine))
|
25
source/08-text-files/text-files/files/don-diego.txt
Normal file
25
source/08-text-files/text-files/files/don-diego.txt
Normal file
@ -0,0 +1,25 @@
|
||||
DON DIÈGUE
|
||||
Ô rage ! ô désespoir ! ô vieillesse ennemie !
|
||||
N’ai-je donc tant vécu que pour cette infamie ?
|
||||
Et ne suis-je blanchi dans les travaux guerriers
|
||||
Que pour voir en un jour flétrir tant de lauriers ?
|
||||
Mon bras qu’avec respect tout l’Espagne admire,
|
||||
Mon bras, qui tant de fois a sauvé cet empire,
|
||||
Tant de fois affermi le trône de son roi,
|
||||
Trahit donc ma querelle, et ne fait rien pour moi ?
|
||||
Ô cruel souvenir de ma gloire passée !
|
||||
Œuvre de tant de jours en un jour effacée !
|
||||
Nouvelle dignité fatale à mon bonheur !
|
||||
Précipice élevé d’où tombe mon honneur !
|
||||
Faut-il de votre éclat voir triompher le comte,
|
||||
Et mourir sans vengeance, ou vivre dans la honte ?
|
||||
Comte, sois de mon prince à présent gouverneur ;
|
||||
Ce haut rang n’admet point un homme sans honneur ;
|
||||
Et ton jaloux orgueil par cet affront insigne
|
||||
Malgré le choix du roi, m’en a su rendre indigne.
|
||||
Et toi, de mes exploits glorieux instrument,
|
||||
Mais d’un corps tout de glace inutile ornement,
|
||||
Fer, jadis tant à craindre, et qui, dans cette offense,
|
||||
M’as servi de parade, et non pas de défense,
|
||||
Va, quitte désormais le derniers des humains,
|
||||
Passe, pour me venger, en de meilleurs mains.
|
18
source/08-text-files/text-files/source/fileread_classic.py
Normal file
18
source/08-text-files/text-files/source/fileread_classic.py
Normal file
@ -0,0 +1,18 @@
|
||||
"""
|
||||
Base file reading example.
|
||||
|
||||
This example uses:
|
||||
- classic open/close of file
|
||||
- while loop with walrus operator (Python 3.8).
|
||||
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
file = open("../files/don-diego.txt", "r", encoding="utf-8")
|
||||
# Read the file contents line by line using the walrus operator
|
||||
# (introduced in Python 3.8)
|
||||
# While the line read is not empty, you're not at the end of file.
|
||||
while line := file.readline().strip():
|
||||
# Lines contain caret return. Using `.strip()`
|
||||
# removes spaces and caret returns at the start and end.
|
||||
print(f"{line}")
|
||||
file.close()
|
16
source/08-text-files/text-files/source/fileread_iterator.py
Normal file
16
source/08-text-files/text-files/source/fileread_iterator.py
Normal file
@ -0,0 +1,16 @@
|
||||
"""
|
||||
Base file reading example.
|
||||
|
||||
This example uses:
|
||||
- context manager (with ... as)
|
||||
- iterator (for ... in object).
|
||||
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
with open("../files/don-diego.txt", "r", encoding="utf-8") as file:
|
||||
# Read the file contents line by line
|
||||
for count, line in enumerate(file):
|
||||
# Lines contain caret return. Using `strip`
|
||||
# removes spaces and caret returns at the start and end
|
||||
# of the string.
|
||||
print(f"{count} : {line.strip()}")
|
0
source/08-text-files/xml/__init__.py
Normal file
0
source/08-text-files/xml/__init__.py
Normal file
27
source/08-text-files/xml/demo.xml
Normal file
27
source/08-text-files/xml/demo.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<users>
|
||||
<user data-id="101">
|
||||
<nom>Zorro</nom>
|
||||
<metier>Danseur</metier>
|
||||
</user>
|
||||
<user data-id="102">
|
||||
<nom>Hulk</nom>
|
||||
<metier>Footballeur</metier>
|
||||
</user>
|
||||
<user data-id="103">
|
||||
<nom>Zidane</nom>
|
||||
<metier>Star</metier>
|
||||
</user>
|
||||
<user data-id="104">
|
||||
<nom>Beans</nom>
|
||||
<metier>Epicier</metier>
|
||||
</user>
|
||||
<user data-id="105">
|
||||
<nom>Batman</nom>
|
||||
<metier>Veterinaire</metier>
|
||||
</user>
|
||||
<user data-id="106">
|
||||
<nom>Spiderman</nom>
|
||||
<metier>Veterinaire</metier>
|
||||
</user>
|
||||
</users>
|
51
source/08-text-files/xml/xmldecode.py
Normal file
51
source/08-text-files/xml/xmldecode.py
Normal file
@ -0,0 +1,51 @@
|
||||
from typing import List
|
||||
|
||||
from lxml import etree
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Balise principale
|
||||
base = etree.Element("FastDMU", {"version": "2.0"})
|
||||
# Balise Search system
|
||||
root = etree.Element("SearchSystem")
|
||||
# Deux champs pour Search System
|
||||
root_title = etree.Element("Title")
|
||||
root_mode = etree.Element("Mode")
|
||||
root_title.text = "01"
|
||||
root_mode.text = "AssembliesOnly"
|
||||
root.append(root_title)
|
||||
root.append(root_mode)
|
||||
|
||||
# Liste des items à ajouter
|
||||
items: List[str] = ["A", "B", "C"]
|
||||
for item in items:
|
||||
# Création des éléments
|
||||
search_item = etree.Element("SearchItem")
|
||||
item_mode = etree.Element("Mode")
|
||||
item_type = etree.Element("Type")
|
||||
item_value = etree.Element("Value")
|
||||
item_field = etree.Element("Field")
|
||||
item_field_name = etree.Element("FieldName")
|
||||
item_title = etree.Element("Title")
|
||||
# Définition du texte
|
||||
item_mode.text = "Add"
|
||||
item_type.text = "Wildcard"
|
||||
item_value.text = item
|
||||
item_field.text = "PARTNUMBER"
|
||||
item_field_name.text = "PARTNUMBER"
|
||||
item_title.text = "Partnumber"
|
||||
# Ajout des éléments au parent
|
||||
search_item.append(item_mode)
|
||||
search_item.append(item_type)
|
||||
search_item.append(item_value)
|
||||
search_item.append(item_field)
|
||||
search_item.append(item_field_name)
|
||||
search_item.append(item_title)
|
||||
# Ajout du searchitem à la balise SearchSystem
|
||||
root.append(search_item)
|
||||
# Ajout de la balise SearchSystem à la balise FastDMU
|
||||
base.append(root)
|
||||
# J'aurais peut-être préféré faire ça avec BeautifulSoup4
|
||||
# Ou peut-être gagner du temps en convertissant du texte directement
|
||||
# en éléments XML.
|
||||
print(etree.tostring(base, pretty_print=True, xml_declaration=True, encoding="iso8859-1",
|
||||
doctype="<!DOCTYPE FastDMU>"))
|
0
source/08-text-files/xml/xmlmake.py
Normal file
0
source/08-text-files/xml/xmlmake.py
Normal file
17
source/08-text-files/xml/xmlread.py
Normal file
17
source/08-text-files/xml/xmlread.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Installer d'abord lxml avec pip install lxml
|
||||
from lxml import etree
|
||||
|
||||
# Read content from XML file
|
||||
with open("demo.xml", "rb") as file:
|
||||
text = file.read()
|
||||
|
||||
# Read structure into Element object
|
||||
structure = etree.fromstring(text)
|
||||
print(type(structure))
|
||||
|
||||
print(structure.text)
|
||||
print(structure.attrib)
|
||||
for child in structure:
|
||||
print(child, type(child), child.attrib)
|
||||
|
||||
print(structure.find("user"))
|
BIN
source/09-sqlite/basic-table/source/database.sqlite3
Normal file
BIN
source/09-sqlite/basic-table/source/database.sqlite3
Normal file
Binary file not shown.
28
source/09-sqlite/basic-table/source/sqlitedemo.py
Normal file
28
source/09-sqlite/basic-table/source/sqlitedemo.py
Normal file
@ -0,0 +1,28 @@
|
||||
import sqlite3
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Il existe dans Python une API unifiée, où les mêmes opérations
|
||||
# s'effectuent avec les mêmes fonctions et méthodes, quelle que
|
||||
# soit la base de données relationnelle SQL à laquelle on accède.
|
||||
# Pour SQLite en l'occurrence, la méthode `.connect` ne prend qu'un seul
|
||||
# paramètre, le nom de fichier, car SQLite est une base de données
|
||||
# embarquée sans sécurité et n'a donc pas besoin de mot de passe ou de nom
|
||||
# d'utilisateur.
|
||||
connection = sqlite3.connect("database.sqlite3", isolation_level=None)
|
||||
# Ici on crée une nouvelle table dans notre base de données si elle n'existe pas déjà.
|
||||
# Cette fonction de la base de données renvoie un seul résultat pour dire que tout
|
||||
# est OK.
|
||||
connection.execute("CREATE TABLE IF NOT EXISTS person (nom varchar(30), prenom varchar(20), age int)")
|
||||
# Insérer quelques nouvelles lignes de données dans notre nouvelle table.
|
||||
connection.execute("INSERT INTO person VALUES ('Bouquet','Carole',62)")
|
||||
connection.execute("INSERT INTO person VALUES ('Connery','Sean',85)")
|
||||
connection.execute("INSERT INTO person VALUES ('Kotto','Yaphet',76)")
|
||||
connection.execute("INSERT INTO person VALUES ('Zhang','Zhang',39)")
|
||||
# Valider l'ajout et les nouvelles modifications en bloc (lorsque la base de données le permet)
|
||||
connection.commit()
|
||||
|
||||
values = connection.execute("SELECT * FROM person WHERE age > 50")
|
||||
print(values.fetchall())
|
||||
# Ne pas oublier à la fin, lorsqu'on en a plus besoin, de fermer la connexion à la base (ou au fichier
|
||||
# pour le cas de SQLite).
|
||||
connection.close()
|
0
source/10-graphical-ui/gui/__init__.py
Normal file
0
source/10-graphical-ui/gui/__init__.py
Normal file
69
source/10-graphical-ui/gui/basecontrols.py
Normal file
69
source/10-graphical-ui/gui/basecontrols.py
Normal file
@ -0,0 +1,69 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtGui import QIcon
|
||||
from PySide6.QtWidgets import QApplication, QPushButton, QVBoxLayout, QMenuBar, QMenu, QAction, \
|
||||
QMainWindow, QWidget
|
||||
|
||||
|
||||
class Window(QMainWindow):
|
||||
"""
|
||||
Classe définissant notre fenêtre principale.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Initialiser chaque nouvelle instance de notre fenêtre.
|
||||
|
||||
Args:
|
||||
parent: Widget qui va contenir notre boîte de dialogue.
|
||||
|
||||
"""
|
||||
super().__init__(parent=parent)
|
||||
self.setWindowTitle("Ma nouvelle fenêtre")
|
||||
# Create widgets
|
||||
self.icon = QIcon.fromTheme("scanner")
|
||||
self.basewidget = QWidget(self)
|
||||
self.edit = QLineEdit("Zone de texte")
|
||||
self.button = QPushButton("Appuyez ici !")
|
||||
self.menubar = QMenuBar(self)
|
||||
self.menu = QMenu(self.menubar, title="Élément de menu principal")
|
||||
self.menuentry = QAction(QIcon.fromTheme("network-wired"), "&Action")
|
||||
self.menu.addAction(self.menuentry)
|
||||
self.menubar.addAction(self.menu.menuAction())
|
||||
# Insérer un objet de mise en page dans notre fenêtre,
|
||||
# et y ajouter nos champ de texte et bouton
|
||||
self.setCentralWidget(self.basewidget)
|
||||
layout = QVBoxLayout(self)
|
||||
self.basewidget.setLayout(layout)
|
||||
self.setMenuBar(self.menubar)
|
||||
layout.addWidget(self.edit)
|
||||
layout.addWidget(self.button)
|
||||
self.setMinimumSize(640, 128)
|
||||
# Connecter le clic du bouton à la méthode "validate_button"
|
||||
self.button.clicked.connect(self.validate_button)
|
||||
|
||||
def validate_button(self):
|
||||
"""
|
||||
On définit cette méthode pour répondre au clic sur un bouton.
|
||||
|
||||
Il faut que l'on configure notre bouton pour que lorsqu'on clique
|
||||
dessus, cette méthode soit exécutée.
|
||||
|
||||
"""
|
||||
print(f"Vous avez saisi {self.edit.text()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Créer une instance qui définit une application Qt
|
||||
# Via la variable "sys.argv", qui correspond aux arguments passés en ligne de commande
|
||||
# dans une liste de chaînes (ex. python programme.py arg1 arg2 arg3)
|
||||
# on peut configurer le comportement de notre application...
|
||||
# Également : notre application est un Singleton
|
||||
application = QApplication(sys.argv)
|
||||
# Ajouter notre boîte de dialogue
|
||||
window = Window()
|
||||
window.show()
|
||||
# Lancer la boucle Qt, qui ferme le programme quand on ferme toutes les fenêtres
|
||||
application.exec_()
|
||||
sys.exit()
|
27
source/10-graphical-ui/gui/basecreator.py
Normal file
27
source/10-graphical-ui/gui/basecreator.py
Normal file
@ -0,0 +1,27 @@
|
||||
import sys
|
||||
|
||||
from PySide6.QtCore import QFile, QObject
|
||||
|
||||
QObject
|
||||
|
||||
# Exemple avec QT Designer
|
||||
if __name__ == "__main__":
|
||||
# Créer une instance qui définit une application Qt
|
||||
# Via la variable "sys.argv", qui correspond aux arguments en ligne de commande
|
||||
# on peut configurer le comportement de notre application...
|
||||
# Également : notre application est un Singleton
|
||||
application = QApplication(sys.argv)
|
||||
# Utiliser Qt pour ouvrir le fichier Qt Designer qu'on a créé
|
||||
file = QFile("files/base-ui.ui")
|
||||
file.open(QFile.ReadOnly)
|
||||
# Utiliser une classe de Qt qui est capable de créer des interfaces depuis des fichiers
|
||||
loader = QUiLoader()
|
||||
window = loader.load(file)
|
||||
file.close()
|
||||
# Ici on teste que l'on a bien accès aux variables membres du fichier qu'on a chargé
|
||||
window.lineEdit: QLineEdit = window.lineEdit
|
||||
window.lineEdit.setText("Bonjour les amis")
|
||||
# Afficher notre fenêtre chargée
|
||||
window.show()
|
||||
# Lancer la boucle Qt, qui ferme le programme quand on ferme toutes les fenêtres
|
||||
sys.exit(application.exec_())
|
34
source/10-graphical-ui/gui/basedialog.py
Normal file
34
source/10-graphical-ui/gui/basedialog.py
Normal file
@ -0,0 +1,34 @@
|
||||
import sys
|
||||
|
||||
from PySide2.QtWidgets import QDialog, QApplication
|
||||
|
||||
|
||||
class Dialog(QDialog):
|
||||
"""
|
||||
Classe définissant notre fenêtre principale.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Initialiser chaque nouvelle instance de notre fenêtre.
|
||||
|
||||
Args:
|
||||
parent: Widget qui va contenir notre boîte de dialogue.
|
||||
|
||||
"""
|
||||
super().__init__(parent=parent)
|
||||
self.setWindowTitle("Ma nouvelle fenêtre")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Créer une instance qui définit une application Qt
|
||||
# Via la variable "sys.argv", qui correspond aux arguments en ligne de commande
|
||||
# on peut configurer le comportement de notre application...
|
||||
# Également : notre application est un Singleton
|
||||
application = QApplication(sys.argv)
|
||||
# Ajouter notre boîte de dialogue
|
||||
dialog = Dialog()
|
||||
dialog.show()
|
||||
# Lancer la boucle Qt, qui ferme le programme quand on ferme toutes les fenêtres
|
||||
sys.exit(application.exec_())
|
91
source/10-graphical-ui/gui/files/base-ui.ui
Normal file
91
source/10-graphical-ui/gui/files/base-ui.ui
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>MainWindow</class>
|
||||
<widget class="QMainWindow" name="MainWindow">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>575</width>
|
||||
<height>317</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>MainWindow</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget">
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinimumSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pushButton">
|
||||
<property name="text">
|
||||
<string>PushButton</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lineEdit_2"/>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QMenuBar" name="menubar">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>575</width>
|
||||
<height>31</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuMenu_principal">
|
||||
<property name="title">
|
||||
<string>Menu principal</string>
|
||||
</property>
|
||||
<addaction name="actionAction_1"/>
|
||||
<addaction name="actionAction_2"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAction_3"/>
|
||||
</widget>
|
||||
<addaction name="menuMenu_principal"/>
|
||||
</widget>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
<action name="actionAction_1">
|
||||
<property name="icon">
|
||||
<iconset theme="input-tablet">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Action 1</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAction_2">
|
||||
<property name="icon">
|
||||
<iconset theme="network-wired">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Action 2</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAction_3">
|
||||
<property name="icon">
|
||||
<iconset theme="scanner">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Action 3</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
BIN
source/10-graphical-ui/gui/files/python.png
Normal file
BIN
source/10-graphical-ui/gui/files/python.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Window</class>
|
||||
<widget class="QMainWindow" name="Window">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>800</width>
|
||||
<height>600</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Window</string>
|
||||
</property>
|
||||
<widget class="QWidget" name="centralwidget"/>
|
||||
<widget class="QMenuBar" name="menubar"/>
|
||||
<widget class="QStatusBar" name="statusbar"/>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -0,0 +1,28 @@
|
||||
# This Python file uses the following encoding: utf-8
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
from PySide2.QtWidgets import QApplication, QMainWindow
|
||||
from PySide2.QtCore import QFile
|
||||
from PySide2.QtUiTools import QUiLoader
|
||||
|
||||
|
||||
class Window(QMainWindow):
|
||||
def __init__(self):
|
||||
super(Window, self).__init__()
|
||||
self.load_ui()
|
||||
|
||||
def load_ui(self):
|
||||
loader = QUiLoader()
|
||||
path = os.path.join(os.path.dirname(__file__), "form.ui")
|
||||
ui_file = QFile(path)
|
||||
ui_file.open(QFile.ReadOnly)
|
||||
loader.load(ui_file, self)
|
||||
ui_file.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication([])
|
||||
widget = Window()
|
||||
widget.show()
|
||||
sys.exit(app.exec_())
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"files": ["main.py", "form.ui"]
|
||||
}
|
@ -0,0 +1,196 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE QtCreatorProject>
|
||||
<!-- Written by QtCreator 4.14.0, 2021-02-25T10:34:49. -->
|
||||
<qtcreator>
|
||||
<data>
|
||||
<variable>EnvironmentId</variable>
|
||||
<value type="QByteArray">{f52a1091-6a1a-4c8e-9bfd-fca585155f33}</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.ActiveTarget</variable>
|
||||
<value type="int">0</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.EditorSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="bool" key="EditorConfiguration.AutoIndent">true</value>
|
||||
<value type="bool" key="EditorConfiguration.AutoSpacesForTabs">false</value>
|
||||
<value type="bool" key="EditorConfiguration.CamelCaseNavigation">true</value>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.0">
|
||||
<value type="QString" key="language">Cpp</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">CppGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="EditorConfiguration.CodeStyle.1">
|
||||
<value type="QString" key="language">QmlJS</value>
|
||||
<valuemap type="QVariantMap" key="value">
|
||||
<value type="QByteArray" key="CurrentPreferences">QmlJSGlobal</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
<value type="int" key="EditorConfiguration.CodeStyle.Count">2</value>
|
||||
<value type="QByteArray" key="EditorConfiguration.Codec">UTF-8</value>
|
||||
<value type="bool" key="EditorConfiguration.ConstrainTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.IndentSize">4</value>
|
||||
<value type="bool" key="EditorConfiguration.KeyboardTooltips">false</value>
|
||||
<value type="int" key="EditorConfiguration.MarginColumn">80</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseHiding">true</value>
|
||||
<value type="bool" key="EditorConfiguration.MouseNavigation">true</value>
|
||||
<value type="int" key="EditorConfiguration.PaddingMode">1</value>
|
||||
<value type="bool" key="EditorConfiguration.ScrollWheelZooming">true</value>
|
||||
<value type="bool" key="EditorConfiguration.ShowMargin">false</value>
|
||||
<value type="int" key="EditorConfiguration.SmartBackspaceBehavior">0</value>
|
||||
<value type="bool" key="EditorConfiguration.SmartSelectionChanging">true</value>
|
||||
<value type="bool" key="EditorConfiguration.SpacesForTabs">true</value>
|
||||
<value type="int" key="EditorConfiguration.TabKeyBehavior">0</value>
|
||||
<value type="int" key="EditorConfiguration.TabSize">8</value>
|
||||
<value type="bool" key="EditorConfiguration.UseGlobal">true</value>
|
||||
<value type="int" key="EditorConfiguration.Utf8BomBehavior">1</value>
|
||||
<value type="bool" key="EditorConfiguration.addFinalNewLine">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanIndentation">true</value>
|
||||
<value type="bool" key="EditorConfiguration.cleanWhitespace">true</value>
|
||||
<value type="QString" key="EditorConfiguration.ignoreFileTypes">*.md, *.MD, Makefile</value>
|
||||
<value type="bool" key="EditorConfiguration.inEntireDocument">false</value>
|
||||
<value type="bool" key="EditorConfiguration.skipTrailingWhitespace">true</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.PluginSettings</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<valuemap type="QVariantMap" key="AutoTest.ActiveFrameworks">
|
||||
<value type="bool" key="AutoTest.Framework.Boost">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.Catch">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.GTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtQuickTest">true</value>
|
||||
<value type="bool" key="AutoTest.Framework.QtTest">true</value>
|
||||
</valuemap>
|
||||
<valuemap type="QVariantMap" key="AutoTest.CheckStates"/>
|
||||
<value type="int" key="AutoTest.RunAfterBuild">0</value>
|
||||
<value type="bool" key="AutoTest.UseGlobal">true</value>
|
||||
<valuelist type="QVariantList" key="ClangCodeModel.CustomCommandLineKey"/>
|
||||
<value type="bool" key="ClangCodeModel.UseGlobalConfig">true</value>
|
||||
<value type="QString" key="ClangCodeModel.WarningConfigId">Builtin.Questionable</value>
|
||||
<valuemap type="QVariantMap" key="ClangTools">
|
||||
<value type="bool" key="ClangTools.AnalyzeOpenFiles">true</value>
|
||||
<value type="bool" key="ClangTools.BuildBeforeAnalysis">true</value>
|
||||
<value type="QString" key="ClangTools.DiagnosticConfig">Builtin.DefaultTidyAndClazy</value>
|
||||
<value type="int" key="ClangTools.ParallelJobs">6</value>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedDirs"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SelectedFiles"/>
|
||||
<valuelist type="QVariantList" key="ClangTools.SuppressedDiagnostics"/>
|
||||
<value type="bool" key="ClangTools.UseGlobalSettings">true</value>
|
||||
</valuemap>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Target.0</variable>
|
||||
<valuemap type="QVariantMap">
|
||||
<value type="QString" key="DeviceType">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Desktop</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">{d044ec2e-da82-4493-a678-1f0961b658b1}</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveBuildConfiguration">-1</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveDeployConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.ActiveRunConfiguration">0</value>
|
||||
<value type="int" key="ProjectExplorer.Target.BuildConfigurationCount">0</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.DeployConfiguration.0">
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.BuildConfiguration.BuildStepList.0">
|
||||
<value type="int" key="ProjectExplorer.BuildStepList.StepsCount">0</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DefaultDisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">Deploy</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.BuildSteps.Deploy</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.BuildConfiguration.BuildStepListCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.DeployConfiguration.CustomData"/>
|
||||
<value type="bool" key="ProjectExplorer.DeployConfiguration.CustomDataEnabled">false</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">ProjectExplorer.DefaultDeployConfiguration</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.DeployConfigurationCount">1</value>
|
||||
<valuemap type="QVariantMap" key="ProjectExplorer.Target.RunConfiguration.0">
|
||||
<value type="QString" key="Analyzer.Perf.CallgraphMode">dwarf</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Perf.Events">
|
||||
<value type="QString">cpu-cycles</value>
|
||||
</valuelist>
|
||||
<valuelist type="QVariantList" key="Analyzer.Perf.ExtraArguments"/>
|
||||
<value type="int" key="Analyzer.Perf.Frequency">250</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Perf.RecordArguments">
|
||||
<value type="QString">-e</value>
|
||||
<value type="QString">cpu-cycles</value>
|
||||
<value type="QString">--call-graph</value>
|
||||
<value type="QString">dwarf,4096</value>
|
||||
<value type="QString">-F</value>
|
||||
<value type="QString">250</value>
|
||||
</valuelist>
|
||||
<value type="QString" key="Analyzer.Perf.SampleMode">-F</value>
|
||||
<value type="bool" key="Analyzer.Perf.Settings.UseGlobalSettings">true</value>
|
||||
<value type="int" key="Analyzer.Perf.StackSize">4096</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.AggregateTraces">false</value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.FlushEnabled">false</value>
|
||||
<value type="uint" key="Analyzer.QmlProfiler.FlushInterval">1000</value>
|
||||
<value type="QString" key="Analyzer.QmlProfiler.LastTraceFile"></value>
|
||||
<value type="bool" key="Analyzer.QmlProfiler.Settings.UseGlobalSettings">true</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.AddedSuppressionFiles"/>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectBusEvents">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.CollectSystime">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableBranchSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableCacheSim">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Callgrind.EnableEventToolTips">true</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.MinimumCostRatio">0.01</value>
|
||||
<value type="double" key="Analyzer.Valgrind.Callgrind.VisualisationMinimumCostRatio">10</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.FilterExternalIssues">true</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.KCachegrindExecutable">kcachegrind</value>
|
||||
<value type="int" key="Analyzer.Valgrind.LeakCheckOnFinish">1</value>
|
||||
<value type="int" key="Analyzer.Valgrind.NumCallers">25</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.RemovedSuppressionFiles"/>
|
||||
<value type="int" key="Analyzer.Valgrind.SelfModifyingCodeDetection">1</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.Settings.UseGlobalSettings">true</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.ShowReachable">false</value>
|
||||
<value type="bool" key="Analyzer.Valgrind.TrackOrigins">true</value>
|
||||
<value type="QString" key="Analyzer.Valgrind.ValgrindExecutable">valgrind</value>
|
||||
<valuelist type="QVariantList" key="Analyzer.Valgrind.VisibleErrorKinds">
|
||||
<value type="int">0</value>
|
||||
<value type="int">1</value>
|
||||
<value type="int">2</value>
|
||||
<value type="int">3</value>
|
||||
<value type="int">4</value>
|
||||
<value type="int">5</value>
|
||||
<value type="int">6</value>
|
||||
<value type="int">7</value>
|
||||
<value type="int">8</value>
|
||||
<value type="int">9</value>
|
||||
<value type="int">10</value>
|
||||
<value type="int">11</value>
|
||||
<value type="int">12</value>
|
||||
<value type="int">13</value>
|
||||
<value type="int">14</value>
|
||||
</valuelist>
|
||||
<valuelist type="QVariantList" key="CustomOutputParsers"/>
|
||||
<value type="int" key="PE.EnvironmentAspect.Base">2</value>
|
||||
<valuelist type="QVariantList" key="PE.EnvironmentAspect.Changes"/>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.DisplayName">main</value>
|
||||
<value type="QString" key="ProjectExplorer.ProjectConfiguration.Id">PythonEditor.RunConfiguration./home/steve/Code/python/initiation/source/gui/files/qtcreatordemo/basewindow/main.py</value>
|
||||
<value type="QString" key="ProjectExplorer.RunConfiguration.BuildKey">/home/steve/Code/python/initiation/source/gui/files/qtcreatordemo/basewindow/main.py</value>
|
||||
<value type="QString" key="PythonEditor.RunConfiguation.Interpreter">{9e6235fb-9067-40a7-be7d-a5b33f406c75}</value>
|
||||
<value type="QString" key="PythonEditor.RunConfiguation.Script">/home/steve/Code/python/initiation/source/gui/files/qtcreatordemo/basewindow/main.py</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseCppDebuggerAuto">true</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebugger">false</value>
|
||||
<value type="bool" key="RunConfiguration.UseQmlDebuggerAuto">true</value>
|
||||
<value type="QString" key="RunConfiguration.WorkingDirectory.default">/home/steve/Code/python/initiation/source/gui/files/qtcreatordemo/basewindow</value>
|
||||
</valuemap>
|
||||
<value type="int" key="ProjectExplorer.Target.RunConfigurationCount">1</value>
|
||||
</valuemap>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.TargetCount</variable>
|
||||
<value type="int">1</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>ProjectExplorer.Project.Updater.FileVersion</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
<data>
|
||||
<variable>Version</variable>
|
||||
<value type="int">22</value>
|
||||
</data>
|
||||
</qtcreator>
|
20
source/10-graphical-ui/gui/fileselector.py
Normal file
20
source/10-graphical-ui/gui/fileselector.py
Normal file
@ -0,0 +1,20 @@
|
||||
"""
|
||||
Example code to select file using a file dialog.
|
||||
|
||||
Needs a Qt Application object and then a File dialog object.
|
||||
The `getOpenFileName()` method on the file dialog object opens
|
||||
a dialog for loading a single file. It returns a tuple, containing
|
||||
the selected file name (str) and the filter (str).
|
||||
If no file was selected (cancelled), you get empty strings in the tuple.
|
||||
"""
|
||||
from PySide6.QtWidgets import QFileDialog, QApplication
|
||||
|
||||
application = QApplication()
|
||||
# You don't need application.exec() here because the method blocks the python script execution.
|
||||
dialog = QFileDialog(directory="/home/steve", caption="Sélectionnez un fichier", filter="Text files (*.txt);; All files (*.*)")
|
||||
selection: tuple[str, str] = dialog.getOpenFileName()
|
||||
print(selection)
|
||||
|
||||
with open("/home/steve/Code/training/python/beginner/documentation/12-logging.md", "r") as file:
|
||||
lines = file.readlines()
|
||||
print(lines)
|
86
source/10-graphical-ui/gui/menucontrols.py
Normal file
86
source/10-graphical-ui/gui/menucontrols.py
Normal file
@ -0,0 +1,86 @@
|
||||
import sys
|
||||
|
||||
from PySide2.QtGui import QIcon
|
||||
from PySide2.QtWidgets import QApplication, QLineEdit, QPushButton, QVBoxLayout, QAction, \
|
||||
QMainWindow, QWidget
|
||||
|
||||
|
||||
class Dialog(QMainWindow):
|
||||
"""
|
||||
Classe définissant notre fenêtre principale.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, parent=None):
|
||||
"""
|
||||
Initialiser chaque nouvelle instance de notre fenêtre.
|
||||
|
||||
Args:
|
||||
parent: Widget qui va contenir notre boîte de dialogue.
|
||||
|
||||
"""
|
||||
super().__init__(parent=parent)
|
||||
# Changer le titre de la fenêtre et changer la position et taille
|
||||
self.setWindowTitle("Ma nouvelle fenêtre")
|
||||
self.setGeometry(50, 50, 640, 480)
|
||||
# Définir l'icône pour la fenêtre
|
||||
self.setWindowIcon(QIcon.fromTheme("exit"))
|
||||
# On crée manuellement des contrôles dans notre fenêtre et des menus
|
||||
self.edit = QLineEdit("Zone de texte")
|
||||
self.button = QPushButton("Appuyez ici !")
|
||||
self.menuentry1 = QAction(QIcon.fromTheme("exit"), "&Fin")
|
||||
self.menuentry2 = QAction(QIcon.fromTheme("sleep"), "&Dodo")
|
||||
self.filemenu = self.menuBar().addMenu("&File")
|
||||
self.filemenu.addAction(self.menuentry1)
|
||||
self.filemenu.addSeparator()
|
||||
self.filemenu.addAction(self.menuentry2)
|
||||
# Insérer un objet de mise en page dans notre fenêtre,
|
||||
# et y ajouter nos champ de texte et bouton
|
||||
frame = QWidget()
|
||||
layout = QVBoxLayout()
|
||||
layout.addWidget(self.edit)
|
||||
layout.addWidget(self.button)
|
||||
# Ajouter la mise en page au widget
|
||||
frame.setLayout(layout)
|
||||
# Définir le widget comme le contenu de la fenêtre
|
||||
self.setCentralWidget(frame)
|
||||
# Connecter le clic du bouton à la méthode "validate_button"
|
||||
self.button.clicked.connect(self.button_clicked)
|
||||
# Connecter la validation du menu1 à la même méthode
|
||||
self.menuentry1.triggered.connect(self.button_clicked)
|
||||
# Connecter la modification du champ de texte à une méthode
|
||||
self.edit.textEdited.connect(self.text_modified)
|
||||
|
||||
def button_clicked(self):
|
||||
"""
|
||||
On définit cette méthode pour répondre au clic sur un bouton.
|
||||
|
||||
Il faut que l'on configure notre bouton pour que lorsqu'on clique
|
||||
dessus, cette méthode soit exécutée.
|
||||
|
||||
"""
|
||||
print(f"Vous avez saisi {self.edit.text()}")
|
||||
|
||||
def text_modified(self):
|
||||
"""
|
||||
On définit cette méthode pour répondre à la modification du champ texte.
|
||||
|
||||
Cette méthode n'est pas spécifique à Qt, c'est juste qu'on peut lier
|
||||
des événements de contrôles Qt à des fonctions arbitraires. Et on
|
||||
va lier l'édition du champ de texte à cette méthode-ci.
|
||||
|
||||
"""
|
||||
print(f"{self.edit.text()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Créer une instance qui définit une application Qt
|
||||
# Via la variable "sys.argv", qui correspond aux arguments en ligne de commande
|
||||
# on peut configurer le comportement de notre application...
|
||||
# Également : notre application est un Singleton
|
||||
application = QApplication(sys.argv)
|
||||
# Ajouter notre boîte de dialogue
|
||||
dialog = Dialog()
|
||||
dialog.show()
|
||||
# Lancer la boucle Qt, qui ferme le programme quand on ferme toutes les fenêtres
|
||||
sys.exit(application.exec_())
|
0
source/12-logging/logs/__init__.py
Normal file
0
source/12-logging/logs/__init__.py
Normal file
45
source/12-logging/logs/base.py
Normal file
45
source/12-logging/logs/base.py
Normal file
@ -0,0 +1,45 @@
|
||||
import logging
|
||||
import sys
|
||||
|
||||
|
||||
def logging_without_formatting():
|
||||
"""
|
||||
La base de la journalisation.
|
||||
|
||||
"""
|
||||
logger = logging.getLogger("noformat")
|
||||
logger.debug("This is a debug message")
|
||||
logger.info("This is an info message")
|
||||
logger.warning("This is a warning message")
|
||||
logger.error("This is an error message")
|
||||
logger.critical("This is a critical message")
|
||||
|
||||
|
||||
def logging_with_formatting():
|
||||
"""
|
||||
Configurer un peu notre journalisation pour mettre en forme nos messages.
|
||||
|
||||
"""
|
||||
logger = logging.getLogger("withformat")
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
# Créer une nouvelle configuration qui sort les messages de journal dans la console
|
||||
handler = logging.StreamHandler(sys.stdout)
|
||||
# Configurer cet objet pour utiliser notre format
|
||||
handler.setFormatter(formatter)
|
||||
# Configurer le handler pour afficher aussi les messages DEBUG et INFO
|
||||
handler.setLevel(logging.DEBUG)
|
||||
# Assigner notre configuration à notre logger
|
||||
# On doit faire tout ça parce qu'il n'y a pas de méthode
|
||||
# pour facilement définir une chaîne de formatage pour un logger
|
||||
logger.addHandler(handler)
|
||||
logger.warning("Message d'avertissement")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Par défaut, Python n'affiche des messages de journalisation
|
||||
# que s'ils excèdent une certaine sévérité.
|
||||
# Dans la plupart des langages, par défaut, c'est la sévérité
|
||||
# WARNING (en-dessous, on n'indique généralement pas de problème)
|
||||
# qui est la plus basse prise en compte
|
||||
logging_without_formatting()
|
||||
logging_with_formatting()
|
43
source/12-logging/logs/files.py
Normal file
43
source/12-logging/logs/files.py
Normal file
@ -0,0 +1,43 @@
|
||||
import logging
|
||||
|
||||
|
||||
def logging_with_formatting():
|
||||
"""
|
||||
Configurer un peu notre journalisation pour mettre en forme nos messages.
|
||||
|
||||
Nous devons utiliser un logger et le configurer pour savoir comment afficher nos
|
||||
messages.
|
||||
|
||||
Un logger peut utiliser plusieurs handlers à la fois, et chacun a pour rôle
|
||||
de transporter le message original vers une destination (fichier, console etc.)
|
||||
|
||||
Les handlers sont configurés en deux étapes : d'abord on choisit le type de
|
||||
handler (fichier, console, etc.), et ensuite on lui associe un "formateur" pour
|
||||
savoir comment stocker le message.
|
||||
|
||||
Notez : les loggers peuvent avoir un nom, et peuvent ainsi être retrouvés n'importe où
|
||||
via leur nom pendant l'exécution de votre script, en utilisant la fonction `getLogger`.
|
||||
|
||||
"""
|
||||
logger = logging.getLogger("withformat")
|
||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||
# Créer une nouvelle configuration qui sort les messages de journal dans la console
|
||||
handler = logging.FileHandler("demo.log")
|
||||
# Configurer cet objet pour utiliser notre format
|
||||
handler.setFormatter(formatter)
|
||||
# Et accessoirement, changer le niveau minimal de sévérité
|
||||
handler.setLevel(logging.INFO)
|
||||
# Assigner notre configuration à notre logger
|
||||
# On doit faire tout ça parce qu'il n'y a pas de méthode
|
||||
# pour facilement définir une chaîne de formatage pour un logger
|
||||
logger.addHandler(handler)
|
||||
logger.warning("Message d'avertissement")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Par défaut, Python n'affiche des messages de journalisation
|
||||
# que s'ils excèdent une certaine sévérité.
|
||||
# Dans la plupart des langages, par défaut, c'est la sévérité
|
||||
# WARNING (en-dessous, on n'indique généralement pas de problème)
|
||||
# qui est la plus basse prise en compte
|
||||
logging_with_formatting()
|
7
source/99-ansible-test/base.yaml
Normal file
7
source/99-ansible-test/base.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
- hosts: localhost
|
||||
connection: local
|
||||
gather_facts: yes
|
||||
vars:
|
||||
my_name: a_b_c
|
||||
tasks:
|
||||
- debug: msg={{ my_name|replace('_', '-') }}
|
683
source/99-ansible-test/facts.json
Normal file
683
source/99-ansible-test/facts.json
Normal file
@ -0,0 +1,683 @@
|
||||
{
|
||||
"ansible_facts": {
|
||||
"ansible_all_ipv4_addresses": [
|
||||
"172.17.0.1",
|
||||
"192.168.31.36",
|
||||
"192.168.31.165"
|
||||
],
|
||||
"ansible_all_ipv6_addresses": [
|
||||
"fe80::9fb5:601b:d3e9:a68e"
|
||||
],
|
||||
"ansible_apparmor": {
|
||||
"status": "disabled"
|
||||
},
|
||||
"ansible_architecture": "x86_64",
|
||||
"ansible_bios_date": "09/12/2022",
|
||||
"ansible_bios_vendor": "Dell Inc.",
|
||||
"ansible_bios_version": "1.19.0",
|
||||
"ansible_board_asset_tag": "NA",
|
||||
"ansible_board_name": "00K2XV",
|
||||
"ansible_board_serial": "NA",
|
||||
"ansible_board_vendor": "Dell Inc.",
|
||||
"ansible_board_version": "A00",
|
||||
"ansible_chassis_asset_tag": "NA",
|
||||
"ansible_chassis_serial": "NA",
|
||||
"ansible_chassis_vendor": "Dell Inc.",
|
||||
"ansible_chassis_version": "NA",
|
||||
"ansible_cmdline": {
|
||||
"BOOT_IMAGE": "/boot/vmlinuz-6.9-x86_64",
|
||||
"apparmor": "0",
|
||||
"fsck.mode": "skip",
|
||||
"intel_pstate": "active",
|
||||
"loglevel": "3",
|
||||
"nvidia-drm.modeset": "1",
|
||||
"quiet": true,
|
||||
"ro": true,
|
||||
"root": "UUID=ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8",
|
||||
"splash": true,
|
||||
"udev.log_level": "3",
|
||||
"udev.log_priority": "1",
|
||||
"vga": "current"
|
||||
},
|
||||
"ansible_date_time": {
|
||||
"date": "2024-07-11",
|
||||
"day": "11",
|
||||
"epoch": "1720705695",
|
||||
"epoch_int": "1720705695",
|
||||
"hour": "15",
|
||||
"iso8601": "2024-07-11T13:48:15Z",
|
||||
"iso8601_basic": "20240711T154815266191",
|
||||
"iso8601_basic_short": "20240711T154815",
|
||||
"iso8601_micro": "2024-07-11T13:48:15.266191Z",
|
||||
"minute": "48",
|
||||
"month": "07",
|
||||
"second": "15",
|
||||
"time": "15:48:15",
|
||||
"tz": "CEST",
|
||||
"tz_dst": "CEST",
|
||||
"tz_offset": "+0200",
|
||||
"weekday": "jeudi",
|
||||
"weekday_number": "4",
|
||||
"weeknumber": "28",
|
||||
"year": "2024"
|
||||
},
|
||||
"ansible_default_ipv4": {
|
||||
"address": "192.168.31.36",
|
||||
"alias": "eno2",
|
||||
"broadcast": "192.168.31.255",
|
||||
"gateway": "192.168.31.1",
|
||||
"interface": "eno2",
|
||||
"macaddress": "a4:bb:6d:ef:9d:84",
|
||||
"mtu": 1500,
|
||||
"netmask": "255.255.255.0",
|
||||
"network": "192.168.31.0",
|
||||
"prefix": "24",
|
||||
"type": "ether"
|
||||
},
|
||||
"ansible_default_ipv6": {},
|
||||
"ansible_device_links": {
|
||||
"ids": {
|
||||
"mmcblk0": [
|
||||
"mmc-00000_0x15cdb40e"
|
||||
],
|
||||
"mmcblk0p1": [
|
||||
"mmc-00000_0x15cdb40e-part1"
|
||||
],
|
||||
"nvme0n1": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1"
|
||||
],
|
||||
"nvme0n1p1": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part1",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part1",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part1"
|
||||
],
|
||||
"nvme0n1p2": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part2",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part2",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part2"
|
||||
],
|
||||
"nvme0n1p3": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part3",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part3",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part3"
|
||||
]
|
||||
},
|
||||
"labels": {
|
||||
"mmcblk0p1": [
|
||||
"CLEANED"
|
||||
]
|
||||
},
|
||||
"masters": {},
|
||||
"uuids": {
|
||||
"mmcblk0p1": [
|
||||
"588B-5CCE"
|
||||
],
|
||||
"nvme0n1p1": [
|
||||
"7D05-E937"
|
||||
],
|
||||
"nvme0n1p2": [
|
||||
"ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8"
|
||||
],
|
||||
"nvme0n1p3": [
|
||||
"d626c980-e737-484f-ab18-6324704f4d74"
|
||||
]
|
||||
}
|
||||
},
|
||||
"ansible_devices": {
|
||||
"mmcblk0": {
|
||||
"holders": [],
|
||||
"host": "Unassigned class [ff00]: Realtek Semiconductor Co., Ltd. RTS5260 PCI Express Card Reader (rev 01)",
|
||||
"links": {
|
||||
"ids": [
|
||||
"mmc-00000_0x15cdb40e"
|
||||
],
|
||||
"labels": [],
|
||||
"masters": [],
|
||||
"uuids": []
|
||||
},
|
||||
"model": null,
|
||||
"partitions": {
|
||||
"mmcblk0p1": {
|
||||
"holders": [],
|
||||
"links": {
|
||||
"ids": [
|
||||
"mmc-00000_0x15cdb40e-part1"
|
||||
],
|
||||
"labels": [
|
||||
"CLEANED"
|
||||
],
|
||||
"masters": [],
|
||||
"uuids": [
|
||||
"588B-5CCE"
|
||||
]
|
||||
},
|
||||
"sectors": "125794304",
|
||||
"sectorsize": 512,
|
||||
"size": "59.98 GB",
|
||||
"start": "32768",
|
||||
"uuid": "588B-5CCE"
|
||||
}
|
||||
},
|
||||
"removable": "0",
|
||||
"rotational": "0",
|
||||
"sas_address": null,
|
||||
"sas_device_handle": null,
|
||||
"scheduler_mode": "mq-deadline",
|
||||
"sectors": "125827072",
|
||||
"sectorsize": "512",
|
||||
"serial": "0x15cdb40e",
|
||||
"size": "60.00 GB",
|
||||
"support_discard": "4194304",
|
||||
"vendor": null,
|
||||
"virtual": 1
|
||||
},
|
||||
"nvme0n1": {
|
||||
"holders": [],
|
||||
"host": "Non-Volatile memory controller: Toshiba Corporation XG6 NVMe SSD Controller",
|
||||
"links": {
|
||||
"ids": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1"
|
||||
],
|
||||
"labels": [],
|
||||
"masters": [],
|
||||
"uuids": []
|
||||
},
|
||||
"model": "KXG60ZNV512G NVMe KIOXIA 512GB",
|
||||
"partitions": {
|
||||
"nvme0n1p1": {
|
||||
"holders": [],
|
||||
"links": {
|
||||
"ids": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part1",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part1",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part1"
|
||||
],
|
||||
"labels": [],
|
||||
"masters": [],
|
||||
"uuids": [
|
||||
"7D05-E937"
|
||||
]
|
||||
},
|
||||
"sectors": "614400",
|
||||
"sectorsize": 512,
|
||||
"size": "300.00 MB",
|
||||
"start": "4096",
|
||||
"uuid": "7D05-E937"
|
||||
},
|
||||
"nvme0n1p2": {
|
||||
"holders": [],
|
||||
"links": {
|
||||
"ids": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part2",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part2",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part2"
|
||||
],
|
||||
"labels": [],
|
||||
"masters": [],
|
||||
"uuids": [
|
||||
"ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8"
|
||||
]
|
||||
},
|
||||
"sectors": "981133465",
|
||||
"sectorsize": 512,
|
||||
"size": "467.84 GB",
|
||||
"start": "618496",
|
||||
"uuid": "ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8"
|
||||
},
|
||||
"nvme0n1p3": {
|
||||
"holders": [],
|
||||
"links": {
|
||||
"ids": [
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L-part3",
|
||||
"nvme-KXG60ZNV512G_NVMe_KIOXIA_512GB_40LA75CBK81L_1-part3",
|
||||
"nvme-eui.00000000000000018ce38e030035c0c1-part3"
|
||||
],
|
||||
"labels": [],
|
||||
"masters": [],
|
||||
"uuids": [
|
||||
"d626c980-e737-484f-ab18-6324704f4d74"
|
||||
]
|
||||
},
|
||||
"sectors": "18454939",
|
||||
"sectorsize": 512,
|
||||
"size": "8.80 GB",
|
||||
"start": "981751961",
|
||||
"uuid": "d626c980-e737-484f-ab18-6324704f4d74"
|
||||
}
|
||||
},
|
||||
"removable": "0",
|
||||
"rotational": "0",
|
||||
"sas_address": null,
|
||||
"sas_device_handle": null,
|
||||
"scheduler_mode": "none",
|
||||
"sectors": "1000215216",
|
||||
"sectorsize": "512",
|
||||
"serial": "40LA75CBK81L",
|
||||
"size": "476.94 GB",
|
||||
"support_discard": "512",
|
||||
"vendor": null,
|
||||
"virtual": 1
|
||||
}
|
||||
},
|
||||
"ansible_distribution": "Archlinux",
|
||||
"ansible_distribution_file_path": "/etc/arch-release",
|
||||
"ansible_distribution_file_variety": "Archlinux",
|
||||
"ansible_distribution_major_version": "24",
|
||||
"ansible_distribution_release": "Wynsdey",
|
||||
"ansible_distribution_version": "24.0.3",
|
||||
"ansible_dns": {
|
||||
"nameservers": [
|
||||
"192.168.31.1"
|
||||
],
|
||||
"search": [
|
||||
"toulouse.dawan.fr"
|
||||
]
|
||||
},
|
||||
"ansible_docker0": {
|
||||
"active": false,
|
||||
"device": "docker0",
|
||||
"id": "8000.0242ec8e49bf",
|
||||
"interfaces": [],
|
||||
"ipv4": {
|
||||
"address": "172.17.0.1",
|
||||
"broadcast": "172.17.255.255",
|
||||
"netmask": "255.255.0.0",
|
||||
"network": "172.17.0.0",
|
||||
"prefix": "16"
|
||||
},
|
||||
"macaddress": "02:42:ec:8e:49:bf",
|
||||
"mtu": 1500,
|
||||
"promisc": false,
|
||||
"speed": -1,
|
||||
"stp": false,
|
||||
"type": "bridge"
|
||||
},
|
||||
"ansible_domain": "",
|
||||
"ansible_effective_group_id": 1000,
|
||||
"ansible_effective_user_id": 1000,
|
||||
"ansible_eno2": {
|
||||
"active": true,
|
||||
"device": "eno2",
|
||||
"ipv4": {
|
||||
"address": "192.168.31.36",
|
||||
"broadcast": "192.168.31.255",
|
||||
"netmask": "255.255.255.0",
|
||||
"network": "192.168.31.0",
|
||||
"prefix": "24"
|
||||
},
|
||||
"macaddress": "a4:bb:6d:ef:9d:84",
|
||||
"module": "e1000e",
|
||||
"mtu": 1500,
|
||||
"pciid": "0000:00:1f.6",
|
||||
"promisc": false,
|
||||
"speed": 1000,
|
||||
"type": "ether"
|
||||
},
|
||||
"ansible_env": {
|
||||
"AUTOJUMP_ERROR_PATH": "/home/steve/.local/share/autojump/errors.log",
|
||||
"AUTOJUMP_SOURCED": "1",
|
||||
"DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus",
|
||||
"DEBUGINFOD_URLS": "https://debuginfod.archlinux.org ",
|
||||
"DESKTOP_SESSION": "gnome",
|
||||
"DESKTOP_STARTUP_ID": "gnome-shell/PyCharm Professional Edition/4132-3-skdell_TIME4966895",
|
||||
"DISPLAY": ":1",
|
||||
"EDITOR": "/usr/bin/micro",
|
||||
"GDMSESSION": "gnome",
|
||||
"GDM_LANG": "fr_FR.UTF-8",
|
||||
"GIO_LAUNCHED_DESKTOP_FILE": "/home/steve/.local/share/applications/jetbrains-pycharm.desktop",
|
||||
"GIO_LAUNCHED_DESKTOP_FILE_PID": "111302",
|
||||
"GJS_DEBUG_OUTPUT": "stderr",
|
||||
"GJS_DEBUG_TOPICS": "JS ERROR;JS LOG",
|
||||
"GNUPGHOME": "/home/steve/.local/share/gnupg",
|
||||
"GTK3_MODULES": "xapp-gtk3-module",
|
||||
"HOME": "/home/steve",
|
||||
"INVOCATION_ID": "0dadf7989e324b038ebd153569baec41",
|
||||
"IPYTHONDIR": "/home/steve/.local/share/ipython",
|
||||
"JEDITERM_SOURCE_ARGS": "",
|
||||
"JOURNAL_STREAM": "9:12207",
|
||||
"LANG": "fr_FR.UTF-8",
|
||||
"LOGNAME": "steve",
|
||||
"MAIL": "/var/spool/mail/steve",
|
||||
"MANAGERPID": "3884",
|
||||
"MEMORY_PRESSURE_WATCH": "/sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/session.slice/org.gnome.Shell@x11.service/memory.pressure",
|
||||
"MEMORY_PRESSURE_WRITE": "c29tZSAyMDAwMDAgMjAwMDAwMAA=",
|
||||
"MOTD_SHOWN": "pam",
|
||||
"NODE_PATH": "/home/steve/.local/share/node",
|
||||
"NODE_REPL_HISTORY": "/home/steve/.cache/node_history",
|
||||
"OMF_CONFIG": "/home/steve/.config/omf",
|
||||
"OMF_PATH": "/home/steve/.local/share/omf",
|
||||
"PATH": "/home/steve/Code/python/environments/ansible/bin:/home/steve/.pyenv/shims:/home/steve/.local/bin:~/.pyenv/bin:/usr/local/bin:~/.pyenv/bin:/usr/bin:~/.pyenv/bin:/usr/local/sbin:~/.pyenv/bin:/var/lib/flatpak/exports/bin:~/.pyenv/bin:/usr/lib/jvm/default/bin:~/.pyenv/bin:/usr/bin/site_perl:~/.pyenv/bin:/usr/bin/vendor_perl:~/.pyenv/bin:/usr/bin/core_perl:~/.pyenv/bin",
|
||||
"PWD": "/home/steve/Code/training/python/beginner/source/99-ansible-test",
|
||||
"PYENV_SHELL": "fish",
|
||||
"QT_AUTO_SCREEN_SCALE_FACTOR": "1",
|
||||
"QT_IM_MODULE": "ibus",
|
||||
"QT_STYLE_OVERRIDE": "qt6ct",
|
||||
"SESSION_MANAGER": "local/skdell:@/tmp/.ICE-unix/4103,unix/skdell:/tmp/.ICE-unix/4103",
|
||||
"SHELL": "/usr/bin/fish",
|
||||
"SHLVL": "2",
|
||||
"SSH_AUTH_SOCK": "/run/user/1000/gcr/ssh",
|
||||
"SYSTEMD_EXEC_PID": "4132",
|
||||
"TERM": "xterm-256color",
|
||||
"TERMINAL_EMULATOR": "JetBrains-JediTerm",
|
||||
"TERM_SESSION_ID": "017aefd1-69ab-4952-8dd0-0333292a71e6",
|
||||
"USER": "steve",
|
||||
"USERNAME": "steve",
|
||||
"VIRTUAL_ENV": "/home/steve/Code/python/environments/ansible",
|
||||
"VIRTUAL_ENV_DISABLE_PROMPT": "1",
|
||||
"VIRTUAL_ENV_PROMPT": "ansible",
|
||||
"VSCODE_EXTENSIONS": "/home/steve/.local/share/vscode/extensions",
|
||||
"WINDOWPATH": "2",
|
||||
"WINEPREFIX": "/home/steve/.local/share/wine",
|
||||
"XAUTHORITY": "/run/user/1000/gdm/Xauthority",
|
||||
"XDG_ACTIVATION_TOKEN": "gnome-shell/PyCharm Professional Edition/4132-3-skdell_TIME4966895",
|
||||
"XDG_CURRENT_DESKTOP": "GNOME",
|
||||
"XDG_DATA_DIRS": "/home/steve/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share/:/usr/share/",
|
||||
"XDG_MENU_PREFIX": "gnome-",
|
||||
"XDG_RUNTIME_DIR": "/run/user/1000",
|
||||
"XDG_SESSION_CLASS": "user",
|
||||
"XDG_SESSION_DESKTOP": "gnome",
|
||||
"XDG_SESSION_TYPE": "x11",
|
||||
"XMODIFIERS": "@im=ibus",
|
||||
"_": "/home/steve/Code/python/environments/ansible/bin/python",
|
||||
"_OLD_VIRTUAL_PATH": "/home/steve/.pyenv/shims:/home/steve/.local/bin:~/.pyenv/bin:/usr/local/bin:~/.pyenv/bin:/usr/bin:~/.pyenv/bin:/usr/local/sbin:~/.pyenv/bin:/var/lib/flatpak/exports/bin:~/.pyenv/bin:/usr/lib/jvm/default/bin:~/.pyenv/bin:/usr/bin/site_perl:~/.pyenv/bin:/usr/bin/vendor_perl:~/.pyenv/bin:/usr/bin/core_perl:~/.pyenv/bin"
|
||||
},
|
||||
"ansible_fibre_channel_wwn": [],
|
||||
"ansible_fips": false,
|
||||
"ansible_form_factor": "Notebook",
|
||||
"ansible_fqdn": "skdell",
|
||||
"ansible_hostname": "skdell",
|
||||
"ansible_hostnqn": "",
|
||||
"ansible_interfaces": [
|
||||
"lo",
|
||||
"eno2",
|
||||
"docker0",
|
||||
"wlo1"
|
||||
],
|
||||
"ansible_is_chroot": false,
|
||||
"ansible_iscsi_iqn": "",
|
||||
"ansible_kernel": "6.9.8-1-MANJARO",
|
||||
"ansible_kernel_version": "#1 SMP PREEMPT_DYNAMIC Sat Jul 6 05:51:22 UTC 2024",
|
||||
"ansible_lo": {
|
||||
"active": true,
|
||||
"device": "lo",
|
||||
"ipv4": {
|
||||
"address": "127.0.0.1",
|
||||
"broadcast": "",
|
||||
"netmask": "255.0.0.0",
|
||||
"network": "127.0.0.0",
|
||||
"prefix": "8"
|
||||
},
|
||||
"ipv6": [
|
||||
{
|
||||
"address": "::1",
|
||||
"prefix": "128",
|
||||
"scope": "host"
|
||||
}
|
||||
],
|
||||
"mtu": 65536,
|
||||
"promisc": false,
|
||||
"type": "loopback"
|
||||
},
|
||||
"ansible_loadavg": {
|
||||
"15m": 2.26904296875,
|
||||
"1m": 2.1435546875,
|
||||
"5m": 2.521484375
|
||||
},
|
||||
"ansible_local": {},
|
||||
"ansible_locally_reachable_ips": {
|
||||
"ipv4": [
|
||||
"127.0.0.0/8",
|
||||
"127.0.0.1",
|
||||
"172.17.0.1",
|
||||
"192.168.31.36",
|
||||
"192.168.31.165"
|
||||
],
|
||||
"ipv6": [
|
||||
"::1",
|
||||
"fe80::9fb5:601b:d3e9:a68e"
|
||||
]
|
||||
},
|
||||
"ansible_lsb": {
|
||||
"codename": "Wynsdey",
|
||||
"description": "Manjaro Linux",
|
||||
"id": "ManjaroLinux",
|
||||
"major_release": "24",
|
||||
"release": "24.0.3"
|
||||
},
|
||||
"ansible_lvm": "N/A",
|
||||
"ansible_machine": "x86_64",
|
||||
"ansible_machine_id": "a1c1d092794f4871a548c9775dd5337c",
|
||||
"ansible_memfree_mb": 389,
|
||||
"ansible_memory_mb": {
|
||||
"nocache": {
|
||||
"free": 11746,
|
||||
"used": 20055
|
||||
},
|
||||
"real": {
|
||||
"free": 389,
|
||||
"total": 31801,
|
||||
"used": 31412
|
||||
},
|
||||
"swap": {
|
||||
"cached": 0,
|
||||
"free": 9010,
|
||||
"total": 9011,
|
||||
"used": 1
|
||||
}
|
||||
},
|
||||
"ansible_memtotal_mb": 31801,
|
||||
"ansible_mounts": [
|
||||
{
|
||||
"block_available": 42256932,
|
||||
"block_size": 4096,
|
||||
"block_total": 120436125,
|
||||
"block_used": 78179193,
|
||||
"device": "/dev/nvme0n1p2",
|
||||
"dump": 0,
|
||||
"fstype": "ext4",
|
||||
"inode_available": 28418923,
|
||||
"inode_total": 30662656,
|
||||
"inode_used": 2243733,
|
||||
"mount": "/",
|
||||
"options": "rw,noatime",
|
||||
"passno": 0,
|
||||
"size_available": 173084393472,
|
||||
"size_total": 493306368000,
|
||||
"uuid": "ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8"
|
||||
},
|
||||
{
|
||||
"block_available": 57918,
|
||||
"block_size": 4096,
|
||||
"block_total": 76646,
|
||||
"block_used": 18728,
|
||||
"device": "/dev/nvme0n1p1",
|
||||
"dump": 0,
|
||||
"fstype": "vfat",
|
||||
"inode_available": 0,
|
||||
"inode_total": 0,
|
||||
"inode_used": 0,
|
||||
"mount": "/boot/efi",
|
||||
"options": "rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=mixed,utf8,errors=remount-ro",
|
||||
"passno": 0,
|
||||
"size_available": 237232128,
|
||||
"size_total": 313942016,
|
||||
"uuid": "7D05-E937"
|
||||
},
|
||||
{
|
||||
"block_available": 1964168,
|
||||
"block_size": 32768,
|
||||
"block_total": 1965055,
|
||||
"block_used": 887,
|
||||
"device": "/dev/mmcblk0p1",
|
||||
"dump": 0,
|
||||
"fstype": "vfat",
|
||||
"inode_available": 0,
|
||||
"inode_total": 0,
|
||||
"inode_used": 0,
|
||||
"mount": "/run/media/steve/CLEANED",
|
||||
"options": "rw,nosuid,nodev,relatime,uid=1000,gid=1000,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,showexec,utf8,flush,errors=remount-ro",
|
||||
"passno": 0,
|
||||
"size_available": 64361857024,
|
||||
"size_total": 64390922240,
|
||||
"uuid": "588B-5CCE"
|
||||
}
|
||||
],
|
||||
"ansible_nodename": "skdell",
|
||||
"ansible_os_family": "Archlinux",
|
||||
"ansible_pkg_mgr": "pacman",
|
||||
"ansible_proc_cmdline": {
|
||||
"BOOT_IMAGE": "/boot/vmlinuz-6.9-x86_64",
|
||||
"apparmor": "0",
|
||||
"fsck.mode": "skip",
|
||||
"intel_pstate": "active",
|
||||
"loglevel": "3",
|
||||
"nvidia-drm.modeset": "1",
|
||||
"quiet": true,
|
||||
"ro": true,
|
||||
"root": "UUID=ae7bbe2b-e356-4f63-bdf6-a6a3463bf1a8",
|
||||
"splash": true,
|
||||
"udev.log_level": "3",
|
||||
"udev.log_priority": "1",
|
||||
"vga": "current"
|
||||
},
|
||||
"ansible_processor": [
|
||||
"0",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"1",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"2",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"3",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"4",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"5",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"6",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"7",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"8",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"9",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"10",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz",
|
||||
"11",
|
||||
"GenuineIntel",
|
||||
"Intel(R) Core(TM) i7-10750H CPU @ 2.60GHz"
|
||||
],
|
||||
"ansible_processor_cores": 6,
|
||||
"ansible_processor_count": 1,
|
||||
"ansible_processor_nproc": 12,
|
||||
"ansible_processor_threads_per_core": 2,
|
||||
"ansible_processor_vcpus": 12,
|
||||
"ansible_product_name": "Precision 7750",
|
||||
"ansible_product_serial": "NA",
|
||||
"ansible_product_uuid": "NA",
|
||||
"ansible_product_version": "NA",
|
||||
"ansible_python": {
|
||||
"executable": "/home/steve/Code/python/environments/ansible/bin/python",
|
||||
"has_sslcontext": true,
|
||||
"type": "cpython",
|
||||
"version": {
|
||||
"major": 3,
|
||||
"micro": 4,
|
||||
"minor": 12,
|
||||
"releaselevel": "final",
|
||||
"serial": 0
|
||||
},
|
||||
"version_info": [
|
||||
3,
|
||||
12,
|
||||
4,
|
||||
"final",
|
||||
0
|
||||
]
|
||||
},
|
||||
"ansible_python_version": "3.12.4",
|
||||
"ansible_real_group_id": 1000,
|
||||
"ansible_real_user_id": 1000,
|
||||
"ansible_selinux": {
|
||||
"status": "Missing selinux Python library"
|
||||
},
|
||||
"ansible_selinux_python_present": false,
|
||||
"ansible_service_mgr": "systemd",
|
||||
"ansible_ssh_host_key_dsa_public": "AAAAB3NzaC1kc3MAAACBAOLJ7UDNOsYU1QL6qk+fSxD8fZDG0miFVd1qcYusCRxCPJwEi7ClIcrTosPusubxC3Vvo7FtYqRpCjwmZaSiI+K+BmhkSK7LKN63XsHSAW+pK5Q5gnmARBrzTCF1pDm3uh/AYS0SISMbgc4+I7EXBKOJ94RQg1FliBpiNiv17zg5AAAAFQDU0YgWYQ55gfCr7ZehXDtm0axs4QAAAIEAlAfHG9dyqSmhAwS1s3mSFLTuG2MU8DruNCzX0dub7ObW8iQS4+FDahSdxCMY+PWwZEJ27SgAzWd5wCpAKFoFgoiOVeVCiRt7QyIkQHR4ENDRxEpzUEBvLLp31vQGwLf9mzHRSWxqfKX7h/ZoRpJpj4LTFcfvI0l1P82aszA11uMAAACAQZMqDsYbX2plxDw9ysbcyqP1hCh8WBwJik2wY/LCiV78JjljlVlkoat1HR1dJOQ+qKxA+EI6wEBhsrB9+waUVwdAJ4Aj1yQZnPUbDnFi+MwtAz0V9M6eNKi6SoxyBpaPPewKk24PF0igsyHPR+P+diQHO+momtiWK1SEO+bcnyQ=",
|
||||
"ansible_ssh_host_key_dsa_public_keytype": "ssh-dss",
|
||||
"ansible_ssh_host_key_ecdsa_public": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBHHzABDnum37ZKXtsmf1FJlIxkdCkj31DnFMlyM34MT52YUqstoXA6+AjbWQZ4q5nDP961ubepBjSXQIk4sNCkA=",
|
||||
"ansible_ssh_host_key_ecdsa_public_keytype": "ecdsa-sha2-nistp256",
|
||||
"ansible_ssh_host_key_ed25519_public": "AAAAC3NzaC1lZDI1NTE5AAAAIM54ly57FFU5JCOPN+G57sTJ4VfJuptq235TAfsFIy6v",
|
||||
"ansible_ssh_host_key_ed25519_public_keytype": "ssh-ed25519",
|
||||
"ansible_ssh_host_key_rsa_public": "AAAAB3NzaC1yc2EAAAADAQABAAABgQDsgT8vRhNuBEmevRRUgsrtJfDXyqvaiShBn4UFUUv/2SS1qcme0UIEoBklQlbIAyUACa3+ZxAw08YXt9UuG/Sd/ykUfXtzsrDMKxQlb1VLFaXLpJ3wQYIbJi3gA3lrXfae+sKPOFl0c2+iuXF8B+zwKednu7qtHZbGsWxL3bRkmoQ1nBYcZwLU/s1EFU3/xRVAoo39aLYN3cB1RT7e2eWgPSB0Kv4emeGZHLjB3DNv36K8rwi8yYAXpiawbhxar5asiJ6S6/AchMofcbUo0wGX+/ArLTAysCu/rlUyA5+mh0hMFvqMc8mDHfg/elHf5K8PpphPKCixGpo5GfAzgeV7OjQOTibI2mKB6RAEYBK7/whNzcM0P2kt0xK2U84g3ppNdlOpqC1c3oOwiHxaHP2/BRkO2vntabcpLRjjWhm6hqMrBPgtRS67fChkrsW6hyQtuS9FGpyCDJpqsPhezve8KNy1A+OHwf4tPZTGflIMNnybYEe2B74Mdhn0aRWFP80=",
|
||||
"ansible_ssh_host_key_rsa_public_keytype": "ssh-rsa",
|
||||
"ansible_swapfree_mb": 9010,
|
||||
"ansible_swaptotal_mb": 9011,
|
||||
"ansible_system": "Linux",
|
||||
"ansible_system_capabilities": [
|
||||
""
|
||||
],
|
||||
"ansible_system_capabilities_enforced": "True",
|
||||
"ansible_system_vendor": "Dell Inc.",
|
||||
"ansible_uptime_seconds": 29357,
|
||||
"ansible_user_dir": "/home/steve",
|
||||
"ansible_user_gecos": "Steve Kossouho",
|
||||
"ansible_user_gid": 1000,
|
||||
"ansible_user_id": "steve",
|
||||
"ansible_user_shell": "/usr/bin/fish",
|
||||
"ansible_user_uid": 1000,
|
||||
"ansible_userspace_architecture": "x86_64",
|
||||
"ansible_userspace_bits": "64",
|
||||
"ansible_virtualization_role": "host",
|
||||
"ansible_virtualization_tech_guest": [],
|
||||
"ansible_virtualization_tech_host": [
|
||||
"kvm",
|
||||
"virtualbox"
|
||||
],
|
||||
"ansible_virtualization_type": "kvm",
|
||||
"ansible_wlo1": {
|
||||
"active": true,
|
||||
"device": "wlo1",
|
||||
"ipv4": {
|
||||
"address": "192.168.31.165",
|
||||
"broadcast": "192.168.31.255",
|
||||
"netmask": "255.255.255.0",
|
||||
"network": "192.168.31.0",
|
||||
"prefix": "24"
|
||||
},
|
||||
"ipv6": [
|
||||
{
|
||||
"address": "fe80::9fb5:601b:d3e9:a68e",
|
||||
"prefix": "64",
|
||||
"scope": "link"
|
||||
}
|
||||
],
|
||||
"macaddress": "94:e7:0b:b6:cc:81",
|
||||
"module": "iwlwifi",
|
||||
"mtu": 1500,
|
||||
"pciid": "0000:00:14.3",
|
||||
"promisc": false,
|
||||
"type": "ether"
|
||||
},
|
||||
"gather_subset": [
|
||||
"all"
|
||||
],
|
||||
"module_setup": true
|
||||
},
|
||||
"changed": false
|
||||
}
|
2
source/TODO.txt
Normal file
2
source/TODO.txt
Normal file
@ -0,0 +1,2 @@
|
||||
08 csv
|
||||
09 sqlite mettre à jour l'exemple
|
44
source/cardgame/card_application.py
Normal file
44
source/cardgame/card_application.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""
|
||||
Fausse bataille avec les mauvaises règles.
|
||||
|
||||
Ce n'est absolument pas comme ça qu'on joue à la bataille en
|
||||
temps normal, mais je n'y avais pas joué depuis 30 ans.
|
||||
Ceci étant, avec des règles simples, on arrive à un truc...exécutable.
|
||||
"""
|
||||
from source.cardgame.cards import Deck, Card
|
||||
|
||||
|
||||
def main():
|
||||
full_deck: Deck = Deck(full=True, shuffled=True)
|
||||
player1_deck: Deck = Deck()
|
||||
player2_deck: Deck = Deck()
|
||||
player1_score: int = 0 # dégueulasse
|
||||
player2_score: int = 0
|
||||
# Choice was to distribute cards from one deck to player hands.
|
||||
# Could be any strategy (and could be better).
|
||||
while full_deck:
|
||||
full_deck.pop_to(player1_deck)
|
||||
full_deck.pop_to(player2_deck)
|
||||
|
||||
while player1_deck:
|
||||
# Show available cards to pick
|
||||
selected_index: int = player1_deck.pick_input()
|
||||
selected_card: Card = player1_deck.pop(selected_index)
|
||||
opponent_card: Card = player2_deck.pop(0)
|
||||
print(f"Player 1 plays {selected_card.full_name}")
|
||||
print(f"Player 2 plays {opponent_card.full_name}")
|
||||
if selected_card < opponent_card:
|
||||
player2_score += 1
|
||||
print("Computer wins this round.")
|
||||
elif selected_card > opponent_card:
|
||||
player1_score += 1
|
||||
print("You won this round.")
|
||||
|
||||
print(f"Player 1 final score: {player1_score}")
|
||||
print(f"Player 2 final score: {player2_score}")
|
||||
if player1_score == player2_score:
|
||||
print("Pathetic!")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
127
source/cardgame/cards.py
Normal file
127
source/cardgame/cards.py
Normal file
@ -0,0 +1,127 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from random import shuffle
|
||||
from typing import Literal
|
||||
|
||||
import inquirer
|
||||
|
||||
|
||||
class Card:
|
||||
"""
|
||||
Standard poker card.
|
||||
|
||||
Attributes:
|
||||
value:
|
||||
Absolute card value, between 2 and 14 (for the ace).
|
||||
symbol (int):
|
||||
Symbol of the card, as an integer.
|
||||
0 is hearts; 1 is diamonds, 2 is clubs and 3 is spades.
|
||||
"""
|
||||
SYMBOLS: dict[int, str] = {0: "Hearts", 1: "Diamonds", 2: "Clubs", 3: "Spades"}
|
||||
VALUES: dict[int, str] = {11: "Jack", 12: "Queen", 13: "King", 14: "Ace"}
|
||||
value: int = 2
|
||||
symbol: Literal[0, 1, 2, 3] = 0
|
||||
|
||||
def __init__(self, value: int, symbol: Literal[0, 1, 2, 3]):
|
||||
self.symbol = symbol
|
||||
self.value = value
|
||||
|
||||
def __lt__(self, other: Card):
|
||||
if isinstance(other, Card):
|
||||
return self.value < other.value
|
||||
raise TypeError(f"Can't compare {type(other)}")
|
||||
|
||||
def __eq__(self, other: Card):
|
||||
if isinstance(other, Card):
|
||||
return self.value < other.value
|
||||
raise TypeError(f"Can't compare {type(other)}")
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} de {self.symbol_name}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Get the name of this card."""
|
||||
return self.VALUES.get(self.value, str(self.value))
|
||||
|
||||
@property
|
||||
def symbol_name(self):
|
||||
"""Get the name of the symbol of this card."""
|
||||
return self.SYMBOLS.get(self.symbol, "N/A")
|
||||
|
||||
@property
|
||||
def full_name(self) -> str:
|
||||
"""Get the full name of this card, value and symbol."""
|
||||
return f"{self.name} of {self.symbol_name}"
|
||||
|
||||
|
||||
class Deck:
|
||||
"""A deck of cards."""
|
||||
|
||||
_cards: list[Card] = None
|
||||
|
||||
def __init__(self, full: bool = False, shuffled: bool = False):
|
||||
"""
|
||||
Initialize a deck with cards.
|
||||
|
||||
Args:
|
||||
full: If True, fill with all possible cards.
|
||||
shuffled: If True, shuffle the content of the deck directly.
|
||||
"""
|
||||
self._cards = []
|
||||
if full is True:
|
||||
for value in range(2, 15):
|
||||
for symbol in range(0, 4):
|
||||
self._cards.append(Card(value, symbol)) # noqa
|
||||
if shuffled is True:
|
||||
self.shuffle()
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
"""
|
||||
Tell if the deck is empty or not.
|
||||
|
||||
Returns:
|
||||
True: if not empty
|
||||
False: otherwise.
|
||||
"""
|
||||
return bool(self._cards)
|
||||
|
||||
def shuffle(self):
|
||||
"""Shuffle the deck so the cards are not ordered by value."""
|
||||
shuffle(self._cards)
|
||||
|
||||
def pop_to(self, other: Deck) -> None:
|
||||
"""Remove last card from deck and add to another deck."""
|
||||
other._cards.append(self._cards.pop())
|
||||
|
||||
def pop(self, index: int) -> Card:
|
||||
"""Get card at index in deck and remove from the deck."""
|
||||
return self._cards.pop(index)
|
||||
|
||||
def display_cards(self) -> None:
|
||||
"""Show available cards with their respective index in the deck."""
|
||||
for index, card in enumerate(self._cards):
|
||||
print(f"{index:02}: {card.full_name}", end=" / ")
|
||||
print()
|
||||
|
||||
def pick_input(self) -> int:
|
||||
"""
|
||||
Ask the user to pick a card from the deck.
|
||||
|
||||
Returns:
|
||||
The selected index for a card.
|
||||
"""
|
||||
q = [inquirer.List("selection", message="Pick a card", choices=self.card_selection, default=0)]
|
||||
return inquirer.prompt(q)["selection"]
|
||||
|
||||
@property
|
||||
def card_selection(self) -> list[tuple[str, int]]:
|
||||
"""
|
||||
Get available cards with their respective index in the deck.
|
||||
|
||||
Returns:
|
||||
A list of tuples, where each tuple contains two elements:
|
||||
- The first element being the card name
|
||||
- The second element being the card index
|
||||
"""
|
||||
return [(c.full_name, index) for index, c in enumerate(self._cards)]
|
4
source/requirements.txt
Normal file
4
source/requirements.txt
Normal file
@ -0,0 +1,4 @@
|
||||
py2puml # conversion Python vers PlantUML
|
||||
pdir2 # pretty printing avancé
|
||||
ipython # console Python avancée
|
||||
pyside6 # Qt Framework
|
Reference in New Issue
Block a user