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

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()
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/

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:

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()
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
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 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…
Avec un seul worker, ça compile et les figures sont bien produites, mais j’ai une erreur à la fin:

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
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…

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