Probleme wxformbuilder, wxpython et print()

Bonjour.
J’ai pas l’habitude de poster sur des forums car en général y a tout ce qu’il faut sur internet mais la…
Je me suis amusé a créé une simple fenêtre et un bouton avec wxformbuilder que j’ai convertis ensuite en code wxpython. J’ai créé un 2eme script pour ouvrir la fenêtre que j’ai créé. Comme j’avais indiqué dans Event une class a OnButtonClic mon fichier wx généré contenais a la fin:

    # Virtual event handlers, override them in your derived class
    def clicsurlebouton( self, event ):
        event.Skip()

J’ai copié/collé dans mon fichier qui sert a ouvrir la fenêtre et j’ai mis un print() a la place du event.Skip().
Voici la totalité de mon code “a la main”

# -*- coding: utf-8 -*-
import wx
import wx.xrc

import genwx


app = wx.App()
genwx.Fenetre(None).Show()


def clicsurlebouton( self, event ):
    print("Clic sur bouton")
    return

print("avant MainLoop")

app.MainLoop()

print("apres MainLoop")

Quand je lance ce script la console affiche bien

avant MainLoop

mais plus rien ensuite et le bouton n’affiche rien…
Avez-vous une idée?
Je débute en wxpython mais j’aipas mal essayé des petits trucs avec l’IDLE et python en général.
J’ajoute que plus tard j’aimerais utiliser des scripts créés avant en remplaçant certaines variable par ce que l’utilisateur définis dans l’interface graphique puis en affichant le résultat dans la fenêtre et/ou dans print (ou en faisant l’action de lire ou d’écrire dans un fichier/variables par exemple)…
Bonne soirée.

Bonjour,

Un premier point important c’est ta fonction “clicsurlebouton”. Cette fonction est une “méthode”, c’est à dire qu’elle appartient à un objet et tu dois passer par lui pour appeler cette fonction.
Cela se voit par sa signature : son premier paramètre “self” représente l’objet auquel elle est attachée.

Je suppose donc que le programme appelle la fonction “clicsurlebouton” de ton objet et fait donc un “event.Skip()”.

Comme indiqué au-dessus de la fonction, il faut la surcharger (override), c’est à dire faire ce que tu as fait, mais pour ton objet en particulier.

Si la fonction appartient à ta fenêtre, je fixerais la fenêtre dans une variable, puis je redéfinirais cette méthode :

# Ici, je conserve la fenêtre créée
fenetre = genwx.Fenetre(None)
fenetre.Show()

# Ici, je surcharge son événement déjà défini
def fenetre.clicsurlebouton(self, event):
    print("Clic sur le bouton")

C’est du pseudo code, je n’ai pas tous les éléments :o)

1 « J'aime »

Bonjour.
Je ne peut pas ou pas encore poster de pièces jointes.
Voici le contenue du fichier genwx.py:

# -*- coding: utf-8 -*-

###########################################################################
## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc

import gettext
_ = gettext.gettext

###########################################################################
## Class Fenetre
###########################################################################

class Fenetre ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

        gSizer1 = wx.GridSizer( 0, 2, 0, 0 )

        self.bouton = wx.Button( self, wx.ID_ANY, _(u"Afficher texte dans console"), wx.DefaultPosition, wx.DefaultSize, 0 )
        gSizer1.Add( self.bouton, 0, wx.ALL, 5 )


        self.SetSizer( gSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )

        # Connect Events
        self.bouton.Bind( wx.EVT_BUTTON, self.clicsurlebouton )

    def __del__( self ):
        pass


    # Virtual event handlers, override them in your derived class
    def clicsurlebouton( self, event ):
        event.Skip()

En adaptant le code run.py ca donne:

# -*- coding: utf-8 -*-
import wx
import wx.xrc

import genwx


app = wx.App()
fenetre=genwx.Fenetre(None)
fenetre.Show()


def fenetre.clicsurlebouton( self, event ):
    print("Clic sur bouton")

print("avant MainLoop")

app.MainLoop()

print("apres MainLoop")

La sortie console

C:\Users\Musclor13\Python\test>run.py
  File "C:\Users\Musclor13\Python\test\run.py", line 13
    def fenetre.clicsurlebouton( self, event ):
               ^
SyntaxError: expected '('

Il semble qu’on ne puisse pas metre de point dans les noms de fonctions/variables même dans ce cas-la.
Je me renseignerais sur ce que tu a dit.
En théorie une sorte de “def genwx.Fenetre.clicsurlebouton( self, event ):” aurais pu être une bonne piste (si ca avais été ca ca aurait été une erreur bête de ma part) mais ca semble un poil plus complexe :smiley: .
Bonne journée.

C’est vraiment moi qui ait écrit ça ??? O_o

Je suis désolé pour ce code :

def clicsurlebouton(self, event):
    print("Ok")

fenetre.clicsurlebouton = clicsurlebouton

Ca devrait être plus propre déjà :smiley:

1 « J'aime »

Bonjour…
J’avoue que je ne comprend pas trop…
Si je ne touche pas au fichier généré et que je fais dans mon run.py

# -*- coding: utf-8 -*-
import wx
import wx.xrc

import genwx

app = wx.App()
fenetre=genwx.Fenetre(None)
fenetre.Show()



def clicsurlebouton(self, event):
    print("Clicsurlebouton")
fenetre.clicsurlebouton = clicsurlebouton


print("avant MainLoop")


app.MainLoop()

print("apres MainLoop")

J’ai bien la fenêtre qui s’affiche avec

avant MainLoop"

Et quand je ferme l’appli une deuxième ligne

apres MainLoop

mais rien quand je clic… Comme si il exécutais son fameux

class Fenetre ( wx.Frame ):
    # Le code de la création de fenêtre que je ne mêt pas ici
    # Virtual event handlers, override them in your derived class
    def clicsurlebouton( self, event ):
        event.Skip()

Pour voir ou est le problème j’ai copier mon fichier genwx.py et j’ai fais ce fameux truc du gros bouton rouge “Dont-tuch” qu’on fini toujours par appuyer :smiley: (faut pas modifier le fichier normalement)

# -*- coding: utf-8 -*-

###########################################################################
## Python code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6)
## http://www.wxformbuilder.org/
##
## PLEASE DO *NOT* EDIT THIS FILE!
###########################################################################

import wx
import wx.xrc

import gettext
_ = gettext.gettext

###########################################################################
## Class Fenetre
###########################################################################

class Fenetre ( wx.Frame ):

    def __init__( self, parent ):
        wx.Frame.__init__ ( self, parent, id = wx.ID_ANY, title = wx.EmptyString, pos = wx.DefaultPosition, size = wx.Size( 500,300 ), style = wx.DEFAULT_FRAME_STYLE|wx.TAB_TRAVERSAL )

        self.SetSizeHints( wx.DefaultSize, wx.DefaultSize )

        gSizer1 = wx.GridSizer( 0, 2, 0, 0 )

        self.bouton = wx.Button( self, wx.ID_ANY, _(u"Afficher texte dans console"), wx.DefaultPosition, wx.DefaultSize, 0 )
        gSizer1.Add( self.bouton, 0, wx.ALL, 5 )


        self.SetSizer( gSizer1 )
        self.Layout()

        self.Centre( wx.BOTH )

        # Connect Events
        self.bouton.Bind( wx.EVT_BUTTON, self.clicsurlebouton )

    def __del__( self ):
        pass


    # Virtual event handlers, override them in your derived class
    def clicsurlebouton( self, event ):
       print("Clicsurlebouton")


print("avant MainLoop")
app = wx.App()

Fenetre(None).Show()

app.MainLoop()
print("apres MainLoop")



Et la ca marche:

avant MainLoop
Clicsurlebouton
Clicsurlebouton
apres MainLoop

Du coup je comprend pas trop…

Sinon par curiosité j’ai regardé le fichier genwx.xrc que j’ai créé pour voir vu que ca semblais compatible avec wxglade, wxformbuilder et peut-être d’autre logiciels:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<resource xmlns="http://www.wxwidgets.org/wxxrc" version="2.5.3.0">
  <object class="wxFrame" name="Fenetre">
    <size>500,300</size>
    <style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
    <title></title>
    <centered>1</centered>
    <aui_managed>0</aui_managed>
    <object class="wxButton" name="bouton">
      <label>Afficher texte dans console</label>
      <default>0</default>
      <auth_needed>0</auth_needed>
      <markup>0</markup>
      <bitmap/>
    </object>
  </object>
</resource>

Je ne retrouve pas de “clicsurlebouton” la-dedans mais visiblement il semble q’avec un

import wx.xrc

je pourrais peut-être importer ce fameux fichier puis y assigner mes variables/fonctions/class dans mon ou mes autres codes (même si c’est assez contre intuitif vu qu’on a tendance a vouloir faire en python).
Je viens de voir pendans que j’écris une notif de nouveau message dans ce forum donc peut-être qu’en fait tu y a déja répondu :smiley:
EDIT Non c’est juste moi qui ai encore du mal avec le site :smiley: .

