Bonjour,
Dans une projet j'ai un bouton qui lance un Tread assez long avant d'ouvrir une fenêtre
Comment faire pour que l'utilisateur puisse annuler sa demande ?
Cordialement
Bonjour,
Dans une projet j'ai un bouton qui lance un Tread assez long avant d'ouvrir une fenêtre
Comment faire pour que l'utilisateur puisse annuler sa demande ?
Cordialement
Bonjour,
rajoute une variable globale que tu testes dans ton thread ou utiliser ThreadDemandeArrêt()
Philippe,
N'hésitez à lever le pouce si mon aide vous a été utile.
Bonjour,
Désolé pour la réponse tardive, mais j'avais des urgences .......
Merci de l'aide
Effectivement j'avais pensé à avoir une variable globale, de type booléen, que je testerai à intervalle régulier
Mais comment renseigner cette variable ? C'est là que je bute
Exemple, j'ai une fenêtre avec un bouton "Exécuter" et un bouton "Arrêt" Quand je clique sur le bouton "Exécuter" je lance le thread, assez long, puis j'ouvre la fenêtre suivante
Pendant l'exécution du thread, je n'ai aucun effet sur le bouton "Arrêt" !!!
Le problème est en fait : comment communiquer avec l'utilisateur pendant l’exécution du thread ?
Cordialement
Windev dans ces dernières versions n’a pas l’équivalent des Events de Windows (ou TEvent de Delphi) ?
Bonjour,
Depuis pas mal d'années, il y a évènement().
Dans le cas de scn68100, il faudrait savoir ce qui doit provoquer l'arrêt du thread. C'est-à-dire : que doit faire l'utilisateur pour que ce traitement s'arrête / demande l'arrêt (touche Echap, Touche Fx, double-clic droit,...) ?
On peut imaginer un déclaration d'évènement au début du projet qui capture le keydown et qui dans la procédure associée, identifie la touche et si c'est Echap, il met à vrai le booléen (ou fait la demande d'arrêt du thread).
Autre idée : optimiser le traitement de ce thread pour qu'il soit plus rapide et qu'il n'y ait ainsi plus besoin de faire une demande d'arrêt.
Commencez toujours appuyer sur la touche F1 et puis n'hésitez à passer par un moteur de recherche...
Le forum est fait pour répondre aux questions : pas la peine de me les envoyer par MP. Merci.
Sur internet, tout est vrai ! Honoré de Balzac
Make it real not fantasy... Herman Rarebell
Bonsoir,
OK, je vais essayer la fonction évènement
Je ne manquerais pas de vous rendre compte
Cordialement
Dans la Doc de la fonction Evenement, clique sur le bouton Voir-Aussi... il y a des trucs. Et si tu vois des fonctions que tu ne connais pas, peut-être que ces fonctions correspondent à ton besoin.
En particulier la fonction MULTITACHE.
N'oubliez pas le bouton Résolu si vous avez obtenu une réponse à votre question.
Bonjour,
il y a 2 exemples dans les exemples WINDEV concernant les Threads :
Les Threads
Les Threads (Pool)
dans les Threads, vous verrez qu'ils utilisent "ThreadPause" pour rendre la main.
"ThreadArrête" n'est pas conseillé et vous aurez un Warning, mais il n'est pas interdit de l'utiliser, il faut juste être certain qu'il n'y ait pas interférence de plusieurs Threads.
Les Threads (Pool) est plus développé avec l'utilisation de sections critiques pour sécuriser les traitements.
Sinon, dans un traitement (boucle) vous pouvez utiliser Temporisation pour rendre la main à un bouton qui va arrêter la procédure
par exemple, quand j'appelle ma procédure "Potentiomètre1" :
Temporisation(Potentiomètre1, tempoSourisEtClavier)
Cdlt
Bonjour à tous
Désolé, mon adresse IP a été exclue par l'administrateur du site, puis rétablie, sans que l'on me communique les raisons malgré mes nombreuses demandes ...........
J'ai testé la fonction "Evénement" Je réussit à intercepter le Double Clique Gauche Pour intercepter la touche "Esc" je n'ai pas trouvé la bonne valeur
Mais il y a un autre problème ! Peut être à cause de ma version 26 Le sablier se lance, et s’arrête, automatiquement, et aucune action utilisateur n'est possible pendant le sablier
J'ai mis en commentaire TOUT les "Sablier(vrai)" J'ai ajouter "Sablier(Faux)" de partout, rien n'y fait ......
J'ai mit "Sablier(fauxToutSablier)" en début de la fin d'initialisation de ma page Je n'ai plus de sablier mais je n'ai plus de curseur !!!!
Et ce bien que j'ai mit "CurseurAffiche(Vrai)"
J'en conclue donc, peut être béatement, que même si je peux intercepter une action de l'utilisateur, il ne pourra intervenir à cause du sablier !!
Peut être est-ce du à la définition de la page ... ou du projet ?
Ou le sablier est peut être automatiquement activé quand on lance une procédure ...
Je me rend compte que pendant la procédure qui est "longue" je lance une application
C'est certainement "exeBloquant" qui lance le sablier et empêche toute action de l'utilisateur
Code : Sélectionner tout - Visualiser dans une fenêtre à part gbOK = LanceAppli(ch,exeActif,exeBloquant)
C'est dans cette application, "ch" dans le code, que je devrais intercepter une demande l'utilisateur
Mais je n'ai pas la main .........
La seule solution logique qui me viens à l'esprit est d'avertir l'utilisateur que le traitement serra long et qu'il ne pourra l’interrompre
Cordialement SC
Bonjour,
en l'état, il nous est difficile de vous aider d'avantage.
avez-vous vu les exemples sur les Threads cités plus hauts.
Vous est-il possible de nous montrer le code du thread (code qu'il faut stopper) ?
Cdlt
Bonsoir à tous, et merci
La conversation a déviée ... Sans doute que je n'ai pas pas su clairement m'exprimer
Le problème n'est pas de COMMENT arrêter un Thread ou une Procédure, mais de savoir QUAND il faut l'arrêter ....
Je prend un exemple Un utilisateur lance une fenêtre avec 2 boutons, Traitement Cours et Traitement Long
S'il clique sur Traitement Cours, il récupére la main pratiquement de suite
S'il clique sur Traitement Long, il n'a d'autre ressources que d'attendre, ou lancer le gestionnaire des tâches pour "tuer" la fenêtre ...
Je reprend l'idée de philouZ avoir une variable globale que l'on teste dans le Thread, la procédure
Par exempleDans code fin d'initialisation de la fenêtre utiliser la fonction Event
Code : Sélectionner tout - Visualiser dans une fenêtre à part gbArret est un boolean = faux
Dans la procédure DbClik je met gbArret à vrai
Code : Sélectionner tout - Visualiser dans une fenêtre à part nResultat est un entier = Evénement(DbClik,"*.*", WM_LBUTTONDBLCLK)
Quand je lance la fenêtre et que je fait un double clique, celui ci est détecté et la variable gbArret passe à vrai
Mais quand je lance la procédure qui est longue, le double clique n'est pas détecté !!!
Donc il faudrait trouver le code à mettre pour que la procédure s’arrête un moment, donnant ainsi la main à l'utilisateur qui pourrait faire alors un double clique qui serait capté par la fonction Event qui mettrait la variable à vrai
Et parsemer cette procédure de test de cette variable pour arrêter
Peut être Multitache ....
Je vais creuser cette piste ...
Cordialement
SC
Bonjour scn68100,
Lorsque j'ai un traitement long qui ne rend pas la main, je place un ThreadPause dans la boucle. Ainsi le traitement rend la main pour traiter un clic sur un bouton.
Par exemple, je place un bouton nommé "btn_Bouton1" qui affiche ""Exécuter" dans son libellé et un champ libellé nommé "Libellé1" sur la fenêtre
j'ai un traitement long qui compte jusque 50000 en affichant à chaque tour dans le libellé, ce qui est chronophage.
Je déclare Une variable globale :
dans le code du bouton "btn_Bouton1" :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 gbArret est un booléen = Faux
et ma procédure nommé "Incremente" :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 SI MoiMême..Libellé = "Exécuter" ALORS MoiMême..Libellé = "Stopper" gbArret = Faux ThreadExécute("monthread",threadNormal, Incremente) // j'exécute la procédure "Incremente" dans un Thread SINON MoiMême..Libellé = "Exécuter" gbArret = Vrai FIN
C'est ce type d'exemple qui se trouve dans les exemples Windev cités plus haut.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12 PROCÉDURE Incremente() i est un entier POUR i=1 À 50000 Libellé1 = i SI i modulo 50 = 0 ALORS // j'ai ajouté un Modulo 50 pour mettre en pause le thread toutes les 50 itérations ThreadPause(1) // le Thread pause permet de rendre la main pour permettre de cliquer sur le bouton SI gbArret = Vrai ALORS // si j'ai recliqué sur le bouton, la variable globale est à vrai et on demande l'arrêt du Thread ThreadArrête("monthread") FIN FIN FIN
Cdlt
Bonjour et merci à tous
Voila ce que j'ai testé
Création de la variable
Création d'un bouton
Code : Sélectionner tout - Visualiser dans une fenêtre à part gbArret est un booléen
Lancement du Thread
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 SI MoiMême..Libellé = "Demander Arrêt" ALORS MoiMême..Libellé = "Annuler l'arrêt" gbArret = Vrai SINON MoiMême..Libellé = "Demander Arrêt" gbArret = Faux FIN
Dans la procédure, j'ai ajouter ce code
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 Sablier(Vrai) BTN_Arret.Visible = Vrai gTMonThread = ThreadExécute(CreationListeRAR) ThreadAttend(gTMonThread) BTN_Arret.Visible = Faux
La procédure
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 Sablier(Faux) ThreadPause(1) SI gbArret = Vrai ALORS //////// Info("arrêt demandé") ThreadDemandeArrêt(gTMonThread) //////// gbOK = ThreadAttend(gTMonThread, 300) RETOUR FIN Sablier(Vrai)
Le problème que j'ai, (je dois être nulle !!!), est que le sablier est toujours actif !
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 PROCÉDURE CreationListeRAR() sLigne, sCheminComplet,sNomRAR, sCheminRAR sont des chaîne nNumLigne, nTaille, NbEnr,nNumLu,nNumFic est un entier gbOK = Vrai Sablier(Vrai) gbArret = Faux ////////nResultat est un entier = Evénement(DbClik,"*.*", WM_LBUTTONDBLCLK) ToastAffiche(gPoliceGras() + gPolice("Batang") + gPoliceTaille(12) + gStylo(Blanc) + "Création de la liste du contenu de la sauvegarde",toastLong, cvMilieu, chDroite, BleuFoncé) gbOK = HTransactionAnnule(ConnexionSauveRAR) SI gbOK = Faux ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur à HTransactionAnnule(ConnexionSauveRAR)",HErreurInfo(hErrComplet)) RETOUR FIN gbOK = HSupprimeTout(tabListRAR) SI gbOK = Faux ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur à l'effacement du fichier listant le RAR",HErreurInfo(hErrComplet)) RETOUR FIN SI fFichierExiste(gsFicListeRAR) = Vrai ALORS gbOK=fSupprime(gsFicListeRAR) SI gbOK = Faux ALORS Beep() Sablier(Faux) Erreur("Échec de la suppression du fichier "+gsFicListeRAR, ErreurInfo(errComplet)) RETOUR SINON nNumFic = fOuvre(gsFicErreur,foLectureEcriture+foCréationSiInexistant+foAnsi) SI nNumFic < 0 ALORS Beep() ; gbOK = Faux Sablier(Faux); gbOK = Faux Erreur("Erreur à la création du fichier "+gsFicListeRAR,ErreurInfo(errComplet)) RETOUR SINON fFerme(nNumFic) FIN FIN FIN Sablier(Faux) ThreadPause(1) SI gbArret = Vrai ALORS //////// info("arrêt demandé") ThreadDemandeArrêt(gTMonThread) ////// gbok = ThreadAttend(gTMonThread, 300) RETOUR FIN Sablier(Vrai) ch est une chaîne UNICODE ch = """" + SansEspace(gsFicListeBatchRAR,sscDroite+sscGauche) + """" + " " + """" + SansEspace(gsFicSauve,sscDroite+sscGauche) + """" + " " + """" + SansEspace(gsFicListeErreur,sscDroite+sscGauche) + """" gbOK = LanceAppli(ch,exeActif,exeBloquant) SI gbOK = Faux ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur au lancement du batch "+gsFicListeBatchRAR ,HErreurInfo(hErrComplet)) RETOUR FIN Sablier(Faux) ThreadPause(1) SI gbArret = Vrai ALORS //////// Info("arrêt demandé") ThreadDemandeArrêt(gTMonThread) //////// gbOK = ThreadAttend(gTMonThread, 300) RETOUR FIN Sablier(Vrai) sCodeRetour est une chaîne = fChargeTexte(gsFicListeErreur) nCodeRetour est un entier = Val(sCodeRetour) SI ((nCodeRetour <> 0) ET (nCodeRetour <> 10)) ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur durant la création de la liste de la sauvegarde : " + nCodeRetour) RETOUR FIN gbOK = HTransactionDébut(ConnexionSauveRAR) SI gbOK = Faux ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur à HTransactionDébut(ConnexionSauveRAR)",HErreurInfo(hErrComplet)) RETOUR FIN // Chargement en mémoire du contenu du fichier HPremier(tabListRAR,CheminRARNomRAR) // On est obligé de passer par un HSQL pour les fichiers dans l'ordre nNumLu = 0 nNumFichier est un entier nNumFichier = fOuvre(gsFicListeRAR, foLecture) // Pour traiter les fichiers SI nNumFichier <> -1 ALORS sLigne = fLitLigne(nNumFichier) // Lecture de la 1ère ligne nNumLu++ TraitementLigneFichier() TANTQUE sLigne<> EOT // Test de la fin de fichier SI nNumLu modulo 50 = 0 ALORS // j'ai ajouté un Modulo 50 pour mettre en pause le thread toutes les 50 itérations Sablier(Faux) ThreadPause(1) // le Thread pause permet de rendre la main pour permettre de cliquer sur le bouton SI gbArret = Vrai ALORS // si j'ai recliqué sur le bouton, la variable globale est à vrai et on demande l'arrêt du Thread ThreadDemandeArrêt(gTMonThread) FIN Sablier(Vrai) FIN // Lecture de la ligne suivante sLigne = fLitLigne(nNumFichier) nNumLu++ TraitementLigneFichier() FIN fFerme(nNumFichier) FIN SI nNumLu < 1 ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Le fichier listant le RAR est vide !!") RETOUR FIN nNumFichier = fOuvre (gsFicListeRAR, foLecture) // Pour traiter les Répertoires SI nNumFichier <> -1 ALORS sLigne = fLitLigne(nNumFichier) // Lecture de la 1ère ligne TraitementLigneRepertoire() TANTQUE sLigne<> EOT // Test de la fin de fichier // Lecture de la ligne suivante sLigne = fLitLigne(nNumFichier) TraitementLigneRepertoire() FIN fFerme(nNumFichier) FIN NbEnr = HNbEnr(tabListRAR) SI NbEnr = 0 ALORS Sablier(Faux); gbOK = Faux Erreur("Le fichier tabListRAR est vide !!") RETOUR FIN gbOK = HTransactionFin(ConnexionSauveRAR) SI gbOK = Faux ALORS Beep() Sablier(Faux); gbOK = Faux Erreur("Erreur à HTransactionFin(ConnexionSauveRAR)",HErreurInfo(hErrComplet)) RETOUR FIN Sablier(Faux) HEnregistre(tabListRAR) ToastSupprimeTout() ToastAffiche(gPoliceGras() + gPolice("Batang") + gPoliceTaille(12) + gStylo(Blanc) + "Fin de la liste du contenu de la sauvegarde",toastLong, cvMilieu, chDroite, BleuFoncé) Sablier(Faux)
J'ai remplacéque j'ai pris dans l'exemple par
Code : Sélectionner tout - Visualiser dans une fenêtre à part ThreadPause(1)Cela ne suffit pas ... Et de plus cela rallonge le temps de traitement
Code : Sélectionner tout - Visualiser dans une fenêtre à part ThreadPause(500)
En fait, je ne sais pas donner la main à l'utilisateur
Si Je ne lance pas le sablier cela pourrait peut être "marcher" Mais je crains en ce cas des cliques intempestifs .......
Comment arrêter le sablier dans une procédure ???
Cordialement
SC
Bonsoir,
Sauf erreur de ma part (je n'ai pas votre appli entre les mains et je n'ai pas tout le code), vous faites erreur concernant l'utilisation de la fonction "ThreadDemandeArrêt".
Cette fonction peut s'apparenter à votre variable "gbArret" mais elle ne permettra en aucun cas de stopper le Thread. Lisez les remarques sur cette page :
https://doc.pcsoft.fr/fr-FR/?1000021218
Pour stopper le Thread, lorsque vous cliquez sur le bouton "Demander Arrêt", le code du bouton doit contenir "ThreadDemandeArrêt", et dans le code du Thread, vous vérifiez avec ThreadArrêtDemandé.
Si ThreadArrêtDemandé est Vrai, alors vous arrêtez l'exécution avec la fonction ThreadFin.
Je ne vois aucun ThreadFin dans votre code
Voir la documentation :
https://doc.pcsoft.fr/fr-FR/?1000021219
Au besoin, commencer par faire des essais avec "ThreadArrête", fonction brutale mais qui vous permettra éventuellement de mieux comprendre.
Je vous conseille également de tester avec des portions de code avant de vous attaquer à votre code dans son intégralité.
La ligne qui précède "ThreadArrête" ou "ThreadFin" doit effectivement supprimer le sablier avec : Sablier(Faux)
Cdlt
Bonjour,
C'est normale que ça soit plus lent car tu fais un threadpause() à toutes les itérations de ta boucle. Il faut que tu le fasses tous les X itérations. Dans l'exemple de devocc, toutes les 50 itérations.
J'ai toujours la même question : est-il possible d'optimiser ta boucle, typîquement, remplacer des tantque pas hendehors() / hlitrecherchepremier(), par une requête SQL ?
Commencez toujours appuyer sur la touche F1 et puis n'hésitez à passer par un moteur de recherche...
Le forum est fait pour répondre aux questions : pas la peine de me les envoyer par MP. Merci.
Sur internet, tout est vrai ! Honoré de Balzac
Make it real not fantasy... Herman Rarebell
Bonjour à tous, et merci
Pardonnez-moi, mais mon souci n'est pas l'arrêt du Thread, ni d'optimiser ma boucle de lecture, ou autre .........
Le problème est que je n'arrive pas à donner la main à l'utilisateur, arrêter le sablier pour qu'il puisse demander l'arrêt
Cordialement
SC
Pour optimiser un peu ton code (et le simplifier) : Remplacer le fouvre() par un fchargetexte() et la boucle TANTQUE sligne<> EOT par un POUR TOUTE CHAINE sligne de sContenuFichier SEPAREE PAR RC
Si tu ajoutes, voire le remplaces, un multitache(-1) après le threadpause(). Si j'ai bien tout compris, le threadpause ne gère pas les évènements utilisateur.
Commencez toujours appuyer sur la touche F1 et puis n'hésitez à passer par un moteur de recherche...
Le forum est fait pour répondre aux questions : pas la peine de me les envoyer par MP. Merci.
Sur internet, tout est vrai ! Honoré de Balzac
Make it real not fantasy... Herman Rarebell
Bonjour,
effectivement, comme le précise FrenchSting, on peut lire que le ThreadPause ne gère pas les évènements utilisateur, mais ça fonctionne dans mon code qui contient une boucle et traitement d'un fichier .txt et qui ne rend pas la main sans le ThreadPause.
Ceci dit, si ça ne fonctionne pas pour vous, essayer effectivement le Multitache.
Perso, je parviens à reprendre la main avec un Multitache(2) minimum, mais attention, il faut faire appel au modulo pour ne pas freiner la boucle à chaque itération.
A vous de faire des tests pour trouver le temps du multitache qui pourrait convenir, et définir aussi un Modulo convenable (perso un Modulo 2000 est perceptible).
Il vous faudra un Multitache dans chacune des boucles Chronophages.
Cdlt
Bonsoir à tous, et merci
Je cherche la quadrature du cercle .........
J'ai supprimé tout les codes qui essaient de donner la main à l'utilisateur, et ma procédure est bien plus rapide ........
C'est la lecture et le traitement du fichier texte qui prend bien du temps
Je vais tacher d'améliorer
Mon intention était bonne, mais effectivement quand une procédure "travaille", qu'elle soit lancée par un Thread ou seule, il est difficile de l’interrompt
Et le code que l'on inclut pour prendre la main est inefficace et ralenti encore plus
Cordialement
SC
Bonjour,
Comme je le disais précédemment, remplace le fouvre()/flit() par un fchargetexte(). Il est coûteux en temps de faire des accès disque. Il est plus rapide de lire un fichier de 10 Mo en une seule fois que de ce fichier en 10 000 fois.
Commencez toujours appuyer sur la touche F1 et puis n'hésitez à passer par un moteur de recherche...
Le forum est fait pour répondre aux questions : pas la peine de me les envoyer par MP. Merci.
Sur internet, tout est vrai ! Honoré de Balzac
Make it real not fantasy... Herman Rarebell
Partager