Contenu | Rechercher | Menus

Annonce

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 10/11/2011, à 14:29

erpiu

[astuce]zenity --progress : bouton Annuler qui marche à tous les coups

Bonjour,

Peut-être avez-vous constaté comme moi qu'il n'est pas toujours possible de faire fonctionner correctement le bouton "Annuler" d'une boîte de dialogue zenity --progress lorsqu’on veut interrompre un traitement lancé sous la forme :

traitement  | zenity --progress ....

Si "traitement" n'écrit pas ou plus sur stdout, "Annuler" n'annule rien. L'option --auto-kill de zenity ne résoud le problème que dans un nombre très limité de cas. Une analyse du fonctionnement de "Annuler" est détaillée dans ce post.

Pour faire fonctionner à tous les coups la touche "Annuler" des boîtes de dialogue  zenity --progress, c'est-à-dire avertir le traitement en cours, quel qu'il soit, de l'action de l'utilisateur, j'ai réalisé une procédure shell, start_and_monitor qui lance le traitement dans un processus P, lui associe une boîte de dialogue de type zenity --progress et, en cas d'appui sur le bouton "Annuler" de la boîte, prévient le processus P en lui envoyant le signal SIGUSR1.

Un exemple d'utilisation simple est :

start_and_monitor --witdth=350 --title="Titre de ma boîte" --pulsate traitement param1 param2

Voici le code de cette procédure start_and_monitor :

#!/bin/bash
# Version 0.1
#
# Procedure start_and_monitor
#    Procedure permettant de déclencher l'exécution d'une procédure ou commande donnée en paramètre et d'en contrôler
#    le déroulement grâce à une interface de type fenêtre de contrôle zenity --progress.
#
#    La procédure contrôlée est lancée dans un processus séparé et peut interagir avec la fenêtre de contrôle zenity
#    en lui envoyant des messages via la procédure send_to_sam (synopsis ci-dessous) ou directement en écrivant
#    sur le file descripteur 3. 
#    Ces messages sont lues par zenity et, suivant leur contenu, peuvent modifier l'affichage du message dans la boîte
#    de dialogue ou celui de la barre de progression (voir man zenity).
#
#    Par ailleurs, la boîte de dialogue est dotée d'une touche "Annuler" qui, lorsqu'elle est cliquée par l'utilisateur
#    provoque l'envoi du signal SIGUSR1 à la procédure contrôlée, procédure qui s'exécute dans un processus séparé.
#
#    La procédure contrôlée peut prévoir dans son corps le handler récupérant et traitant le signal SIGUSR1.
#    Par défaut, la procédure contrôlée est arrêtée avec un code de sortie égal à 128+SIGUSR1 et un message du type 
#    "Signal utilisateur 1 reçu" est affiché sur stderr.
#
#    Synopsis :
#        start_and_monitor [option [option]...] action [param [param]...]
#    
#    où option est l'une des options suivantes d'affichage de la fenêtre zenity (voir man zenity)
#        --width=INT
#        --pulsate
#        --auto-close
#        --percentage=INT
#        --title=STRING
#
#    où action désigne la commande ou procédure à exécuter sous contrôle
#    et où chaque param désigne un paramètre de action.
#
#    Retour :
#        le code de sortie de la procédure contrôlée action
#        et, en cas d'erreur détectée par start_and_monitor :
#            30 : en cas de paramètre erroné
#            31 ou 32 : si l'un des processus nécessaire à l'implantation du mécanisme n'a pu être créé.
#        
#    Synopsis :
#        send_to_sam message
#
#        où message est une chaîne de caractère interprétée par zenity (voir man zenity)
#
#    Exemples :
#        Voir fichier séparé (sam_exemples)
#        Voir notamment exemples 3 et 4 pour le contrôle de tâches lourdes.
#
# Copyright :     erpiu 2011
# License :     GPL V3
# Contact :     erpiu sur forum www.ubuntu-fr.org

# Note d'implantation:
#        start_and_monitor lance deux processus et leur fournit un canal de communication.
#        La procédure contrôlée est lancée dans un processus ctr_proc.
#        La fenêtre zenity est affichée par un processus zen_proc.
#        Les deux processus peuvent communiquer via un fifo accessible :
#            - via le file descriptor 0 (stdin) pour zen_proc,
#            - via le file descriptor 3 pour ctr_proc. 
#        Le fifo est entièrement géré, créé, puis détruit par la procédure start_and_monitor
#        Ainsi, si la procédure contrôlé n'est pas un shell script, la procédure contrôlée peut
#        envoyer des messages à la fenêtre zenity en écrivant sur le fichier correspondant au file descripteur 3.



