Bonjour à toutes et tous,
Je suis sous debian 11, stable et j’utilise python 3.9
J’ai un script python qui fonctionne bien quand il est lancé seul, en gros je produis des cartes météo avec matplotlib et des fichiers grib2 des modèles de météo-france.
Mon script "standalone", on retrouve une classe qui contient une méthode une utilisant un pool de workers (multiprocessing) qui permet de parallèliser le dessin des cartes.
En gros plusieurs cartes sont produites en parallèle (simultanément et sans attendre qu’une carte soit finie au lieu de séquentiellement), aucun problème, les données traitées sont indépendantes entres elles et j’ai 6 cœurs (12threads) sur mon core i5, autant en profiter
La compilation en parallèle se passe nickel, j’ai un gain de temps de facteur correspondant au nombre de cœurs (et donc de workers) utilisés \o/
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 import matplotlib import pygrib import numpy as np import time from datetime import datetime from datetime import timedelta import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature import cartopy.io.shapereader as shpreader import matplotlib.dates as mdates import matplotlib.colors as mcolors import metpy.plots as metplots from matplotlib.figure import Figure from multiprocessing import Pool class Arome_001_Cartes(): def __init__(self,jour,mois,annee,run,zoom,verification): [ ] def cartes_T2m(self): """Renvoie les cartes de températures à deux mètre prévues par Arome 0.025° ou 0.01°. Crée une figure par échéance donc une par heure, renvoie et sauvegarde l'intégralité des cartes.""" self.type_de_carte = "T2m" self.construire_Noms() print(self.nom_fichier_1) print(self.nom_fichier_2) t_fichiers = ("00H","02H","03H","04H","05H","06H","07H","08H","09H","10H","11H","12H","13H","14H","15H", "16H","17H","18H","19H","20H","21H","22H","23H","24H","25H","26H","27H","28H","29H","30H", "31H","32H","33H","34H","35H","36H",) with Pool(6) as p: print(p.map(self.par_fichier_T2m, t_fichiers)) h=Arome_001_Cartes("03","10","2021","06",zoom = 0,verification = 0) h.cartes_T2m()
Maintentant j’essaye d’implémenter cette production de cartes dans une interface graphique.
Je précise que je veux juste créer les cartes avec matplotib/cartopy et les enregistrer, les cartes produites ne sont pas affichées dans un canevas ou autre.
Je veux appuyer sur un bouton et lancer la production de ces cartes, rien de graphique dans la gui, rien d’autre.
Rapidement, voici une version simplifiée de ma gui avec tkinter:
Le code de la classe fille est quasiment indentique à sa version standalone:
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 from multiprocessing import Pool import matplotlib matplotlib.use("TkAgg") import pygrib import numpy as np import time from datetime import datetime from datetime import timedelta import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature import matplotlib.dates as mdates import matplotlib.colors as mcolors import metpy.plots as metplots from matplotlib.backends.backend_tkagg import FigureCanvasTk, NavigationToolbar2Tk from matplotlib.figure import Figure from tkinter import * from v0_6_Arome_Cartes import * # Classe d'interface graphique, les objets de cette classe sont appelées dans le main du projet. class Application(Tk): # Héritière de Tk, cette classe code pour une interface graphique. def __init__(self): Tk.__init__(self) # constructeur de la classe parente # Pour Arome 0.025° # Boutons pour le tracé et l'enregistrement de toutes les cartes de toutes les échéances B_tout_3 = Button(self, text ="Dessiner toutes les cartes 3", command =self.dessiner_tout_aro_0025_T2m).pack() def dessiner_tout_aro_0025_T2m(self): mod = "aro" res = "0.025" self.tout_aro_0025 = Arome_Cartes(self.date_du_run,modele=mod,resolution=res,zoom = self.chk,verification = 0) self.tout_aro_0025.cartes_T2m()
Avec un pool identique de plus de 1 workers, j’ai un freeze complet de ma machine, seule la souris bouge mais le reste est figé, obligation de hard reboot…
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 import matplotlib import pygrib import numpy as np import time from datetime import datetime from datetime import timedelta import matplotlib.pyplot as plt import cartopy.crs as ccrs import cartopy.feature as cfeature import cartopy.io.shapereader as shpreader import matplotlib.dates as mdates import matplotlib.colors as mcolors import metpy.plots as metplots from matplotlib.figure import Figure from multiprocessing import Pool class Arome_Cartes(): def __init__(self,date_du_run,modele,resolution,zoom,verification): [ ] def cartes_T2m(self): self.type_de_carte = "T2m" t_fichiers = self.construire_t_fichiers() with Pool(1) as p: print(p.map(self.par_fichier_T2m, t_fichiers))
Avec un seul worker, ça compile et les figures sont bien produites, mais j’ai une erreur à la fin:
En me renseignant sur le web, je crois avoir compris que le thead qui exécute la boucle de la gui tkinter est le même qui se charge de lancer la méthode de production des cartes, donc si un thread doit se transformer en n-threads, ça coince…
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 [xcb] Unknown sequence number while processing queue [xcb] Most likely this is a multi-threaded client and XInitThreads has not been called [xcb] Aborting, sorry about that. python3: ../../src/xcb_io.c:269: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed. Abandon
D’où ma question, est-il possible de résoudre mon problème, d’éviter que mes workers ne cassent le thread de la boucle tkinter ?
Je me trompe peut-être mais je veux utiliser un pool de threads qui ne s’attendent pas pour se lancer, or il es fait mention d’une processing queue, donc on aurait plusieurs workers à la queue pour un seul thread.
Cela équivaut à un retour au séquentiel non ?
En l’état je me dis que je vais passer par subprocess.call pour lancer cette production, mais alors aurais-je la même erreur si le subprocess.call est lancé par un bouton de ma gui tkinter ?
Bonne journée à toutes et tous et au plaisir de lire vos réponses![]()
Partager