Ok, il y a une certaine logique tout de même. Ton objet est une “Fenetre”, et le bind du “onclick” se fait à son init.

Donc, il faut essayer :

import wx
import wx.xrc

import genwx


class MaFenetre(genwx.Fenetre):
    def clicsurlebouton(self, event):
        print("clic sur le bouton")

app = wx.App()
MaFenetre(None).Show()
print("avant MainLoop")
app.MainLoop()
print("apres MainLoop")

C’est à dire que tu crées un objet qui hérite de la Fenetre créée dnas le truc généré. Il y a pas de doc à leur outil ???

1 « J'aime »

Si mais j’avoue la j’ai testé ton code dans un autre fichier (il fonctionne) et j’ai comparé avec le mien… On doit obligatoirement mettre dans une class? Et le app = wx.App() n’était donc pas obligatoire?.. En gros je voulais faire le plus simple possible en me disant qu’après je convertirais en fonction ou class une fois que ca marche et si besoin… Dans une class ca marche donc si y a pas plus simple pour commencer je prendrais. En fait je pense d’abord a ce que ce soit facile a faire/modifier dans l’interpreteur et donc j’éssaie d’éviter un maximum les class/fonctions puis une fois que je me suis bien amusé je script :smiley: . Désolé je suis peut-être un peu casse-pied mais j’ai toujours fais des petits trucs en interpreteurs et modules simples pour ce même interpreteur… Remarque en fait ca va si on regarde bien ca se fait en 4 “lignes” (je compte pas les print, les import et la class je la compte comme une seule ligne). C’est juste qu’en ligne de commande faut a chaque fois faire une class + une définition ce qui peu alourdir un peu quand on veut juste modifier un petit truc. Heureusement qu’on peu retrouver ses commande d’avant.
Du coup Class obligatoire?
En général on fait plutôt ce code ou on import un fichier xrc et on code en python les boutons et le reste (sachant que je pense a la facilité de modification/ajout/suppression de composant graphiques et lecture/ecriture de textes dans des labels via python par exemple)?
Une fois mainloop lancé j’ai plus acces a la console… Y a moyen de garder mon appli ouverte et taper des trucs pour modifier la taille d’un bouton ou le nom d’une fenêtre via interpreteur par exemple?
Wxglade/wxformbuilder pour de l’opensource vaut mieux exporter en python ou xrc en plus de partager le ou les fichiers fbp (compatible avec les 2 applis en import/export même si je n’utilise que wxformbuilder je pense a l’édition facile)?
Si l’AFPY propose un wiki ou sinon sur wikilivre ce genre d’infos en francais est introuvable et on peu creer un rapide article pour aider ceux qui ont le même problème (le forum semble mal référencé sur les moteurs de recherches). J’ai déja édité des wiki donc pas de probleme pour moi. Je comprend 1 mot d’anglais sur 10 donc j’arrive un peu a comprendre les doc officiels mais ca va difficilement plus loins et Google Translate fonctionne mais pas totalement pour ce genre de doc/tutos en anglais.

Encore mieux: J’en ai profité pour tester un truc:

import wx
import wx.xrc

import genwx

global compteclic
compteclic= 0

class MaFenetre(genwx.Fenetre):
    def clicsurlebouton(self, event):
        print("clic sur le bouton")
        global compteclic
        compteclic+=1

app = wx.App()
MaFenetre(None).Show()
print("avant MainLoop")
app.MainLoop()
print("apres MainLoop et", compteclic, "clics")

Ca donne par exemple:


C:\Users\Musclor13\Python\test>Test.py
avant MainLoop
clic sur le bouton
clic sur le bouton
clic sur le bouton
apres MainLoop et 3 clics

C:\Users\Musclor13\Python\test>Test.py
avant MainLoop
clic sur le bouton
clic sur le bouton
clic sur le bouton
clic sur le bouton
clic sur le bouton
apres MainLoop et 5 clics

C:\Users\Musclor13\Python\test>

C’est tout bête surtout que print a pars afficher ca fait rien de plus mais c’est surement une grosse étape :smiley:
Bonne soirée.

Et voici une version plus propre avec explications en-dessous :

import wx
import wx.xrc

import genwx


class MaFenetre(genwx.Fenetre):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        combien_de_clics = 0
        
    def clicsurlebouton(self, event):
        print("clic sur le bouton")
        combien_de_clics += 1


app = wx.App()
MaFenetre(None).Show()
print("avant MainLoop")
app.MainLoop()
print(f"apres MainLoop et {MaFenetre.combien_de_clics} clics")

