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

Tkinter Python Discussion :

Thread et tkinter [Python 3.X]


Sujet :

Tkinter Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Août 2016
    Messages
    80
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Technicien maintenance

    Informations forums :
    Inscription : Août 2016
    Messages : 80
    Par défaut Thread et tkinter
    Bonjour,
    J'ai des difficulté pour visualisé l'état de différentes thread dans tkinter.
    Pouvez vous m'accorder un peu d'aide? (Je ne suis pas informaticien.)

    J'ai essayé de faire un fichier de test court, mais ça reste lourd:
    Une classe Cycle créé 3 threads
    La classe démo cycle essaye de visualisé se qui se passe:
    Appuis sur start: Visu du clignotement provoqué par les 3 threads
    Tentative de changer un slidders, qui est censé modifié le paramétrage de la vitesse d'une thread. Et la, je n'est plus qu'un seule thread qui est visualisé.
    J'ai mis un print de retour etat is_alive des threads sur le thread 'rapide' pour confirmé que les 2 autres thread sont bien en cours.

    Il est possible que se soit un problème évident pour vous. Moi je galère un peu.

    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
     
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
     
    from time import sleep, monotonic_ns
    import threading
     
    import os
    import tkinter as tk
    from tkinter import ttk
    import importlib
     
    import math
     
     
    class cycle:
    	# Constantes pour les noms des cycles
    	RAPIDE = "rapide"
    	APPLICATION = "application"
    	DISPLAY = "display"
     
    	default_cycle_settings = {
    			RAPIDE     : 50,
    			APPLICATION: 100,
    			DISPLAY    : 200
    			}
     
    	state = None
    	_running = False
    	_threads = {}
    	_time = None
    	_cycle_functions = {}
    	_cycle_times = {}
    	_cy_tscrut = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_times = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_last_time = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_real_time_cycle = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_remaining_time = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
     
            @classmethod
    	def _initialize(cls, rapide_app=None, application_app=None, display_app=None):
    		cls._cycle_functions[cls.RAPIDE] = rapide_app[0] if isinstance(rapide_app, tuple) else rapide_app
    		cls._cycle_functions[cls.APPLICATION] = application_app[0] if isinstance(
    			application_app, tuple
    			) else application_app
    		cls._cycle_functions[cls.DISPLAY] = display_app[0] if isinstance(display_app, tuple) else display_app
     
    		cls._cycle_times[cls.RAPIDE] = cls.default_cycle_settings[cls.RAPIDE] if rapide_app is None else rapide_app[1]
    		cls._cycle_times[cls.APPLICATION] = cls.default_cycle_settings[cls.APPLICATION] if application_app is None else \
    		application_app[1]
    		cls._cycle_times[cls.DISPLAY] = cls.default_cycle_settings[cls.DISPLAY] if display_app is None else display_app[
    			1]
     
            @classmethod
    	def initialize(cls, rapide_app=None, application_app=None, display_app=None):
    		# Définir les fonctions et les temps de cycle pour chaque type de cycle
    		for cycle_name, value in zip(
    				[cls.RAPIDE, cls.APPLICATION, cls.DISPLAY],
    				[rapide_app, application_app, display_app]
    				):
    			# Vérifier si l'application est un tuple
    			if isinstance(value, tuple):
    				cls._cycle_functions[cycle_name], cls._cycle_times[cycle_name] = value
    			elif isinstance(value, float):
    				cls._cycle_times[cycle_name] = value
    			else:
    				cls._cycle_functions[cycle_name] = value
    				cls._cycle_times[cycle_name] = cls.default_cycle_settings[cycle_name]
     
            @classmethod
    	def start(cls):
    		cls._running = True
    		for cycle_name, function in cls._cycle_functions.items():
    			cls._times[cycle_name] = monotonic_ns() * 10 ** (-6)
    			cls._last_time[cycle_name] = cls._times[cycle_name]
    			if function is not None:
    				cls._threads[cycle_name] = threading.Thread(target=cls._create_update_cycle, args=(cycle_name,))
    				cls._threads[cycle_name].daemon = True
    				cls._threads[cycle_name].start()
     
            @classmethod
    	def stop(cls):
    		cls._running = False
    		for thread in cls._threads.values():
    			thread.join()
     
            @classmethod
    	def _create_update_cycle(cls, cycle_name):
    		while cls._running:
    			cls._last_time[cycle_name] = cls._times[cycle_name]
    			cls._times[cycle_name] = monotonic_ns() * 10 ** (-6)
    			cls._real_time_cycle[cycle_name] = cls._times[cycle_name] - cls._last_time[cycle_name]
    			cls._cy_tscrut[cycle_name] = cls._real_time_cycle[cycle_name] - cls._remaining_time[cycle_name]
    			cls._remaining_time[cycle_name] = cls._cycle_times[cycle_name] - cls._cy_tscrut[cycle_name]
    			if cls._remaining_time[cycle_name] < 0:
    				cls._remaining_time[cycle_name] = 0
     
    			if cls._cycle_functions[cycle_name]:
    				cls._cycle_functions[cycle_name]()
     
    			sleep(cls._remaining_time[cycle_name] / 1000)
     
     
    class DemosCycle(ttk.Frame):
    	instance = []
     
    	def __init__(self, master, **kwargs):
    		super().__init__(master, **kwargs)
    		self._start = None
    		self.etatbpstart = False
    		self.etatbpstop = False
    		self.etatff = {
    				cycle.RAPIDE     : False,
    				cycle.APPLICATION: False,
    				cycle.DISPLAY    : False,
    				}
    		DemosCycle.instance.append(self)
     
    		self.fgrid = ttk.Frame(self)
    		self.fgrid.grid()
    		self.genwidget()
     
    	def genwidget(self):
    		"""Initialize widgets and styles."""
    		self.create_labels()
    		self.create_buttons()
    		self.create_slide()
    		self.create_visu()
    		self.setup_styles()
     
    	def create_labels(self):
    		"""Create and pack labels."""
    		self.fstate = tk.Label(self.fgrid, text='INCONNU', fg='blue')
    		self.fstate.grid(column=0, columnspan=2, row=0)
     
    	def create_visu(self):
    		self.fnthread = ttk.Label(self.fstate, text='Nombre de threads: 0', )
    		self.fnthread.grid(column=0, row=4)
     
    	def create_buttons(self):
    		"""Create and pack buttons."""
    		self.fbpstart = ttk.Button(self.fgrid, text='START', command=self.start)
    		self.fbpstart.grid(column=0, row=1)
    		self.fbpstop = ttk.Button(self.fgrid, text='STOP', command=self.stop)
    		self.fbpstop.grid(column=0, row=2)
     
    	def create_slide(self):
    		self.fslidegride = ttk.Frame(self.fgrid)
    		self.fslidegride.grid(column=1, columnspan=2, row=1, rowspan=2)
    		self.fvoy = {}
    		self.fnameLabel = {}
    		self.fslide = {}
    		self.fslideValue = {}
    		self.slideValue = {}
    		n = 0
    		for key in (cycle.RAPIDE, cycle.APPLICATION, cycle.DISPLAY):
    			self.fnameLabel[key] = ttk.Label(self.fslidegride, text=key)
    			self.fnameLabel[key].grid(column=n, row=0)
     
    			self.fvoy[key] = ttk.Label(self.fslidegride, text=key, style="Black.TLabel")
    			self.fvoy[key].grid(column=n, row=1)
     
    			self.fslideValue[key] = tk.Label(self.fslidegride)
    			self.fslideValue[key].grid(column=n, row=2)
     
    			self.slideValue[key] = None
    			self.fslide[key] = tk.Scale(
    					self.fslidegride,
    					orient=tk.VERTICAL,
    					tickinterval=3,
    					from_=math.log10(5),
    					to=math.log10(0.001),
    					showvalue=True,
    					resolution=0.1,
    					sliderlength=10,
    					command=lambda value, k=key: self._affValueSlide(k),
    					)
    			self.fslide[key].grid(column=n, row=3)
     
    			self.frealtimeValue = {}
    			self.frealtimeValue[key] = tk.Label(self.fslidegride, text='--')
    			self.frealtimeValue[key].grid(column=n, row=4)
     
    			n += 1
     
    	def _affValueSlide(self, key):
    		self.slideValue[key] = round(10 ** self.fslide[key].get(), 2) * 1000
    		self.fslideValue[key].configure(text=f'{self.slideValue[key]} ms')
    		cycle.initialize(
    				rapide_app=self.slideValue[cycle.RAPIDE],
    				application_app=self.slideValue[cycle.APPLICATION],
    				display_app=self.slideValue[cycle.DISPLAY]
    				)
     
    	def setup_styles(self):
    		"""Setup styles for labels."""
    		self.style = ttk.Style()
    		self.style.configure("Black.TLabel", background="black")
    		self.style.configure("Lime.TLabel", background="lime")
     
    	def updatevalue(self):
    		is_alive = 0
    		for key in (cycle.RAPIDE, cycle.APPLICATION, cycle.DISPLAY):
    			if (key in self.frealtimeValue
    					and key in cycle._real_time_cycle):
    				self.frealtimeValue[key].configure(text=round(cycle._real_time_cycle[key], 2))
    			if cycle._threads[key].is_alive():
    				is_alive +=1
    		self.fnthread.configure(text=f'Nombre de threads: {len(cycle._threads)} en cours: {is_alive}')
     
    	def flipflop(self, namethread):
    		"""Toggle the background color of the label."""
    		self.etatff[namethread] = not self.etatff[namethread]
    		style_name = "Lime.TLabel" if self.etatff[namethread] else "Black.TLabel"
    		self.fvoy[namethread].configure(style=style_name)
     
    	def start(self):
    		self._start = True
    		self.fstate.configure(
    				bg='lime',
    				text='En marche'
    				)
    		cycle.start()
     
    	def stop(self):
    		self._start = False
    		self.fstate.configure(
    				bg='black',
    				text="A l'arret"
    				)
    		cycle.stop()
     
     
    class Test:
    	def __init__(self):
    		"""Initialize the cycle with predefined functions and times."""
    		cycle.initialize(
    				rapide_app=(self.rapide, 50),
    				application_app=(self.application, 150),
    				display_app=(self.display, 400)
    				)
     
    	def rapide(self):
    		"""Callback for the 'rapide' cycle."""
    		DemosCycle.instance[0].flipflop(cycle.RAPIDE)
    		print(f'rapide : {cycle._real_time_cycle[cycle.RAPIDE]} {cycle._threads[cycle.RAPIDE].is_alive()} {cycle._threads[cycle.APPLICATION].is_alive()} {cycle._threads[cycle.DISPLAY].is_alive()}')
     
    	def application(self):
    		"""Callback for the 'application' cycle."""
    		DemosCycle.instance[0].flipflop(cycle.APPLICATION)
    		print(f'application : {cycle._real_time_cycle[cycle.APPLICATION]}')
     
    	def display(self):
    		"""Callback for the 'display' cycle."""
    		DemosCycle.instance[0].flipflop(cycle.DISPLAY)
    		DemosCycle.instance[0].updatevalue()
    		print(f'display : {cycle._real_time_cycle[cycle.DISPLAY]}')
     
     
    def run():
    	"""Run the pulse test."""
    	print("Exécution du test pour la classe Pulse")
    	inst_test = Test()
     
     
    def create_test_frame(parent):
    	"""Create the test frame with controls and test area."""
    	frame = ttk.Frame(parent)
     
    	label = ttk.Label(frame, text="Test de la classe Cycle")
    	label.pack()
     
    	frametest = DemosCycle(frame)
    	frametest.pack()
     
    	run()
     
    	return frame
     
     
    if __name__ == "__main__":
    	root = tk.Tk()
    	app = create_test_frame(root)
    	app.pack()  # Ajoutez cette ligne pour afficher le cadre dans la fenêtre principale
    	root.mainloop()  # Appelez mainloop sur root

  2. #2
    Expert confirmé
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    4 035
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 4 035
    Par défaut
    Hello,

    Tkinter n'est pas thread-safe pour un accès direct aux widgets depuis plusieurs threads Python.

    La documentation officielle recommande d'isoler les opérations liées à l'interface utilisateur dans le thread principal où la boucle d'événements mainloop() est en cours d'exécution.
    Pour effectuer des tâches en arrière-plan, il est préférable d'utiliser des threads séparés et de communiquer avec le thread principal de Tkinter pour mettre à jour l'interface utilisateur en utilisant des mécanismes sûrs comme la méthode after() ou la file d'attente d'événements.

    Ces fonctions (self.rapide, self.application, self.display dans la classe Test) interagissent directement avec des éléments de l'interface utilisateur Tkinter (via DemosCycle.instance). Vous avez de grands risques que ça plante !
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  3. #3
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 681
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 681
    Par défaut
    Citation Envoyé par alexis_c Voir le message
    J'ai des difficulté pour visualisé l'état de différentes thread dans tkinter.
    Pouvez vous m'accorder un peu d'aide? (Je ne suis pas informaticien.)
    Pour ajouter à ce qui a déjà été dit... Un GUI tel que tkinter sait fait du "multitâche" dit coopératif. Les threads réalisent un multitâche "pré-emptif".

    En "coopératif", si une fonction doit être appelée lorsque l'utilisateur clique sur un button, cette fonction sera la seule à s'exécuter tant qu'elle ne se termine pas (ou qu'elle suspende son exécution de façon explicite).

    En "pré-emptif", le thread en cours d'exécution pourra être interrompu à n'importe quel moment et pourront s'exécuter en même temps autant de threads qu'il y a de processeurs dans la machine (sauf avec Python ou c'est comme si la machine n'a qu'un CPU).

    La grosse différence entre les deux mécaniques est côté synchronisation des accès et intégrité de l'état (global) du programme. Il n'y a pas grand chose à faire en "coopératif" puisqu'une seule activité modifiera cet état. Elle devra être explicite en pré-emptif.

    Normalement, le multitâche du GUI suffit tant que les fonctions appelées se terminent rapidement. Si ce n'est pas le cas, le GUI se gèle (puisque qu'étant bloqué dans la fonction les mises à jours ne se feront plus). Dans ce cas, on peut utiliser des threads avec des précautions côté mises à jours du GUI.

    note: cela ne veut pas dire qu'il n'y a pas d'autres problèmes qui causent le comportement constaté... (virez les threads et le soucis sera peut être toujours là). Ca veut juste dire qu'on ne va pas passer trop de temps à comprendre ce que vous avez écrit.
    ligne 67 dans cycle.initialize:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    			elif isinstance(value, float):
    				cls._cycle_times[cycle_name] = value
    			else:
    				#cls._cycle_functions[cycle_name] = value
    				cls._cycle_times[cycle_name] = cls.default_cycle_settings[cycle_name]
    à mettre en commentaire, ça "marche mieux".

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  4. #4
    Membre confirmé
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Août 2016
    Messages
    80
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Technicien maintenance

    Informations forums :
    Inscription : Août 2016
    Messages : 80
    Par défaut
    En vous remerciant pour vos réponse.
    Visiblement il va falloir que je me penche sérieusement sur les moyens d’échange entre thread.

  5. #5
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 681
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 681
    Par défaut
    Citation Envoyé par alexis_c Voir le message
    Visiblement il va falloir que je me penche sérieusement sur les moyens d’échange entre thread.
    En général oui, mais dans ce cas particulier... utiliser des threads ne paraît pas justifié (et n'est pas la cause du soucis constaté).

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Membre confirmé
    Homme Profil pro
    Technicien maintenance
    Inscrit en
    Août 2016
    Messages
    80
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Marne (Champagne Ardenne)

    Informations professionnelles :
    Activité : Technicien maintenance

    Informations forums :
    Inscription : Août 2016
    Messages : 80
    Par défaut
    Voici ma solution au problème : Si ça peut être utile à d'autres.

    Donc utilisation de _lock = threading.Lock() pour l'écriture par les threads de variable commune.

    Mon problème n'avait pas de rapport avectkinter.
    Dans le code fourni, possibilité d'utiliser la boucle tkinter en decomentant self.after(100, self.update_display) et en commentant DemosCycle.instance[0].update_display()

    Faire ceci sans thread me paraît impossible? (À moins de simuler le fonctionnement d'un thread)
    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
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
     
    import threading
    import time
    import tkinter as tk
    from tkinter import ttk
    import math
     
    from baseAppliNF.ttkwidgetNF.utils.pulse import cycle
     
     
    class Cycle:
    	# Constantes pour les noms des cycles
    	RAPIDE = "rapide"
    	APPLICATION = "application"
    	DISPLAY = "display"
     
    	default_cycle_settings = {
    			RAPIDE     : 50,
    			APPLICATION: 100,
    			DISPLAY    : 200
    			}
     
    	_running = False
    	_threads = {}
    	_cycle_functions = {}
    	_cycle_times = {}
    	_cy_tscrut = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_times = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_last_time = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_real_time_cycle = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_remaining_time = {RAPIDE: 0, APPLICATION: 0, DISPLAY: 0}
    	_lock = threading.Lock()
     
            @classmethod
    	def initialize(cls, rapide_app=None, application_app=None, display_app=None):
    		cls._cycle_functions[cls.RAPIDE] = rapide_app[0] if isinstance(rapide_app, tuple) else rapide_app
    		cls._cycle_functions[cls.APPLICATION] = application_app[0] if isinstance(
    				application_app, tuple
    				) else application_app
    		cls._cycle_functions[cls.DISPLAY] = display_app[0] if isinstance(display_app, tuple) else display_app
     
    		cls._cycle_times[cls.RAPIDE] = cls.default_cycle_settings[cls.RAPIDE] if rapide_app is None else rapide_app[1]
    		cls._cycle_times[cls.APPLICATION] = cls.default_cycle_settings[cls.APPLICATION] if application_app is None else \
    			application_app[1]
    		cls._cycle_times[cls.DISPLAY] = cls.default_cycle_settings[cls.DISPLAY] if display_app is None else display_app[
    			1]
     
    		# Assurez-vous que les temps de cycle sont toujours définis
    		for cycle_name in [cls.RAPIDE, cls.APPLICATION, cls.DISPLAY]:
    			if cls._cycle_times[cycle_name] is None:
    				cls._cycle_times[cycle_name] = cls.default_cycle_settings[cycle_name]
     
            @classmethod
    	def start(cls):
    		cls._running = True
    		for cycle_name, function in cls._cycle_functions.items():
    			cls._times[cycle_name] = time.monotonic_ns() * 10 ** (-6)
    			cls._last_time[cycle_name] = cls._times[cycle_name]
    			if function is not None:
    				cls._threads[cycle_name] = threading.Thread(target=cls._create_update_cycle, args=(cycle_name,))
    				cls._threads[cycle_name].daemon = True
    				cls._threads[cycle_name].start()
     
            @classmethod
    	def stop(cls):
    		cls._running = False
    		for thread in cls._threads.values():
    			thread.join()
     
            @classmethod
    	def _create_update_cycle(cls, cycle_name):
    		while cls._running:
     
    			with cls._lock:
    				cls._last_time[cycle_name] = cls._times[cycle_name]
    				cls._times[cycle_name] = time.monotonic_ns() * 10 ** (-6)
    				cls._real_time_cycle[cycle_name] = cls._times[cycle_name] - cls._last_time[cycle_name]
    				cls._cy_tscrut[cycle_name] = cls._real_time_cycle[cycle_name] - cls._remaining_time[cycle_name]
    				cls._remaining_time[cycle_name] = cls._cycle_times[cycle_name] - cls._cy_tscrut[cycle_name]
    				if cls._remaining_time[cycle_name] < 0:
    					cls._remaining_time[cycle_name] = 0
     
    			if cls._cycle_functions[cycle_name]:
    				cls._cycle_functions[cycle_name]()
     
    			time.sleep(cls._remaining_time[cycle_name] / 1000)
     
     
    class DemosCycle(ttk.Frame):
    	instance = []
     
    	def __init__(self, master, **kwargs):
    		super().__init__(master, **kwargs)
    		self._start = None
    		self.etatbpstart = False
    		self.etatbpstop = False
    		self.etatff = {
    				Cycle.RAPIDE     : False,
    				Cycle.APPLICATION: False,
    				Cycle.DISPLAY    : False,
    				}
    		DemosCycle.instance.append(self)
     
    		self.fgrid = ttk.Frame(self)
    		self.fgrid.grid()
    		self.genwidget()
     
    		# Planifiez la première mise à jour
    		#self.update_display()
     
    	def genwidget(self):
    		"""Initialize widgets and styles."""
    		self.create_labels()
    		self.create_buttons()
    		self.create_slide()
    		self.create_visu()
    		self.setup_styles()
     
    	def create_labels(self):
    		"""Create and pack labels."""
    		self.fstate = tk.Label(self.fgrid, text='INCONNU', fg='blue')
    		self.fstate.grid(column=0, columnspan=2, row=0)
     
    	def create_visu(self):
    		self.fnthread = ttk.Label(self.fstate, text='Nombre de threads: 0', )
    		self.fnthread.grid(column=0, row=4)
     
    	def create_buttons(self):
    		"""Create and pack buttons."""
    		self.fbpstart = ttk.Button(self.fgrid, text='START', command=self.start)
    		self.fbpstart.grid(column=0, row=1)
    		self.fbpstop = ttk.Button(self.fgrid, text='STOP', command=self.stop)
    		self.fbpstop.grid(column=0, row=2)
     
    	def create_slide(self):
    		self.fslidegride = ttk.Frame(self.fgrid)
    		self.fslidegride.grid(column=1, columnspan=2, row=1, rowspan=2)
    		self.fvoy = {}
    		self.fnameLabel = {}
    		self.fslide = {}
    		self.fslideValue = {}
    		self.frealtimeValue = {}  # Initialisez ici
    		self.slideValue = {}
    		n = 0
    		for key in (Cycle.RAPIDE, Cycle.APPLICATION, Cycle.DISPLAY):
    			self.fnameLabel[key] = ttk.Label(self.fslidegride, text=key)
    			self.fnameLabel[key].grid(column=n, row=0)
     
    			self.fvoy[key] = ttk.Label(self.fslidegride, text=key, style="Black.TLabel")
    			self.fvoy[key].grid(column=n, row=1)
     
    			self.fslideValue[key] = tk.Label(self.fslidegride)
    			self.fslideValue[key].grid(column=n, row=2)
     
    			self.slideValue[key] = None
    			self.fslide[key] = tk.Scale(
    					self.fslidegride,
    					orient=tk.VERTICAL,
    					tickinterval=3,
    					from_=math.log10(5),
    					to=math.log10(0.001),
    					showvalue=True,
    					resolution=0.1,
    					sliderlength=10,
    					command=lambda value, k=key: self._affValueSlide(k),
    					)
    			self.fslide[key].grid(column=n, row=3)
     
    			self.frealtimeValue[key] = tk.Label(self.fslidegride, text='--')  # Ajoutez ici
    			self.frealtimeValue[key].grid(column=n, row=4)
     
    			n += 1
     
    	def _affValueSlide(self, key):
    		self.slideValue[key] = round(10 ** self.fslide[key].get(), 2) * 1000
    		self.fslideValue[key].configure(text=f'{self.slideValue[key]} ms')
     
    		# Assurez-vous que les valeurs passées sont des tuples
    		Cycle.initialize(
    				rapide_app=(Test().rapide, self.slideValue[Cycle.RAPIDE]),
    				application_app=(Test().application, self.slideValue[Cycle.APPLICATION]),
    				display_app=(Test().display, self.slideValue[Cycle.DISPLAY])
    				)
     
    	def setup_styles(self):
    		"""Setup styles for labels."""
    		self.style = ttk.Style()
    		self.style.configure("Black.TLabel", background="black")
    		self.style.configure("Lime.TLabel", background="lime")
     
    	def update_display(self):
    		"""Mise à jour périodique de l'affichage."""
    		is_alive = 0
    		for key in (Cycle.RAPIDE, Cycle.APPLICATION, Cycle.DISPLAY):
    			if key in self.frealtimeValue and key in Cycle._real_time_cycle:
    				self.frealtimeValue[key].configure(text=round(Cycle._real_time_cycle[key], 2))
    			if key in Cycle._threads and Cycle._threads[key].is_alive():
    				is_alive += 1
    		self.fnthread.configure(text=f'Nombre de threads: {len(Cycle._threads)} en cours: {is_alive}')
     
    		# Planifiez la prochaine mise à jour
    		#self.after(100, self.update_display)
     
    	def flipflop(self, namethread):
    		"""Toggle the background color of the label."""
    		self.etatff[namethread] = not self.etatff[namethread]
    		style_name = "Lime.TLabel" if self.etatff[namethread] else "Black.TLabel"
    		self.fvoy[namethread].configure(style=style_name)
     
    	def start(self):
    		self._start = True
    		self.fstate.configure(
    				bg='lime',
    				text='En marche'
    				)
    		Cycle.start()
     
    	def stop(self):
    		self._start = False
    		self.fstate.configure(
    				bg='black',
    				text="A l'arret"
    				)
    		Cycle.stop()
     
     
    class Test:
    	def __init__(self):
    		"""Initialize the cycle with predefined functions and times."""
    		Cycle.initialize(
    				rapide_app=(self.rapide, 50),
    				application_app=(self.application, 150),
    				display_app=(self.display, 400)
    				)
     
    	def rapide(self):
    		"""Callback for the 'rapide' cycle."""
    		DemosCycle.instance[0].flipflop(Cycle.RAPIDE)
    		print(
    				f'rapide : {Cycle._real_time_cycle[Cycle.RAPIDE]} {Cycle._threads[Cycle.RAPIDE].is_alive()} {Cycle._threads[Cycle.APPLICATION].is_alive()} {Cycle._threads[Cycle.DISPLAY].is_alive()}'
    				)
     
    	def application(self):
    		"""Callback for the 'application' cycle."""
    		DemosCycle.instance[0].flipflop(Cycle.APPLICATION)
    		print(f'application : {Cycle._real_time_cycle[Cycle.APPLICATION]}')
     
    	def display(self):
    		"""Callback for the 'display' cycle."""
    		DemosCycle.instance[0].flipflop(Cycle.DISPLAY)
    		print(f'display : {Cycle._real_time_cycle[Cycle.DISPLAY]}')
    		DemosCycle.instance[0].update_display()
     
    def run():
    	"""Run the pulse test."""
    	print("Exécution du test pour la classe Pulse")
    	inst_test = Test()
     
     
    def create_test_frame(parent):
    	"""Create the test frame with controls and test area."""
    	frame = ttk.Frame(parent)
     
    	label = ttk.Label(frame, text="Test de la classe Cycle")
    	label.pack()
     
    	frametest = DemosCycle(frame)
    	frametest.pack()
     
    	run()
     
    	return frame
     
     
    if __name__ == "__main__":
    	root = tk.Tk()
    	app = create_test_frame(root)
    	app.pack()  # Ajoutez cette ligne pour afficher le cadre dans la fenêtre principale
    	root.mainloop()  # Appelez mainloop sur root

  7. #7
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 681
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 681
    Par défaut
    Citation Envoyé par alexis_c Voir le message
    Donc utilisation de _lock = threading.Lock() pour l'écriture par les threads de variable commune.
    Ce lock protège mes mises à jour d'un dictionnaire Python, toujours protégé par le global interlock du langage dans les versions courantes.
    Par contre, le problème de départ "persiste" puisque depuis chaque threads, on appelle la fonction cls._cycle_functions[cycle_name] qui exécute DemosCycle.instance[0].flipflop(...) qui mets à jour le GUI hors du thread principal (celui du GUI).

    Citation Envoyé par alexis_c Voir le message
    Mon problème n'avait pas de rapport avec tkinter.
    Parce que vous avez de la chance... mais mélanger ainsi threads et tkinter n'est pas toujours sans soucis. L'ennui est qu'on ne saura pas le démontrer et qu'on se résignera à programmer sans threads (ou avec les précautions qui vont bien) en cas de soucis.

    Citation Envoyé par alexis_c Voir le message
    Faire ceci sans thread me paraît impossible? (À moins de simuler le fonctionnement d'un thread)
    Vos threads font 2/3 calculs et attendent via time.sleep avant de recommencer.
    Imaginez mettre vos calculs dans une fonction genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
        @classmethod
        def do_update_cycle(cls, cycle_name):
                cls._last_time[cycle_name] = cls._times[cycle_name]
                cls._times[cycle_name] = time.monotonic_ns() * 10 ** (-6)
                cls._real_time_cycle[cycle_name] = cls._times[cycle_name] - cls._last_time[cycle_name]
                cls._cy_tscrut[cycle_name] = cls._real_time_cycle[cycle_name] - cls._remaining_time[cycle_name]
                cls._remaining_time[cycle_name] = cls._cycle_times[cycle_name] - cls._cy_tscrut[cycle_name]
                if cls._remaining_time[cycle_name] < 0:
                    cls._remaining_time[cycle_name] = 0
     
                if cls._cycle_functions[cycle_name]:
                    cls._cycle_functions[cycle_name]()
     
                return cls._remaining_time[cycle_name] / 1000
    l'activité réalisée par le thread sera:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        @classmethod
        def _create_update_cycle(cls, cycle_name):
            while cls._running:
                delay = cls.do_update_cycle(cls, cycle_name)
                time.sleep(delay)
    Avec after, ce pourrait être;
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        @classmethod
        def _create_update_cycle(cls, cycle_name):
            while cls._running:
                delay = cls.do_update_cycle(cls, cycle_name)
                app.after(delay, cls._create_update_cycle, cycle_name)
    Il y a d'autres petits changements à faire car votre code est actuellement basé sur les threads, mais vos threads ne sont pas indispensables: ce qui est pris en charge n'est pas long ni d'une durée indéterminée.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Python 3.X] Socket, threads et tkinter: ouvrir, fermer et réouvrir une connexion
    Par AlexDabe dans le forum Réseau/Web
    Réponses: 4
    Dernier message: 24/02/2021, 13h34
  2. Thread et rafraichissement Tkinter
    Par panda31 dans le forum Tkinter
    Réponses: 8
    Dernier message: 25/05/2009, 08h48
  3. thread et tkinter
    Par Dbutant dans le forum Tkinter
    Réponses: 1
    Dernier message: 11/04/2009, 15h38
  4. python, tkinter et les sockets (et les threads)
    Par bomberwaterman dans le forum Réseau/Web
    Réponses: 6
    Dernier message: 02/01/2009, 19h53
  5. [Tkinter] Thread pas logique!
    Par airod dans le forum Tkinter
    Réponses: 4
    Dernier message: 26/11/2006, 22h36

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