28 novembre 2006

Inhiber une action standard sur un événement (clavier, souris...)

Windows apporte pour tous les champs des comportements standard pour lesquels aucune programation n'est requise, et c'est tant mieux par ce que je ne programmerai pas tout ça tous les jours !

Mais comme il est toujours bon d'en vouloir plus, on peut dans certain cas ne pas vouloir du comportement standard sur une action bien précise.

Par exemple, on pourrait décider qu'un champ ascenseur ne doit pas réagir à la roulette de la souris. Il suffit pour y parvenir d'ajouter le traitement optionnel "roulette souris", puis de faire renvoyer la valeur "0" à l'événement Windows correspondant (cf. montage écran).

En effet, le traitement optionnel est appelé lorsque Windows fait l'envoi de l'événement au champ, et avant même que Windows n'ait effectué son action standard. De ce fait on peut très bien exécuter un code avant l'action standard de Windows, et aller jusqu'à l'inhiber comme dans ces exemples.

Cela est également valable avec la fonction "Evénement" du WLangage qui fonctionne suivant le même principe.

27 novembre 2006

Imprimer directement depuis un aperçu...

La fenêtre d'aperçu propose lors de l'impression de choisir le nombre de pages à imprimer, ainsi que le nombre de copies. D'un point de vue ergonomique, cela peut ne pas convenir à certains usages, voici la méthode pour ne pas avoir de confirmation et imprimer directement :

Aperçu personnalisé :
Il faut tout d'abord copier dans le répertoire du projet la fenêtre de l'aperçu, c'est une fenêtre WINDEV dans le répertoire des exemples. Ouvrir ensuite cette fenêtre dans le projet, afin de l'enregistrer dans les éléments du projet. Par le menu "Projet / Description du projet" et le volet "Style", il faut donner cette fenêtre comme fenêtre d'aperçu. A ce stade, il est possible de personnaliser la fenêtre utilisée lors de l'aperçu depuis l'application.

Modification de l'aperçu :
Ouvrir la description du bouton "Imprimer", et simplement changer son nom en "ImprimerTout". Au prochaine aperçu, ce bouton lancera directement l'impression !

23 novembre 2006

Big Brother dans les applications ...

Dans un site en exploitation avec des données en perpétuelle mouvements, il n'est pas rare de rencontrer deux utilisateurs se disputant la propriété ... d'une erreur de manipulation.

- C'est toi il y a une semaine qui m'a dit de rentrer la nouvelle adresse.
- Oui mais ensuite je l'ai changée à nouveau, tu n'aurais pas du l'écraser !
- Non je ne l'ai pas changée à nouveau, c'est toi qui a oublié de la changer ...

Au final le conflit est stoppé en se tournant vers l'informaticien en charge du programme :
- [en coeur] : on vient te voir car l'application a encore perdu une adresse.

Ca sent le vécu pour beaucoup j'en suis certain. Un remède très efficace existe, c'est la journalisation. Cerise sur le gâteau, son activation se fait par une case à cocher dans la description des fichiers de l'analyse, aucune programmation n'est requise. La journalisation des écritures est suffisante pour régler tout litige tel que décrit ci-dessus.

Lorsque la journalisation est active, le menu contextuel des champs reliés aux rubriques de fichiers dispose d'une option "Historique..." qui permet de connaître toutes les valeurs successivement prises par la rubrique avec :
- la date de l'heure de modification,
- la valeur,
- le poste depuis lequel le changement a été fait,
- le login de l'utilisateur qui a fait le changement.

Toutes ces informations peuvent également être proposées par une interface spécifique de l'application, avec la fonction "HHistoriqueModification" qui retourne, entre autre, toutes ces informations sur les modifications.

Plus de doute sur qui a fait quoi !

Autre intérêt, la journalisation permet après une restauration de données de rajouter à la sauvegarde les dernières modifications.

Deux recommandations pour l'utilisation de la journalisation :
- utiliser dans le programme la fonction "HPoste" pour renseigner le journal précisément sur l'auteur des changements,
- placer les journaux sur un autre disque que celui contenant les données, avec la fonction "HChangeRepJNL".

22 novembre 2006

Mémorisation de la position des colonnes de table inopérante ?

Option à ne pas oublier dans la description des tables : mémoriser la position des colonnes. Elle permet sans ajouter de code, de proposer à l'utilisateur final de toujours retrouver les colonnes comme il les a lui-même positionnées.

Seulement il peut arriver que cette mémorisation soit inefficace sur une table donnée, sans raison apparente. Il y a bien sûr une explication : la propriété "..MenuContextuel". En effet, l'affectation de la propriété "..MenuContextuel" d'une table, inhibe la restauration de ses paramètres mémorisés. Ceci dans le but de ne pas avoir de colonne invisible, qui ne pourrait pas être restaurée. Mais encore faut-il le savoir, je n'ai pas trouvé l'information dans la documentation. Je l'ai obtenue du support après avoir cherché un long moment pourquoi dans une même fenêtre une première table avait ses colonnes bien remises en place, mais pas une seconde.

Peut-être qu'une prochaine version de WINDEV pourra afficher des informations aux développeurs pour les informer lors de l'utilisation d'une propriété de ce type des effets engendrés sur d'autres fonctions.

17 novembre 2006

Fichier en cours d'utilisation ... mais par qui ?

Un fichier de données reste parfois ouvert, alors que l'on est persuadé qu'il n'est plus utilisé, et que tous les programmes l'utilisant sont arrêtés...

Fichier en cours d'utilisation sur un autre poste ...
Le fichier est utilisé par un autre processus ...

Un utilitaire pouvant se substituer au gestionnaire de tâches est très commode, car il permet de connaître les processus qui utilisent un fichier. Il s'agit de Process Explorer, à toujours garder dans ses outils de travail. Anecdote, l'éditeur d'origine est Sysinternal, mais il semble qu'il y ait eu récemment une absorption de Microsoft. En effet, l'adresse http://www.sysinternals.com est directement redirigée sur un site Microsoft !

Bref, le menu "Find / Find handle or DLL" permet de donner le chemin d'accès au fichier de données en cours d'utilisation ... qui ne devrait pas l'être. La recherche vous donne ensuite la liste de tous les processus utilisant ce fichier.

Au passage, deux "pièges" pouvant aboutit à laisser un fichier de données ouvert ...
- fichier Hyper File, appeler la fonction "HFerme", mais oublier les "HAnnuleDéclaration" sur les requêtes exécutées sur ce même fichier,
- fichier externe : appeler "fCrée" et "fOuvre", et ne faire qu'un seul "fFerme". Pour éviter tout risque d'erreur, préférer "fOuvre" seule, mais avec le paramètre "foCréation".

Une image (celle du site Microsoft) de l'utilitaire complet, tel qu'il apparaît si vous le substituez au gestionnaire de tâches :

15 novembre 2006

Importer un fichier texte dans un fichier Hyper File en formatant des dates

L'importation d'un fichier texte dans un fichier Hyper File est facilitée par la commande "HImporteTexte". Dans le cas favorable, l'importation est donc extrêmement simple grâce à cette fonction. Bien évidemment, suivant l'origine des données à importer, on se trouve en dehors du "cas favorable", il suffit par exemple qu'une date n'ait pas le format "AAAAMMJJ" attendu.

Dans ce cas, il faut avoir recours à une bonne vieille moulinette, facilitée par l'instruction POUR TOUT.

L'exemple ci-dessous utilise une demande du forum, ce sujet revenant assez régulièrement d'ou l'idée de ce billet. Il faut souligner qu'il s'applique aux dates, mais également à tout ce qui peut nécessiter une conversion avant la mise en base.

Soit un fichier texte à importer avec le contenu suivant (une tabulation sépare les colonnes, un retour chariot sépare les lignes ) :

15/03/2002 abc imo 560,33
19/04/2003 klo hap 561,00
12/06/2001 dol pme 7840,00

Si la date dans le fichier texte avait été codée sous la forme "20020315" la fonction "HImporteTexte" aurait fait l'affaire. Mais avec ce codage de date, il faut mouliner (une fois de plus ?). Voici un exemple de code pouvant être utilisé :
sEnregistrementsAImporter est une chaîne
sNouvelEnreg est une chaîne
sDateNonformatée est une chaîne

sEnregistrementsAImporter = fChargeTexte("C:\MonFichier.txt")

POUR TOUTE CHAINE sNouvelEnreg DE sEnregistrementsAImporter SEPAREE PAR RC

sDateNonformatée = ExtraitChaîne(sNouvelEnreg, 1, TAB)
Fichier.Date = ChaîneVersDate(sDateNonformatée, "JJ/MM/AAAA")
Fichier.Personne = ExtraitChaîne(sNouvelEnreg, 2, TAB)
Fichier.Libellé = ExtraitChaîne(sNouvelEnreg, 3, TAB)
Fichier.Montant = ExtraitChaîne(sNouvelEnreg, 4, TAB)

HAjoute(Fichier)
FIN

Une bonne évolution de la fonction "HImporteTexte", serait de pouvoir donner dans les délimiteurs un format de date comme le prend la fonction "ChaîneVersDate". Le champ d'action de "HImporteTexte" serait grandement étendu. J'en ai fait la suggestion au support.

14 novembre 2006

Une option de menu contextuel en gras

Une astuce permet de passer une unique option d'un menu contextuel, ayant le look XP, en gras.

Il suffit d'intervenir sur le libellé, et de l'encadrer avec les balises HTML de mise en gras, l'image ci-contre met en évidence ce procédé.

Cela permet par exemple de mettre en évidence l'option la plus couramment utilisée, un "petit plus" dans une interface !

10 novembre 2006

Bêta Blogger avant WINDEV 11, pour une leçon d'ergonomie !

En attendant de pouvoir m'initier aux nouveautés de WINDEV 11, je découvre la bêta version des Blogs façon Google. Un sacré chamboulement puisqu'en terme de fonctionnalités, visibles tout autour de ce billet, il est maintenant possible :
- d'ajouter des flux RSS,
- de regrouper les billets par sujets,
- d'avoir une arborescence des archives ...

Mais surtout, et là chapeau, surtout il est possible d'obtenir tout cela sans aucune programmation Web, dans un environnement totalement dynamique. Un simple glisser/déplacer d'un élémement dans la page permet de disposer les différentes zones du blog. Un modèle d'ergonomie.

J'encourage tous ceux qui le peuvent à prendre quelques minutes pour créer un blog de test, juste pour découvrir la simplicité avec laquelle toutes les possibilités du blog sont proposées. Les développeurs se sont réellement creusés pour parvenir à un tel résultat, c'est un très bel exemple de respect de l'utlisateur.

Stockage des images, optimisation de l'espace en changeant le format...

Une rubrique mémo image est généralement affectée par une commande de la forme Fichier.RubriqueImage = ChampImage, ou "EcranVersFichier".

Cette affectation copie fidèlement l'image contenue dans le champ, au format BMP.

En fonction du contenu de l'image, un enregistrement en JPEG est souvent suffisant en qualité, et permet de diviser la taille de l'image par un facture important. Il suffit pour cela d'affecter la rubrique avec un appel de la fonction "dSauveimageJPEG" :

Fichier.RubriqueImage = dSauveImageJPEG(ChampImage, enMémoire)


Petite astuce bien utile s'il faut gagner un peu de place, surtout si l'application doit à un moment donné transférer ces images.

09 novembre 2006

Présentation par défaut de l'interface d'une application déployée

L'utilisateur final d'une application peut configurer la présentation de son application :
- position et taille des fenêtres,
- largeur des colonnes des tables,
- mémorisation de valeurs pour les champs...

Comme il est courant pour tout le monde de changer d'avis, il est souhaitable de lui proposer une option de l'application permettant de revenir à l'état initial pour toutes les informations mémorisées. Il peut s'agit d'un menu "Paramètres / Rétablir l'affichage par défaut". Cette option devra simplement faire la suppression dans la base de registres de la clé retournée par la fonction "ProjetInfo(piRegistre)".

Pour aller plus loin sur ce mécanisme, il est également possible de remplacer la sauvegarde faite dans la base de registres, par une sauvegarde dans un fichier XML. Cette possibilité est donnée par la fonction "InitParamètre". L'avantage est alors de pouvoir facilement "transporter" les paramètres sauvegardés d'un poste à un autre. Il est même envisageable de conserver le fichier XML dans un fichier de la base de données, afin de le restituer automatiquement au lancement de l'application en fonction de l'utilisateur qui se connecte !

08 novembre 2006

Loupe de recherche des colonnes des tables

Voici une "checklist" à appliquer lorsque la loupe d'une colonne de table fichier n'apparaît pas lors de l'exécution.

- la coche "Avec recherche (loupe)" doit être active dans la description de la colonne, tout comme la coche "Triable" (volet "Général"),

- la rubrique du fichier associé à la colonne (volet "Liaison") doit être une clé dans le fichier (unique ou doublon peut importe),

- le remplissage de la table doit se faire sans programmation. Il s'agit du sélecteur "Parcours automatique" dans la description de la table (volet "Contenu"),

- l'option "Ascenseur proportionnel" doit également être cochée toujours dans la description de la table (volet "Détail").

Toutes ces conditions doivent être réunies dans le cas d'une table directement reliée à un fichier. Si la table est reliée à une requête, la nécessité de clé dans le fichier de données disparaît.

03 novembre 2006

Accélérateur de traitement ...

Avec son nom tout à fait anodin, la propriété "..AffichageActif" peut accélérer de façon considérables des traitements. En effet, vous pouvez l'observer avec l'analyseur de performances, les accès à l'interface sont coûteux temps. Par exemple un parcours de données avec des "TableAjoute" dans une table mémoire (champ d'affichage) sera plus long qu'un même parcours chargeant une variable tableau (fonction "TableauAjoute"). Là peut intervenir avantageusement la propriété, en inhibant provisoirement l'affichage. Exemple :

NomFenêtre..AffichageActif = Faux
TraitementXYZ()
NomFenêtre..AffichageActif = Vrai

Simple, mais redoutable d'efficacité dans bien des cas de figure !

02 novembre 2006

Etat imprimé à tout faire (zone répétée, variable tableau...)

Par défaut l'assistant de création des états permet de sélectionner des données dans les fichiers de l'analyse, une requête, un champ table...

Il existe une méthode d'impression "passe partout" car elle permet d'imprimer tout aussi facilement tout et n'importe quoi. L'idée de ce billet m'est venue en voyant le forum, pour l'impression des données d'une zone répétée.

Il suffit dans l'assistant de création d'état d'indiquer "Je programme la lecture de ma source de données", puis de créer dans l'état des champs avec les types et masques désirés. C'est le code de l'état et du bloc corps que se chargeront du parcours des données, la programmation est minime :

// code d'ouverture de l'état
// Déclaration d'une globale pour parcourir les répétitions
// ou pourquoi pas les éléments d'une variable tableau...
gnIndice est un entier = 1


// Code de lecture des données de l'état
// Termine l'impression si toutes les répétitions ont été vues
SI gnIndice > ZoneRépétéeOccurrence(FEN_FenetreAppelante.ZR_ZoneRépétée1) ALORS
RENVOYER Faux
SINON
RENVOYER Vrai
FIN


// Code avant impression du bloc corps
// Affectation des champs de l'état avec les attributs
// de la zone qui permettent d'avoir les valeurs
RUB_RUBRIQUE1 = FEN_FenetreAppelant.ZR_ZoneRépétée1.ATT_Attribut1[gnIndice]
RUB_RUBRIQUE2 = FEN_FenetreAppelant.ZR_ZoneRépétée1.ATT_Attribut2[gnIndice]
RUB_RUBRIQUE3 = FEN_FenetreAppelant.ZR_ZoneRépétée1.ATT_Attribut3[gnIndice]
...
// Incrémentation du compteur pour passer à la prochaine répétition.
gnIndice++


Cette méthode peut être étendue à tout ce qui peut se parcourir, dans l'exemple on lit les répétitions d'une zone répétée, mais il peut s'agit d'un tableau, d'une zone mémoire, un fichier texte...

Représentation binaire d'une valeur.

A toute fin utile, voici un traitement proposé en réponse sur le forum pour obtenir la représentation binaire d'une valeur. La fonction de conversion inverse est donnée également.

FONCTION VersBinaire(nValeurDecimal)

sBinaire est une chaîne = ""
nb est un entier = nValeurDecimal

TANTQUE nb > 0
SI modulo(nb,2) ALORS
sBinaire = "1" + sBinaire
SINON
sBinaire = "0" + sBinaire
FIN
nb = nb/2
FIN
RENVOYER sBinaire


Et la récupération d'une valeur exprimée en décimal, à partir de sa représentation binaire :

FONCTION DepuisBinaire(schaineBinaire)

sBinaire est une chaîne = schaineBinaire
i est un entier
j est un entier
nDecimal est un entier
nCalcul est un entier

POUR i = Taille (sBinaire) A 1 PAS -1
nCalcul = 2*Val (sBinaire[[i A i]])
SI nCalcul ALORS
nDecimal += Puissance (nCalcul,j)
FIN
j++
FIN

RENVOYER nDecimal