# Procedure send_to_sam
#    Utilisée depuis la procédure contrôlée
#    Permet d'envoyer un message texte au processus zen_proc
#    Paramètre :
#        $1 : texte du message

function send_to_sam ()
{
    echo "$1" >&3
}


function start_and_monitor ()
{
    local -i ctr_proc_pid=0    # pid du processus ctr_proc
    local -i zen_proc_pid=0    # pid du processus zenity
    local -i ires 
    local -i base_err=29        # toutes les erreurs transmises seront de code > à 29
    
    local fifo_name signal_name
    fifo_name="/tmp/start_and_mon$$_$(date +%H%M%S%N)"    # Fifo de communication avec le processus zenity
        # Nom du fifo en fonction de l'identité du process shell et de l'instant de création ==> unicité
    signal_name="USR1"


    
    # =========== Vérification des paramètres ===========
    local -i fl_err fl_done 
    local -a zen_tab    # tableau des options pour zenity
    local -i zen_tab_ix=0    # et index courant
    local cur_par
    IFS=$'\n'
    fl_err=0
    fl_done=0

    
    if test $# -eq 0; then fl_err=1; fi
    while test $# -gt 0 -a $fl_done -eq 0 -a $fl_err -eq 0; do
        cur_par=$1
        case $cur_par in
            --* )    case $cur_par in
                    --width=* | --pulsate | --auto-close | --percentage=* | --title=*)
                        zen_tab[$zen_tab_ix]="$cur_par" 
                        ((zen_tab_ix++)) ;; # Une option de plus dans le tableau
                    *) fl_err=2 ;;
                esac 
                shift ;;
            *) fl_done=1 ;;
        esac
    done
    if test $fl_done -eq 0; then 
        fl_err=3
    fi
    if test $fl_err -ne 0; then
        case $fl_err in
            1)    echo "Erreur : au moins un paramètre nécessaire" >&2 ;;
            2)    echo "Erreur : paramètre \"$cur_par\" non reconnu" >&2 ;;
            3)    echo "Erreur : action à lancer non spécifée" >&2 ;;
        esac
        return $((base_err+1))    # Paramètres erronés : ne pas aller plus loin
    fi
    
    # Ici $@ désigne la commande à exécuter avec l'ensemble de ses paramètres
    # et les paramètres pour zenity sont dans zen_tab

    # =========== Traitement proprement dit ===========
    
    mkfifo "$fifo_name"    # Création du fifo
    # Création du process zenity
    zenity --progress "${zen_tab[@]}" < "$fifo_name" &
    ires=$?
    if test $ires -eq 0 ; then
        zen_proc_pid=$!

        # Puis lancement de la procédure paramètre
        {
        # Au cas où on est en mode pulsate et si jamais la procédure contrôlée n'envoie rien à zenity
        # forcer l'affichage d'un message vide, ce qui aura au moins pour effet de démarrer le mode
        # pulsate
        send_to_sam "#"    
        
        # Lancement proprement dit dans un sous-processus    
        "$@" &
        
        } 3> "$fifo_name"
        # le tout en ouvrant le fd 3 sur le fifo
         
        ires=$?
        
        if test $ires -eq 0 ; then 
            ctr_proc_pid=$!        
        else
            # Tuer zen_proc car on n'a pas réussi à créer ctr_proc
            kill -TERM $zen_proc_pid
            ires=$((base_err+2))
        fi
    else
        # On n'a pas réussi à créer zen_proc
        ires=$((base_err+3))
    fi
    
    if test $ires -eq 0 ; then
        # Attendre la fin du processus zenity
        wait $zen_proc_pid
        ires=$?
        if test $ires -ne 0 ; then
            # Cas du cancel par l'utilisateur : envoyer le signal à ctr_proc pour le prévenir
            kill -$signal_name $ctr_proc_pid
        fi
        # Attendre la fin du process ctr_proc
        wait $ctr_proc_pid
        ires=$?
    fi
    # Nettoyer
    rm -f "$fifo_name"        
    return $ires
}

Pour mieux comprendre l'utilisation de start_and_monitor, voici un script d'exemples (le nom du fichier paramètre de la commande source est à adapter) :