Pour explications :

  • “global” est un mot clef qui sert à dire que la variable que tu vas utiliser dans ton scope (ici ta méthode) est déjà définie globalement et que tu veux l’utiliser. Sinon, il te crée une seconde étiquette qui ne pointera pas sur la même chose.
  • Ton premier global est donc totalement inutile :wink:
  • Tu utilises ta variable uniquement pour ta classe, on va donc la mettre uniquement dans la classe.
  • Pour cela, on l’initialise dans à la création de l’objet Fenetre. On utilise donc la méthode __init__ en prenant en compte n’importe quels arguments (car je ne veux pas m’en préoccuper) et j’appelle immédiatement la même méthode __init__ de l’objet dont j’hérite histoier de ne pas casser son fonctionnement.
  • Enfin, on initialise notre variable
  • Ensuite on utilise noter variable
  • Et enfin, pour l’affichage, on s’appuie sur les f-string (parce que ça commence par un “f” ?) pour ajouter le contenu de la variable dans la chaine de caractères.
1 « J'aime »

Lol j’avoue concernant les F-strings je ne les utilise pas souvent mais plus j’en voit et plus je me dit que je devrais :smiley: … Mais après je pense aussi a la facilité de changer print par autre chose. Pour le “def init(self, *args, **kwargs):” je ne comprenais pas pourquoi VisualStudioCode voulais absolument m’ajouter le super() qui viens un peu de nulles part :smiley: J’avais essayé sans vu que init suffisait d’après la doc officielle de Python (ca a donné une belle erreur) mais si ca permet de “recopier” une ancienne fonction/methode/class dans une autre alors ca règle pas mal de soucis (je regarderais la doc sur ce sujet et je testerais en interpreteur). Bonne journée :smiley:
EDIT après pas mal de tests en console avec print et ce fameux super() je viens de comprendre même si le point après la parenthèse fait bizarre dans python sans modules importés (pas même wx).
Python prend le init le plus proche donc si on ne crés pas de nouveau init ca fait “genwx.init” mu que j’avais essayé ce fameux init hier pour finalement me rabattre sur global ca a cherché les infos dans genwx.Fenetre.init et il n’y avait rien. du coup ton super().init(*args, **kwargs) crée (ou redirige) ce qu’il y avait dans “genwx.init”. C’est bien ca? En tout cas je test avec des print et autre trucs de bases mais vu qu’il y a l’équivelent des get et set de godot pour lire/ecrire du texte dans les fenêtres je devrais y arriver :smiley: (godot n’a rien a voir avec python donc y a l’équivelent de l’éditeur de script et du “logiciel de dessin” de fenêtre mais les bibliothèques python ne fonctionnent pas dessus d’ou mon retour sur python apres avoir abandonné suite a l’arrivé de la v3 et des bibliothèques wx, pygame et autre bloqués sur python2).

Les fichiers finaux si ca peu aider quelqu’un un jour (je peut enfin envoyer des py et autre fichiers et donc alléger les messages)
Je vais mêttre les fichiers finaux sur le git dans un projet (bout de code faciles) qui serait une sorte de “démo” en francais que chacun pourra utiliser et modifier.
Par contre petit soucis (comme quoi):
combien_de_clics += 1
ne marche pas mais
semf.combien_de_clics += 1
fonctionne.
J’ai fais tellement de tests avec des class, des fonctions, print() et super() cet après-midi que j’ai vu le problème direct (je préfère tester des morceaux plutôt que copier/coller bêtement du coup j’ai testé seulement a partir de 20h).

Bien vu pour l’oubli du “self.” devant la variable !

Pour le __init__, c’est celui de l’objet créé qui est pris en compte (donc celui qu’on écrit nous ici). Il est donc nécessaire de rappeler celui de l’objet dont on hérite pour ne pas “casser” ce qui existe potentiellement déjà : notre seul but étant juste d’ajouter un attribut à l’objet :wink:

1 « J'aime »

Par contre j’imagne que je doit rajouter une 3eme fonction pour pouvoir utiliser mon

print(f"apres MainLoop et {MaFenetre.combien_de_clics} clics")

vu que j’ai une erreur

    print(f"apres MainLoop et {MaFenetre.combien_de_clics} clics")
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: type object 'MaFenetre' has no attribute 'combien_de_clics'

J’avoue que je galère avec les priorités/héritages :motorway: :skull_and_crossbones: autres trucs lisible en lecture, ecriture ou non dans les “arbres” :leafless_tree: que sont les fonctions/class :smiley:
J’ai beau lire la doc :books: je m’emmêle toujours un peu :upside_down_face: