débordement de pile

a marqué ce sujet comme résolu.

Bonjour Je suis encore trés débutant sur Python et je n’ai choisi ce langage que pour des raisons essentiellement pratiques. J’ai, il y a longtemps développé une application ( en objective C mais sans appeler les spécificités du langage objet) j’avais fait ce choix car mon appli devait tourner sur MACOS. Aujourd’hui mon intention est de transférer cette application sur un Rasberry. Il m’a semblé qu' après l’installation de Linux sur mon Mac (j’ai fais le choix d' Ubuntu) le transfert sur un Rasberry serait relativement facile, d’autant que même s’il est de taille imposante, mon programme n’utilise pas de ressources systemes très importantes, et que la vitesse d’exécution d' un Python me semble suffisante. En gros, il y a une soixantaine de fonctions élémentaires, une interface graphique (GUI) assez sophistiquée (une quarantaine de canvas, labels et autres amusettes). Pour éviter les surprises désagréables lors du transfert en Rasberry, je n’utilise dans mes scripts que Tkinter et les imports standards. Je ne vous imposerai pas mon listing (trop gros) mais je pense que le squelette de l' application devrait suffire. Sachez simplement que pour des raisons pratiques seulement 7 interrupteurs sont utilisés pour gérer toute l’application. Actuellement se sont les touches du clavier de l’ordinateur, mais sur le Rasberry ce seront 7 boutons poussoirs. (via le port GPIB). Mais ces 7 poussoirs remplissent une douzaines de fonctions différentes il s’ensuit qu’en fonction du contexte une même touche peut effectuer différentes tâches… Mon programme a donc une boucle principale (ma_boucle() pour ne pas la nommer) ainsi organisée

def ma_boucle ():
    if contexte == 1:
        # on gère les touches a,t, KP_1
        if la_touche == 'a':
        fonction 1
    if la touche == 't':
        fonction 2
    if la_touche == 'KP_1' :
        fonction 3
    if contexte == 2:
    # on gère les touches a,z,e,r,t, KP_1,KP_2, KP_3
    if la_touche == 'a':
        fonction 5
    if la touche == 'z':
        fonction 2
    if la_touche == 'KP_1' :
        fonction 9
        .
        .
        .
        
    if contexte == 4:
    # on gère les touches a,z,e,r,t, KP_1,KP_2, KP_3
    if la_touche == 'a':
        fonction 18
    if la touche == 'z':
        fonction 7
    if la_touche == 'KP_1' :
        fonction 5
        .
        .
        .

Je vous passe la suite il me semble que tout est assez clair. Hormis les différents imports (GUI, tkinter et librairie standatd) mon script principal est de la forme :

import ...
# mes définitions de fonctions 
def maj():
def stat_DD():
def stat_RAM():
....
def affiche1(a,b):
def affiche2(c,d):
....
# la fameuse boucle principale
def ma_boucle(la_touche):
....
....
# et le script se termine en
....
# on passe la touche frappée à ma_boucle()
fen1.bind("<Key>",ma_boucle)
fen1.mainloop()

############################################
#                                          #
#     SAUVEGARDE AVANT ARRET DU SCRIPT     #
# Ceci s'execute quand on quitte le script #
#                                          #
############################################

lit_stat_RAM()
maj_stat_DD()
quitter()

Voilà, c’est tout. Et maintenant mon appel au secours : Nulle part je n’ai trouvé de documentation claire sur la façon dont s’execute un script en Python En C je sais toujours où se trouve le pointeur programme, en Python c’est le flou en la matière. Le résultat c’est que j’ai des "stack error" (ou assimilés) des récursions trop nombreuses (je crois) et cela après un temps d’utilisation du script trop long à son gré ! Et tout cela sans aucun message d’erreur pendant l’exécution. Si ces erreurs proviennent, comme je le pense, d’un débordement de la pile d' évènements comment remettre à zéro cette structure ? Veuillez pardonner mon ignorance. Merci pour vos avis et aussi pour quelques liens qui me rendront (on peut rêver, moins bête)

+0 -0

Salut, comme tu l’as sans doute vu, ton code n’est pas formaté joliment, ce qui le rend plus difficile à comprendre.

Tu peux utiliser la structure suivante pour activer la coloration syntaxique:

```python
import this

# Do something with the (mangled) Zen of Python
print(this.s)

def foo(bar):
    bar += 1
    return bar
```

Ce qui donne:

import this

# Do something with the (mangled) Zen of Python
print(this.s)

def foo(bar):
    bar += 1
    return bar
+0 -0

La manière dont s’exécute un programme python est la même qu’en C. La différence est qu’il n’y a pas de fonction main(), le script exécuté sert de fonction main().

Mais comme ici il y a du tkinder, c’est la fonction mainloop() qui exécute des actions quand les événements attachés apparaissent (frappe clavier, clic de souris, etc). Le programme reste dans cette fonction jusqu’à l’arrêt de la boucle (un destroy sur la fenêtre principale ou assimilé).

Les actions à faire doivent être "rapides" pour ne pas bloquer la boucle et figer l’interface. Du coup, pour les traitements lourds, on utilise des threads, de l’asynchrone ou n’importe quoi permettant une exécution parallèle.

Si tu as un débordement de pile, cela vient probablement d’une mauvaise gestion de boucle. J’ai l’impression que ma_boucle() utilise mainloop() qui appelle ma_boucle(). Ce n’est pas à toi de faire la boucle, ma_boucle() n’a pas beaucoup de sens ou est très mal nommée.

heuh ! Oui, j’ai vu, et je déplore. Pourtant dans mon éditeur de texte il était joliment présenté, c’est quand j’ai fait : "envoyer" que tout c’est dégradé. je vais tenter de corriger le tir …. Donc, j’efface et recommence tout :

Bonjour Je suis encore trés débutant sur Python et je n’ai choisi ce langage que pour des raisons essentiellement pratiques. J’ai, il y a longtemps développé une application ( en objective C mais sans appeler les spécificités du langage objet) j’avais fait ce choix car mon appli devait tourner sur MACOS. Aujourd’hui mon intention est de transferer cette application sur un Rasberry. Il m’a semblé qu' après l’installation de Linux sur mon Mac (j’ai fais le choix d' Ubuntu) le transfert sur un Rasberry serait relativement facile, d’autant que même s’il est de taille imposante, mon programme n’utilise pas de ressources systeme très importantes, et que la vitesse d’exécution d' un Python me semble suffisante. En gros, il y a une soixantaine de fonctions élémentaires, une interface graphique (GUI) assez sophistiquée (en gros, quarantaine de canvas, labels et autres amusettes). Pour éviter les surprises désagréables lors du transfert en Rasberry, je n’utilise dans mes scripts que Tkinter et les imports standards. Je ne vous imposerai pas mon listing (trop gros) mais je pense que le squelette de l' application devrait suffire. Sachez simplement que pour des raisons pratiques seulement 7 interrupteurs sont utilisés pour gérer toute l’application. Actuellement se sont les touches du clavier de l’ordinateur, mais sur le Rasberry ce seront 7 boutons poussoirs. (via le port GPIB). Mais ces 7 poussoirs remplissent une douzaines de fonctions différentes il s’ensuit qu’en fonction du contexte une même touche peut effectuer différentes tâches… Mon programme a donc une boucle principale (ma_boucle() pour ne pas la nommer) ainsi organisée

def ma_boucle():

    if contexte == 1:
      # on gère les touches a,t, KP_1
      if la_touche == 'a':
        fonction1()
      if la touche == 't':
        fonction2()
      if la_touche == 'KP_1' :
        fonction3(x,y)      
    if contexte == 2:
      # on gère les touches a,z,e,r,t, KP_1,KP_2, KP_3
      if la_touche == 'a':
        fonction5()
      if la touche == 'z':
        fonction2(r,u)
      if la_touche == 'KP_1' :
        fonction9(moyenne)
      .
      .
      .
        
    if contexte == 5:
      # on gère les touches a,F2,e,r,CANCEL, KP_1,KP_2, KP_3
      if la_touche == 'a':
        fonction18(x)
      if la touche == 'F2':
        fonction7()
      if la_touche == 'KP_1' :
        fonction5()
        .
        .
        .

Je vous passe la suite il me semble que tout est assez clair. Hormis les différents imports (GUI, tkinter et librairie standard) mon script principal est de la forme :

import  (tous mes imports)

(mes fonctions)

def maj():

def stat_DD():

def stat_RAM():

def affiche1(a,b):

def affiche2(c,d):

.........


ma_boucle(la_touche):

Et le script se termine par

fen1.bind("<Key>",ma_boucle)
fen1.mainloop()

apres ce mainloop() j’execute la sauvegarde d’un RAM disk dans mon disk dur

lit_stat_RAM()

maj_stat_DD()

et je quitte pour de bon

quitter()

Voilà, c’est tout. Et maintenant mon appel au secours : Nulle part je n’ai trouvé de documentation claire sur la façon dont s’execute un script en Python En C je sais toujours où se trouve le pointeur programme, en Python c’est le flou en la matière. Le résultat c’est que j’ai des "stack error" (ou assimilés) des récursions trop nombreuses (je crois) après un temps d’utilisation du script trop long à son gré ! Et tout cela sans aucun message d’erreur pendant l’exécution. Si ces erreurs proviennent, comme je le pense, d’un débordement de lapile d' évènements comment remettre à zero cette structure ? Merci pour vos avis et aussi pour quelques liens qui me rendront (on peut réver, moins bête)

PS le dièse des commentaires en Python semble mal vu par les éditeurs du site .

+0 -0

Je pense que tu devrais commencer avec des exemples simples de Tkinter.
Comme expliqué plus haut, Tkinter intègre une boucle de façon à intercepter et traiter mes événements.
Tu ne dois pas essayer de refaire la même chose, ça ne marchera pas.
https://zestedesavoir.com/tutoriels/1729/programmation-avec-tkinter/

+0 -0

Ok, je suis d’accord avec vous, mais tout de même je vous signale que ce n’est pas parce que j’appelle ma fonction (ma_boucle) que c’en est une. En fait c’est simplement un dispatching, l’équivalent du "selectCase" propre à d’autres langage. Tout passe par là, c’est vrai, mais rien n’y est essoré ou centrifugé. C’est le gestionnaire spécifique à mes évènements propres, à savoir aiguiller, en fonction du contexte,le déroulement du script vers plus de directions que je ne dispose de commandes. Regardez bien la fin de mon message, on y voit la façon dont se termine mon script : juste avant le classique fen1.mainloop(), on trouve fen1.bind("Key>",ma_boucle) qui me sert à intercepter les évènements tkinter, n’est ce pas là la cause de mes soucis ? Lesquels sont réels même si ma_boucle n’en n’est pas réellement une.

Sur le tuto que je t’ai cité, il y a des exemples qui marchent.
Tu devrais au moins essayer un de ces exemples.
Quand tu auras fait un truc qui marche, tu pourras toujours essayer d’autres méthodes.

Maintenant, à mon avis, le plus simple, c’est d’utiliser la gestion des évènements directement par Tkinter.
Il ne faut pas essayer de réinventer l’eau tiède, surtout quand ça ne marche pas du tout.

+0 -0

Salut etherpin. Je crois que tu n’as pas vraiment saisi ce que je souhaite. J’ai un programme qui fonctionne parfaitement en C et que j’ai transposé de sa plateforme d' origine (le Macintosh) vers les PC. Pour passer du Mac au PC, j’ai repris la structure de mon code et l’ai réécrit avec un Basic que je ne connaissais absolument pas, à savoir Xojo et cela a fonctionné en très peu de temps. Aujourd’hui je souhaite utiliser Python, que je ne prétend pas connaître pour passer sur du Raspberry. J’ai donc installé Linux sur mon Mac et transcris le programme en Python. Cela me fut facile car quasiment toutes les fonctions que j’avais écrites en C étaient presque directement utilisables. Bien sûr j’ai utilisé quelques spécificités de Python pour générer des choses qu’a le C et que n’a pas Python. Mais ce Python de @"$@^^ (et je suis poli) souffre d’un très gros défaut selon moi : il a trop d’admirateurs, et trop de librairies. Le résultat c’est que chacun ne jure que par les méthodes qu’il trouve dans ses imports personnels. Comme Python est un sujet dont j’aurais du mal à parler, je me méfie et c’est pourquoi je n’utilise rien d' autre que Tkinter car je ne suis pas sûr du résultat après le passage au Raspberry. La seule question que je pose est celle-ci Quelq’un connaît-il la raison pour laquelle placer fen1.bind("Key>",ma_boucle) en fin de script, c’est à dire juste avant le mainloop()final que semble exiger tkinter engendre une erreur de récursion. Et cela après plusieurs heures de fonctionnement correct à priori (en mode manuel ou en mode automatique) Notez également qu' augmenter la taille du Heap n’est pas une solution acceptable à mes yeux. Idéalement, ce que je souhaite c’est que l’on m’explique la méthode à utiliser pour vider proprement la pile de retour ou mieux à me décrire la connerie que j’ai pu faire. Merci de m’avoir lu. A plus, j’espère.