#!/bin/bash
# 
# Exemples d'utilisation de start_and_monitor
#    Script à lancer dans un terminal pour en observer les effets sur stderr ou stdout
#
source sam_src #Fichier contenant le code de start_and_monitor (nom à adapter si nécessaire)


exemple_suivant() {
    zenity --question --title="Start_and_monitor" --width=400 --text="Passage à exemple $1?"
    if [ $? -ne 0 ]; then exit 1; fi
}

# Exemple 1 : Monitoring d'un processus affichant régulièrement son état d'avancement
#    dans la boîte de dialogue
#    Note : c'est un exemple tout à fait implémentable avec une construction directe
#           du type "traitement | zenity --progress"
traitement1() {
    # Paramètres :     $1 = % d'avancement initial
    #         $2 = Message à afficher
    local -i i
    do_on_usr1() {
        echo "Traitement 1 interrompu à $i% d'avancement" >&2
        exit 1    # Abandon (fin du processus)
        }
    trap do_on_usr1 USR1
    for ((i=$1; i<=100;i=i+10)); do
        send_to_sam "$i"
        send_to_sam "#$2 : $i%"
        sleep 1
    done
    send_to_sam "100"; send_to_sam "#Fin de traitement"
}

start_and_monitor --width=500 --title="Exemple 1" traitement1 5 "Progression de T1"
echo "== Exemple 1 - Code de sortie : $?" >&2

exemple_suivant 2
# Exemple 2 : Monitoring d'un processus n'affichant rien dans la boîte de dialogue
#    (mode pulsate par exemple). Les affichages se font uniquement sur stderr.
#
traitement2() {
    # Paramètres :     $1 = % d'avancement initial
    #         $2 = Message à afficher
    local -i i
    do_on_usr1() {
        echo "Traitement 2 interrompu à $i% d'avancement" >&2
        exit 1 # Abandon (fin du processus)
        }
    trap do_on_usr1 USR1
    for ((i=$1; i<=100;i=i+10)); do
        echo "$2 : $i%" >&2
        sleep 1
    done
    echo "Fin de traitement" >&2
}    


start_and_monitor --width=500 --title="Exemple 2" --pulsate traitement2 10 "Avancement de T2"
echo "== Exemple 2 - Code de sortie : $?" >&2

exemple_suivant "2 (sans start_and_monitor)"
# Note : avec une construction directe "traitement | zenity --progress --pulsate"
#    un tel traitement ne peut être annulé par un clic sur le bouton "Annuler"
#
traitement2 10 "Avancement" | zenity --progress --pulsate --width=400 --title="Exemple 2 sans start_and_monitor"


exemple_suivant 3
# Exemple 3 : Monitoring d'un processus consommateur de CPU ou d'affichage,
#    non prévu pour être monitoré, mais pouvant tout de même être stoppé
#    par un clic sur "Annuler".

start_and_monitor --width=300 --title="Exemple 3" --pulsate find $HOME -name '*a*'
echo "== Exemple 3 - Code de sortie : $?" >&2


exemple_suivant 4
# Exemple 4 : Monitoring d'un processus consommateur de CPU ou d'affichage
#    mais prévu pour récupérer une interruption suite à un clic sur "Annuler"
#
traitement4() {
    local -i pid
    do_on_usr1() {
        kill -TERM $pid
        echo "Traitement 4 interrompu - Process find stoppé" >&2
        return # Retour (derrière wait)
        }
    trap do_on_usr1 USR1
    # Lancement de la commande find (supposée durer longtemps) dans un sous-process
    # Note importante : si find était exécuté directement dans le même process, comme le
    #    trap do_on_usr1 n'est éxécuté par le shell qu'à la fin de la commande en cours,
    #    on ne pourrait traiter le signal d'Annulation qu'à la fin de l'exécution de
    #    la commande, ce qui serait sans intérêt.
    find $HOME -name '*a*' &
    pid=$!
    wait $pid
}
start_and_monitor --width=300 --pulsate --title="Exemple 4" traitement4
echo "== Exemple 4 - Code de sortie : $?" >&2

Peut-être cette procédure peut-elle servir aussi à certains d'entre vous.
En tous cas, merci d'avance de vos commentaires,  suggestions et ... rapports de bugs!

Hors ligne

#2 Le 21/12/2011, à 01:08

compte supprimé

Re : [astuce]zenity --progress : bouton Annuler qui marche à tous les coups

