#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