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

VBA Access Discussion :

Chronomètre et progress bar pour suivre le déroulement d'un traitement long


Sujet :

VBA Access

  1. #1
    FMJ
    FMJ est déconnecté
    Membre averti
    Profil pro
    tutu
    Inscrit en
    Octobre 2003
    Messages
    416
    Détails du profil
    Informations personnelles :
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : tutu

    Informations forums :
    Inscription : Octobre 2003
    Messages : 416
    Points : 363
    Points
    363
    Par défaut Chronomètre et progress bar pour suivre le déroulement d'un traitement long
    Bonjour,

    Dans un formulaire où l'utilisateur lance à destination d'un SQL Server un traitement long de synchronisation avec de nombreuses requêtes SQL, j'ai souhaité inclure une progress bar et un chronomètre permettant à l'utilisateur de visualiser l'avancement et la durée de ce traitement de plusieurs minutes (et surtout d'avoir une idée approximative de la durée restante).

    Pour la progress bar, j'ai utilisé un contrôle activex et pour le calcul du pourcentage d'avancement, je n'ai pas réinventé la poudre --> au début, calcul du nombre d'enregistrements puis insertion d'une clause if dans les boucles de recordsets qui met à jour la progress bar tous les 100, 200, ou etc. enregistrements. Ca marche bien.

    Pour le chronomètre, j'ai un peu plus de mal.
    Tout d'abord, j'ai essayé de passer par le Form_Timer et le GetTickCount du kernel32. Ca marche très bien pour des phases statiques mais ... comme VBA ne gère pas le parallel processing, Form_Timer perd la main dès qu'une sub s'exécute. Et donc le chrono se fige jusqu'à ce que la sub ait fini de s'exécuter --> méthode inutilisable à l'intérieur d'un traitement.
    Ensuite, j'ai procédé comme pour la progress bar, en incrémentant le chrono dans les boucles de recordsets.

    Cela fonctionnerait pas trop mal ... s'il n'y avait un gros souci. En fait, certaines requêtes chargent via le réseau des recordsets assez volumineux (de type rst.Open "SELECT ......"), ce qui rend plutôt très long leur temps de chargement (plusieurs dizaines de secondes). Et donc durant ce laps de temps où cette instruction est exécutée, la progress bar et le chronomètre sont bien évidemment scotchés. Le chrono prend ensuite x secondes d'un coup dès que le rst.open est chargé et que la boucle du rst est exécutée.



    Au final, à moins de réaliser un recordset par enregistrement (ce qui serait contre-productif en terme de durée, puisque certaines tables font des dizaines de milliers de lignes), je ne vois pas comment je pourrais obtenir une progress bar et un chronomètre qui tiendraient la route ???!!!

    Auriez-vous quelques suggestions lumineuses qui pourraient m'aiguiller ? Merci d'avance.

  2. #2
    Membre éprouvé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2010
    Messages
    801
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Maritime (Haute Normandie)

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

    Informations forums :
    Inscription : Février 2010
    Messages : 801
    Points : 1 107
    Points
    1 107
    Par défaut
    Citation Envoyé par FMJ Voir le message
    Ca marche très bien pour des phases statiques mais ... comme VBA ne gère pas le parallel processing, Form_Timer perd la main dès qu'une sub s'exécute. Et donc le chrono se fige jusqu'à ce que la sub ait fini de s'exécuter --> méthode inutilisable à l'intérieur d'un traitement.
    Bonjour,
    Question bête : As-tu regardé la fonction DoEvents ?

  3. #3
    FMJ
    FMJ est déconnecté
    Membre averti
    Profil pro
    tutu
    Inscrit en
    Octobre 2003
    Messages
    416
    Détails du profil
    Informations personnelles :
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : tutu

    Informations forums :
    Inscription : Octobre 2003
    Messages : 416
    Points : 363
    Points
    363
    Par défaut
    Bonjour et merci pour ta réponse.

    Malheureusement, DoEvents qui permet de passer momentanément la main à l'OS n'aide pas dans mon cas. Le problème ne vient pas d'un grand nombre d'itérations dans une boucle mais du fait qu'une seule instruction (un rst.open "SELECT ....") prend beaucoup de temps à charger un gros volume de données.

    Comme j'indiquais précédemment, il serait possible de faire un rst.open "SELECT ..." pour une seule ligne, avec la clause WHERE qui va bien, mais cela serait très contreproductif compte tenu du nombre de lignes (si ajouter un chrono pour patienter entraîner une rallongement de X2 de la durée, c'est pas glop).


    Autrement, je pourrais faire une boucle où le rst.open charge traite 100 lignes par chaque itération. Mais c'est compliqué du point de vue du requêtage car en SQL il y un "TOP x" (les x premiers) mais il n'existe pas un "TOP x y" (de la ligne x à la ligne y).
    Hummmm, je vais quand même explorer cette piste car il me vient une petite idée. Mais il faut que je vérifie quel est l'impact en terme de durée de traitement supplémentaire.


    Un début d'autre piste ? --> le multi-threating
    http://access.developpez.com/sources...acc#creaThread

  4. #4
    Rédacteur/Modérateur

    Avatar de Jean-Philippe André
    Homme Profil pro
    Architecte Power Platform, ex-Développeur VBA/C#/VB.Net
    Inscrit en
    Juillet 2007
    Messages
    14 652
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : Canada

    Informations professionnelles :
    Activité : Architecte Power Platform, ex-Développeur VBA/C#/VB.Net
    Secteur : Finance

    Informations forums :
    Inscription : Juillet 2007
    Messages : 14 652
    Points : 34 356
    Points
    34 356
    Par défaut
    Bonjour,

    j'ai une autre piste pour toi, les images d'attente ^^

    L'excellent tuto d'Arkham46 devrait t'aider à faire patienter tout ton petit monde:
    http://arkham46.developpez.com/artic...s/formattente/

  5. #5
    FMJ
    FMJ est déconnecté
    Membre averti
    Profil pro
    tutu
    Inscrit en
    Octobre 2003
    Messages
    416
    Détails du profil
    Informations personnelles :
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : tutu

    Informations forums :
    Inscription : Octobre 2003
    Messages : 416
    Points : 363
    Points
    363
    Par défaut
    Bonjour et merci pour la proposition. On s'en tiendra aux images car l'autre solution proposée ne permet pas de résoudre mon pb : elle utilise un formulaire d'attente mis à jour toutes les X itérations du traitement.
    Or dans mon cas, ce sont des traitements correspondant à une seule commande (rst.open "SELECT ....") qui génère l'essentiel du temps d'attente. Donc je ne peux donc pas y insérer de DoEvents permettant de mettre à jour le formulaire d'attente.

    Il faut que je scinde cette commande en x commandes de durée largement inférieure pour pouvoir utiliser cette stratégie.

  6. #6
    Modérateur

    Homme Profil pro
    Inscrit en
    Octobre 2005
    Messages
    15 365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2005
    Messages : 15 365
    Points : 23 835
    Points
    23 835
    Par défaut
    Oui si Access n'a pas la main, il ne peut pas faire la mise à jour.

    Deux idées :
    • Non testé : ouvrir une fenêtre IE qui affiche une page HTML toute bête avec un GIF animé et un message "Merci de patienter".
      On peut peut-être utiliser un ActiveX qui affiche le navigateur dans le form.
    • Ce que j'ai fait pour donner l'illusion de l'avancement progressif : avant le long traitement je fais avancer ma barre de progression de 50% de la distance prévue et quand le traitement est terminé je la met à 100%.


    A+

  7. #7
    FMJ
    FMJ est déconnecté
    Membre averti
    Profil pro
    tutu
    Inscrit en
    Octobre 2003
    Messages
    416
    Détails du profil
    Informations personnelles :
    Localisation : France, Aveyron (Midi Pyrénées)

    Informations professionnelles :
    Activité : tutu

    Informations forums :
    Inscription : Octobre 2003
    Messages : 416
    Points : 363
    Points
    363
    Par défaut
    C'est une possibilité mais cela ne donne pas vraiment d'indication à l'utilisateur sur la durée qu'il va devoir poireauter !


    Si l'on veut un peu de précision, on n'a pas le choix il faut atomiser la commande qui prend trop de temps. Alors j'ai testé la solution dont je parlais un peu plus haut. Au lieu de faire un rst.open "SELECT ....." sur l'ensemble des lignes à charger, je le décompose par une boucle comme suit :
    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
    ' Variable qui permet de filtrer la tranche suivante de lignes dans le SELECT
    v_lastrecord=""
    ' Boucle infinie qui permet de charger à chaque itération 100 nouvelles lignes
    Do
      rst.Open "SELECT TOP 100 [champ1], [champ2], (etc.) FROM [table] WHERE [conditions] AND [champ1]>'" &  v_lastrecord & "'", ..........
     
      ' S'il n'y a plus de ligne à charger, on met fin à la boucle
      If rst.EOF then Exit Do
     
      ' Toutes les 100 lignes, le chronomètre et la Progress Bar sont mise à jour
      v_compteur=v_compteur+1
      if v_compteur Mod 100 = 0 then 
        {clause permettant d'incrémenter le chronomètre et la progressbar}
        DoEvents
    end if
     
      'Boucle de traitement de toutes les lignes chargées  
      While Not rst.EOF then
        ' A l'itération suivante, permet de faire le SELECT après la dernière ligne chargée
         v_lastrecord= rst.fields("[champ1]")
         {traitement des ligne chargées}
      Wend
    Loop
    Dans ce cas, c'est simple car la clé unique porte sur un seul champ via [champ1]>'" & v_lastrecord & "'". Il faudra faire un peu d'adaptation dans le cas d'une clé composée (soit en utilisant autant de variable v_lastracord[x] que de clés composées, soit en créant une variable composée du style v_lastracord=[champ1] & [champ2] & ....).

    En définitive, toute la subtilité de la chose réside dans le choix du nombre de lignes à charger à chaque itération (TOP 100). Trop peu charge le traitement pour rien. Trop rend le chronomètre et la Progress Bar saccadés.

  8. #8
    Modérateur

    Homme Profil pro
    Inscrit en
    Octobre 2005
    Messages
    15 365
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations forums :
    Inscription : Octobre 2005
    Messages : 15 365
    Points : 23 835
    Points
    23 835
    Par défaut
    Citation Envoyé par FMJ
    C'est une possibilité mais cela ne donne pas vraiment d'indication à l'utilisateur sur la durée qu'il va devoir poireauter !
    Parfaitement d'accord, c'est un écran de fumée pas une holrloge suisse :-).

    A+

Discussions similaires

  1. Réponses: 4
    Dernier message: 30/05/2012, 13h40
  2. Progress bar pour le chargement d'une datagrid
    Par johnaliashead dans le forum Silverlight
    Réponses: 6
    Dernier message: 28/04/2010, 12h43
  3. Progress Bar pour une initialisation de table
    Par souminet dans le forum Débuter
    Réponses: 4
    Dernier message: 01/12/2008, 10h58
  4. [FLASH 8 PRO] progress bar pour une fonction php
    Par jc_cornic dans le forum Flash
    Réponses: 6
    Dernier message: 08/11/2006, 20h55
  5. marche a suivre pour la creation d'un progress bar
    Par azde7015 dans le forum Access
    Réponses: 1
    Dernier message: 19/02/2006, 11h06

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