J'avais aussi était confronté à ce problème dans mon script ci-dessous :

#!/bin/bash

# AUTEUR: Orion79 ( www.linuxmint-fr.org/ et www.ubuntu-fr.org/ )
# LICENSE: GPL
# DESCRIPTION: Crée un backup des DVD sur le disque dur
# REQUIRES: zenity, dvdbackup

#######################################################################################################
# RAPPEL LEGAL
#######################################################################################################

# La copie de DVD protégés, même à des fins personnelles, est prohibée dans de nombreux pays ! 
# Suivez la suite de ces opérations que si la législation de votre pays le permet !


#######################################################################################################
#CONFIGURATION
#######################################################################################################
dvd_device="/dev/sr0"
dossier_destination="/home/$USER/Bureau"
disc_title=$(lsdvd -dvd ${dvd_device} | grep ^"Disc Title:" | cut -c 13-) # Recherche nom du DVD
path_media=$(df | grep ${dvd_device} |  awk -F "/media" '{print $2}' |  awk -F " " '{print "/media"$1"*"}')
#######################################################################################################
# Fonction ZENITY_QUESTION
#######################################################################################################
ZENITY_QUESTION()
{
zenity --question --text="Voulez-vous lancer le backup de votre DVD ?"
}
#######################################################################################################
# Fonction ZENITY_NOM_PROJET
#######################################################################################################
ZENITY_NOM_PROJET()
{
nom_projet=$(zenity --entry --title="" --text="Entrez le nom de votre projet" --entry-text="${disc_title}")
if   [[ $? = "1" ]]
then exit 0
fi

# Vérification si le nom du dossier exsite déjà si oui proproition de le supprimer
if   [[ -d "${dossier_destination}/${nom_projet}" ]] 
then zenity --question --title="" --text="Le dossier ${nom_projet} exsite déjà ! Voulez-vous continuer et le mettre à la corbeille ?" 

          if   [[ $? = "0" ]]
          then mv ${dossier_destination}/${nom_projet} /home/$USER/.local/share/Trash/files/${nom_projet}"_$(date +%Y.%m.%d_%H:%M:%S)"
               sleep 2
          else ZENITY_NOM_PROJET
          fi

     else ZENITY_NOM_PROJET
fi
}
#######################################################################################################
# Fonction DVDBACKUP
#######################################################################################################
DVDBACKUP()
{
dvdbackup -v -M -i ${dvd_device} -o ${dossier_destination} -n ${nom_projet} | ZENITY_PROGRESS
}
#######################################################################################################
# Fonction ZENITY_ERROR
#######################################################################################################
ZENITY_ERROR()
{
zenity --error --text="Processus interrompu..."
exit 0
}
#######################################################################################################
# Fonction ZENITY_PROGRESS_SEIZE
#######################################################################################################
ZENITY_PROGRESS()
{
pid=$(pgrep dvdbackup) # Recherche numéro du processus

while [ ! -z ${pid} ] 
do    taille_source=$(du -c ${path_media} | grep "total" | awk '{print $1}')
      taille_copie=$(du -c "${dossier_destination}/${nom_projet}" | grep "total" | awk '{print $1}')
      taille_source_humain=$(du -ch ${path_media} | grep "total" | awk '{print $1}')
      taille_copie_humain=$(du -ch "${dossier_destination}/${nom_projet}" | grep "total" | awk '{print $1}')

      pourcent=$(echo "scale=2; ${taille_copie}*100/${taille_source}" | bc | awk -F "." '{print $1}')

echo "${pourcent}" # Renvoi la valeur du pourcent à la barre de progression de zenity
echo "# Pourcent copie sur le disque dur --> ${pourcent} %"
done | zenity --progress --auto-close --width=600 --height=200 --title="Dvdbackup copie le DVD sur le disque dur"

if [[ $? -eq "1" ]]
then kill ${pid}
     ! echo # le point ! inverse le code de retour de 0 à 1
fi
}
#######################################################################################################

ZENITY_QUESTION && ZENITY_NOM_PROJET && DVDBACKUP && ZENITY_QUESTION || ZENITY_ERROR

#######################################################################################################

J'étais arrivé à pallier à ce problème d'annuler avec un simple " ! echo" et les && et ||.

La partie inintéressante de ce script est :

if [[ $? -eq "1" ]]
then kill ${pid}
     ! echo # le point ! inverse le code de retour de 0 à 1
