Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[WxPython] comment éviter le multi-threading avec GUI

14 réponses
Avatar
N. Pourcelot
Bonjour,
je suis confronté au problème suivant sous WxPython.
Je veux pouvoir lancer des animations graphiques dans le Panel.

Basiquement, j'ai par exemple un objet A, avec des attributs x et y, et
j'ouvre dans mon programme un fichier qui contient un truc du genre :

for i in range(90):
A.x += 0.1
A.y -= 0.1

J'exécute le code contenu dans le fichier, et mon objet A se déplace.
Seul problème : mon programme est bloqué pendant ce temps ; si mon
script d'animation est un peu long, c'est embêtant.

J'ai pensé exécuter le code dans un thread auxiliaire, ça marche bien
sous Windows, mais sous Linux, le serveur X n'aime pas ça du tout.
("Xlib: unexpected async reply")
De manière générale, j'ai lu qu'il était déconseillé de toucher à tout
ce qui est GUI quand on fait du multi-threading, surtout avec le serveur X.
http://www.faqs.org/faqs/x-faq/part7/section-15.html

Cela dit, je ne vois pas trop comment contourner le problème.
Dans le dernier lien, je lis ("being sure to enable Xlib's multi-thread
support")
Comment je peux faire ça (dynamiquement) depuis WxPython ?

Sinon, comment parvenir au même résultat sans utiliser de threads (j'ai
souvent lu ce conseil aussi) ??

Merci beaucoup !

10 réponses

1 2
Avatar
Jerome
N. Pourcelot wrote:
Bonjour,
je suis confronté au problème suivant sous WxPython.
Je veux pouvoir lancer des animations graphiques dans le Panel.

Basiquement, j'ai par exemple un objet A, avec des attributs x et y, et
j'ouvre dans mon programme un fichier qui contient un truc du genre :

for i in range(90):
A.x += 0.1
A.y -= 0.1

J'exécute le code contenu dans le fichier, et mon objet A se déplace.
Seul problème : mon programme est bloqué pendant ce temps ; si mon
script d'animation est un peu long, c'est embêtant.

J'ai pensé exécuter le code dans un thread auxiliaire, ça marche bien
sous Windows, mais sous Linux, le serveur X n'aime pas ça du tout.
("Xlib: unexpected async reply")
De manière générale, j'ai lu qu'il était déconseillé de toucher à tout
ce qui est GUI quand on fait du multi-threading, surtout avec le serveur X.
http://www.faqs.org/faqs/x-faq/part7/section-15.html

Cela dit, je ne vois pas trop comment contourner le problème.
Dans le dernier lien, je lis ("being sure to enable Xlib's multi-thread
support")
Comment je peux faire ça (dynamiquement) depuis WxPython ?

Sinon, comment parvenir au même résultat sans utiliser de threads (j'ai
souvent lu ce conseil aussi) ??

Merci beaucoup !



Bonjour,
Pour t'aider, il nous faut plus d'informations concernant les
versions que tu utilises pour python/wxpython/linux ainsi que le code
qui pose problème sous linux mais pas sous windows.

Avatar
jean-michel bain-cornu
Bonjour,

N. Pourcelot wrote:
Comment je peux faire ça (dynamiquement) depuis WxPython ?
As-tu essayé l'exemple dans la démo, ce devrait être rapide de voir si

ça marche bien sous Linux ? Il se sert de wx.PostEvent pour éviter le
problème dont tu parles.
Sinon, il y a wx.Threads, mais je n'ai jamais essayé.
A+
jm

Avatar
N. Pourcelot
N. Pourcelot wrote:
Bonjour,
je suis confronté au problème suivant sous WxPython.
Je veux pouvoir lancer des animations graphiques dans le Panel.

Basiquement, j'ai par exemple un objet A, avec des attributs x et y,
et j'ouvre dans mon programme un fichier qui contient un truc du genre :

for i in range(90):
A.x += 0.1
A.y -= 0.1

J'exécute le code contenu dans le fichier, et mon objet A se déplace.
Seul problème : mon programme est bloqué pendant ce temps ; si mon
script d'animation est un peu long, c'est embêtant.

J'ai pensé exécuter le code dans un thread auxiliaire, ça marche bien
sous Windows, mais sous Linux, le serveur X n'aime pas ça du tout.
("Xlib: unexpected async reply")
De manière générale, j'ai lu qu'il était déconseillé de toucher à tout
ce qui est GUI quand on fait du multi-threading, surtout avec le
serveur X.
http://www.faqs.org/faqs/x-faq/part7/section-15.html

Cela dit, je ne vois pas trop comment contourner le problème.
Dans le dernier lien, je lis ("being sure to enable Xlib's multi-thread
support")
Comment je peux faire ça (dynamiquement) depuis WxPython ?

Sinon, comment parvenir au même résultat sans utiliser de threads
(j'ai souvent lu ce conseil aussi) ??

Merci beaucoup !



Bonjour,
Pour t'aider, il nous faut plus d'informations concernant les versions
que tu utilises pour python/wxpython/linux ainsi que le code qui pose
problème sous linux mais pas sous windows.


Pour Linux :
Ubuntu Breezy
Python 2.3.4
WxPython 2.5.3.1
Matplotlib 0.80
Note: je n'ai pas accès à Linux de nouveau avant 15 jours...
Le code qui pose problème, c'est celui que j'ai indiqué...
Quand je tape A.x += 0.1, ça me redessine tout le Panel, via les
commandes suivantes de matplotlib :

self.axes.clear()
self.axes.plot(["6.1"], ["5.9"], "k+")
self.axes.show()

dans le cas présent.

Mais d'après ce que j'ai pu lire sur Internet, le problème est très
général, et non lié spécifiquement à ce que fait matplotlib.

Le nouveau thread est appelé de la sorte :

___________________________________


def ouvrir_fichier(self, fichier = "defaut"):
try:
f=open(fichier,"rU")
self.message("Le fichier %s a bien ete ouvert." %fichier)
except IOError:
self.message("Le fichier n'existe pas.")
return
except UnicodeError:
self.message("Caracteres non reconnus.")
return
except:
self.message("Impossible d'ouvrir le fichier.")
return

ligne = 1
erreurs = ""
mode_macro = False
commandes_macro = []
commande = f.readline()

while commande:
if commande.startswith("<Macro>"):
mode_macro = True
if commande.startswith("</Macro>"):
mode_macro = False
commande = ":" + "".join(commandes_macro)
commandes_macro = []
if param.multi_threading: # la ligne qui pose probleme.
thread.start_new_thread(self.commande.executer,
(commande,), {"abreviations":False})
commande = "<"


if not commande.startswith("<"):
try:
if mode_macro:
commandes_macro.append(commande)
elif commande:
self.commande.executer(commande, abreviations =
False)
except:
erreurs += " " + str(ligne)
self.message("Le fichier est corrompu ligne" +
erreurs + ".")
if param.debug: raise

ligne += 1
commande = f.readline()

f.close()


________________________________________


le fichier qui est ouvert :

________________________________________


<?xml version='1.0' encoding='utf-8'?>
<Document type='WxGeometrie' version='0.100'>
<Figure>
creer_feuille()
set_fenetre(-11.13450293, 4.86549707, -5.66666667, 4.33333333,)
A=Point(-6.29036359797,2.73913042899)
C=Point(-2.71345030023,-1.60869565797)
B=Point(-2.52631579433,2.82608695478)
M=Point(4.01575716942,-2.29965289777)
AB=Segment(A,B)
cerÎrcle_diametre(B,C)
d=Droite(A,M)
u=Vecteur(A,C)
</Figure>
<Macro>
for i in range(30):
A=self.parent.actuelle.objets.A
A.x += 0.1
A.y -= 0.1
self.parent.affiche()
for i in range(30):
A.x -=0.2
self.parent.affiche()
</Macro>
</Document>


________________________________________


sous Windows, tout s'exécute fluidement ; sous Linux, le nouveau thread
s'éxécute par à-coups, et quand le thread meurt, tout plante.


Merci d'avance !


Avatar
N. Pourcelot
Merci!
j'essaie de comprendre comment ça marche (mais je n'ai pas la
possibilité de tester sous Linux pour le moment).
Cela dit, je ne suis pas sûr de pouvoir utiliser tout ça, vu que
j'utilise des commandes "de haut niveau" de matplotlib, et je ne gère
pas tellement les évènements moi-même.


Bonjour,

N. Pourcelot wrote:
Comment je peux faire ça (dynamiquement) depuis WxPython ?
As-tu essayé l'exemple dans la démo, ce devrait être rapide de voir si

ça marche bien sous Linux ? Il se sert de wx.PostEvent pour éviter le
problème dont tu parles.
Sinon, il y a wx.Threads, mais je n'ai jamais essayé.
A+
jm



Avatar
N. Pourcelot
Bonjour,

N. Pourcelot wrote:
Comment je peux faire ça (dynamiquement) depuis WxPython ?
As-tu essayé l'exemple dans la démo, ce devrait être rapide de voir si

ça marche bien sous Linux ? Il se sert de wx.PostEvent pour éviter le
problème dont tu parles.
Sinon, il y a wx.Threads, mais je n'ai jamais essayé.
A+
jm


Bon, je ne vois pas trop comment utiliser ça (comment intercepter les
évènements des commandes de haut-niveau matplotlib, et les rediriger via
wx.PostEvent ??)

Par contre, je commence à me dire que je dois pouvoir me passer de faire
de l'affichage dans le thread secondaire.
Je peux utiliser l'évènement WX.TIMER dans le module d'affichage pour
vérifier une variable qui indique si il faut le mettre à jour ou non.
Et je peux modifier sans problème cette variable depuis le thread
secondaire.

Bon, ça rajoute encore des lignes de codes...

Merci en tout cas !


Avatar
loufoque

Sinon, il y a wx.Threads, mais je n'ai jamais essayé.


Non, il n'y a pas wx.Threads...
Il y a une classe de threads dans wxwidgets mais dans wxpython elle
n'existe pas étant donné qu'on a déjà les outils de python.

Avatar
loufoque

for i in range(90):
A.x += 0.1
A.y -= 0.1


A priori il suffit de faire

for i in range(90):
A.x += 0.1
A.y -= 0.1
wx.Yield()

Avatar
N. Pourcelot
Merci !

Je vais essayer.
Ca m'arrangerait bien si ça pouvait effectivement être aussi simple! :-)



for i in range(90):
A.x += 0.1
A.y -= 0.1


A priori il suffit de faire

for i in range(90):
A.x += 0.1
A.y -= 0.1
wx.Yield()



Avatar
jean-michel bain-cornu
loufoque wrote:

Sinon, il y a wx.Threads, mais je n'ai jamais essayé.



Non, il n'y a pas wx.Threads...
Il y a une classe de threads dans wxwidgets mais dans wxpython elle
n'existe pas étant donné qu'on a déjà les outils de python.


Désolé cher ami, il y a bien une classe wx.Threads, ne vous en déplaise,
et qui est documentée comme une implémentation spécifique à WX.
Au vu des sources, il s'agit bien de cela (voir ci-après).
Il faut rappeler que wx n'est pas spécifique à Python et que beaucoup de
classes ont des équivalents en Python : par exemple wx.DateTime,
wx.RegEx, wx.String, etc..
A+
jm

NB: Pour info, voici un extrait des sources :
/////////////////////////////////////////////////////////////////////////////
// Name: src/msw/thread.cpp
// Purpose: wxThread Implementation
// Author: Original from Wolfram Gloger/Guilhem Lavaux
// Modified by: Vadim Zeitlin to make it work :-)
// Created: 04/22/98
// RCS-ID: $Id: thread.cpp,v 1.94 2005/05/17 11:41:46 JS Exp $
// Copyright: (c) Wolfram Gloger (1996, 1997), Guilhem Lavaux (1998);
// Vadim Zeitlin (1999-2002)
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
...
class wxThreadInternal
{
public:
wxThreadInternal(wxThread *thread)
{
m_thread = thread;
m_hThread = 0;
m_state = STATE_NEW;
m_priority = WXTHREAD_DEFAULT_PRIORITY;
m_nRef = 1;
}
...


Avatar
jean-michel bain-cornu
N. Pourcelot wrote:
Merci !

Je vais essayer.
Ca m'arrangerait bien si ça pouvait effectivement être aussi simple! :-)




for i in range(90):
A.x += 0.1
A.y -= 0.1



A priori il suffit de faire

for i in range(90):
A.x += 0.1
A.y -= 0.1
wx.Yield()
J'ai compris Yield comme étant une méthode de wxApp qui permet de


laisser respirer le système de temps en temps quand on fait une boucle.
Je ne vois pas le rapport avec ce qui nous occupe !
En faisant ça, à chaque pas de boucle, tu dis à WX : 'ok, prends un peu
de temps pour ta fenêtre', mais tu restes quand même dans ta boucle, qui
ne fais rien d'autre que gérer le déplacement de l'objet !
A+
jm



1 2