Beispiel #1
0
def _minitel_avp(niveau):
    """Convertit un niveau en une séquence de codes Minitel définissant la
    couleur d’avant-plan.

    :param niveau:
        Le niveau à convertir (valeur de 0 à 7 inclus).
    :type niveau:
        un entier

    :returns:
        Un objet de type Sequence contenant la séquence à envoyer au
        Minitel pour avec une couleur d’avant-plan correspondant au niveau.
    """
    assert isinstance(niveau, int)

    try:
        return Sequence([ESC, 0x40 + COULEURS_MINITEL[niveau]])
    except IndexError:
        return Sequence([ESC, 0x47])
Beispiel #2
0
 def WriteString(self, text="default"):
     nb_column = 40
     nb_line = 24
     if (self.minitel.capacite['80colonnes'] == True):
         if (self.minitel.mode == 'MIXTE'):
             nb_column = 80
     text = text[0:nb_column * nb_line]
     s_send = Sequence()
     s_send.ajoute(text)
     self.minitel.envoyer(s_send)
Beispiel #3
0
    def appeler(self, contenu, attente):
        """Envoie une séquence au Minitel et attend sa réponse.

        Cette méthode permet d’envoyer une commande au Minitel (configuration,
        interrogation d’état) et d’attendre sa réponse. Cette fonction attend
        au maximum 1 seconde avant d’abandonner. Dans ce cas, une séquence
        vide est retournée.

        Avant de lancer la commande, la méthode vide la file d’attente en
        réception.

        :param contenu:
            Une séquence de caractères interprétable par la classe
            Sequence
        :type contenu:
            un objet Sequence, une chaîne de caractères, une chaîne unicode
            ou un entier

        :param attente:
            Nombre de caractères attendu de la part du Minitel en
            réponse à notre envoi.
        :type attente:
            un entier

        :returns:
            un objet Sequence contenant la réponse du Minitel à la commande
            envoyée.
        """
        assert isinstance(attente, int)

        # Vide la file d’attente en réception
        self.entree = Queue()

        # Envoie la séquence
        self.envoyer(contenu)

        # Attend que toute la séquence ait été envoyée
        self.sortie.join()

        # Tente de recevoir le nombre de caractères indiqué par le paramètre
        # attente avec un délai d’1 seconde.
        retour = Sequence()
        for _ in range(0, attente):
            try:
                # Attend un caractère
                entree_bytes = self.entree.get(block=True, timeout=1)
                retour.ajoute(entree_bytes.decode())
            except Empty:
                # Si un caractère n’a pas été envoyé en moins d’une seconde,
                # on abandonne
                break

        return retour
Beispiel #4
0
 def WriteLnString(self, text=""):
     nb_column = 40
     nb_line = 24
     if (self.minitel.capacite['80colonnes'] == True):
         if (self.minitel.mode == 'MIXTE'):
             nb_column = 80
     space_to_add = nb_column - (len(text) % nb_column)
     for i in range(space_to_add):
         text += " "
     text = text[0:nb_column * nb_line]
     s_send = Sequence()
     s_send.ajoute(text)
     self.minitel.envoyer(s_send)
Beispiel #5
0
    def envoyer(self, contenu):
        """Envoi de séquence de caractères 

        Envoie une séquence de caractère en direction du Minitel.

        :param contenu:
            Une séquence de caractères interprétable par la classe Sequence.
        :type contenu:
            un objet Sequence, une chaîne de caractères ou unicode, une liste,
            un entier
        """
        # Convertit toute entrée en objet Sequence
        if not isinstance(contenu, Sequence):
            contenu = Sequence(contenu)

        # Ajoute les caractères un par un dans la file d’attente d’envoi
        for valeur in contenu.valeurs:
            self.sortie.put(chr(valeur))
