Aller au contenu

Un mot pour Fahmio

Tu écris. Tu continues.

Un problème, une idée — assez pour qu’on comprenne, puis tu retournes à ta copie.

On lit. Tu n’as rien d’autre à faire.

Où veux-tu aller ?

↑ ↓ pour parcourir · Entrée pour ouvrir · Échap pour fermer

Fahmio
Fahmio
Bac Tunisie
SD-OStructures de données et modularité

Épreuve pratique (préparation) : relier une interface QtDesigner à un module Python — cryptage d'une chaîne par décalage

Idée directrice

Scénario de préparation à l'épreuve pratique : l'écrit national ne le pose jamais. Les séries de lycée enchaînent une partie théorique — l'algorithme d'un module, ici le cryptage d'une chaîne — et une partie pratique sur machine : l'interface dessinée sous QtDesigner est enregistrée en .ui, chargée par loadUi, et le clic du bouton est relié au module Python par clicked.connect. Le module lit les zones de saisie avec text(), contrôle la saisie et écrit le résultat, ou un message d'erreur, dans une étiquette avec setText. L'algorithme ne change pas en passant à l'interface : seules l'entrée (plus de Lire) et la sortie (plus d'Ecrire) changent.

Ce qui aide à reconnaître la démarche

« Réaliser l'interface graphique suivante (sous QtDesigner) » ; « enregistrer-le sous le nom … .ui » ; « appeler l'interface graphique en exploitant l'annexe » ; « afficher le message … via le label » ; « Partie 1 : théorique » / « Partie 2 : pratique »

Besoin d’un point de départ ? Voir la méthode
  • La partie théorique se rédige comme à l'écrit, algorithme et tableau de déclaration ; la partie pratique n'en est que la traduction en Python.
  • Squelette à connaître : deux imports (loadUi, QApplication), app = QApplication([]), windows = loadUi("….ui"), windows.show(), windows.bouton.clicked.connect(module), app.exec_().
  • Lire : windows.zone.text(), qui rend toujours une chaîne ; écrire : windows.etiquette.setText(chaine), en convertissant un nombre avec str.
  • Contrôle de saisie dans une interface : un test au moment du clic et un message dans l'étiquette, jamais une boucle while avec input().
  • Les noms donnés aux objets sous QtDesigner doivent être exactement ceux du script : Python distingue majuscules et minuscules.
Les pièges à vérifier dans ta démarche

Écrire clicked.connect(traiter()) : les parenthèses exécutent le module au lancement et passent son résultat à connect, le clic n'est jamais relié. Oublier les parenthèses de text(), ou appeler int() sur la saisie avant d'avoir vérifié qu'elle est formée de chiffres : int("3a") provoque une erreur à l'exécution. Enregistrer l'interface sous un nom différent de celui passé à loadUi, ou écrire txtmsg au lieu de txtMsg.

Sujet principal

Exercice — partie théorique puis partie pratique (séries de préparation à l'épreuve pratique)

7 questionsFigure fournieCorrigé masqué

On se propose d'écrire un programme intitulé Cryptage qui crypte un message, une chaîne ch formée uniquement de lettres majuscules et d'espaces, de 1 à 50 caractères. Chaque lettre est remplacée par celle qui la suit de p positions dans l'alphabet, avec 1 ≤ p ≤ 25 ; la lettre qui suit "Z" est "A", et le caractère espace ne subit aucune modification. On rappelle que Ord("A") = 65 et Ord("Z") = 90.

Exemple : pour ch = "BAC PRATIQUE" et p = 3, le message crypté est "EDF SUDWLTXH".

Pour la partie pratique, on a réalisé sous QtDesigner l'interface ci-dessous, enregistrée sous le nom cryptage.ui ; le nom de chaque objet est indiqué à sa droite.

  1. Partie 1 — théorique. Écrire l'algorithme d'une fonction Valide(ch) qui retourne Vrai si ch contient entre 1 et 50 caractères, tous des lettres majuscules ou des espaces, et Faux sinon. Dresser le tableau de déclaration des objets locaux.
  2. Écrire l'algorithme d'une fonction Crypter(ch, p) qui retourne le message crypté, et dresser le tableau de déclaration de ses objets locaux.
  3. Donner le résultat de l'appel Crypter("XYZ BAC", 4), en détaillant le calcul pour la lettre "X".
  4. Partie 2 — pratique. Pour chacun des objets txtMsg, txtP, btnCrypter et lblRes, donner sa classe (QLineEdit, QPushButton ou QLabel) et l'instruction Python qui permet de lire son contenu, d'y écrire, ou de le relier à un module.
  5. Écrire en Python les fonctions valide(ch) et crypter(ch, p) correspondant aux algorithmes de la partie 1.
  6. Écrire le module traiter() exécuté lors d'un clic sur le bouton Crypter : il lit le message et la valeur de p, puis affiche dans lblRes le message crypté si la saisie est correcte, et le message "Saisie invalide" sinon.
  7. Compléter le programme principal en exploitant l'annexe ci-dessous, puis donner le texte affiché dans lblRes pour les saisies suivantes : "XYZ BAC" et "4" ; "Bac 2026" et "3" ; "ZOO" et "30".

Annexe

from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication
………………………………
app = QApplication([])
windows = loadUi("nom_fichier.ui")
windows.show()
windows.nom_bouton.clicked.connect(nom_module)
app.exec_()
Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : un parcours qui s'arrête au premier caractère interdit ; la longueur est testée avant tout accès à ch[i].

Fonction Valide (ch : Chaîne) : Booléen
DEBUT
  ok ← (Long(ch) ≥ 1) ET (Long(ch) ≤ 50)
  i ← 0
  Tant que ok ET (i < Long(ch)) Faire
    ok ← (ch[i] ∈ ["A".."Z"]) OU (ch[i] = " ")
    i ← i + 1
  Fin Tant que
  Retourner ok
FIN

TDOL de Valide : ok — booléen — résultat du contrôle ; i — entier — indice du caractère examiné.

2. Idée : travailler sur le rang de la lettre, 0 pour "A" et 25 pour "Z" ; ajouter p puis prendre le reste modulo 26 fait passer de "Z" à "A" sans aucun test.

Fonction Crypter (ch : Chaîne ; p : Entier) : Chaîne
DEBUT
  res ← ""
  Pour i de 0 à Long(ch) - 1 Faire
    Si ch[i] = " " Alors
      res ← res + " "
    Sinon
      res ← res + Chr((Ord(ch[i]) - 65 + p) Mod 26 + 65)
    FinSi
  Fin Pour
  Retourner res
FIN

TDOL de Crypter : res — chaîne — message crypté en construction ; i — entier — indice du caractère traité.

3. Pour "X" : Ord("X") - 65 = 23, (23 + 4) Mod 26 = 1, et Chr(1 + 65) = "B". De même "Y" donne "C", "Z" donne "D", puis "B", "A", "C" donnent "F", "E", "G" ; l'espace est conservé. Le résultat est "BCD FEG".

4.

ObjetClasseInstruction Python
txtMsgQLineEditwindows.txtMsg.text() lit le message
txtPQLineEditwindows.txtP.text() lit p, sous forme de chaîne
btnCrypterQPushButtonwindows.btnCrypter.clicked.connect(traiter) relie le clic au module
lblResQLabelwindows.lblRes.setText(…) écrit le résultat

5. Idée : traduire ligne à ligne — Long devient len, Ord et Chr deviennent ord et chr, Mod devient %, la boucle Pour devient for … in range(…).

from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication

def valide(ch):
    ok = 1 <= len(ch) <= 50
    i = 0
    while ok and i < len(ch):
        ok = ("A" <= ch[i] <= "Z") or ch[i] == " "
        i = i + 1
    return ok

def crypter(ch, p):
    res = ""
    for i in range(len(ch)):
        if ch[i] == " ":
            res = res + " "
        else:
            res = res + chr((ord(ch[i]) - 65 + p) % 26 + 65)
    return res

6. Idée : dans une interface, on ne redemande pas la saisie dans une boucle ; on contrôle une fois, au clic, et on affiche un message d'erreur. text() rend toujours une chaîne : on teste isdigit() avant d'appeler int().

def traiter():
    ch = windows.txtMsg.text()
    sp = windows.txtP.text()
    if valide(ch) and sp.isdigit() and 1 <= int(sp) <= 25:
        windows.lblRes.setText(crypter(ch, int(sp)))
    else:
        windows.lblRes.setText("Saisie invalide")

Grâce à l'évaluation de gauche à droite de and, int(sp) n'est jamais calculé lorsque sp contient autre chose que des chiffres.

7.

app = QApplication([])
windows = loadUi("cryptage.ui")
windows.show()
windows.btnCrypter.clicked.connect(traiter)
app.exec_()

"XYZ BAC" et "4" : l'étiquette affiche BCD FEG (question 3). "Bac 2026" et "3" : Saisie invalide, car le message contient des minuscules et des chiffres. "ZOO" et "30" : Saisie invalide, car p = 30 dépasse 25.

Changer d’énoncé, garder le raisonnement

Choisis un seul transfert après le sujet principal. Qu’est-ce qui change ? Quel résultat ou quelle méthode peux-tu réutiliser ? Les autres exercices restent disponibles pour une prochaine séance.

Variantes rencontrées (séries de lycée, jamais posées à l'écrit national) : décalage p égal au nombre de mots du message ; interface à deux zones de saisie qui affiche le PGCD de deux entiers après une fonction de vérification ; message « Erreur !! » affiché dans l'étiquette résultat quand la saisie est invalide.

Exercices d'entraînement — une nuance à la fois

1 Drill SD-O.1 — deux zones de saisie dépendantes : contrôler k par rapport à la longueur de la chaîne
Entraînement 01 / 02

Drill SD-O.1 — deux zones de saisie dépendantes : contrôler k par rapport à la longueur de la chaîne

4 questionsCorrigé masqué

On veut une interface, enregistrée sous le nom rotation.ui, qui contient une zone de saisie txtCh pour une chaîne, une zone txtK pour un entier k, un bouton btnTourner et une étiquette lblRes. Un clic sur le bouton affiche la chaîne obtenue en déplaçant ses k premiers caractères à la fin : par exemple, "INFORMATIQUE" et k = 4 donnent "RMATIQUEINFO".

La saisie est correcte si la chaîne contient entre 2 et 30 caractères et si k est un entier tel que 1 ≤ k < Long(ch) ; sinon l'étiquette affiche "Erreur !!".

  1. Écrire l'algorithme d'une fonction Tourner(ch, k) qui retourne la chaîne transformée, en utilisant la fonction prédéfinie Sous_chaine.
  2. Écrire en Python une fonction verif(ch, sk) qui reçoit la chaîne et le texte tapé pour k, et retourne True si la saisie est correcte. Pourquoi faut-il tester sk.isdigit() avant de calculer int(sk) ?
  3. Écrire en Python la fonction tourner(ch, k), le module afficher() exécuté lors du clic et le programme principal.
  4. Donner le texte affiché dans lblRes pour les saisies : "INFORMATIQUE" et "4" ; "BAC" et "3" ; "PYTHON" et "x2" ; "PYTHON" et "1".
Voir la correction commentéeAprès avoir posé votre démarche

1. Idée : la chaîne transformée est la sous-chaîne qui commence à l'indice k, suivie des k premiers caractères.

Fonction Tourner (ch : Chaîne ; k : Entier) : Chaîne
DEBUT
  Retourner Sous_chaine(ch, k, Long(ch)) + Sous_chaine(ch, 0, k)
FIN

2. Idée : la borne de k dépend de l'autre zone de saisie : on ne peut la tester qu'une fois k converti, et on ne peut le convertir que s'il est formé de chiffres. int("x2") provoquerait une erreur à l'exécution ; avec and, la conversion n'est tentée que si isdigit() est vrai.

def verif(ch, sk):
    ok = 2 <= len(ch) <= 30 and sk.isdigit()
    if ok:
        ok = 1 <= int(sk) < len(ch)
    return ok

3.

from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication

def verif(ch, sk):
    ok = 2 <= len(ch) <= 30 and sk.isdigit()
    if ok:
        ok = 1 <= int(sk) < len(ch)
    return ok

def tourner(ch, k):
    return ch[k:len(ch)] + ch[0:k]

def afficher():
    ch = windows.txtCh.text()
    sk = windows.txtK.text()
    if verif(ch, sk):
        windows.lblRes.setText(tourner(ch, int(sk)))
    else:
        windows.lblRes.setText("Erreur !!")

app = QApplication([])
windows = loadUi("rotation.ui")
windows.show()
windows.btnTourner.clicked.connect(afficher)
app.exec_()

4. "INFORMATIQUE" et "4" : RMATIQUEINFO. "BAC" et "3" : Erreur !!, car k doit être strictement inférieur à la longueur 3. "PYTHON" et "x2" : Erreur !!, car "x2" n'est pas formé de chiffres. "PYTHON" et "1" : YTHONP.

2 Drill SD-O.2 — script d'interface à corriger : text(), setText(), loadUi et clicked.connect
Entraînement 02 / 02

Drill SD-O.2 — script d'interface à corriger : text(), setText(), loadUi et clicked.connect

4 questionsCorrigé masqué

Un élève a réalisé sous QtDesigner une interface enregistrée sous le nom chiffres.ui : une zone de saisie txtN (QLineEdit), un bouton btnCalculer (QPushButton) et une étiquette lblRes (QLabel). Un clic doit afficher le nombre de chiffres et la somme des chiffres de l'entier naturel tapé. Il écrit le programme suivant, qui contient cinq erreurs :

from PyQt5.uic import loadUi
from PyQt5.QtWidgets import QApplication

def chiffres():
    n = windows.txtN.text
    nb = 0
    s = 0
    while n != 0:
        s = s + n % 10
        n = n // 10
        nb = nb + 1
    windows.lblRes.text(str(nb) + " chiffres, somme " + str(s))

app = QApplication([])
windows = loadUi("chiffres")
windows.show()
windows.btnCalculer.clicked.connect(chiffres())
app.exec_()
  1. Relever les cinq erreurs et donner pour chacune l'instruction corrigée.
  2. Réécrire le module chiffres() corrigé, en affichant "Saisie invalide" lorsque le texte tapé est vide ou contient un caractère qui n'est pas un chiffre.
  3. Donner le texte affiché par la version corrigée pour les saisies "2026", "90817" et "0". Quel résultat est faux ?
  4. Proposer une instruction, sans structure itérative, qui calcule le nombre de chiffres à partir de l'entier n, et vérifier qu'elle corrige le cas précédent. Pourquoi ne pas utiliser directement la longueur du texte tapé ?
Voir la correction commentéeAprès avoir posé votre démarche

1.

Instruction erronéeInstruction corrigée
n = windows.txtN.textt = windows.txtN.text() : sans parenthèses, la méthode n'est pas appelée
boucle while sur une chaînen = int(t) : text() rend une chaîne, n % 10 exige un entier
windows.lblRes.text(…)windows.lblRes.setText(…) : text() lit, setText() écrit
loadUi("chiffres")loadUi("chiffres.ui") : le nom exact du fichier enregistré
clicked.connect(chiffres())clicked.connect(chiffres) : on passe le module sans l'exécuter

La dernière erreur est la plus trompeuse : avec les parenthèses, chiffres() s'exécute au lancement, avant tout clic, et connect reçoit sa valeur de retour, None, au lieu du module.

2. Idée : contrôler le texte avant de le convertir ; "".isdigit() vaut False, donc un seul test couvre la saisie vide et les caractères interdits.

def chiffres():
    t = windows.txtN.text()
    if not t.isdigit():
        windows.lblRes.setText("Saisie invalide")
    else:
        n = int(t)
        nb = 0
        s = 0
        while n != 0:
            s = s + n % 10
            n = n // 10
            nb = nb + 1
        windows.lblRes.setText(str(nb) + " chiffres, somme " + str(s))

3. "2026" : 4 chiffres, somme 10. "90817" : 5 chiffres, somme 25. "0" : 0 chiffres, somme 0 — ce résultat est faux, car 0 s'écrit avec un chiffre : la condition n != 0 est fausse dès le départ et la boucle ne s'exécute jamais.

4. Idée : comme Long(Convch(n)) en algorithmique, convertir l'entier en chaîne et prendre sa longueur. L'instruction nb = len(str(n)) donne 1 pour n = 0.

def chiffres():
    t = windows.txtN.text()
    if not t.isdigit():
        windows.lblRes.setText("Saisie invalide")
    else:
        n = int(t)
        nb = len(str(n))
        s = 0
        while n != 0:
            s = s + n % 10
            n = n // 10
        windows.lblRes.setText(str(nb) + " chiffres, somme " + str(s))

La longueur du texte tapé ne convient pas : pour "007", len(t) vaut 3, alors que l'entier 7 n'a qu'un chiffre ; str(int("007")) vaut "7".

Relier ce raisonnement au bac

Choisis une filière pour voir les sujets qui étayent ce scénario.