Compare commits
4 commits
python-ver
...
main
Author | SHA1 | Date | |
---|---|---|---|
c0c6f8e718 | |||
|
4a67f35cd3 | ||
|
e20c49b01e | ||
5f98004851 |
9 changed files with 266 additions and 869 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +0,0 @@
|
||||||
__pycache__/
|
|
||||||
*~
|
|
|
@ -1,28 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Dernière révision : 2021/10/31
|
|
||||||
# Auteur : Zatalyz. Merci à YannK, Tycho, Pulkomandy, Madi, Link Mauve et Glorf pour leurs nombreux conseils, les explications, l'aide et la patience !
|
|
||||||
# Licence CC0
|
|
||||||
# Ce script permet de ne garder que l'alentours et les emotes.
|
|
||||||
# Système adapté au nouveau système de log (à la fois plus simple puisque pas besoin du syslog,
|
|
||||||
# et un peu moins régulier dans les noms...)
|
|
||||||
# Fonctionnement idéal : /usr/bin/bash ./1analyse_new_logryzom.sh fichier_entrée fichier_sortie
|
|
||||||
# Par exemple :
|
|
||||||
# ./1analyse_new_logryzom.sh log_zatalyz.txt log_zatalye_alentours.txt
|
|
||||||
# On peut aussi renseigner les noms et chemins ici sur les variables :
|
|
||||||
logfile1="$1"
|
|
||||||
logsortie="$2"
|
|
||||||
# Canaux à garder
|
|
||||||
# Alentours + Endroit où on est
|
|
||||||
grep 'SAY\|SHOUT\|SYSTEM\/ZON' "$logfile1" > templog.txt
|
|
||||||
# vire le timestamp de début des lignes. Garde le * si ça peut servir à nettoyer le sys.info..
|
|
||||||
cut -b 21- templog.txt > templog2.txt
|
|
||||||
# Enlève les couleurs des canaux
|
|
||||||
sed 's/@{[A-F0-9]\{4\}}//g' templog2.txt > templog3.txt
|
|
||||||
# enlever le nom des canaux :
|
|
||||||
sed -re 's:^\([A-Z/]+\) +\* +(.*)$:\1:' templog3.txt > templog4.txt
|
|
||||||
# laisser uniquement les phrases traduites (attention, ça peut être étrange)
|
|
||||||
sed -re 's#\{:[a-zA-Z]{2}:[^}]+\}@\{[[:space:]]##' templog4.txt > templog5.txt
|
|
||||||
# Si quelqu'un joue avec des caractères bizarres, ça remet tout d'aplomb
|
|
||||||
iconv -t utf-8 -c templog5.txt > "$logsortie"
|
|
||||||
# Enlever les fichiers temporaires
|
|
||||||
rm templog*.txt
|
|
164
2split_log.sh
164
2split_log.sh
|
@ -1,164 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
# Dernière révision : 2021/10/31
|
|
||||||
# Auteur : Zatalyz. Merci à YannK, Tycho et Glorf pour leurs nombreux conseils, les explications, l'aide et la patience !
|
|
||||||
# Licence CC0
|
|
||||||
# Ce script sépare les logs en 1 fichier de log par jour.
|
|
||||||
# Il est prévu pour les logs du jeu Ryzom mais peut s'adapter à d'autres types de log (virer "pseudo !")
|
|
||||||
# Mettre tous les logs en vrac dans le même dossier que le script, et lancer la moulinette.
|
|
||||||
# La bonne commande à passer est
|
|
||||||
# cmd ./dossiersource ./dossier final [nomperso]
|
|
||||||
# Sans argument des variables par défaut seront utilisées. Le nom du perso peut être déduit des logs
|
|
||||||
# Exemple :
|
|
||||||
# ./split_log.sh ./logbrut/zatalyz/ ./final john
|
|
||||||
# => les fichiers seront traités depuis ./logbrut/zatalyz/, et mis dans le dossier ./final sous le nom de log_john_année_mois_jour.txt
|
|
||||||
# sans préciser "john", le nom des logs sera probablement celui de zatalyz, si les fichiers d'origine sont bien formatés comme log_zatalyz.txt ou log_zatalyz_*.txt
|
|
||||||
# Le script range aussi les logs.
|
|
||||||
# Attention ! Il laisse quelques fichiers dans le dossier courant.
|
|
||||||
# Attention ! Gardez les logs originaux, dans quelques cas les lignes
|
|
||||||
# ne sont pas analysées correctement (cas des retours à la ligne comme
|
|
||||||
# dans les poèmes : la ligne ne commence plus par une date).
|
|
||||||
|
|
||||||
|
|
||||||
#############
|
|
||||||
# Variables #
|
|
||||||
#############
|
|
||||||
# Variables par défaut dans les checks : pouvoir les changer ?
|
|
||||||
|
|
||||||
# Vérification si le dossier source des logs existe et est renseigné
|
|
||||||
if [ -d "$1" ]
|
|
||||||
then originalfolderlog="$1"
|
|
||||||
else originalfolderlog="./logsource"
|
|
||||||
fi
|
|
||||||
# Vérification si le dossier final des logs existe et est renseigné
|
|
||||||
if [ -d "$2" ]
|
|
||||||
then finalfolder="$2"
|
|
||||||
else finalfolder="./final_logs"
|
|
||||||
fi
|
|
||||||
# Vérification si un nom de perso est donné et sinon, extraction depuis les noms de fichier
|
|
||||||
if [ -z "$3" ]
|
|
||||||
then
|
|
||||||
myfunc() {
|
|
||||||
basename -a "$originalfolderlog"/* | cut -d_ -f2 | uniq
|
|
||||||
}
|
|
||||||
perso="$(myfunc)"
|
|
||||||
else perso="$3"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Résultat final : nous partons de $originalfolderlog pour mettre les logs de $perso dans $finalfolder"
|
|
||||||
|
|
||||||
# Variables des dossiers de travail
|
|
||||||
tmpfolder="./tmplogfolder"
|
|
||||||
listrawlog="$tmpfolder/listrawlog.txt"
|
|
||||||
rawfolder="$tmpfolder/raw_logs"
|
|
||||||
yearfolder="$tmpfolder/year_logs"
|
|
||||||
monthfolder="$tmpfolder/month_logs"
|
|
||||||
dayfolder="$tmpfolder/day_logs"
|
|
||||||
|
|
||||||
#############
|
|
||||||
# Script #
|
|
||||||
#############
|
|
||||||
# Vérifier si on a les dossiers temporaires de travail et sinon, les créer
|
|
||||||
if [ -d "$rawfolder" ];then
|
|
||||||
echo "Le dossier $rawfolder existe, passons à la suite";
|
|
||||||
else
|
|
||||||
mkdir -p "$rawfolder"
|
|
||||||
echo "Le dossier $rawfolder a été créé, passons à la suite";
|
|
||||||
fi
|
|
||||||
if [ -d "$yearfolder" ];then
|
|
||||||
echo "Le dossier $yearfolder existe, passons à la suite";
|
|
||||||
else
|
|
||||||
mkdir -p "$yearfolder"
|
|
||||||
echo "Le dossier $yearfolder a été créé, passons à la suite";
|
|
||||||
fi
|
|
||||||
if [ -d "$monthfolder" ];then
|
|
||||||
echo "Le dossier $monthfolder existe, passons à la suite";
|
|
||||||
else
|
|
||||||
mkdir -p "$monthfolder"
|
|
||||||
echo "Le dossier $monthfolder a été créé, passons à la suite";
|
|
||||||
fi
|
|
||||||
if [ -d "$dayfolder" ];then
|
|
||||||
echo "Le dossier $dayfolder existe, passons à la suite";
|
|
||||||
else
|
|
||||||
mkdir -p "$dayfolder"
|
|
||||||
echo "Le dossier $dayfolder a été créé, passons à la suite";
|
|
||||||
fi
|
|
||||||
if [ -d "$finalfolder" ];then
|
|
||||||
echo "Le dossier $finalfolder existe, passons à la suite";
|
|
||||||
else
|
|
||||||
mkdir -p "$finalfolder"
|
|
||||||
echo "Le dossier $finalfolder a été créé, passons à la suite";
|
|
||||||
fi
|
|
||||||
|
|
||||||
# On ne travaille pas sur les fichiers sources. Jamais.
|
|
||||||
# Parce qu'il y a parfois des blagues dans les logs, on nettoie l'encodage.
|
|
||||||
for file in "$originalfolderlog"/*
|
|
||||||
do
|
|
||||||
originallog="$(basename $file)"
|
|
||||||
cat "$file" | tr -d "\000-\010" | tr -d "\016-\037" > "$rawfolder"/"$originallog"
|
|
||||||
done
|
|
||||||
# on va faire un très gros fichier avec tous les logs. Évitez de l'ouvrir avec un éditeur de texte basique.
|
|
||||||
cat "$rawfolder"/* > "$tmpfolder"/logcomplet_"$perso".txt
|
|
||||||
|
|
||||||
#Et on va faire un fichier de controle donnant toutes les dates avec logs, et les lignes foireuses (genre chants) (à vérifier manuellement en cas de doute)
|
|
||||||
cut -c 1-11 "$tmpfolder"/logcomplet_"$perso".txt | sort | uniq > controlline_"$perso".txt
|
|
||||||
echo "$tmpfolder"/logcomplet_"$perso".txt et controlline_"$perso".txt créés
|
|
||||||
|
|
||||||
for logname in "$tmpfolder"/logcomplet_"$perso".txt
|
|
||||||
do
|
|
||||||
# Vérification que les fichiers sont bien des fichiers, ce serait bête de planter le script pour un manque de vérification
|
|
||||||
if [ -f "$logname" ] ; then
|
|
||||||
echo "$logname est un fichier et va être traité"
|
|
||||||
# On va faire un seul gros fichier par année
|
|
||||||
for year in {2009..2025} ; do
|
|
||||||
#echo "${year}"
|
|
||||||
grep "^${year}/" "$logname" >> "$yearfolder"/"${year}".log
|
|
||||||
# Effacer les fichiers vides
|
|
||||||
[ -s "$yearfolder"/"${year}".log ] || rm -f "$yearfolder"/"${year}".log
|
|
||||||
#echo "${year} traitée"
|
|
||||||
# On vérifie si le fichier d'année existe et on ne traite que celles qui existent
|
|
||||||
if [ -f "$yearfolder"/"${year}".log ] ; then
|
|
||||||
# Puis on va faire un fichier par mois
|
|
||||||
for month in {01..12} ; do
|
|
||||||
# echo "${year}/${month} en cours"
|
|
||||||
grep "^${year}/${month}" "$yearfolder"/"${year}".log > "$monthfolder"/"${year}_${month}".log
|
|
||||||
[ -s "$monthfolder"/"${year}_${month}".log ] || rm -f "$monthfolder"/"${year}_${month}".log
|
|
||||||
# On vérifie si le fichier année/mois existe et on ne traite que ceux qui existent
|
|
||||||
if [ -f "$monthfolder"/"${year}_${month}".log ] ; then
|
|
||||||
#echo "$monthfolder"/"${year}_${month}".log
|
|
||||||
# Puis un fichier par jour
|
|
||||||
for day in {01..31} ; do
|
|
||||||
#echo "${year}/${month}/${day} en cours"
|
|
||||||
grep "^${year}/${month}/${day}" "$monthfolder"/"${year}_${month}".log > "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".log
|
|
||||||
[ -s "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".log ] || rm -f "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".log
|
|
||||||
#echo "${year}/${month}/${day} traité"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
else
|
|
||||||
echo "$logname n'est pas un fichier, il n'a pas été traité"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
#Tri des fichiers dans des dossiers du type Dossier_trié/perso/année/mois/log.log
|
|
||||||
# On récupère la date, en formattant comme pour les logs
|
|
||||||
for f in $dayfolder/*.log
|
|
||||||
do
|
|
||||||
year=${f: -14:4}
|
|
||||||
month=${f: -9:2}
|
|
||||||
day=${f: -6:2}
|
|
||||||
|
|
||||||
# On se fait une jolie variable qui imite la partie datée du nom des fichiers de log
|
|
||||||
log=log_"$perso"_"${year}_${month}_${day}.log"
|
|
||||||
# on créé le dossier de chaque mois dans les archives s'il n'existe pas
|
|
||||||
mkdir -p "$finalfolder"/"$perso"/"$year"/"$month/"
|
|
||||||
# on déplace ces logs dans leur archive
|
|
||||||
mv "$dayfolder"/"$log" "$finalfolder"/"$perso"/"$year"/"$month"/"$log"
|
|
||||||
done
|
|
||||||
echo "Les fichiers ont été découpés et rangés"
|
|
||||||
|
|
||||||
# Nettoyage
|
|
||||||
#rm -r "$tmpfolder"
|
|
||||||
echo "faites un rm -r $tmpfolder si le dossier tmp était bien JUSTE pour le script..."
|
|
||||||
echo "Tout est bon. Contrôlez le nombre de fichiers entre $finalfolder et controlline_$perso.txt"
|
|
77
README.md
77
README.md
|
@ -1,38 +1,38 @@
|
||||||
# Python version
|
|
||||||
The script uses basic python3 with no additional 3rd part libraries. The main file is main.py and the only other file it uses is tk_tooltip.py.
|
|
||||||
To just run the GUI, run:
|
|
||||||
`python3 main.py`
|
|
||||||
|
|
||||||
## Mac executable
|
|
||||||
Executable for Mac can be downloaded from https://ryzom.siela1915.com/download/ryzom_log_cleaner_mac.zip (Unzip it and then right-click -> Open the executable to open)
|
|
||||||
|
|
||||||
##
|
|
||||||
If you want to create a binary for distribution, it seems that pyinstaller is the easiest way.
|
|
||||||
Just run:
|
|
||||||
|
|
||||||
`pip3 install pyinstaller`
|
|
||||||
`pyinstaller --onefile --noconsole --clean --log-level=WARN --strip main.py tk_tooltip.py`
|
|
||||||
|
|
||||||
|
|
||||||
# Gestion des logs
|
# Gestion des logs
|
||||||
Ensemble de scripts bash pour nettoyer les logs clients de Ryzom
|
Cet ensemble de script sert à traiter les logs des personnages de Ryzom (action à activer en jeu avec /chatlog). Grâce à ça, on peut ensuite partager et relire les bêtises qu'on a raconté avec les amies.
|
||||||
|
|
||||||
## Analyse New log
|
## Fonctionnement
|
||||||
Pour analyser les logs d'après 2012/2013, par là.
|
Pour analyser les logs d'après 2012/2013 (au moment où le format a changé).
|
||||||
|
|
||||||
Fonctionnement idéal : `/bin/bash ./1analyse_new_logryzom.sh fichier_entrée fichier_sortie`
|
Lancez
|
||||||
|
./clean _log.sh
|
||||||
|
Et regarder ce qui est dit.
|
||||||
|
|
||||||
Par exemple :
|
Mettez les logs d'un seul perso dans le dossier "sources_brutes".
|
||||||
|
Cela ne prends pas les persos mélangés (pas encore...).
|
||||||
|
|
||||||
`./1analyse_new_logryzom.sh log_zatalyz.txt log_zatalye_alentours.txt`
|
On peut aussi passer des arguments pour préciser les dossiers, mais quel intérêt ? Voir options dans le script.
|
||||||
|
|
||||||
|
Le script va découper les logs de base pour avoir un fichier par jour, puis analyser chacun de ces fichiers afin d'en avoir une version nettoyée avec uniquement les infos qu'on souhaite garder (généralement le rp en alentours).
|
||||||
|
|
||||||
|
Attention ! Dans quelques cas les lignes ne sont pas analysées correctement (cas des retours à la ligne comme dans les poèmes : la ligne ne commence plus par une date). Gardez les logs originaux et controllez manuellement selon les indications du script.
|
||||||
|
|
||||||
|
### Vieille version des logs et snippet
|
||||||
|
Pour les logs d'avant le changement de système (donc avant 2013, je crois) : afin de pouvoir continuer à nettoyer ce genre de log, le script "analyse_old_logryzom.sh" est là. À prendre tel quel. Il FAUT un fichier sysinfo.ini, qui contient toutes les expressions régulières à filtrer (tout ce qui est dans le sys.infos). C'était plus lourd. Les canaux sont gardés selon un code couleur. À documenter, un jour, peut-être, ou pas : ça se retrouve en regardant les logs.
|
||||||
|
|
||||||
|
Le script "erase_all.sh" (qui est un snippet plus qu'un script) permet de remettre son dossier à neuf avant de relancer une analyse. Évitez de le lancer si vous ne voulez pas tout effacer, après c'est perdu pour de vrai.
|
||||||
|
|
||||||
|
## Liste des canaux
|
||||||
|
Si on veut filtrer autrement. Par défaut, le script est réglé pour l'alentours (emotes et cris compris) + une indication sur les zones traversées, afin de suivre quand les persos se déplacent.
|
||||||
|
|
||||||
Liste des canaux, si on veut filtrer autrement :
|
|
||||||
- SAY : alentours
|
- SAY : alentours
|
||||||
- SAY/EMT : emotes
|
- SAY/EMT : emotes
|
||||||
- SAY/BBL : messages de PNJ
|
- SAY/BBL : messages de PNJ
|
||||||
- SAY/SHOUT : cris
|
- SAY/SHOUT : cris
|
||||||
|
|
||||||
- UNIVERSE : Univers
|
- UNIVERSE : Univers
|
||||||
- REGION : comme indiqué
|
- REGION : comme indiqué
|
||||||
|
|
||||||
- SYSTEM : messages systèmes
|
- SYSTEM : messages systèmes
|
||||||
- SYSTEM/BC : Broadcast (annonces des administrateurs)
|
- SYSTEM/BC : Broadcast (annonces des administrateurs)
|
||||||
- SYSTEM/AROUND : messages en alentours qui ne sont pas dit par des homins (genre "bienvenue sur ryzom)
|
- SYSTEM/AROUND : messages en alentours qui ne sont pas dit par des homins (genre "bienvenue sur ryzom)
|
||||||
|
@ -49,39 +49,16 @@ Liste des canaux, si on veut filtrer autrement :
|
||||||
- SYSTEM/TSK : informations à propos des missions
|
- SYSTEM/TSK : informations à propos des missions
|
||||||
- SYSTEM/XP : expérience gagnée
|
- SYSTEM/XP : expérience gagnée
|
||||||
- SYSTEM/THM : encyclopédie
|
- SYSTEM/THM : encyclopédie
|
||||||
|
|
||||||
- GUILD/MTD : Mot du jour de la Guilde
|
- GUILD/MTD : Mot du jour de la Guilde
|
||||||
- GUILD : Messages en guilde
|
- GUILD : Messages en guilde
|
||||||
|
|
||||||
- DYN0, DYN1, DYN3, etc : canaux dynamiques. Le canal de langue est souvent sur DYN0
|
- DYN0, DYN1, DYN3, etc : canaux dynamiques. Le canal de langue est souvent sur DYN0
|
||||||
|
|
||||||
- TELL : messages privés
|
- TELL : messages privés
|
||||||
- TEAM : messages en équipe
|
- TEAM : messages en équipe
|
||||||
|
|
||||||
|
|
||||||
## Analyse Old Log
|
|
||||||
Pour les logs d'avant le changement de système (donc avant 2013, je crois). Il FAUT un fichier sysinfo.ini, qui contient toutes les expressions régulières à filtrer (tout ce qui est dans le sys.infos). C'était plus lourd. Les canaux sont gardés selon un code couleur. À documenter, un jour, peut-être, ou pas : ça se retrouve en regardant les logs.
|
|
||||||
|
|
||||||
## Splitlog
|
|
||||||
Ce script sépare les logs en 1 fichier de log par jour.
|
|
||||||
|
|
||||||
Il est prévu pour les logs du jeu Ryzom mais peut s'adapter à d'autres types de log (virer "pseudo !")
|
|
||||||
|
|
||||||
Mettre tous les logs en vrac dans le même dossier que le script, et lancer la moulinette.
|
|
||||||
|
|
||||||
La bonne commande à passer est
|
|
||||||
|
|
||||||
`cmd ./dossiersource ./dossier final [nomperso]`
|
|
||||||
|
|
||||||
Sans argument des variables par défaut seront utilisées. Le nom du perso peut être déduit des logs
|
|
||||||
|
|
||||||
Exemple :
|
|
||||||
|
|
||||||
`./split_log.sh ./logbrut/zatalyz/ ./final john`
|
|
||||||
|
|
||||||
=> les fichiers seront traités depuis ./logbrut/zatalyz/, et mis dans le dossier ./final sous le nom de log_john_année_mois_jour.txt sans préciser "john", le nom des logs sera probablement celui de zatalyz, si les fichiers d'origine sont bien formatés comme log_zatalyz.txt ou log_zatalyz_*.txt
|
|
||||||
|
|
||||||
Le script range aussi les logs.
|
|
||||||
|
|
||||||
Attention ! Il laisse quelques fichiers dans le dossier courant. Gardez les logs originaux, dans quelques cas les lignes ne sont pas analysées correctement (cas des retours à la ligne comme dans les poèmes : la ligne ne commence plus par une date).
|
|
||||||
|
|
||||||
## Crédits et licence
|
## Crédits et licence
|
||||||
|
|
||||||
Auteur : Zatalyz. Tout est sous licence CC0, c'est de l'assemblage de bons conseils et de tests, rien de transcendant. Plus de détail dans chaque script.
|
Auteur : Zatalyz. Tout est sous licence CC0, c'est de l'assemblage de bons conseils et de tests, rien de transcendant. Plus de détail dans chaque script.
|
||||||
|
|
||||||
|
|
235
clean_log.sh
Executable file
235
clean_log.sh
Executable file
|
@ -0,0 +1,235 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Dernière révision : 2022/02/14
|
||||||
|
# Auteur : Zatalyz. Merci à YannK, Tycho et Glorf pour leurs nombreux conseils, les explications, l'aide et la patience !
|
||||||
|
# Licence CC0
|
||||||
|
# Ce script fait tout en un coup !
|
||||||
|
# 1) Splitter les logs de Ryzom qu'on lui file et les ranger dans les bons dossiers
|
||||||
|
# 2) Nettoyer et ne garder que les logs alentours
|
||||||
|
# Et ceci avec des lots de fichiers.
|
||||||
|
# ! Il faut que ce soit un seul perso par contre, ça ne veut pas marcher sinon !
|
||||||
|
# Attention, le script cherche en "dur" des années 2009 à 2025. Si les logs sont en dehors de ces dates, corrigez.
|
||||||
|
# Syntaxe : par défaut il va tout trouver et dire s'il y a un souci. Lancez simplement
|
||||||
|
# ./clean _log.sh
|
||||||
|
# Mais si besoin de préciser :
|
||||||
|
# ./clean _log.sh dossier_source dossier_tri dossier_alentours perso
|
||||||
|
# dossier_source = dossier des logs bruts au format ryzom
|
||||||
|
# dossier_tri = dossier de tri des fichiers bruts en un fichier par jour
|
||||||
|
# dossier_alentours = dossiers avec les fchiers de logs nettoyés pour ne garder que "alentours".
|
||||||
|
# perso = nom du perso
|
||||||
|
# Attention ! Gardez les logs originaux, dans quelques cas les lignes
|
||||||
|
# ne sont pas analysées correctement (cas des retours à la ligne comme
|
||||||
|
# dans les poèmes : la ligne ne commence plus par une date). Cela se controle avec le fichier
|
||||||
|
#############
|
||||||
|
# Variables #
|
||||||
|
#############
|
||||||
|
# Canaux à analyser
|
||||||
|
channels="SAY\|SHOUT\|SYSTEM\/ZON"
|
||||||
|
# Vérification si le dossier source des logs existe et est renseigné
|
||||||
|
if [ -d "$1" ]
|
||||||
|
then foldersource="$1"
|
||||||
|
echo "Le dossier $foldersource existe, passons à la suite";
|
||||||
|
else foldersource="./sources_brutes"
|
||||||
|
if [ -d "$foldersource" ]
|
||||||
|
then echo "Le dossier $foldersource existe, passons à la suite";
|
||||||
|
else
|
||||||
|
mkdir -p "$foldersource"
|
||||||
|
echo "Le dossier $foldersource n'existait pas et été créé, déplacez les logs sources dedans";
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
# Vérification si le dossier des logs bruts triés existe et sinon, le créer
|
||||||
|
if [ -d "$2" ]
|
||||||
|
then folderrawsorted="$2"
|
||||||
|
echo "Le dossier $folderrawsorted existe, passons à la suite";
|
||||||
|
else folderrawsorted="./sources_tri"
|
||||||
|
if [ -d "$folderrawsorted" ]
|
||||||
|
then echo "Le dossier $folderrawsorted existe, passons à la suite";
|
||||||
|
else mkdir -p "$folderrawsorted"
|
||||||
|
echo "Le dossier $folderrawsorted a été créé, passons à la suite";
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Vérifier si on a le dossier pour les logs alentours et sinon, le créer
|
||||||
|
if [ -d "$3" ]
|
||||||
|
then folderaround="$3"
|
||||||
|
echo "Le dossier $folderaround existe, passons à la suite";
|
||||||
|
else folderaround="./alentours"
|
||||||
|
if [ -d "$folderaround" ]
|
||||||
|
then echo "Le dossier $folderaround existe, passons à la suite";
|
||||||
|
else mkdir -p "$folderaround"
|
||||||
|
echo "Le dossier $folderaround a été créé, passons à la suite";
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extraire le nom du persos depuis les noms de fichier
|
||||||
|
# TODO truc améliorable : arriver à gérer plusieurs persos
|
||||||
|
if [ -z "$4" ]
|
||||||
|
then
|
||||||
|
for file1 in "$foldersource"/*
|
||||||
|
do
|
||||||
|
myfunc() {
|
||||||
|
basename "$file1" .txt | cut -d_ -f2 | uniq
|
||||||
|
}
|
||||||
|
perso="$(myfunc)"
|
||||||
|
done
|
||||||
|
else perso="$4"
|
||||||
|
fi
|
||||||
|
# Avoir une variable avec la majuscule au pseudo
|
||||||
|
pseudo=${perso^}
|
||||||
|
|
||||||
|
# Dossiers nécessaires à faire le travail
|
||||||
|
tmpfolder="./tmplogfolder"
|
||||||
|
listrawlog="$tmpfolder/listrawlog.txt"
|
||||||
|
rawfolder="$tmpfolder/raw_logs"
|
||||||
|
yearfolder="$tmpfolder/year_logs"
|
||||||
|
monthfolder="$tmpfolder/month_logs"
|
||||||
|
dayfolder="$tmpfolder/day_logs"
|
||||||
|
tmparoundfolder="$tmpfolder/tmparoundfolder"
|
||||||
|
tmparoundfolder2="$tmpfolder/tmparoundfolder2"
|
||||||
|
# Comme ils sont détruits à la fin, on les recrée à chaque fois
|
||||||
|
mkdir -p "$tmpfolder" "$rawfolder" "$yearfolder" "$monthfolder" "$dayfolder" "$tmparoundfolder" "$tmparoundfolder2"
|
||||||
|
|
||||||
|
echo "Fichiers du personnage $pseudo prêts à être traités" ;
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Split #
|
||||||
|
#############
|
||||||
|
# Cette partie sépare les logs bruts en 1 fichier de log par jour.
|
||||||
|
# On ne travaille pas sur les fichiers sources. Jamais.
|
||||||
|
# Parce qu'il y a parfois des blagues dans les logs, on nettoie l'encodage.
|
||||||
|
for file in "$foldersource"/*
|
||||||
|
do
|
||||||
|
originallog="$(basename $file)"
|
||||||
|
cat "$file" | tr -d "\000-\010" | tr -d "\016-\037" > "$rawfolder"/"$originallog"
|
||||||
|
done
|
||||||
|
# on va faire un très gros fichier avec tous les logs. Évitez de l'ouvrir avec un éditeur de texte basique.
|
||||||
|
cat "$rawfolder"/* > "$tmpfolder"/logcomplet_"$perso".txt
|
||||||
|
|
||||||
|
#Et on va faire un fichier de controle donnant toutes les dates avec logs, et les lignes foireuses (genre chants) (à vérifier manuellement en cas de doute)
|
||||||
|
cut -c 1-11 "$tmpfolder"/logcomplet_"$perso".txt | sort | uniq > controlline_"$perso".txt
|
||||||
|
|
||||||
|
for logname in "$tmpfolder"/logcomplet_"$perso".txt
|
||||||
|
do
|
||||||
|
# Vérification que les fichiers sont bien des fichiers, ce serait bête de planter le script pour un manque de vérification
|
||||||
|
if [ -f "$logname" ] ; then
|
||||||
|
#echo "$logname est un fichier et va être traité"
|
||||||
|
# On va faire un seul gros fichier par année
|
||||||
|
for year in {2009..2025} ; do
|
||||||
|
#echo "${year}"
|
||||||
|
grep "^${year}/" "$logname" >> "$yearfolder"/"${year}".txt
|
||||||
|
# Effacer les fichiers vides
|
||||||
|
[ -s "$yearfolder"/"${year}".txt ] || rm -f "$yearfolder"/"${year}".txt
|
||||||
|
#echo "${year} traitée"
|
||||||
|
# On vérifie si le fichier d'année existe et on ne traite que celles qui existent
|
||||||
|
if [ -f "$yearfolder"/"${year}".txt ] ; then
|
||||||
|
# Puis on va faire un fichier par mois
|
||||||
|
for month in {01..12} ; do
|
||||||
|
# echo "${year}/${month} en cours"
|
||||||
|
grep "^${year}/${month}" "$yearfolder"/"${year}".txt > "$monthfolder"/"${year}_${month}".txt
|
||||||
|
[ -s "$monthfolder"/"${year}_${month}".txt ] || rm -f "$monthfolder"/"${year}_${month}".txt
|
||||||
|
# On vérifie si le fichier année/mois existe et on ne traite que ceux qui existent
|
||||||
|
if [ -f "$monthfolder"/"${year}_${month}".txt ] ; then
|
||||||
|
#echo "$monthfolder"/"${year}_${month}".txt
|
||||||
|
# Puis un fichier par jour
|
||||||
|
for day in {01..31} ; do
|
||||||
|
#echo "${year}/${month}/${day} en cours"
|
||||||
|
grep "^${year}/${month}/${day}" "$monthfolder"/"${year}_${month}".txt > "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".txt
|
||||||
|
[ -s "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".txt ] || rm -f "$dayfolder"/log_"$perso"_"${year}_${month}_${day}".txt
|
||||||
|
#echo "${year}/${month}/${day} traité"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
echo "$logname n'est pas un fichier, il n'a pas été traité"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
#Tri des fichiers dans des dossiers du type Dossier_trié/année/mois/log.txt
|
||||||
|
# On récupère la date, en formattant comme pour les logs
|
||||||
|
for f in $dayfolder/*.txt
|
||||||
|
do
|
||||||
|
year=${f: -14:4}
|
||||||
|
month=${f: -9:2}
|
||||||
|
day=${f: -6:2}
|
||||||
|
|
||||||
|
# On se fait une jolie variable qui imite la partie datée du nom des fichiers de log
|
||||||
|
log=log_"$perso"_"${year}_${month}_${day}.txt"
|
||||||
|
# on créé le dossier de chaque mois dans les archives s'il n'existe pas
|
||||||
|
mkdir -p "$folderrawsorted"/"$year"/"$month/"
|
||||||
|
# on déplace ces logs dans leur archive
|
||||||
|
mv "$dayfolder"/"$log" "$folderrawsorted"/"$year"/"$month"/"$log"
|
||||||
|
done
|
||||||
|
echo "Les fichiers ont été découpés et rangés"
|
||||||
|
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Alentours #
|
||||||
|
#############
|
||||||
|
# Alors là, la misère... car on a des fichiers dans des sous-dossiers.
|
||||||
|
# Le plus simple : tout remettre dans un dossier en vrac et traiter à partir de là.
|
||||||
|
find "$folderrawsorted" -maxdepth 4 -name '*.txt' -exec cp {} "$tmparoundfolder"/ \;
|
||||||
|
|
||||||
|
for mylog in "$tmparoundfolder/log"*.txt
|
||||||
|
do
|
||||||
|
justnamelog() {
|
||||||
|
basename -s .txt "$mylog"
|
||||||
|
}
|
||||||
|
namelog="$(justnamelog)"
|
||||||
|
sourcelog="$tmparoundfolder/${namelog}.txt"
|
||||||
|
finallog="$tmparoundfolder2/${namelog}_alentours.txt"
|
||||||
|
#./1analyse_new_logryzom.sh $sourcelog $finallog
|
||||||
|
# Analyse des logs proprement dites
|
||||||
|
# Canaux à garder
|
||||||
|
# Alentours + Endroit où on est
|
||||||
|
grep "$channels" "$sourcelog" > templog.txt
|
||||||
|
# vire le timestamp de début des lignes. Garde le * si ça peut servir à nettoyer le sys.info..
|
||||||
|
cut -b 21- templog.txt > templog2.txt
|
||||||
|
# Enlève les couleurs des canaux
|
||||||
|
sed 's/@{[A-F0-9]\{4\}}//g' templog2.txt > templog3.txt
|
||||||
|
# enlever le nom des canaux :
|
||||||
|
sed -re 's:^\([A-Z/]+\) +\* +(.*)$:\1:' templog3.txt > templog4.txt
|
||||||
|
# laisser uniquement les phrases traduites (attention, ça peut être étrange)
|
||||||
|
sed -re 's#\{:[a-zA-Z]{2}:[^}]+\}@\{[[:space:]]##' templog4.txt > templog5.txt
|
||||||
|
# Remplacer "vous dites" par "Pseudo dit"
|
||||||
|
sed s/"Vous dites :"/"$pseudo dit :"/g templog5.txt > templog6.txt
|
||||||
|
sed s/"Vous criez"/"$pseudo crie :"/g templog6.txt > templog7.txt
|
||||||
|
# Si quelqu'un joue avec des caractères bizarres, ça remet tout d'aplomb
|
||||||
|
iconv -t utf-8 -c templog7.txt > "$finallog"
|
||||||
|
# Enlever les fichiers temporaires
|
||||||
|
rm templog*.txt
|
||||||
|
#echo "$sourcelog a été traité en tant que $finallog" #très bavard
|
||||||
|
done
|
||||||
|
# Retrier par année les fichiers nettoyés
|
||||||
|
for faround in $tmparoundfolder2/*.txt
|
||||||
|
do
|
||||||
|
# penser à compter les 10 caractères "alentours" pour que la variable marche
|
||||||
|
year=${faround: -24:4}
|
||||||
|
month=${faround: -19:2}
|
||||||
|
day=${faround: -16:2}
|
||||||
|
|
||||||
|
# On se fait une jolie variable qui imite la partie datée du nom des fichiers de log
|
||||||
|
finallogsorted=log_"$perso"_"${year}_${month}_${day}_alentours.txt"
|
||||||
|
# on créé le dossier de chaque mois s'il n'existe pas
|
||||||
|
mkdir -p "$folderaround"/"$year"/"$month/"
|
||||||
|
# on déplace ces logs dans leur archive
|
||||||
|
mv "$tmparoundfolder2"/"$finallogsorted" "$folderaround"/"$year"/"$month"/"$finallogsorted"
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
#############
|
||||||
|
# Dernier ménage #
|
||||||
|
#############
|
||||||
|
# Nettoyage
|
||||||
|
rm -r "$tmpfolder"
|
||||||
|
|
||||||
|
# On compte et vérifie les fichiers
|
||||||
|
count=$(find $folderrawsorted -maxdepth 4 -name '*.txt' | wc -l)
|
||||||
|
echo "Les sources sont triées en $count fichiers dans $folderrawsorted. Contrôlez ce nombre sur controlline_$perso.txt"
|
||||||
|
countaround=$(find $folderaround -maxdepth 4 -name '*.txt' | wc -l)
|
||||||
|
echo "$countaround fichiers ont été nettoyés et rangés dans $folderaround. "
|
||||||
|
if [ "$count" = "$countaround" ]
|
||||||
|
then echo "Il semble y avoir le bon nombre de fichiers traités entre les bruts triés et les log nettoyés" ;
|
||||||
|
else echo "Attention, il y a une incohérence dans le nombre de fichiers traités entre les bruts triés et les log nettoyés" ;
|
||||||
|
fi
|
4
erase_all.sh
Normal file
4
erase_all.sh
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Dernière révision : 2022/02/14
|
||||||
|
# Erase_all remet le dossier "à vide". Attention ça efface comme un bourrin.
|
||||||
|
rm -r ./alentours ./sources_tri ./sources_brutes/* controlline_*.txt
|
539
main.py
539
main.py
|
@ -1,539 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
from tkinter import filedialog
|
|
||||||
from tk_tooltip import CreateToolTip
|
|
||||||
import re
|
|
||||||
import os
|
|
||||||
import platform
|
|
||||||
|
|
||||||
channel_names = [
|
|
||||||
"SAY", # 0
|
|
||||||
"SHOUT", # 1
|
|
||||||
"TEAM", # 2
|
|
||||||
"GUILD", # 3
|
|
||||||
# "CIVILIZATION", # Unused
|
|
||||||
# "TERRITORY", # Unused
|
|
||||||
"UNIVERSE", # 4
|
|
||||||
"TELL", # 5
|
|
||||||
# "PLAYER", # Unused
|
|
||||||
# "ARROUND", # Unused
|
|
||||||
"REGION", # 6
|
|
||||||
"DYN0", # 7
|
|
||||||
"DYN1", # 8
|
|
||||||
"DYN2", # 9
|
|
||||||
"DYN3", # 10
|
|
||||||
"DYN4", # 11
|
|
||||||
"EMOTES", # 12
|
|
||||||
"SYSTEM", # 13
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
system_info_categories = [
|
|
||||||
("SYS", "Default system messages"),
|
|
||||||
("BC", "Broadcast messages"),
|
|
||||||
("TAGBC", "Tagged Broadcast messages"),
|
|
||||||
("XP", "XP Gain"),
|
|
||||||
("SP", "SP Gain"),
|
|
||||||
("TTL", "Title"),
|
|
||||||
("TSK", "Task"),
|
|
||||||
("ZON", "Zone"),
|
|
||||||
("DG", "Damage to me"),
|
|
||||||
("DMG", "Damage to me"),
|
|
||||||
("DGP", "Damage to me from player"),
|
|
||||||
("DGM", "Damage from me"),
|
|
||||||
("MIS", "Opponent misses"),
|
|
||||||
("MISM", "I miss"),
|
|
||||||
("ITM", "Item"),
|
|
||||||
("ITMO", "Item other in group"),
|
|
||||||
("ITMF", "Item failed"),
|
|
||||||
("SPL", "Spell to me"),
|
|
||||||
("SPLM", "Spell from me"),
|
|
||||||
("EMT", "Emote"),
|
|
||||||
("MTD", "Message of the day"),
|
|
||||||
("FORLD", "Forage locate deposit"),
|
|
||||||
("CHK", "Failed check"),
|
|
||||||
("CHKCB", "Failed check in combat"),
|
|
||||||
("PVPTM", "PVP Timer"),
|
|
||||||
("THM", "Thema finished (encyclopedia)"),
|
|
||||||
("AMB", "Ambiance (Occupation)"),
|
|
||||||
("ISE", "Item special effect"),
|
|
||||||
("ISE2", "Item special effect centered text"),
|
|
||||||
("OSM", "Outpost state message"),
|
|
||||||
("AROUND", "Around channel system message"),
|
|
||||||
("R2_INVITE", "Ring invitation"),
|
|
||||||
]
|
|
||||||
|
|
||||||
say_1st_to_3rd_person = {
|
|
||||||
"Vous": " dit ",
|
|
||||||
"You": " says",
|
|
||||||
"Du": " sagt",
|
|
||||||
}
|
|
||||||
|
|
||||||
class GUI:
|
|
||||||
color_regex = re.compile('@\{[A-F0-9]{4}\}')
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.originalfolderlog = ''
|
|
||||||
self.keep_color = False
|
|
||||||
self.keep_channel = False
|
|
||||||
self.keep_lang_flag = False
|
|
||||||
self.keep_original_part = False
|
|
||||||
self.keep_translated_part = False
|
|
||||||
self.keep_timestamp = False
|
|
||||||
self.replace_charname = False
|
|
||||||
|
|
||||||
self.window = tk.Tk()
|
|
||||||
self.window.title("Ryzom Log Cleaner")
|
|
||||||
|
|
||||||
self.window.columnconfigure(0, weight=1)
|
|
||||||
#self.window.rowconfigure([0,1], minsize=50)
|
|
||||||
|
|
||||||
self.ntb_file_selection = ttk.Notebook(self.window)
|
|
||||||
|
|
||||||
### Single file tab
|
|
||||||
self.frm_single_file = tk.Frame(self.window)
|
|
||||||
self.frm_single_file.columnconfigure(0, weight=1)
|
|
||||||
btn_input = tk.Button(self.frm_single_file, text="Input file", command=self.get_input_filepath)
|
|
||||||
self.ent_input = tk.Entry(self.frm_single_file, text="Input path")
|
|
||||||
# ent_input.insert(0, "~/log.txt")
|
|
||||||
btn_output = tk.Button(self.frm_single_file, text="Output file", command=self.get_output_filepath)
|
|
||||||
self.ent_output = tk.Entry(self.frm_single_file, text="Output path")
|
|
||||||
# ent_output.insert(0, "~/cleaned/")
|
|
||||||
self.frm_charname = tk.Frame(self.frm_single_file)
|
|
||||||
lbl_charname = tk.Label(self.frm_charname, text="Char name:")
|
|
||||||
self.ent_charname = tk.Entry(self.frm_charname, text="Charname")
|
|
||||||
self.ent_charname.insert(0, "Select input file to auto-fill")
|
|
||||||
|
|
||||||
btn_input.grid(row=0, column=1, sticky='ew')
|
|
||||||
self.ent_input.grid(row=0, column=0, sticky='ew')
|
|
||||||
btn_output.grid(row=1, column=1, sticky='ew')
|
|
||||||
self.ent_output.grid(row=1, column=0, sticky='ew')
|
|
||||||
|
|
||||||
self.frm_charname.grid(row=2, column=0, columnspan=2, sticky='ew')
|
|
||||||
self.frm_charname.columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
lbl_charname.grid(row=0, column=0)
|
|
||||||
self.ent_charname.grid(row=0, column=1, sticky='ew')
|
|
||||||
|
|
||||||
|
|
||||||
### Multi file tab
|
|
||||||
self.frm_multi_file = tk.Frame(self.window)
|
|
||||||
self.frm_multi_file.columnconfigure(0, weight=1)
|
|
||||||
btn_input_multi = tk.Button(self.frm_multi_file, text="Input files", command=self.get_input_filepaths)
|
|
||||||
self.ent_input_multi = tk.Entry(self.frm_multi_file, text="Input paths")
|
|
||||||
# ent_input.insert(0, "~/log.txt")
|
|
||||||
btn_output_multi = tk.Button(self.frm_multi_file, text="Output directory", command=self.get_output_directory)
|
|
||||||
self.ent_output_multi = tk.Entry(self.frm_multi_file, text="Output paths")
|
|
||||||
# ent_output.insert(0, "~/cleaned/")
|
|
||||||
self.frm_charname_multi = tk.Frame(self.frm_multi_file)
|
|
||||||
lbl_charname_multi = tk.Label(self.frm_charname_multi, text="Char name (detected from file name of each file if empty):")
|
|
||||||
self.ent_charname_multi = tk.Entry(self.frm_charname_multi, text="Charname Multi")
|
|
||||||
|
|
||||||
btn_input_multi.grid(row=0, column=1, sticky='ew')
|
|
||||||
self.ent_input_multi.grid(row=0, column=0, sticky='ew')
|
|
||||||
btn_output_multi.grid(row=1, column=1, sticky='ew')
|
|
||||||
self.ent_output_multi.grid(row=1, column=0, sticky='ew')
|
|
||||||
|
|
||||||
self.frm_charname_multi.grid(row=2, column=0, columnspan=2, sticky='ew')
|
|
||||||
self.frm_charname_multi.columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
lbl_charname_multi.grid(row=0, column=0)
|
|
||||||
self.ent_charname_multi.grid(row=0, column=1, sticky='ew')
|
|
||||||
|
|
||||||
|
|
||||||
### Organise logs tab
|
|
||||||
self.frm_organise_logs = tk.Frame(self.window)
|
|
||||||
self.frm_organise_logs.columnconfigure(0, weight=1)
|
|
||||||
btn_input_orgalogs = tk.Button(self.frm_organise_logs, text="Input directory", command=self.get_input_dir_orgalogs)
|
|
||||||
self.ent_input_orgalogs = tk.Entry(self.frm_organise_logs, text="Input dir")
|
|
||||||
# ent_input.insert(0, "~/log.txt")
|
|
||||||
btn_output_orgalogs = tk.Button(self.frm_organise_logs, text="(Empty) Output directory", command=self.get_output_dir_orgalogs)
|
|
||||||
self.ent_output_orgalogs = tk.Entry(self.frm_organise_logs, text="Output dir")
|
|
||||||
# ent_output.insert(0, "~/cleaned/")
|
|
||||||
self.frm_charname_orgalogs = tk.Frame(self.frm_organise_logs)
|
|
||||||
lbl_charname_orgalogs = tk.Label(self.frm_charname_orgalogs, text="Char name (detected from file name of each file if empty):")
|
|
||||||
self.ent_charname_orgalogs = tk.Entry(self.frm_charname_orgalogs, text="Charname Orgalogs")
|
|
||||||
|
|
||||||
btn_input_orgalogs.grid(row=0, column=1, sticky='ew')
|
|
||||||
self.ent_input_orgalogs.grid(row=0, column=0, sticky='ew')
|
|
||||||
btn_output_orgalogs.grid(row=1, column=1, sticky='ew')
|
|
||||||
self.ent_output_orgalogs.grid(row=1, column=0, sticky='ew')
|
|
||||||
|
|
||||||
self.frm_charname_orgalogs.grid(row=2, column=0, columnspan=2, sticky='ew')
|
|
||||||
self.frm_charname_orgalogs.columnconfigure(1, weight=1)
|
|
||||||
|
|
||||||
lbl_charname_orgalogs.grid(row=0, column=0)
|
|
||||||
self.ent_charname_orgalogs.grid(row=0, column=1, sticky='ew')
|
|
||||||
|
|
||||||
self.btn_organise = tk.Button(self.frm_organise_logs, text="Organise!", command=self.organise_logs)
|
|
||||||
self.btn_organise.grid(row=10, column=0, columnspan=2, sticky='ew')
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Setup tab notebook
|
|
||||||
self.ntb_file_selection.add(self.frm_single_file, text="Single file input/output")
|
|
||||||
self.ntb_file_selection.add(self.frm_multi_file, text="Multi file input/output")
|
|
||||||
self.ntb_file_selection.add(self.frm_organise_logs, text="Organise logs")
|
|
||||||
self.ntb_file_selection.grid(row=0, column=0, columnspan=2, sticky='ew', ipady='2.5')
|
|
||||||
def on_tab_change(event):
|
|
||||||
tab = event.widget.tab('current')['text']
|
|
||||||
if tab == "Single file input/output":
|
|
||||||
self.btn_process.config(command=self.process_file)
|
|
||||||
self.frm_process_settings.grid(in_=self.frm_single_file)
|
|
||||||
elif tab == "Multi file input/output":
|
|
||||||
self.btn_process.config(command=self.process_files)
|
|
||||||
self.frm_process_settings.grid(in_=self.frm_multi_file)
|
|
||||||
self.ntb_file_selection.bind('<<NotebookTabChanged>>', on_tab_change)
|
|
||||||
|
|
||||||
|
|
||||||
self.frm_process_settings = tk.Frame(self.window)
|
|
||||||
self.frm_process_settings.grid(row=5, column=0, columnspan=2, sticky='ew', in_=self.frm_single_file)
|
|
||||||
self.frm_process_settings.columnconfigure([0,1], weight=1)
|
|
||||||
|
|
||||||
|
|
||||||
sep_general_settings = ttk.Separator(self.frm_process_settings, orient='horizontal')
|
|
||||||
sep_general_settings.grid(row=5, column=0, columnspan=2, sticky='ew', pady='5')
|
|
||||||
|
|
||||||
### General settings
|
|
||||||
self.frm_general_settings = tk.Frame(self.frm_process_settings)
|
|
||||||
self.frm_general_settings.grid(row=6, column=0, columnspan=2, sticky='ew', pady='5')
|
|
||||||
self.frm_general_settings.columnconfigure([0,1,2], weight=1)
|
|
||||||
|
|
||||||
self.btn_keep_color = self.create_toggle_button(self.frm_general_settings, "Keep color", self.toggle_setting("keep_color"))
|
|
||||||
self.btn_keep_channel = self.create_toggle_button(self.frm_general_settings, "Keep channel name", self.toggle_setting("keep_channel"))
|
|
||||||
self.btn_keep_lang_flag = self.create_toggle_button(self.frm_general_settings, "Keep language flag", self.toggle_setting("keep_lang_flag"))
|
|
||||||
self.btn_keep_original_part = self.create_toggle_button(self.frm_general_settings, "Keep original text", self.toggle_setting("keep_original_part"))
|
|
||||||
self.btn_keep_translated_part = self.create_toggle_button(self.frm_general_settings, "Keep translated text", self.toggle_setting("keep_translated_part"))
|
|
||||||
self.btn_keep_timestamp = self.create_toggle_button(self.frm_general_settings, "Keep timestamp", self.toggle_setting("keep_timestamp"))
|
|
||||||
self.btn_replace_charname = self.create_toggle_button(self.frm_general_settings, "Replace charname", self.toggle_setting("replace_charname"))
|
|
||||||
|
|
||||||
self.btn_keep_color.grid(row=0, column=0, sticky='ew')
|
|
||||||
self.btn_keep_channel.grid(row=0, column=1, sticky='ew')
|
|
||||||
self.btn_keep_lang_flag.grid(row=0, column=2, sticky='ew')
|
|
||||||
self.btn_keep_original_part.grid(row=1, column=0, sticky='ew')
|
|
||||||
self.btn_keep_translated_part.grid(row=1, column=1, sticky='ew')
|
|
||||||
self.btn_keep_timestamp.grid(row=1, column=2, sticky='ew')
|
|
||||||
self.btn_replace_charname.grid(row=2, column=0, sticky='ew')
|
|
||||||
|
|
||||||
sep_channels = ttk.Separator(self.frm_process_settings, orient='horizontal')
|
|
||||||
sep_channels.grid(row=10, column=0, columnspan=2, sticky='ew')
|
|
||||||
|
|
||||||
lbl_channel_select = tk.Label(self.frm_process_settings, text="Select channels to keep:")
|
|
||||||
lbl_channel_select.grid(row=11, column=0, sticky='w')
|
|
||||||
|
|
||||||
self.btn_chan_toggle = []
|
|
||||||
|
|
||||||
self.frm_btn_channel = tk.Frame(self.frm_process_settings)
|
|
||||||
self.frm_btn_channel.grid(row=12, column=0, columnspan=2, sticky='ew')
|
|
||||||
self.frm_btn_channel.columnconfigure([0,1,2,3], weight=1)
|
|
||||||
self.frm_btn_channel.rowconfigure([0,1,2], weight=1)
|
|
||||||
for i,name in enumerate(channel_names):
|
|
||||||
self.btn_chan_toggle.append(self.create_toggle_button(self.frm_btn_channel, name, self.toggle_channel(i)))
|
|
||||||
self.btn_chan_toggle[i].grid(row=int(i/4), column=i%4, sticky='ew')
|
|
||||||
|
|
||||||
self.btn_sys_toggle = []
|
|
||||||
|
|
||||||
self.frm_btn_sys = tk.Frame(self.frm_process_settings, bg="#808080", borderwidth=5)
|
|
||||||
self.frm_btn_sys.grid(row=13, column=0, columnspan=2, sticky='e')
|
|
||||||
self.frm_btn_sys.columnconfigure([0,1,2,3,4,5,6], weight=1)
|
|
||||||
self.frm_btn_sys.rowconfigure([0,1,2,3,4], weight=1)
|
|
||||||
for i,(name,tooltip) in enumerate(system_info_categories):
|
|
||||||
self.btn_sys_toggle.append(self.create_toggle_button(self.frm_btn_sys, name, self.toggle_system(i)))
|
|
||||||
self.btn_sys_toggle[i].grid(row=int(i/7), column=i%7, sticky='ew')
|
|
||||||
ttp_sys = CreateToolTip(self.btn_sys_toggle[i], tooltip)
|
|
||||||
|
|
||||||
self.btn_process = tk.Button(self.frm_process_settings, text="Process!", command=self.process_file)
|
|
||||||
self.btn_process.grid(row=20, column=0, columnspan=2, sticky='ew')
|
|
||||||
|
|
||||||
# Default selection
|
|
||||||
self.chan_toggle = [False] * len(channel_names)
|
|
||||||
self.sys_toggle = [False] * len(system_info_categories)
|
|
||||||
self.toggle_channel(0)()
|
|
||||||
self.toggle_channel(1)()
|
|
||||||
self.toggle_channel(12)()
|
|
||||||
self.toggle_channel(13)()
|
|
||||||
self.toggle_system(7)()
|
|
||||||
self.toggle_setting("keep_translated_part")()
|
|
||||||
|
|
||||||
def create_toggle_button(self, parent, text, command):
|
|
||||||
btn = None
|
|
||||||
if platform.system() == "Darwin":
|
|
||||||
btn = tk.Label(parent, text=text, bg="#ffcccb", relief='raised')
|
|
||||||
btn.bind("<Button-1>",lambda e:command())
|
|
||||||
btn.grid(padx=1, pady=1, ipady=3)
|
|
||||||
else:
|
|
||||||
btn = tk.Button(parent, text=text, command=command, bg="#ffcccb")
|
|
||||||
return btn
|
|
||||||
|
|
||||||
|
|
||||||
def toggle_setting(self, setting):
|
|
||||||
def tgl_pref():
|
|
||||||
if getattr(self, setting):
|
|
||||||
getattr(self, f"btn_{setting}").config(relief="raised", bg="#ffcccb")
|
|
||||||
setattr(self, setting, False)
|
|
||||||
else:
|
|
||||||
getattr(self, f"btn_{setting}").config(relief="sunken", bg="#99e599")
|
|
||||||
setattr(self, setting, True)
|
|
||||||
return tgl_pref
|
|
||||||
|
|
||||||
def get_input_filepath(self):
|
|
||||||
filepath = filedialog.askopenfilename(
|
|
||||||
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
|
|
||||||
initialdir=os.path.dirname(self.ent_input.get())
|
|
||||||
)
|
|
||||||
if not filepath:
|
|
||||||
return
|
|
||||||
self.ent_input.delete(0, tk.END)
|
|
||||||
self.ent_input.insert(0, filepath)
|
|
||||||
filename = os.path.basename(filepath)
|
|
||||||
name_start = filename.find("log_")
|
|
||||||
name_end = filename.find("_", name_start+4)
|
|
||||||
if name_end == -1:
|
|
||||||
name_end = filename.find(".", name_start+4)
|
|
||||||
if name_start != -1:
|
|
||||||
self.ent_charname.delete(0, tk.END)
|
|
||||||
self.ent_charname.insert(0, os.path.basename(filepath)[name_start+4:name_end])
|
|
||||||
|
|
||||||
def get_output_filepath(self):
|
|
||||||
filepath = filedialog.asksaveasfilename(
|
|
||||||
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
|
|
||||||
initialdir=os.path.dirname(self.ent_output.get())
|
|
||||||
)
|
|
||||||
if not filepath:
|
|
||||||
return
|
|
||||||
self.ent_output.delete(0, tk.END)
|
|
||||||
self.ent_output.insert(0, filepath)
|
|
||||||
|
|
||||||
def get_input_filepaths(self):
|
|
||||||
filepaths = filedialog.askopenfilenames(
|
|
||||||
filetypes=[("Text Files", "*.txt"), ("All Files", "*.*")],
|
|
||||||
initialdir=os.path.dirname(self.ent_input_multi.get().split(';')[0])
|
|
||||||
)
|
|
||||||
if not filepaths:
|
|
||||||
return
|
|
||||||
self.ent_input_multi.delete(0, tk.END)
|
|
||||||
self.ent_input_multi.insert(0, ';'.join(filepaths))
|
|
||||||
|
|
||||||
def get_output_directory(self):
|
|
||||||
directory = filedialog.askdirectory(
|
|
||||||
initialdir=self.ent_output_multi.get()
|
|
||||||
)
|
|
||||||
if not directory:
|
|
||||||
return
|
|
||||||
self.ent_output_multi.delete(0, tk.END)
|
|
||||||
self.ent_output_multi.insert(0, directory)
|
|
||||||
|
|
||||||
def get_input_dir_orgalogs(self):
|
|
||||||
directory = filedialog.askdirectory(
|
|
||||||
initialdir=self.ent_input_orgalogs.get()
|
|
||||||
)
|
|
||||||
if not directory:
|
|
||||||
return
|
|
||||||
self.ent_input_orgalogs.delete(0, tk.END)
|
|
||||||
self.ent_input_orgalogs.insert(0, directory)
|
|
||||||
|
|
||||||
def get_output_dir_orgalogs(self):
|
|
||||||
directory = filedialog.askdirectory(
|
|
||||||
initialdir=self.ent_output_orgalogs.get()
|
|
||||||
)
|
|
||||||
if not directory:
|
|
||||||
return
|
|
||||||
self.ent_output_orgalogs.delete(0, tk.END)
|
|
||||||
self.ent_output_orgalogs.insert(0, directory)
|
|
||||||
|
|
||||||
|
|
||||||
def toggle_channel(self, index):
|
|
||||||
def tgl_chan():
|
|
||||||
if self.chan_toggle[index]:
|
|
||||||
self.btn_chan_toggle[index].config(relief="raised", bg="#ffcccb")
|
|
||||||
self.chan_toggle[index] = False
|
|
||||||
if channel_names[index] == "SYSTEM":
|
|
||||||
self.frm_btn_sys.grid_remove()
|
|
||||||
else:
|
|
||||||
self.btn_chan_toggle[index].config(relief="sunken", bg="#99e599")
|
|
||||||
self.chan_toggle[index] = True
|
|
||||||
if channel_names[index] == "SYSTEM":
|
|
||||||
self.frm_btn_sys.grid()
|
|
||||||
return tgl_chan
|
|
||||||
|
|
||||||
def toggle_system(self,index):
|
|
||||||
def tgl_sys():
|
|
||||||
if self.sys_toggle[index]:
|
|
||||||
self.btn_sys_toggle[index].config(relief="raised", bg="#ffcccb")
|
|
||||||
self.sys_toggle[index] = False
|
|
||||||
else:
|
|
||||||
self.btn_sys_toggle[index].config(relief="sunken", bg="#99e599")
|
|
||||||
self.sys_toggle[index] = True
|
|
||||||
return tgl_sys
|
|
||||||
|
|
||||||
def process_file(self):
|
|
||||||
if not self.check_path_exists("ent_input"):
|
|
||||||
return
|
|
||||||
chan_pattern = '|'.join(['\(' + name + '\)' for i,name in enumerate(channel_names[:-2]) if self.chan_toggle[i]])
|
|
||||||
if self.chan_toggle[-2]:
|
|
||||||
chan_pattern += '|\(SAY/EMT\)'
|
|
||||||
if self.chan_toggle[-1]:
|
|
||||||
if self.sys_toggle[0]:
|
|
||||||
chan_pattern += '|\(SYSTEM\)'
|
|
||||||
for i,(name,tooltip) in enumerate(system_info_categories[1:]):
|
|
||||||
if self.sys_toggle[i+1]:
|
|
||||||
chan_pattern += '|\(SYSTEM/' + name + '\)'
|
|
||||||
chan_regex = re.compile(chan_pattern)
|
|
||||||
with open(self.ent_input.get(), 'r', errors='surrogateescape') as in_file, open(self.ent_output.get(), 'w', errors='surrogateescape') as out_file:
|
|
||||||
orig_lines = 0
|
|
||||||
filtered_lines = 0
|
|
||||||
self.btn_process["text"] = "Started Processing..."
|
|
||||||
for line in in_file:
|
|
||||||
orig_lines += 1
|
|
||||||
if chan_regex.search(line) == None:
|
|
||||||
continue
|
|
||||||
if not self.keep_color:
|
|
||||||
line = self.color_regex.sub('',line)
|
|
||||||
if not self.keep_channel:
|
|
||||||
line = line[:20] + line[line.find(') * ')+1:]
|
|
||||||
if not self.keep_timestamp:
|
|
||||||
line = line[20:]
|
|
||||||
original_start = line.find('{:')
|
|
||||||
if original_start != -1 and not self.keep_lang_flag:
|
|
||||||
line = line[:original_start+1] + line[original_start+5:]
|
|
||||||
original_end = line.find('}@{')
|
|
||||||
if original_end != -1 and not self.keep_translated_part:
|
|
||||||
line = line[:original_end+4] + '\n'
|
|
||||||
if original_end != -1 and not self.keep_original_part:
|
|
||||||
original_text_start = original_start + (5 if self.keep_lang_flag else 1)
|
|
||||||
line = line[:original_text_start] + line[original_end:]
|
|
||||||
if original_end != -1:
|
|
||||||
original_end = line.find('}@{')
|
|
||||||
line = line[:original_start] + line[original_start+1:original_end] + line[original_end+4:]
|
|
||||||
|
|
||||||
if self.replace_charname:
|
|
||||||
char_name_start = line.find(' * ') + 3 if not self.keep_color or line.find('}') == -1 else line.find('}') + 1
|
|
||||||
if line[char_name_start] == '[' and line[char_name_start+2] == ']':
|
|
||||||
char_name_start += 3
|
|
||||||
char_name_end = line.find(':', char_name_start)
|
|
||||||
char_talks_end = char_name_end
|
|
||||||
char_name_end -= len(line[:char_name_end].rstrip().split(' ')[-1]) + 1 + (len(line[:char_name_end])-len(line[:char_name_end].rstrip()))
|
|
||||||
char_name = line[char_name_start:char_name_end]
|
|
||||||
|
|
||||||
if char_name in say_1st_to_3rd_person:
|
|
||||||
line = line[:char_name_start] + ' '.join([s.capitalize() for s in self.ent_charname.get().split(' ')]) + say_1st_to_3rd_person[char_name] + line[char_talks_end:]
|
|
||||||
|
|
||||||
line = line[:line.find(' * ')] + line[line.find(' * ')+3:]
|
|
||||||
# channel_name = line[21:line.find(')')]
|
|
||||||
out_file.write(line.lstrip())
|
|
||||||
filtered_lines += 1
|
|
||||||
self.btn_process["text"]="Processing done! (" + str(filtered_lines) + " lines kept out of " + str(orig_lines) + " original lines)"
|
|
||||||
|
|
||||||
def process_files(self):
|
|
||||||
input_filepaths = self.ent_input_multi.get().split(';')
|
|
||||||
for filepath in input_filepaths:
|
|
||||||
filename = os.path.basename(filepath)
|
|
||||||
self.ent_input.delete(0, tk.END)
|
|
||||||
self.ent_input.insert(0, filepath)
|
|
||||||
self.ent_output.delete(0, tk.END)
|
|
||||||
self.ent_output.insert(0, os.path.join(self.ent_output_multi.get(), filename))
|
|
||||||
if len(self.ent_charname_multi.get()) == 0:
|
|
||||||
name_start = filename.find("log_")
|
|
||||||
name_end = filename.find("_", name_start+4)
|
|
||||||
if name_end == -1:
|
|
||||||
name_end = filename.find(".", name_start+4)
|
|
||||||
if name_start != -1:
|
|
||||||
self.ent_charname.delete(0, tk.END)
|
|
||||||
self.ent_charname.insert(0, os.path.basename(filepath)[name_start+4:name_end])
|
|
||||||
else:
|
|
||||||
self.ent_charname.delete(0, tk.END)
|
|
||||||
self.ent_charname.insert(0, self.ent_charname_multi.get())
|
|
||||||
|
|
||||||
self.process_file()
|
|
||||||
|
|
||||||
|
|
||||||
def organise_logs(self):
|
|
||||||
if not self.check_path_exists("ent_output_orgalogs") or not self.check_path_exists("ent_input_orgalogs"):
|
|
||||||
return
|
|
||||||
self.my_pathes = {}
|
|
||||||
for dirpath, _, filenames in os.walk(self.ent_input_orgalogs.get()):
|
|
||||||
for f in filenames:
|
|
||||||
with open(os.path.join(dirpath, f), 'r', errors='surrogateescape') as in_f:
|
|
||||||
last_year = -1
|
|
||||||
last_month = -1
|
|
||||||
last_day = -1
|
|
||||||
out_f = None
|
|
||||||
if len(self.ent_charname_orgalogs.get()) == 0:
|
|
||||||
name_start = f.find("log_")
|
|
||||||
name_end = f.find("_", name_start+4)
|
|
||||||
if name_end == -1:
|
|
||||||
name_end = f.find(".", name_start+4)
|
|
||||||
if name_start != -1:
|
|
||||||
self.ent_charname_orgalogs.delete(0, tk.END)
|
|
||||||
self.ent_charname_orgalogs.insert(0, f[name_start+4:name_end])
|
|
||||||
charname = self.ent_charname_orgalogs.get().lower()
|
|
||||||
manual_check_p = self.make_path_safe(os.path.join(self.ent_output_orgalogs.get(), f"log_{charname}_manual_check.log"))
|
|
||||||
manual_check_f = open(manual_check_p, 'a', errors='surrogateescape')
|
|
||||||
for line in in_f:
|
|
||||||
year_end = line.find('/')
|
|
||||||
unclassified = False
|
|
||||||
year = month = day = 0
|
|
||||||
if year_end == -1 or year_end+3 >= len(line) or line[year_end+3] != '/':
|
|
||||||
if last_year != -1:
|
|
||||||
year, month, day = last_year, last_month, last_day
|
|
||||||
else:
|
|
||||||
unclassified = True
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
year = int(line[:year_end])
|
|
||||||
month = int(line[year_end+1:year_end+3])
|
|
||||||
day = int(line[year_end+4:year_end+6])
|
|
||||||
except ValueError:
|
|
||||||
unclassified = True
|
|
||||||
if not unclassified and (year != last_year or month != last_month or day != last_day):
|
|
||||||
if out_f != None:
|
|
||||||
out_f.close()
|
|
||||||
out_p = self.make_path_safe(os.path.join(self.ent_output_orgalogs.get(), charname, f"{year:04d}", f"{month:02d}", f"log_{charname}_{year:04d}_{month:02d}_{day:02d}.log"))
|
|
||||||
out_f = open(out_p, 'a', errors='surrogateescape')
|
|
||||||
if unclassified:
|
|
||||||
manual_check_f.write(line)
|
|
||||||
else:
|
|
||||||
out_f.write(line)
|
|
||||||
last_year = year
|
|
||||||
last_month = month
|
|
||||||
last_day = day
|
|
||||||
if out_f != None:
|
|
||||||
out_f.close()
|
|
||||||
manual_check_f.close()
|
|
||||||
|
|
||||||
def make_path_safe(self, path):
|
|
||||||
if path in self.my_pathes:
|
|
||||||
return self.my_pathes[path]
|
|
||||||
(dirpath, filename) = os.path.split(path)
|
|
||||||
(basefile, ext) = os.path.splitext(filename)
|
|
||||||
if ext == '':
|
|
||||||
os.makedirs(path, exist_ok=True)
|
|
||||||
self.my_pathes[path] = path
|
|
||||||
return path
|
|
||||||
else:
|
|
||||||
os.makedirs(dirpath, exist_ok=True)
|
|
||||||
counter = 1
|
|
||||||
new_path = path
|
|
||||||
while os.path.exists(new_path):
|
|
||||||
new_path = os.path.join(dirpath, f"{basefile}_{counter}{ext}")
|
|
||||||
counter += 1
|
|
||||||
self.my_pathes[path] = new_path
|
|
||||||
return new_path
|
|
||||||
|
|
||||||
|
|
||||||
def check_path_exists(self, attribute):
|
|
||||||
if not hasattr(self, attribute) or getattr(self, attribute) == None or not os.path.exists(getattr(self, attribute).get()):
|
|
||||||
tk.messagebox.showerror(title="Error in files selection", message=f"Error with input/output files. Make sur the input/output files or directories are set and exist")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
gui = GUI()
|
|
||||||
|
|
||||||
gui.window.mainloop()
|
|
||||||
|
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
""" tk_ToolTip_class101.py
|
|
||||||
gives a Tkinter widget a tooltip as the mouse is above the widget
|
|
||||||
tested with Python27 and Python34 by vegaseat 09sep2014
|
|
||||||
www.daniweb.com/programming/software-development/code/484591/a-tooltip-class-for-tkinter
|
|
||||||
|
|
||||||
Modified to include a delay time by Victor Zaccardo, 25mar16
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# for Python2
|
|
||||||
import Tkinter as tk
|
|
||||||
except ImportError:
|
|
||||||
# for Python3
|
|
||||||
import tkinter as tk
|
|
||||||
|
|
||||||
class CreateToolTip(object):
|
|
||||||
"""
|
|
||||||
create a tooltip for a given widget
|
|
||||||
"""
|
|
||||||
def __init__(self, widget, text='widget info'):
|
|
||||||
self.waittime = 200 #miliseconds
|
|
||||||
self.wraplength = 180 #pixels
|
|
||||||
self.widget = widget
|
|
||||||
self.text = text
|
|
||||||
self.widget.bind("<Enter>", self.enter)
|
|
||||||
self.widget.bind("<Leave>", self.leave)
|
|
||||||
self.widget.bind("<ButtonPress>", self.leave)
|
|
||||||
self.id = None
|
|
||||||
self.tw = None
|
|
||||||
|
|
||||||
def enter(self, event=None):
|
|
||||||
self.schedule()
|
|
||||||
|
|
||||||
def leave(self, event=None):
|
|
||||||
self.unschedule()
|
|
||||||
self.hidetip()
|
|
||||||
|
|
||||||
def schedule(self):
|
|
||||||
self.unschedule()
|
|
||||||
self.id = self.widget.after(self.waittime, self.showtip)
|
|
||||||
|
|
||||||
def unschedule(self):
|
|
||||||
id = self.id
|
|
||||||
self.id = None
|
|
||||||
if id:
|
|
||||||
self.widget.after_cancel(id)
|
|
||||||
|
|
||||||
def showtip(self, event=None):
|
|
||||||
x = y = 0
|
|
||||||
x, y, cx, cy = self.widget.bbox("insert")
|
|
||||||
x += self.widget.winfo_rootx() + self.widget.winfo_width() - 10
|
|
||||||
y += self.widget.winfo_rooty() + self.widget.winfo_height() - 10
|
|
||||||
# creates a toplevel window
|
|
||||||
self.tw = tk.Toplevel(self.widget)
|
|
||||||
# Leaves only the label and removes the app window
|
|
||||||
self.tw.wm_overrideredirect(True)
|
|
||||||
self.tw.wm_geometry("+%d+%d" % (x, y))
|
|
||||||
label = tk.Label(self.tw, text=self.text, justify='left',
|
|
||||||
background="#ffffff", relief='solid', borderwidth=1,
|
|
||||||
wraplength = self.wraplength)
|
|
||||||
label.pack(ipadx=1)
|
|
||||||
|
|
||||||
def hidetip(self):
|
|
||||||
tw = self.tw
|
|
||||||
self.tw= None
|
|
||||||
if tw:
|
|
||||||
tw.destroy()
|
|
||||||
|
|
||||||
# testing ...
|
|
||||||
if __name__ == '__main__':
|
|
||||||
root = tk.Tk()
|
|
||||||
btn1 = tk.Button(root, text="button 1")
|
|
||||||
btn1.pack(padx=10, pady=5)
|
|
||||||
button1_ttp = CreateToolTip(btn1, \
|
|
||||||
'Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, '
|
|
||||||
'consectetur, adipisci velit. Neque porro quisquam est qui dolorem ipsum '
|
|
||||||
'quia dolor sit amet, consectetur, adipisci velit. Neque porro quisquam '
|
|
||||||
'est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit.')
|
|
||||||
|
|
||||||
btn2 = tk.Button(root, text="button 2")
|
|
||||||
btn2.pack(padx=10, pady=5)
|
|
||||||
button2_ttp = CreateToolTip(btn2, \
|
|
||||||
"First thing's first, I'm the realest. Drop this and let the whole world "
|
|
||||||
"feel it. And I'm still in the Murda Bizness. I could hold you down, like "
|
|
||||||
"I'm givin' lessons in physics. You should want a bad Vic like this.")
|
|
||||||
root.mainloop()
|
|
Loading…
Reference in a new issue