IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

GTK+ avec Python Discussion :

la fonction signal.alarm() a un comportement inattendu en python3/Gtk3


Sujet :

GTK+ avec Python

  1. #1
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Avril 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Avril 2018
    Messages : 2
    Points : 1
    Points
    1
    Par défaut la fonction signal.alarm() a un comportement inattendu en python3/Gtk3
    Bonjour à tous,

    Il y a quelques années dans le cadre d'un projet j'ai écrit un petit chronomètre
    en python2/pygtk qui marche très bien. Maintenant je veux porter ce projet en
    python3/Gtk3 et la mon chronomètre ne marche plus du tout. Il fonctionne de
    manière classique avec le signal SIGALRM arrêté par un handler toutes les secondes.
    Curieusement en python3/Gtk3 le signal n'est pas transmis comme on s'y attendrait.
    Pour donner plus de détails sur ce problème voici la version simplifiée du programme
    en python2/pygtk qui marche parfaitement suivi du même programme en python3/Gtk3
    qui ne fonctionne pas. Comment adapter cette gestion du signal en python3 ?

    Je précise que je travaille sous Linux Debian 9 Stretch 64 bits. On voit mieux
    le fonctionnement du signal si on lance les programmes dans un terminal.

    La version qui marche en python2/pygtk
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
     
    #!/usr/bin/env python2
    # -*- coding: UTF-8
    import pygtk
    pygtk.require('2.0')
    import gtk, gobject
    import signal
     
    class MainWindow(gtk.Window):
        def __init__(self):
            gtk.Window.__init__(self)
            self.set_border_width(6)
            self.set_title("Petit Chronomètre")
            self.stop = 0
            self.seconde = 0
     
            self.vbox = gtk.VBox(homogeneous=False,spacing=5)
            self.add(self.vbox)
     
            self.button1 = gtk.Button("Lancer le chrono")
            self.button1.connect("clicked", self.Button_Chrono)
            self.vbox.pack_start(self.button1, False, True, 1)
     
            self.button2 = gtk.Button("Mettre à zéro le chronomètre")
            self.button2.connect("clicked", self.init)
            self.vbox.pack_start(self.button2, False, True, 1)
     
            self.button3 = gtk.Button("Fermer")
            self.button3.connect("clicked", self.fermer)
            self.vbox.pack_start(self.button3, False, True, 1)
     
        def Button_Chrono(self,button):
            if(self.stop == 1):
                signal.alarm(1)
                button.set_label("Arrêter le chronomètre  ( "+str(self.seconde)+" secondes )")
                self.stop = 0
            else:
                signal.alarm(0)
                button.set_label("Relancer le chronomètre ( "+str(self.seconde)+" secondes )")
                self.stop = 1
     
        def init(self,button):
            self.seconde = 0
            if(self.stop == 1):
                self.button1.set_label("Relancer le chronomètre ( "+str(self.seconde)+" secondes )")
            else:
                self.button1.set_label("Arrêter le chronomètre  ( "+str(self.seconde)+" secondes )")
     
        def fermer(self,button):
            gtk.main_quit()
     
    def gerechrono():
        def handler(signum, frame):
            print "DANS HANDLER: ",signum
            signal.signal(signal.SIGALRM, handler)
            win.button1.set_label("Arrêter le chronomètre  ( "+str(win.seconde)+" secondes )")
            win.seconde = win.seconde + 1
            signal.alarm(1)
        handler(0,0)
     
    win = MainWindow()
    win.connect("delete-event", gtk.main_quit)
    win.show_all()
    gerechrono()
    gtk.main()
    La version en python3/Gtk3
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
     
    #!/usr/bin/env python3
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    import signal
     
    class MainWindow(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self, title="Petit Chronomètre")
            self.set_border_width(6)
            self.stop = 0
            self.seconde = 0
     
            self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,homogeneous=False,spacing=5)
            self.add(self.vbox)
     
            self.button1 = Gtk.Button("Lancer le chronomètre")
            self.button1.connect("clicked", self.Button_Chrono)
            self.vbox.pack_start(self.button1, False, True, 1)
     
            self.button2 = Gtk.Button("Mettre à zéro le chronomètre")
            self.button2.connect("clicked", self.init)
            self.vbox.pack_start(self.button2, False, True, 1)
     
            self.button3 = Gtk.Button("Fermer")
            self.button3.connect("clicked", self.fermer)
            self.vbox.pack_start(self.button3, False, True, 1)
     
        def Button_Chrono(self,button):
            if(self.stop == 1):
                signal.alarm(1)
                button.set_label("Arrêter le chronomètre ( "+str(self.seconde)+" secondes )")
                self.stop = 0
            else:
                signal.alarm(0)
                button.set_label("Relancer le chronomètre ( "+str(self.seconde)+" secondes )")
                self.stop = 1
     
        def init(self,button):
            self.seconde = 0
            if(self.stop == 1):
                self.button1.set_label("Relancer le chronomètre ( "+str(self.seconde)+" secondes )")
            else:
                self.button1.set_label("Arrêter le chronomètre  ( "+str(self.seconde)+" secondes )")
     
        def fermer(self,button):
            Gtk.main_quit()
     
    def gerechrono():
        def handler(signum, frame):
            print("DANS HANDLER: ",signum)
            signal.signal(signal.SIGALRM, handler)
            win.button1.set_label("Arrêter le chronomètre  ( "+str(win.seconde)+" secondes )")
            win.seconde = win.seconde + 1
            signal.alarm(1)
        handler(0,0)
     
    win = MainWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    gerechrono()
    Gtk.main()
    Avec mes remerciements pour vos conseils

  2. #2
    Nouveau Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Avril 2018
    Messages
    2
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Finistère (Bretagne)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux

    Informations forums :
    Inscription : Avril 2018
    Messages : 2
    Points : 1
    Points
    1
    Par défaut
    Pour revenir sur fil que j'ai initié, et pour mieux illustrer le problème on peut
    simplifier drastiquement les deux programmes ci-dessus comme suit:

    Le programme en PyGTK (Gtk+2) qui marche:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/usr/bin/env python
    # -*- coding: UTF-8
    import gtk
    import signal
     
    def prehandler():
        def handler(signum, frame):
            signal.signal(signal.SIGALRM, handler)
            print( 'Signal handler called with signal', signum)
            signal.alarm(1)
        handler(0,0)
     
    prehandler()
    gtk.main()
    et le même en PyGObject (Gtk+3) qui bloque le signal alarm:
    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
    #!/usr/bin/env python3
    # -*- coding: UTF-8
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk
    import signal
     
    def prehandler():
        def handler(signum, frame):
            signal.signal(signal.SIGALRM, handler)
            print( 'Signal handler called with signal', signum)
            signal.alarm(1)
        handler(0,0)
     
    prehandler()
    Gtk.main()
    Je n'ai pas avancé pour comprendre le blocage du signal, MAIS en creusant un peu
    (même beaucoup) et pour en revenir à mon projet initial de chronomètre en Gtk+3
    si on descend au niveau de la Glib il y a un timer (GLib.timeout_add) qui permet
    de gérer des interruptions au millième de seconde, ce qui résout de manière plus
    élégante mon problème de chronomètre.
    Pour ceux qui seraient interessés voici le petit chronomètre au centième de seconde
    en Gtk+3 et qui marche:
    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
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
     
    #! /usr/bin/env python3
    import gi
    gi.require_version('Gtk', '3.0')
    from gi.repository import Gtk, GLib
     
    class MainWindow(Gtk.Window):
        def __init__(self):
            Gtk.Window.__init__(self, title="Petit Chronomètre")
            self.set_border_width(6)
            self.stop = 0
            self.seconde = 0
            self.timer = None
     
            self.vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL,homogeneous=False,spacing=5)
            self.add(self.vbox)
     
            self.label = Gtk.Label("0 seconde ")
            self.vbox.pack_start(self.label, False, True, 1)
            self.button1 = Gtk.Button("Arrêter le chronomètre")
            self.button1.connect("clicked", self.Button_Chrono)
            self.vbox.pack_start(self.button1, False, True, 1)
     
            self.button2 = Gtk.Button("Mettre à zéro le chronomètre")
            self.button2.connect("clicked", self.init)
            self.vbox.pack_start(self.button2, False, True, 1)
     
            self.button3 = Gtk.Button("Fermer")
            self.button3.connect("clicked", self.fermer)
            self.vbox.pack_start(self.button3, False, True, 1)
     
        def Button_Chrono(self,button):
            if(self.stop == 1):
                self.gerechrono()
                button.set_label("Arrêter le chronomètre")
                self.stop = 0
            else:
                button.set_label("Relancer le chronomètre")
                self.stop = 1
     
        def init(self,widget):
            self.seconde = 0
            self.label.set_label(str(self.seconde))
     
        def fermer(self,widget):
            Gtk.main_quit()
     
        def horo(self):
            self.seconde = self.seconde + 1
            self.label.set_label(str(self.seconde/100))
            if(self.stop):
                return(False)
            return(True)
     
        def gerechrono(self):
            self.timer = GLib.timeout_add(10,self.horo)
     
    win = MainWindow()
    win.connect("delete-event", Gtk.main_quit)
    win.show_all()
    win.gerechrono()
    Gtk.main()
    Merci de m'avoir lu

  3. #3
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Bonjour,

    ton chronomètre ne fonctionnera pas sur des longues durées, g_timeout_add n'est pas fait pour ça. Il ne te garantit pas l'exactitude du temps réellement écoulé entre 2 appels. Cela veut dire que plus tu laisses ton chronomètre tourner, et plus il risque de dériver, et en le laissant tourner longtemps tu peux potentiellement te retrouver avec plusieurs secondes d'écart avec la réalité.

    Utilise plutôt un GTimer. Tu peux ainsi contrôler le démarrage, l'arrêt et la pause de ton chronomètre. Ensuite l'affichage se fait via g_idle_add, qui fonctionne un peu comme g_timeout_add, à la différence que ta callback ne sera appelée que quand GTK+ n'aura rien de mieux à faire. Ainsi la charge du système ne risque pas de t'afficher des chiffres faux. Dans le pire des cas, tu auras juste un peu plus de temps entre 2 raffraichissements, mais ce sera (sauf cas extrême) invisible pour toi.

    Pour récupérer la valeur du GTimer, pas bsoin de l'arrêter, il faut juste appeler g_timer_elapsed. Bon, là je t'ai donné les noms de l'API C de la GLib, il te faudra trouver la correspondance dans l'API python.

  4. #4
    Modérateur

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2009
    Messages
    1 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2009
    Messages : 1 395
    Points : 2 002
    Points
    2 002
    Par défaut
    Autre chose: connecte toi à "destroy" et pas "delete-event" pour quitter ton application. C'est "destroy" qui est émis quand on quitte réellement l'application.

Discussions similaires

  1. Utilisation de la fonction SIGNAL en C
    Par Xystres dans le forum Linux
    Réponses: 2
    Dernier message: 15/01/2008, 01h17
  2. [XSLT]Problème de comportement inattendu
    Par kalimatchoum dans le forum XSL/XSLT/XPATH
    Réponses: 4
    Dernier message: 06/03/2007, 18h33
  3. La fonction signal en c
    Par Verboz dans le forum Linux
    Réponses: 2
    Dernier message: 12/11/2006, 12h44
  4. [PL/SQL][Oracle9] Proc. Stoc. comportement inattendue
    Par Carlito_superheros dans le forum Oracle
    Réponses: 2
    Dernier message: 30/08/2006, 16h13
  5. Comportement inattendu de Firefox
    Par reggae dans le forum Firefox
    Réponses: 3
    Dernier message: 08/05/2006, 21h15

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo