Traitements longs: barres de progression et autres solutions

Si Paradox permet, grâce aux index primaires et secondaires un accès rapide aux informations, il arrive cependant que des traitements portent sur un nombre important d'enregistrements et durent un temps qui peut paraître long à l'utilisateur. ;Celui-ci peut croire que Paradox est planté et interrompre alors de façon inopinée le traitement (par Ctrl+Alt+Sup en général).

Première solution: instruction message()

La première solution (et la plus simple) consiste à informer l'utilisateur dans la zone message de la barre de status qu'un traitement est en cours et éventuellement de la progression de celui-ci.


scan tc:
  message("traitement en cours, enregistrement: +string(tc.recNo())+" sur " + string(tc.nrecords())
  attention recNo n'est pas utilisable si votre table est filtrée, utiliser alors un compteur
endscan

Limites de la solution message

Le premier inconvénient de cette méthode est la discrétion de l'information affichée. En effet si l'utilisateur averti et habitué regardera la bas de sa fiche, ce ne sera probablement pas un comportement général.

 

Le second inconvénient est qu'une action sur l'interface visuel, ce qui est le cas de l'instruction message, ralentit sensiblement le traitement. Ainsi, à titre indicatif (cela dépend évidemment du poste de test et de la table) le scan ci-dessus avec l'instruction message a une durée de 25" tandis que le même scan sans message dure 10" pour une table de 50000 enregistrements.

 

Pour élimier ce second inconvénient, nous conseillons de n'envoyer le message d'informations que tous les x enregistrements. Par exemple, avec notre table de 50000 enregistrements, on va choisir un pas de 1000.


pasSI = 0
scan tc:
if pasSI = 1000 then
message("traitement en cours, enregistrement: +string(tc.recNo())+" sur " + string(tc.nrecords())
   pasSI = 1
   else
pasSI = pasSI+1
endif
....  
endscan

Avec cette solution, notre temps de traitement est revenu à 11".

Seconde solution: barre de progression

Paradox fournit depuis la version 8 une barre de progression sous forme d'un objet ocx. Cette barre permet de matérialiser l'avancement de votre traitement.

 

La barre de progression dispose de 2 méthodes permettant pour la première de décrire les valeurs minimum et maximum dela plage des valeurs et pour la seconde de faire la progression. On peut également intervenir sur la propriété Value de l'objet.

 

Exemple pour voir le fonctionnement (à mettre dans la méthode pushbutton d'un bouton):


BarreDeProgression.setRangeAndStep(50,100,2)   valeurs 50 et100 minimum et maximum de la plage de valeurs de la barre et 2 = pas deprogession (valeurs de type smallint)
BarreDeProgression.Value = 75 ;la progression va être postionnée au milieu (la valeur doit être comprise entre les plages)
sleep(500)
for i from 1 to 100
BarreDeProgression.stepIt()   chaque fois on avance d'un pas de 2. 25 pas = la barre a été parcourue et on repartau début
; dans cet exemple, on part de plus depuis le milieu de la barre (l'instruction value a positonnée la barre)
  sleep(100)
endfor

Pour utilisation à l'intérieur d'un scan, un piège:

la solution simple consistant à définir une page par 0,nrecords() et un pas de 1 ne marche plus si le nombre d'enregistrement est > 32565 (on parcourt plusieurs fois la barre ce qui peut être décourageant pour l'utilisateur qui croyait la fin proche !). On peut se ramener par exemple à un pas standard de 100 et en utilisant la propriété value dans la boucle.


i = 0
scan tc:
i = i+1
barreDeProgression.Value = smallint(100*i/tc.nRecords())
endscan

On pourra se prémunir contre un ralentissement du traitement dû à l'intervention sur l'interface visuel (comme pour la solution message) en ne déclencahnt la mise à jour de la barre (par value ou par stepIt) que tous les x passages dans la boucle d'itération.

 

Limites de la barre de progression:

Pour que la barre de progression soit bien perçue par l'utilisateur, il faut:

 

1. qu'elle progresse, évidement: or si vous lancer une requête, vous n'avez pas de point d'intervention qui vous permette de signaler la progression réelle de la requête. Vous avez donc 0% au début et brutalement 100% à la fin. L'utilisateur peut avoir l'impression que son système est bloqué (il a raison: la barre de progression ne progresse pas);

 

2. qu'elle ne régresse pas: il est angoissant (et même désespérant) qu'alors que vous étiez à 90% vous repartiez à zéro. Or, c'est le cas si vous utilisez une barre de progression sans pouvoir déliimiter correctement sa plage de valeurs.

 

3. que sa progression soit (à peu près) régulière: il est énervant d'arriver très vite à 40%, d'attendre longtemps pour passer à 50%, d'aller très vite à 99% et là d'y rester un temps certain.Même si cela reflète assez bien la vie d'un développement informatique (il ya en plus quelquefois des régressions), l'utilisateur est peu sensible à ces explications.

 

Troisième solution: fiche Patience

Cette solution consiste à afficher une petite fiche, nous l'appelerons 'Patience", et à l'intérieur de celle-ci des messages l'informant de la progression du traitement. Pour cela notre fiche "Patience" a une zone de texte dans laquelle vont s'accumuler les messages. Vous trouverez ci-après une méthode (à situer de préférence en librairie) permettant la gestion d'une telle fiche.


method fmPatience_LO(VAR bddPatienceFM FORM,CONST MsgST STRING) LOGICAL
CONST
NbLigneCST  = 5   nombre de lignes pouvant appraître dans la zone Texte de la boîte de dialogue
ENDCONST
;--- la fiche 0WAIT contient une zone texte (TXTmsg) permettant l'affichage de 5 lignes de texte
IF NOT bddPatienceFM.isAssigned() THEN   
IF NOT bddPatienceFM.openAsDialog(":alias:Patience.FDL") THEN errorShow() RETURN FALSE ENDIF
ELSE
bddPatienceFM.show()
ENDIF
bddPatienceFM.setTitle("Veuillez patienter...")
if bddPatienceFM.TXTmsg.text = blank() then
bddPatienceFM.TXTmsg.text = msgST
else
;--- on ajoute une ligne de texte au contenu précédent
bddPatienceFM.TXTmsg.text = bddPatienceFM.TXTmsg.text+"\n"+msgST
endif
;--- si on a affiché plus de lignes que la zone de texte n'en a, on remonte d'une ligne
if bddPatienceFM.TXTmsg.text.CursorLine > NbLigneCST then
bddPatienceFM.TXTmsg.text.TopLine = bddPatienceFM.TXTmsg.text.TopLine + 1
endif
RETURN TRUE
endMethod

et dans votre programme principal, vous faites des appels successifs, en fonction de la progression des traitements, sans oublier de fermer la fiche "Patience" à la fin du traitement.


Uses ObjectPal
fmPatience_LO(VAR bddPatienceFM FORM,CONST MsgST STRING) LOGICAL
enduses
var
bddPatienceFM Form
endvar
glib.fmPatience_LO(bddPaitenceFM,"Début du traitement")
.....
glib.fmPatience_LO(bddPaitenceFM,"ça avance ....")
....
glib.fmPatience_LO(bddPaitenceFM,"c'est presque fini ...")
...
bddPatienceFM.close()

Améliorations:

Vous pouvez bien sûr à l'intérieur de votre fiche 'Patience", utilisez une barre de progression que vous rendez visible à volonté et dont vous modifiez les paramètres pour chaque phase de traitement. Vous pouvez ainsi matérialiser la liste des traitements avec leur état (fait, en cours, à faire) et pour le traitement en cours, matérialiser l'avancement.

Vous pouvez aussi, mettre un bouton (ou une case à cocher) permettant à l'utilisateur d'interrompre un traitement. ;Il suffit alors que votre programme teste à chaque phase de traitement la zone concernée pour arrêter prématurément le traitement.