fi
}
#######################################################################################################

ZENITY_QUESTION && ZENITY_NOM_PROJET && DVDBACKUP && ZENITY_QUESTION || ZENITY_ERROR

#######################################################################################################

Petite explication en appuyant sur Annuler de la fenêtre de pourcent copie, Annuler renvoie le code de retour 1 qui est utiliser pour tuer backup ... if [[ $? -eq "1" ] ]...

! echo  --> le point ! inverse le code de retour de 0 à 1 ce qui permet :
si Ok renvoi 0 --> && ZENITY_QUESTION
si Annuler renvoi 1 --> || ZENITY_ERROR

Dernière modification par Orion79 (Le 21/12/2011, à 01:10)

#3 Le 30/04/2012, à 16:48

Hizoka

Re : [astuce]zenity --progress : bouton Annuler qui marche à tous les coups

Orion79 => ca marche pas ton truc car tant que la commande du pipe de zenity n'est pas arrêté, le if n'est pas exécuté...

erpiu => je vais tester ça.

merci à vous


KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github

Hors ligne

#4 Le 30/04/2012, à 16:56

Hizoka

Re : [astuce]zenity --progress : bouton Annuler qui marche à tous les coups

j'ai un peu de mal a piger ton script erpiu, comment on fait avec pour action :

wget -v http://ubuntuone.com/4QTK2lJPmxfyelOkLHoeCs

merci


KDE Neon 64bits
Tous mes softs (MKVExtractorQt, HizoSelect, HizoProgress, Qtesseract, Keneric, Services menus...) sont sur github

Hors ligne

#5 Le 04/05/2012, à 08:42

erpiu

Re : [astuce]zenity --progress : bouton Annuler qui marche à tous les coups

Bonjour,

Désolé de répondre avec un peu de retard. Ces derniers jours étaient un peu chauds..

La commande wget -v .... que tu veux "monitorer" affiche par défaut ses messages d'avancement sur stderr. D'après mes test, wget récupère le signal usr1 et, dans ce cas, bascule l'affichage des messages d'avancement sur un fichier log (wget-log) et continue son travail sans sourciller. Il faut donc l'arrêter explicitement lorsque usr1 est reçu.

Pour contrôler une telle commande, il faut ainsi utiliser la méthode décrite dans l'exemple N°4 que j'ai donné dans mon premier post. Il faut contrôler par start_and_monitor une procédure de traitement qui :
- lance wget dans un sous-processus P,
- et récupère le signal usr1 pour arrêterr le processus P.

Cela donnerait le script suivant :

source "fichier où se trouve le source de start_and_monitor"
traitement() {
    local -i pid
    do_on_usr1() {
        kill -TERM $pid
        echo "wget interrompu par utilisateur - Abandon" >&2
        exit 1    # Abandon (fin du processus)
        }
    trap do_on_usr1 USR1
    wget -v http://ubuntuone.com/4QTK2lJPmxfyelOkLHoeCs &
    pid=$!
    wait $pid
    }
start_and_monitor --width=300 --title="Test wget" --pulsate traitement
echo "== Test wget - Code de sortie : $?" >&2

Hors ligne

#6 Le 09/07/2017, à 09:17

kholo

Re : [astuce]zenity --progress : bouton Annuler qui marche à tous les coups

je déterre le topic !
exemple de zenity progress sur un wget : voir cette page
NB : l'annulation sur le progress interrompra le processus

ce script est à adapter :

# mettre le lien ici
set "http://..."
#!/bin/bash
# ----------------------------------------------
nomlogiciel="${0##*/}"
FONCTION="tuto : zenity progress sur un wget
affiche la vitesse de téléchargement et le temps restant"
VERSION="alpha"
# NOTES DE VERSIONS
# ----------------------------------------------
# 
# ----------------------------------------------
# mettre le lien ici
set "http://..."

wget "$@" 2>&1 | sed -u 's/.* \([0-9]\+%\)\ \+\([0-9.]\+.\) \(.*\)/\1\n# Downloading at \2\/s, ETA \3/' | zenity --progress --title="Downloading" --auto-close

#Start a loop testing if zenity is running, and if not kill wget
RUNNING=0
while [ $RUNNING -eq 0 ]
do
if [ -z "$(pidof zenity)" ]
then
  pkill wget
  RUNNING=1
fi
done

exit 0

Hors ligne