Beispiel #6
0
    def importer(self, image):
        """Importe une image de PIL et crée les séquences de code Minitel
        correspondantes. L’image fournie doit avoir été réduite à des
        dimensions inférieures ou égales à 80×72 pixels. La largeur doit être
        un multiple de 2 et la hauteur un multiple de 3.

        :param image:
            L’image à importer.
        :type niveau:
            une Image
        """
        assert image.size[0] <= 80
        assert image.size[1] <= 72

        # En mode semi-graphique, un caractère a 2 pixels de largeur
        # et 3 pixels de hauteur
        self.largeur = int(image.size[0] / 2)
        self.hauteur = int(image.size[1] / 3)

        # Initialise la liste des séquences
        self.sequences = []

        for hauteur in range(0, self.hauteur):
            # Variables pour l’optimisation du code généré
            old_arp = -1
            old_avp = -1
            old_alpha = 0
            compte = 0

            # Initialise une nouvelle séquence
            sequence = Sequence()

            # Passe en mode semi-graphique
            sequence.ajoute(SO)

            if self.disjoint:
                sequence.ajoute([ESC, 0x5A])

            for largeur in range(0, self.largeur):
                # Récupère 6 pixels
                pixels = [
                    image.getpixel((largeur * 2 + x, hauteur * 3 + y))
                    for x, y in [(0, 0), (1, 0),
                                  (0, 1), (1, 1),
                                  (0, 2), (1, 2)]
                ]

                if self.disjoint:
                    # Convertit chaque couleur de pixel en deux niveaux de gris
                    pixels = [_huit_niveaux(pixel) for pixel in pixels]

                    arp, avp = _deux_couleurs(pixels)

                    if arp != 0:
                        arp, avp = 0, arp

                else:
                    # Convertit chaque couleur de pixel en huit niveau de gris
                    pixels = [_huit_niveaux(pixel) for pixel in pixels]

                    # Recherche les deux couleurs les plus fréquentes
                    # un caractère ne peut avoir que deux couleurs !
                    arp, avp = _deux_couleurs(pixels)

                # Réduit à deux le nombre de couleurs dans un bloc de 6 pixels
                # Cela peut faire apparaître des artefacts mais est inévitable
                pixels = [_arp_ou_avp(pixel, arp, avp) for pixel in pixels]

                # Convertit les 6 pixels en un caractère mosaïque du minitel
                # Le caractère est codé sur 7 bits
                bits = [
                    '0',
                    str(pixels[5]),
                    '1',
                    str(pixels[4]),
                    str(pixels[3]),
                    str(pixels[2]),
                    str(pixels[1]),
                    str(pixels[0])
                ]

                # Génère l’octet (7 bits) du caractère mosaïque
                alpha = int(''.join(bits), 2)

                # Si les couleurs du précédent caractères sont inversés,
                # inverse le caractère mosaïque. Cela évite d’émettre
                # à nouveau des codes couleurs. Cela fonctionne uniquement
                # lorsque le mode disjoint n’est pas actif
                if not self.disjoint and old_arp == avp and old_avp == arp:
                    # Inverse chaque bit à l’exception du 6e et du 8e
                    alpha = alpha ^ 0b01011111
                    avp, arp = arp, avp
                    
                if old_arp == arp and old_avp == avp and alpha == old_alpha:
                    # Les précédents pixels sont identiques, on le retient
                    # pour utiliser un code de répétition plus tard
                    compte += 1
                else:
                    # Les pixels ont changé, mais il peut y avoir des pixels
                    # qui n’ont pas encore été émis pour cause d’optimisation
                    if compte > 0:
                        if compte == 1:
                            sequence.ajoute(old_alpha)
                        else:
                            sequence.ajoute([DC2, 0x40 + compte])

                        compte = 0

                    # Génère les codes Minitel
                    if old_arp != arp:
                        # L’arrière-plan a changé
                        sequence.ajoute(_minitel_arp(arp))
                        old_arp = arp

                    if old_avp != avp:
                        # L’avant-plan a changé
                        sequence.ajoute(_minitel_avp(avp))
                        old_avp = avp

                    sequence.ajoute(alpha)
                    old_alpha = alpha

            if compte > 0:
                if compte == 1:
                    sequence.ajoute(old_alpha)
                else:
                    sequence.ajoute([DC2, 0x40 + compte])

                compte = 0

            if self.disjoint:
                sequence.ajoute([ESC, 0x59])

            # Une ligne vient d’être terminée, on la stocke dans la liste des
            # séquences
            self.sequences.append(sequence)
Beispiel #7
0
    def recevoir_sequence(self, bloque=True, attente=None):
        """Lit une séquence en provenance du Minitel

        Retourne un objet Sequence reçu depuis le Minitel. Cette fonction
        analyse les envois du Minitel pour en faire une séquence consistante
        du point de vue du Minitel. Par exemple, si le Minitel envoie un
        caractère SS2, SEP ou ESC, celui-ci ne fait qu’annoncer une suite de
        caractères désignant un résultat ou un caractère non existant dans la
        norme ASCII. Par contre, le nombre de caractères pouvant être reçus
        après des caractères spéciaux est normalisé. Cela permet de savoir
        exactement le nombre de caractères qui vont constituer la séquence.

        C’est cette méthode qui doit être utilisée plutôt que la méthode
        recevoir lorsqu’on dialogue avec le Minitel.

        :param bloque:
            True pour attendre une séquence s’il n’y en a pas dans la
            file d’attente de réception. False pour ne pas attendre et
            retourner immédiatement.
        :type bloque:
            un booléen

        :param attente:
            attente en secondes, valeurs en dessous de la seconde
            acceptées. Valide uniquement en mode bloque = True
            Si attente = None et bloque = True, alors on attend
            indéfiniment qu'un caractère arrive. 
        :type attente:
            un entier, ou None

        :returns:
            un objet Sequence
        """
        # Crée une séquence
        sequence = Sequence()

        # Ajoute le premier caractère lu à la séquence en mode bloquant
        sequence.ajoute(self.recevoir(bloque=bloque, attente=attente))
        assert sequence.longueur != 0

        # Teste le caractère reçu
        if sequence.valeurs[-1] in [SS2, SEP]:
            # Une séquence commençant par SS2 ou SEP aura une longueur de 2
            sequence.ajoute(self.recevoir(bloque=True))
        elif sequence.valeurs[-1] == ESC:
            # Les séquences ESC ont des tailles variables allant de 1 à 4
            try:
                # Essaie de lire un caractère avec un temps d’attente de 1/10s
                # Cela permet de lire la touche la touche Esc qui envoie
                # uniquement le code ESC sans rien après.
                sequence.ajoute(self.recevoir(bloque=True, attente=0.1))

                # Une séquence CSI commence par ESC, 0x5b
                if sequence.valeurs == CSI:
                    # Une séquence CSI appelle au moins 1 caractère
                    sequence.ajoute(self.recevoir(bloque=True))

                    if sequence.valeurs[-1] in [0x32, 0x34]:
                        # La séquence ESC, 0x5b, 0x32/0x34 appelle un dernier
                        # caractère
                        sequence.ajoute(self.recevoir(bloque=True))
            except Empty:
                # Si aucun caractère n’est survenu après 1/10s, on continue
                pass

        return sequence