Affichage dynamique en Python sous Debian

Avec Debian 11

Principe de fonctionnement

Un partage sur le réseau héberge un diaporama LibreOffice Impress, quand ce dernier est modifié il est automatiquement téléchargé par un programme en Python et affiché sur la télévision qui est connectée en HDMI à un PC sous la distribution GNU Linux Debian 11. Les dernières versions de mes programmes intègrent également la possibilité de télécharger et de lire en boucle une vidéo. Les paramétrages son enregistrés dans un fichier ini « diaporamaini ». Les fichiers en Python doivent se trouver dans le même dossier.

Le partage Réseau

Le partage Samba sur le réseau à été créé. Les paramètres sont le nom du partage, l’utilisateur y ayant accès avec son mot de passe.

Adressage IP du PC sous Debian

Le PC sous Debian qui héberge les scripts en Python doit se trouver sur le même réseau IP que le partage hébergeant le diaporama.

Environnement pour la programmation et les tests

Pour travailler sur les programmes j’utilise un PC sous Windows que héberge le partage, une machine virtuelle Debian 11 sous VirtualBox permet de programmer et de tester les résultats. La machine virtuelle est sur le même réseau IP que la machine hôte grâce à un pont sur la carte réseau. J’utilise l’éditeur Sublime Text. Un dossier est accessible par les deux machines pour pouvoir récupérer les fichiers créés, l’explorateur de fichier de Debian doit être lancé en administrateur root su avec la commande nautilus.

Installation de Sublime Text sous Debian :

https://wiki.debian.org/SublimeText

Connexion à la machine sous Debian

J’utilise un petit clavier Bluetooth.

Mini Clavier Rii i8 disponible chez Kubii


Programme permettant de paramétrer l’application et de télécharger et lire en boucle les vidéos

Pour lancer le programme depuis le dossier où il se trouve :

./Gestion-Affichage-Dynamique.py

Le programme avec les paramètres de mon environnement de tests.

Les paramètres, nom du serveur ou IP, partage réseau, utilisateur, mot de passe, nom de domaine, fichier de diaporama doivent être saisis puis enregistrés dans le fichier ini.

  • [Lancer le Diaporama] Permet de lancer le diaporama en utilisant le paramètres du fichier ini.
  • [Enregistrer le fichier ini] Permet d’enregistrer les paramètres saisis dans les champs dans le fichier .ini « diaporamaini ».
  • [Lister les fichiers du partage depuis ini] Permet de lister les fichiers contenus dans le partage réseau à partir des paramètres du fichier ini « diaporamaini ».
  • [Transférer et lancer la vidéo] Permet de télécharger depuis le partage et de lire en boucle un fichier vidéo sélectionné dans la liste. Le nom du fichier est modifié automatiquement si nécessaire, les espaces remplacés par des _ et les accents supprimés.

Pour arrêter l’exécution du programme en Python depuis le terminal [Ctrl] + [C].

Depuis le menu Fichier ==> Quitter


Programme qui gère le diaporama

Le programme qui gère le diaporama n’a pas d’interface graphique, il lit les données du fichier ini puis télécharge le diaporama et lance LibreOffice Impress pour l’afficher. Quand le diaporama est modifié dans le partage du réseau LibreOffice est arrêté, le nouveau fichier est téléchargé automatiquement, Impress est ensuite relancé avec le fichier mis à jour.

Le mieux est de lancer le programme en lignes de commande depuis un terminal avec votre utilisateur.

Pour lancer le programme depuis le dossier où il se trouve :

./Diapo-py.py

Pour arrêter l’affichage du diaporama [Alt] + [F4].

Pour arrêter l’exécution du programme en Python depuis le terminal [Ctrl] + [C].


Prérequis pour l’installation sur la machine sous Debian 11

Version de Python installée

Pour savoir quelle est la version de Python installée :

python3 --version

Pour l’interface graphique avec Tkinter

Le paquet python3-tk doit être présent sinon on l’installe.

En administrateur root, su + mot de passe :

apt-get install python3-tk

Accès au partage Samba

Vous devez disposer d’un partage réseau Windows ou sous Linux (Samba) avec les login/mot de passe pour y avoir accès. Pour pouvoir accéder au partage réseau vous devez installer le paquet smbclient (client samba) sous Linux en administrateur :

apt-get install smbclient

En cas de problème, il peut être utile de vérifier la connexion au partage depuis le terminal :

smbclient -U utilisateur //ip_du_serveur/nom_du_partage

Pour installer une bibliothèque externe pour Python 3

Installation des paquets nécessaire à la mise en place des bibliothèques pour Python 3. Pip est un gestionnaire de paquets :

apt-get install python3-pip

Installer la bibliothèque pysmbclient (client samba) pour l’accès au partage réseau depuis Python :

pip3 install pysmbclient

Plus d’informations :

https://pypi.org/project/PySmbClient/

Installer la bibliothèque unidecode pour supprimer les caractères accentués des noms de fichiers vidéo :

pip3 install unidecode

Pour la lecture des vidéos sous Debian mplayer doit être installé :

apt-get install mplayer

La commande utilisée pour lire un fichier vidéo en boucle et en plein écran est :

mplayer Fichier_video -loop 0 -fs

Si on adapte le programme à un Raspberry Pi sous Pi OS on utilisera OMXPlayer.

omxplayer --loop fichier_video

Les droits sur les fichiers

Les deux programmes doivent être exécutables et je réduis les droits à l’utilisateur :

chmod 700 nom_du_fichier

Pour le fichier diaporamaini, droits en lecture et en écriture au seul utilisateur :

chmod 600 diaporamaini

Pour une installation sous Debian, j’ai également installé le paquet libreoffice-java-common :

apt-get install libreoffice-java-common

Pour exécuter un programme

./nom_du_fichier.py

Pour supprimer un fichier vidéo téléchargé précédemment :

rm nom_du_fichier

Installation de Java JRE

J’ai également installé Java JRE, attention le nom du fichier téléchargé peut être différent !

Pour télécharger Java JRE pour Linux :

https://www.java.com/fr/download/linux_manual.jsp

J’ai sélectionné Linux x64.

En administrateur root dans un terminal :

cd /usr
mkdir java
cd java

Lancez le navigateur nautilus depuis le terminal en administrateur root su pour copier l’archive TAR téléchargée dans /usr/java

Décompressez l’archive TAR (jre-8u321-linux-x64.tar.gz dans mon cas) pour installer Java :

tar zxvf jre-8u371-linux-x64.tar.gz

Plus d’informations, installation de Java JRE sous Debian 11 (sur mon autre site WEB) :


Si cela ne fonctionne pas sous Debian

Avec Samba sous Debian 11

Pour tester la connexion au partage Samba depuis le terminal sous Linux :

smbclient -U utilisateur //ip_du_serveur/nom_du_partage

Si vous avez le message suivant :

Solution

Depuis la version 4.11 Samba ne prend plus en charge le vieux protocole SMBv1, seuls SMB2 et SMB3 sont gérés. Certains partages ne sont plus accessibles. Pour voir la version du client Samba :

smbclient --version

Version 4.13.13-Debian Dans mon cas.

Dans le fichier /etc/samba/smb.conf sous la section [global] j’ai ajouté :

client min protocol = NT1


Pour désactiver la boîte de dialogue « Astuce du jour » dans LibreOffice

Outils > Options > LibreOffice > Général > décocher Afficher la boîte de dialogue « Astuces du jour » au démarrage.


Codes sources des programmes en Python

Programme Diapo-py.py

#!/usr/bin/env python3.9
# -- coding: utf-8 --
import pickle
import os
import smbclient
import subprocess
import time
# Version juin 2023
# Lecture du fichier ini
fichierini = "diaporamaini"
pid = ''
# Ouverture du fichier s'il existe et récupération de la liste
if os.path.isfile(fichierini):
    fichierSauvegarde = open(fichierini,"rb")
    variables = pickle.load(fichierSauvegarde)
    fichierSauvegarde.close()
# Affichage d'informations
    print("***** Programme de Dominique Renaudeau en langage Python *****")
    print("Initialisation des variables")
# Récupération des données dans les variables
    serveursmb = variables[0]
    partagesmb = variables[1]
    utilisateur = variables[2]
    mot_de_passe = variables[3]
    domaine = variables[4]
    fichier_diaporama = variables[5]
else:
    # Le fichier n'existe pas
    print("Fichier " + fichierini + " non trouvé")
# initialisation des variables
Version_Old = "vide"
local_path = "./" + fichier_diaporama
smb = smbclient.SambaClient(server=serveursmb, share=partagesmb, username=utilisateur,password=mot_de_passe, domain=domaine)
Boucle = True
while Boucle == True:
    try:
        # Afficher des informations sur le fichier
        Version_New = smb.info(fichier_diaporama)
    except:
        Version_New = Version_Old
    if Version_New != Version_Old:
        try:
            ### On arrête l'application LibreOffice ###
            pid = os.popen("pidof soffice.bin").read()
            if pid != '':
                print("Arrêt de l'application LibreOffice")
            try:
                subprocess.Popen(['/bin/sh', '-c', "pkill office"])
            except:
                print("LibreOffice n'est pas lancé")
            time.sleep(2)
            print("LibreOffice n'est pas lancé")
            ### On supprime l'ancien fichier diaporama s'il existe ###
            if os.path.isfile(local_path):
                os.remove(local_path)
            ### Téléchargement du nouveau fichier ###
            print("Téléchargement du nouveau fichier et lancement de LibreOffice")
            smb.download(fichier_diaporama, local_path)
            time.sleep(2)
            ### Affichage du diaporama ###
            Affichage = subprocess.Popen(["libreoffice", "--norestore", "--show", fichier_diaporama], shell=False)
            Version_Old = Version_New
            time.sleep(60)
            pid = os.popen("pidof soffice.bin").read()
            print("PID LibreOffice = " + pid)
        except:
            print("Erreur lors du téléchargement et de l'affichage du diaporama")
    else:
        time.sleep(20) # Pas de mise à jour - On attend 20 secondes avant de revérifier
    if pid == '':
        print("LibreOffice n'est pas lancé, vérifiez les paramètres du fichier ini et l'accès au partage réseau.")
        exit()

Programme Gestion-Affichage-Dynamique.py

#!/usr/bin/env python3.9
# -- coding: utf-8 --
from tkinter import *
from tkinter import messagebox
import pickle
import os.path
import smbclient
import subprocess
import unidecode # Doit être importée
# Lancer le diaporama avec le second programme en Python
def Lancer_Diaporama():
    try:
        exec(open("./Diapo-py.py").read())
    except:
        print("Impossible de lancer le diaporama.")
# Enregistrer la configuration dans le fichier diaporamaini
def Enregistrer():
    global serveursmb
    global partagesmb
    global utilisateur
    global mot_de_passe
    global domaine
    global fichier_diaporama
    serveursmb = Serveur_Entry.get()
    partagesmb = Partage_Entry.get()
    utilisateur = Utilisateur_Entry.get()
    mot_de_passe = Mot_de_passe_Entry.get()
    domaine = Domaine_Entry.get()
    fichier_diaporama = Fichier_du_diaporama_Entry.get()
    # Enregistrer mes variables sous forme d'une liste dans un fichier
    if serveursmb != "vide" and partagesmb != "vide" and utilisateur != "vide" and mot_de_passe != "vide" and domaine != "vide" and fichier_diaporama != "vide":
        variables = [serveursmb, partagesmb, utilisateur, mot_de_passe, domaine, fichier_diaporama]
        fichierSauvegarde = open("diaporamaini","wb")
        pickle.dump(variables, fichierSauvegarde)
        fichierSauvegarde.close()
        Mon_bouton_Lister_Fichiers['state'] = NORMAL
        Mon_bouton_Lancer_Diaporama['state'] = NORMAL
        menufichier.entryconfigure(0,state=NORMAL)
        menudiaporama.entryconfigure(0,state=NORMAL)
# Lister les fichiers présents dans le partage réseau en utilisant les paramètres enregistrés dans le fichier diaporamaini
def Lister():
    Lire_Ini()
    global smb
    # Vider la liste de fichiers si elle n'est pas vide
    if Liste_Fichiers.size() != 0:
        Liste_Fichiers.delete(0,END)
    print("Lister les fichiers du partage Samba")
    smb = smbclient.SambaClient(server=serveursmb, share=partagesmb, username=utilisateur, password=mot_de_passe, domain=domaine)
    dirs = smb.listdir("/")
    i = 0
    while i < len(dirs):
       print (dirs[i])
       Liste_Fichiers.insert(i+1, dirs[i])
       i += 1
    Mon_bouton_Video['state'] = NORMAL
    menuvideo.entryconfigure(0,state=NORMAL)
# Lire les informations contenues dans le fichier diaporamaini
def Lire_Ini():
    global serveursmb
    global partagesmb
    global utilisateur
    global mot_de_passe
    global domaine
    global fichier_diaporama
    # Lecture du fichier ini
    fichierini = "diaporamaini"
    # Ouverture du fichier s'il existe et récupération de la liste
    if os.path.isfile(fichierini):
        fichierSauvegarde = open(fichierini,"rb")
        variables = pickle.load(fichierSauvegarde)
        fichierSauvegarde.close()
        # Affichage d'informations
        print("Initialisation des variables depuis le fichier ini")
        # Récupération des données dans les variables
        serveursmb = variables[0]
        partagesmb = variables[1]
        utilisateur = variables[2]
        mot_de_passe = variables[3]
        domaine = variables[4]
        fichier_diaporama = variables[5]
    else:
        # Le fichier n'existe pas
        print("Fichier " + fichierini + " non trouvé")
        serveursmb = partagesmb = utilisateur = mot_de_passe = domaine = fichier_diaporama = "vide"
# Transférer et Lire le fichier vidéo
def Video():
    Fichier_video = "vide"
    i=Liste_Fichiers.curselection()  ## Récupération de l'index de l'élément sélectionné
    try:
        Fichier_video = Liste_Fichiers.get(i)
    except:
        print("Sélectionnez un fichier vidéo")
        messagebox.showinfo("Attention !", "Sélectionnez un fichier vidéo .mp4 .avi .MOV.")
    # Extention du fichier dans la variable ext
    _, ext = os.path.splitext(Fichier_video)
    Extentions_Liste = ['.mp4', '.avi', '.MOV']
    #if Fichier_video != "vide" and ext == ".mp4" or ext == ".avi":
    if Fichier_video != "vide" and ext in Extentions_Liste:
        print("Fichier vidéo : "+Fichier_video)
        print("Extention du fichier : "+ext)
        local_path = "./" + Fichier_video
        smb.download(Fichier_video, local_path)
        print("Fichier vidéo téléchargé")
        # ajout domnique
        nom_de_fichier_avant = Fichier_video
        print ("Nom de fichier avant : " + nom_de_fichier_avant)
        nom_de_fichier_apres = nom_de_fichier(nom_de_fichier_avant)
        print("Nom de fichier après : " + nom_de_fichier_apres)
        if nom_de_fichier_avant != nom_de_fichier_apres:
            print("Le nom de fichier a été modifié")
            print("Le fichier doit être renommé")
            os.rename(nom_de_fichier_avant, nom_de_fichier_apres)
            Fichier_video = nom_de_fichier_apres
        else:
            print("Le nom de fichier n'a pas été modifié")
            print(Fichier_video)
        # ajout dominique
        subprocess.Popen(['/bin/sh', '-c', "mplayer " + Fichier_video + " -loop 0 -fs"])
    else:
        print("Sélectionnez un fichier vidéo")
def Aide():
    messagebox.showinfo("À propos", "Programme Dominique Renaudeau - Collège Celles et Melle 2023.")
def nom_de_fichier(nom):
    # Supprimer les espaces et les caractères accentués du nom du fichier
    # Remplacement des espaces
    nom = nom.replace(" ","_")
    # suppression les caractères accentués
    nom = unidecode.unidecode(nom)
    return nom

os.chdir(os.path.dirname(__file__)) # Changer le répertoire courant par celui du programme
Lire_Ini() # lire le fichier ini
Fenetre = Tk() # Création de la fenêtre, avec un nom de mon choix Fenetre
# Création des menus
menubar = Menu(Fenetre)
menufichier = Menu(menubar,tearoff=0)
menubar.add_cascade(label="Fichier", menu=menufichier)
menufichier.add_command(label="Lister",state = DISABLED,command=Lister)
menufichier.add_command(label="Enregistrer", command=Enregistrer)
menufichier.add_separator()
menufichier.add_command(label="Quitter", command=Fenetre.destroy)
menudiaporama = Menu(menubar,tearoff=0)
menubar.add_cascade(label="Diaporama", menu=menudiaporama)
menudiaporama.add_command(label="Lancer",state = DISABLED,command=Lancer_Diaporama)
menuvideo = Menu(menubar,tearoff=0)
menubar.add_cascade(label="Vidéo", menu=menuvideo)
menuvideo.add_command(label="Transférer et lancer la vidéo",state = DISABLED,command=Video)
menuaide = Menu(menubar,tearoff=0)
menubar.add_cascade(label="Aide", menu=menuaide)
menuaide.add_command(label="À propos",command=Aide)
Fenetre.config(menu=menubar)
# Paramètres de la fenêtre
Fenetre.title('Paramètres Diaporama et vidéo - Programme : D Renaudeau') #Titre de la fenêtre
Fenetre.geometry("500x400") # On définit la taille de la fenêtre
Fenetre.resizable(width=False,height=False) #Empêcher de redimentionner la fenêtre
# Création d'un interface graphique avec grid
Serveur_Label= Label(Fenetre, text = 'Votre serveur - Nom ou IP :') #Un label pour afficher du texte
Serveur_Label.grid(row=0)
Serveur_Entry= Entry(Fenetre) # On définit l'objet Entry (zone de saisie) qui porte le nom Serveur_Entry
Serveur_Entry.insert(0, serveursmb)
Serveur_Entry.grid(row=0,column=1,ipadx=30) # ipadx=30 pour créer 10 pixels de plus horizontalement
Partage_Label= Label(Fenetre, text = 'Votre partage réseau :')
Partage_Label.grid(row=1,column=0)
Partage_Entry= Entry(Fenetre)
Partage_Entry.insert(0, partagesmb)
Partage_Entry.grid(row=1,column=1,ipadx=30)
Utilisateur_Label= Label(Fenetre, text = 'Votre utilisateur :')
Utilisateur_Label.grid(row=2,column=0)
Utilisateur_Entry= Entry(Fenetre)
Utilisateur_Entry.insert(0, utilisateur)
Utilisateur_Entry.grid(row=2,column=1,ipadx=30)
Mot_de_passe_Label= Label(Fenetre, text = 'Votre mot de passe :')
Mot_de_passe_Label.grid(row=3,column=0)
Mot_de_passe_Entry= Entry(Fenetre,show="*") # Saisie masquée du mot de passe
Mot_de_passe_Entry.insert(0, mot_de_passe)
Mot_de_passe_Entry.grid(row=3,column=1,ipadx=30)
Domaine_Label= Label(Fenetre, text = 'Votre nom de domaine:')
Domaine_Label.grid(row=4,column=0)
Domaine_Entry= Entry(Fenetre)
Domaine_Entry.insert(0, domaine)
Domaine_Entry.grid(row=4,column=1,ipadx=30)
Fichier_du_diaporama_Label= Label(Fenetre, text = 'Votre fichier de diaporama :')
Fichier_du_diaporama_Label.grid(row=5,column=0)
Fichier_du_diaporama_Entry= Entry(Fenetre)
Fichier_du_diaporama_Entry.insert(0, fichier_diaporama)
Fichier_du_diaporama_Entry.grid(row=5,column=1,ipadx=30)
Mon_bouton_Lancer_Diaporama = Button(Fenetre, text = 'Lancer le Diaporama', state = DISABLED, command = Lancer_Diaporama)
Mon_bouton_Lancer_Diaporama.grid(row=6,column=0,ipadx=0)
Mon_bouton_Enregistrer = Button(Fenetre, text = ' Enregistrer le fichier ini', command = Enregistrer)
Mon_bouton_Enregistrer.grid(row=6,column=1,ipadx=0)
Mon_bouton_Video = Button(Fenetre, text = ' Transférer et lancer la vidéo', state = DISABLED, command = Video)
Mon_bouton_Video.grid(row=8,column=0,ipadx=0)
Liste_Fichiers = Listbox(Fenetre)
Liste_Fichiers.grid(row=7,column=1,ipadx=30)
Mon_bouton_Lister_Fichiers = Button(Fenetre, text = ' Lister les fichiers du partage depuis ini', state = DISABLED, command = Lister)
Mon_bouton_Lister_Fichiers.grid(row=8,column=1,ipadx=0)
if serveursmb != "vide":
    Mon_bouton_Lister_Fichiers['state'] = NORMAL
    Mon_bouton_Lancer_Diaporama['state'] = NORMAL
    menufichier.entryconfigure(0,state=NORMAL)
    menudiaporama.entryconfigure(0,state=NORMAL)
Fenetre.mainloop() # lance la boucle principale

Téléchargements des fichiers au format ZIP

Dans cette archive zip à télécharger, vous pourrez retrouver les deux fichiers en Python :

Fichiers Python pour l’affichage dynamique


Téléchargements au format PDF

Diapo-py.py

Gestion-Affichage-Dynamique.py

Retour en haut