Salut,

Vu les informations que tu donnes jusqu’à présent, la seule chose que j’imagine qu’il se passe c’est que tu appelles mainloop() depuis une des fonctions listée dans ma_boucle().

Seulement, ce n’est qu’une supposition. Pour pouvoir t’aider mieux, il faudrait au moins le message d’erreur exact, et dans l’idéal ton code entier (tu peux le masquer avec un bloc secret pour que ça prenne moins de place, voir ici comment ça fonctionne).

Bonsoir luxera. Tu as raison, je vais changer le nom de ma fonction (ma_boucle()) pour la renommer plus logiquement mon_filtre() -c’est ce qu’elle est en fait, un simple filtre- Mais donner le script complet me semble impossible. Pas par culte du secret mais simplement parce que rien que l’interface graphique avec ses 48 labels, ses 30 canvases et toutes les lignes de configuration que ces widget impliquent pour les faire apparaître,disparaître ou afficher des choses en fonction du contexte rendrait la chose impossible. Bien sûr, tout cela est dans des modules différents mais pourtant tout ce tient. Ajoute à cela quelques 78 fonctions (beaucoup de quelques lignes seulement) et trois ou quatre très grosses (plusieurs pages de texte et bourrées de tests et de comparaisons avec des noms de variables peu parlantes (à cause de la place quelles occuperaient dans le listing si elles étaient plus limpides) Ajoute à cela que ce programme date de plusieurs années et qu’initialement il était ecrit en C++ (le ++ etant facultatif puisque la notion d’objet n’y apparait qu’une seule fois et qu’elle pourrait très bien ne pas y être puisqu’une stucture aurait pu la remplacer. Bref c’est impossible. Pourtant tu as raison : j’appelle souvent mainloop() à partir de mon_filtre parce que, ignare en la matière, je suis incapable de me positionner dans le code Python. Je te rappelle la raison de ce filtre mon_filtre(). Il a été créé parce que le programme est fait pour tourner dans une réalisation électronique (je suis électronicien de profession) de trés petite taille (genre tabloïde) cet objet est équipé de 7 boutons poussoirs remplissant à eux seuls près de 16 fonctions différentes et c’est la fonction mon_filtre() qui aiguille les evènement claviers vers les fonctions visées en fonction de l' état du programme. Donc j’appelle mon_filtre() qui appelle la ou les fonctions souhaitées et après être sorti de la dernière fonction appelée, je me retrouve (si j’en crois mes connaissances) de retour dans mon_filtre. Là j’avoue que je me mettrais bien à prier, si j’avais la foi mais n’étant pas atteint par cette maladie je m’en sort en faisant un appel à mainloop(). J’avais tenté quelques timides return qui m’ont occasionnés pas mal de soucis. Cependant je vais te donner un peitt renseignement qui va peut-être te mettre sur la voie. A plusieurs reprises j’utilise le principe du callback avec la méthode after dans mon programme (par exemple pour afficher des objets dans les canvas avec un certain délai. Et je viens de découvrir une variante du classique fen1.after(200, affiche2(z, photo1, photo2, photo3, photo4, photo5, photo6)) qui serait fen1.after_idle(blablabla) et je crois qu’il y a là une subtilité qui m’avait échappé. Si tu sais quelque chose à ce sujet, je suis toute ouie. Merci.

jo_link_noir : Pour la 4ème fois: on ne fait pas ça. Ha enfin ! Voilà quelqu’un qui donne des explications rationnelles. Et avec un sens de la pédagogie remarquable. Et quatre fois plutôt qu’une dirait-on. Encore que nulle part je ne trouve les trois premières. Bravo les amateurs de Python, vous êtes des bons ! Si, si ! Enfin merci à tous. Si quelqu’un daignait m’indiquer comment on marque comme résolu un sujet, ce serait la cerise sur le gateau. Merci.

Là j’avoue que je me mettrais bien à prier, si j’avais la foi mais n’étant pas atteint par cette maladie je m’en sort en faisant un appel à mainloop(). J’avais tenté quelques timides return qui m’ont occasionnés pas mal de soucis.

PommeS2b

D’accord, donc le problème de la taille de la pile vient bien de là : ta fonction ma_boucle, ou mon_filtre, et quel que soit son nom, est appelée depuis mainloop. Donc, si tu appelles à nouveau mainloop depuis celle-ci, la pile d’appel ne pourra que s’étirer à l’infini.

Tu parles de return, comment les as-tu utilisés ? Avec une valeur ou non ? Quel est le problème que ça a occasionné et dont tu parles ? As-tu également essayé de ne mettre ni return ni appel à mainloop, puisque c’est normalement la marche à suivre ?

A plusieurs reprises j’utilise le principe du callback avec la méthode after dans mon programme (par exemple pour afficher des objets dans les canvas avec un certain délai. Et je viens de découvrir une variante du classique fen1.after(200, affiche2(z, photo1, photo2, photo3, photo4, photo5, photo6)) qui serait fen1.after_idle(blablabla) et je crois qu’il y a là une subtilité qui m’avait échappé. Si tu sais quelque chose à ce sujet, je suis toute ouie.

PommeS2b

Ces 2 méthodes s’utilisent quasiment de la même manière : tu peux échanger

fen1.after(200, affiche2, z, photo1, photo2, photo3, photo4, photo5, photo6)

avec

fen1.after_idle(affiche2, z, photo1, photo2, photo3, photo4, photo5, photo6)

sans problème.

PS le dièse des commentaires en Python semble mal vu par les éditeurs du site .

PommeS2b

Non, j’ai édité tes messages pour que le code soit correctement délimité. Comme @amael te l’avait signalé, il fallait utiliser les balises ```python et ``` pour le délimiter. ;)

À l’intérieur de ces balises, le code se retrouve formaté correctement. Tout cela est expliqué dans le tuto Rédiger sur ZdS.

Mais ce Python de @"$@^^ (et je suis poli) souffre d’un très gros défaut selon moi : il a trop d’admirateurs, et trop de librairies. Le résultat c’est que chacun ne jure que par les méthodes qu’il trouve dans ses imports personnels.

PommeS2b

À quoi fais-tu référence quand tu parles de « librairies personnelles » ? Python est un langage avec une bibliothèque standard plutôt (très) bien fournie, donc au contraire c’est plutôt rare de devoir recourir à d’autres bibliothèques pour d’autres besoins.

Ou alors tu parles juste des import de modules locaux présents dans le code ? Ça n’est là que ce qui permet de découper un projet Python en plusieurs fichiers, pour plus facilement s’y retrouver, ça n’est pas une question de préférences personnelles.

entwranne, bonjour. Je ne parle pas de bibliothèques personnelles, mais d’importations personnelles. Et chacune de ces importations apporte son lot de méthodes ou d’instructions spécifiques et c’est pourquoi le quidam moyen ne peut pas s’y reconnaître avec Python. Car il n’y a pas un mais 1000 Python… Va sur n’importe quel Forum et lit les réponses à une question posée : tu trouveras 1000 réponses différentes et chacune d’elle fait appel à une méthode ou fonction prélevées dans un import personnel au répondeur. import pygame import PIL import vlc (celui là on le pardonne, la vidéo c’est spécifique) import ghostscript import numpy … tu veux que je continue ?

entwranne, bonjour. Je ne parle pas de bibliothèques personnelles, mais d’importations personnelles. Et chacune de ces importations apporte son lot de méthodes ou d’instructions spécifiques et c’est pourquoi le quidam moyen ne peut pas s’y reconnaître avec Python. Car il n’y a pas un mais 1000 Python…

PommeS2b

Certes, les imports de librairies standards (ou pas) changent les réponses apportées. Mais toi-même, tu ne nous liste pas tous tes imports. Tu reproduis le comportement même que tu critiques.

Par ailleurs, on a beau être de bonne volonté, se prendre des critiques comme ce message ce n’est vraiment pas agréable.

+1 -0

Réponse : voici la liste complète de tout ce que j’importe

!/usr/bin/python

from tkinter import *

from random import randrange

import time

import os.path

import tkinter.font as tkFont

import pickle

from gui import *

from mes_fonctions import *

et rien d’autre

gui contient les definitions, positionnements et affichage (ou non) des widgets

que j’utilise et

mes_fonctions contient les fonctions que je ne tiens pas à afficher dans mon

script principal comme vous pouvez le constater, rien d' étranger au Python basique

Et chacune de ces importations apporte son lot de méthodes ou d’instructions spécifiques et c’est pourquoi le quidam moyen ne peut pas s’y reconnaître avec Python. Car il n’y a pas un mais 1000 Python…

PommeS2b

En fait comme je disais, c’est justement moins présent en Python que dans d’autres langages car la bibliothèque standard est déjà bien fournie. Tu as même une bibliothèque fournie pour réaliser des GUI (Tkinter).

Alors oui, pour certains domaines spécifiques comme le calcul numérique, le web, le traitement d’image, le machine learning ou autres, tu vas avoir des besoins spécifiques pour certains bibliothèques. Car le travail consistant à réécrire le code de ces bibliothèques serait tout simplement monstrueux.

Mais ça c’est le fonctionnement de tout langage, ça ne fait pas qu’il y a 1000 Python différents. Le langage et son fonctionnement ne changent pas (contrairement au Ruby par exemple ou une bibliothèque externe peut apporter des changements plus ou moins profonds au langage), c’est juste que chaque bibliothèque apporte son lot de services en plus (c’est à ça que ça sert après tout).

Si tu avais fait du C il t’aurait fallu installer une lib pour la GUI par exemple.

Alors oui, certaines bibliothèques vont être plus imposantes que d’autres et t’imposer un cadre de développement (on parle généralement de cadriciel / framework pour ces bibliothèques d’ailleurs), telles que Django pour le web, mais c’est limité à ce genre de cas particuliers.
Parce que certaines manières de coder se prêtent plus à certains problèmes que d’autres.

Mais dans le cas le plus général, une bibliothèque sera juste une collection de nouvelles fonctions apportées au langage, qui s’utilisent comme les autres. Si tu en as besoin, tu installes la bibliothèque, et sinon tu passes ton tour.

De plus, quand on évite les from truc import *, on sait facilement de quel module (et donc de quelle bibliothèque) provient une fonction, ce qui fait qu’il est très facile de savoir ce dont on a besoin et si on peut s’en passer, tout se retrouve automatiquement dans un espace de noms dédié.

Je n’ai pas bien assimilé la structure de ton progamme. Quoi qu’il en soit, voici ce qu’il est recommandé de faire avec Tkinter :

# inclusion des modules
import tkinter as tk
# definition des fonctions internes
# definition des fonctions interactives
# initialisation du syst`eme graphique
root = tk.Tk()
# initialisation des widgets
# binding
# placement des widgets
# boucle de scrutation
tk.mainloop()
# liberation des ressources
exit(0)

Comme déjà indiqué, si on fait un tk.mainloop(), il faut ensuite libérer les ressources avec exit(0).
Je te conseille de lire https://tkdocs.com/tutorial/firstexample.html, qui décrit le precssus de création d’un petite application.

+0 -0

Enfin, voilà quelqu’un qui m’envoie du concret. Ton squelette d’application ressemble un peu à celui de la mienne. A part ce qui est appelé 'la boucle de scrutation' dont j’ignore tout mais que peut-être le lien que tu me donnes va m’informer. Pour ce qui est de la libération des ressources, je te remercie. J’avais en effet constaté qu’après avoir quitté normalement mon script, l’interpréteur m’envoyait une liste interminable de messages d’erreurs et il me semble possible que cette accumulation d’erreurs engendre une partie de mes soucis. Ha, au fait, j’utilise Pycharm Community comme IDE. Bref je vais oublier un peu mon programme qui me prend la tête et me plonger dans la lecture du lien que tu m’as envoyé. Merci de tout coeur, je te tiens au courant de mes trouvailles si j’en déniche quelques unes. A bientôt.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte