Catégorie : développement

Tout ce qui concerne le développement en général, que ce soit des choses générales, ou des choses bien précises. Cela va de la gestion de projet à la recherche du fonctionnement de pointeurs en C.

Unity

Unity aide-mémoire

Fichiers

Chemin de base Application.persistentDataPath
Encrypter les données.
Trouvé ici.
...

GameObjects dans l’éditeur : mémo

Faire une liste déroulante avec des items sur mesure.
Trouvé ici.
public enum IngredientUnit {
    Spoon, Cup, Bowl, Piece
}
// Custom serializable class
[Serializable]
public class Ingredient
{
    public string name;
    public int amount = 1;
    public IngredientUnit unit;
}

Puis, dans n’importe quelle page, on peut ajouter une property de type Ingredient, et même un tableau d’Ingredient ainsi :

    public Ingredient potionResult;
    public Ingredient[] potionIngredients;

GameObjects : mémo

Taille Mesh Mesh m = obj.GetComponent<MeshFilter>().sharedMesh;
Debug.Log(m.bounds.size);
Taille Sprites var m = transform.GetComponent<Renderer>();
Debug.Log(m.bounds.size);

Assets et Objets

Pour comprendre comment gérer correctement les données dans Unity, il faut impérativement comprendre comment Unity identifie et sérialise les données.
Le premier point-clé est de faire la distinction entre les Assets et UnityEngine.Objects.

Un Asset est un fichier sur le disque, stocké dans le dossier Assets d’un projet Unity. Les textures, modèles 3D, ou encore clips audio sont des types d’Assets très courants. Quelques Assets contiennent des données au format natif de Unity, tels que les materiaux. D’autres Assets doivent être convertis en format natif, comme par exemple les fichiers FBX.

UnityEngine.Object, ou bien Object avec un ‘O‘majuscule, est un ensemble de données sérialisées qui décrivent une instance spécifique d’une ressource. Cela peut être n’importe quelle ressource utilisée par le Unity Engine, telle qu’un mesh, sprite, AudioClip ou AnimationClip. Tous les objets sont des sous-classes de la classe de base UnityEngine.Object.

Alors que la plupart des types Object sont built-in (= natifs), il y a deux types spéciaux.

  1. Un ScriptableObject fournit un système pratique pour les développeurs qui désirent définir leurs propre types de données. Ces types peuvent être nativement sérialisés et dé-sérialisés par Unity, et manipulés dans la fenêtre « inspecteur » de l’éditeur de Unity
  2. Un MonoBehaviour fournit un wrapper qui est lié à un MonoScript. Un MonoScript est un type de données interne que Unity utilise pour garder une référence à une class spécifique de script à l’intérieur d’un namespace et assembly spécifique. Un MonoScript ne contient pas réellement de code exécutable.

Il y a une relation one-to-many entre Asset et Object. Dit autrement, un fichier Asset peut contenir un ou plusieurs Object.

– Créer l’URL de logout :
url(r'^logout/$', LogoutView.as_view(), name='logout'),

– Créer la vue LogoutView
from django.contrib.auth import views

class LogoutView(views.LogoutView):

    def __init__(self):
        self.next_page = '/'

<form action="{% url 'login' %}" method="post" accept-charset="utf-8">
    {% csrf_token %}
    {% for field in form %}
        <label>{{ field.label }}</label>
        {% if field.errors %}
            {{ field.errors }}
        {% endif %}
        {{ field }}
    {% endfor %}
    <input type="hidden" name="next" value="{{ next }}" />
    <input class="button small" type="submit" value="Submit"/>
</form>

Références entre Object‘s

Tous les UnityEngine.Objects peuvent avoir des références vers d’autres UnityEngine.Objects. Ces derniers peuvent résider dans le même fichier Asset, ou peuvent être importé à partir d’autres fichiers Asset. Par exemple, un Object matériau a habituellement une ou plusieurs références vers des Object texture. Ces Object texture sont généralement importés à partir d’un ou plusieurs autres fichiers Asset (tels que des PNG‘s ou JPG‘s).
Lorsqu’elles sont sérialisées, ces références sont constituées en deux blocs de données : un File GUID et un Local ID.
Le File GUID identifie le fichier Asset. Un Local ID identifie chaque objet à l’intérieur d’un fichier Asset, car un fichier Asset peut contenir plusieurs objets.
L’identification et le système de référence peut être visualisé dans un éditeur de texte : créez un nouveau projet Unity et changez Editor Settings en cochant Visible Meta Files et Serialiaze Assets as text. Créez un nouveau matérieau, puis importez une texture dans le projet. Assignez le matériau à un cube dans la scène et sauvez cette dernière.
Avec un éditeur de texte, ouvrez le fichier .meta associé au matériau. Une ligne guid sera présente parmi les première lignes. Cette ligne définit le GUID du matériau. Pour trouver l’id local, ouvrez le fichier matériau dans un éditeur de texte. La première ligne qui ressemble à ‘--- !u! &2100000‘ correspond à l’id local.

Pourquoi des « File GUIDs » et des Local ID‘s ?

Pourquoi le principe des « File GUIDs » et des Local ID‘s est-il important  ? La réponse est : dans la stabilité et la possibilité d’avoir un workflow indépendant de la plateforme.

Comment faire un affichage 100% proportionnel quelle que soit la résolution de l’écran ?

En fait, d’après ce que j’ai compris, il faut comprendre qu’on ne « devrait » pas toucher à la taille de la fenêtre de l’application : c’est l’utilisateur qui la fixe : téléphone mobile, portrait ou paysage, ou écran 4k voire 8k, l’application doit s’adapter et devrait même être redimensionnable.
L’idée que j’ai retirée de mon expérience, qui est peut-être un peu différente de la réalité « Unity », est la suivante :

  • Il faut créer un Canvas et lui dire de rester proportionnel à l’écran. Comme cela, même sur les écrans géants, il s’adaptera ;
  • Dans ce Canvas, il faut créer un « référent » qui servira de conteneur à tout le reste. Ce conteneur, il faut lui dire de « remplir » son parent (donc de remplir le Canvas) selon une échelle à

Pourquoi ne pas faire ces deux opérations dans le Canvas ? (1) dire de rester proportionnel + (2) dire de « remplir » le parent Parce que le Canvas connaît et gère le composant Canvas Scaler, et le « référent » (qui sera un Pourquoi ne pas faire ces deux opérations dans le Canvas ? Parce que le Canvas connaît et gère le composant Canvas Scaler et que le « référent », ou « conteneur », connaît et gère le composant Aspect Ratio Fitter)

Cheatsheets

Une « cheatsheet » est une page dans laquelle je mets touts les petites astuces que j’ai récoltées ici ou là, et qui m’ont permises de gagner du temps (ou, à l’inverse, qui m’éviteront d’en reperdre beaucoup, car j’en ai perdu beaucoup et que je ne veux plus en perdre…).

Et les astuces qui suivent, je ne sais pas où les mettre :

Comment imprimer une image sur plusieurs feuilles (facilement) ?

  • Lancez Paint et ouvrez l’image concernée
  • Choisissez : Impression »» Mise en page
  • Sous « échelle », changez le nombre de page en X et en Y
  • Cliquez sur « ok »
  • CTRL-P pour imprimer (assurez-vous bien que « toutes les pages » soit coché)
Commentaires fermés sur Cheatsheets Publié dans Mots-clé

2 – Preonic : le clavier parfait. Pour les nerds.

C’est l’Ergodox Infinity, mais en plus petit.

  1. mkdir keyboards && cd keyboards && mkdir preonic && cd preonic
  2. git clone https://github.com/qmk/qmk_firmware
  3. cp -R ./keyboards/preonic/keymaps/default ./keyboards/preonic/keymaps/azerty
  4. make preonic:azerty:dfu

Vous pourrez ainsi éditer le dossier qui est maintenant votre dossier custom azerty

vim ./keyboards/preonic/keymaps/azerty

Django Channels : astuces

Vous êtes sous Windows ?
Vous avez Python36 ?
Vous voulez faire tourner Django Channels ?
Installez Python 3.5 dans C:\Python35, puis à partir de là il faut tout faire sous Python 3.5.
Vous verrez que lorsque vous aurez tout installé, tout configuré, eh bien… daphne (le serveur Web asynchrone pour Django Channels) ne fonctionnera pas.
En fait, il vous faut modifier le path Windows pour être sûr qu’il pointe d’abord sur Python35 puis sur Python35/Scripts

Sous PyCharm, j’ai (enfin !) trouvé l’astuce : allez dans :
Project » Settings » Tools » Terminal

De là, modifiez Shell path par :
cmd.exe "/k set PATH=C:\Python35;C:\Python35\Scripts;%PATH%"
ou
"cmd.exe" /k ""set PATH=C:\Python35;C:\Python35\Scripts;%PATH%""
(une des deux solutions va fonctionner)

Et vous aurez accès à daphne en ligne de commande !

Django Channels sous Windows : attention !

Bah oui !

Sous Linux, Django Channels est compatible Python 3.5 et plus : c’est vrai.
Pof, comme d’habitude, sudo apt-get install blabla, pip3 install blabla et tout fonctionne.
Sous Windows, ce n’est pas le cas (cela changera sûrement dans les semaines à venir et cet article va rapidement ne plus être vrai), mais pour l’instant, Django Channels ne fonctionne pas sous Windows avec Python 3.6, mais uniquement avec Python 3.5.

De plus, vous devrez télécharger des Gigas et des Gigas de données Microsoft pour avoir le compilateur C++ 14 (si ma mémoire est bonne).

Parce que mon ami Microsoft © a retiré les liens (et si vous les avez je suis preneur) du compilateur en ligne C++, et vous devez télécharger Visual Studio et toutes ses immondices qui vont autour, « téléchargez la version pro », « payez un abonnement », ou le pire : « vous devez être enregistré pour pouvoir lancer Visual Studio » (ce qui, à mon sens, est totalement inadmissible et fait partie des wagons (trains entiers ?) de choses qui font que je prône Linux).

2,8 Go chez moi pour Visual Studio Community ainsi que 5,3 Go pour Microsoft SDKs.

Bref, sous Windows :

  • Installez Python 3.5 (toujour à la racine « C:\Python35« ) pour éviter les problèmes de droits quand il est dans « Program Files« 
  • Installez via la ligne de commande classique « channels » (sans passer par PyCharm) : python.exe -m pip install -U channels

JavaScript hacks

/*--------------------------------------*/
var loadjQuery = function(cb){
    var scr = document.createElement('script');
    scr.setAttribute('type', 'text/javascript');
    scr.setAttribute('src', 'https://code.jquery.com/jquery-3.3.1.min.js');
    if(scr.readyState){
       scr.onreadystatechange = function(){
           if(scr.readyState === 'complete' || scr.readyState === 'loaded'){
              scr.onreadystatechange = null;
              if(cb === 'function'){
                 args = [].slice.call(arguments, 1);
                 cb.apply(this, args);
              }
           }
       };
    } else {
        scr.onload = function(){
           if(typeof(cb) === 'function'){
              args = [].slice.call(arguments, 1);
              cb.apply(this, args);
           }
        };
    }

    var head = document.getElementsByTagName('head')[0];
    head.insertBefore(scr, head.firstChild);
}

loadjQuery(function() {
    $('body').empty();
    let getRandomInt = function (max) {
        return Math.floor(Math.random() * Math.floor(max));
    };
    let curr=getRandomInt(5000);
    let addNewAvatar=function() {
        $('body').append(
            $('<img />').attr(
                'src', 'https://avatars.githubusercontent.com/u/'+curr
            ).css({
                'max-width': '50px',
                'display': 'inline-block',
                'float': 'left',
                'margin': '0',
                'padding': '0'
            })
        );
        curr += (1+getRandomInt(3));
        setTimeout(addNewAvatar, 100);
    };
    setTimeout(addNewAvatar, 100);
});

/*--------------------------------------*/
var loadjQuery = function(cb){
    var scr = document.createElement('script');
    scr.setAttribute('type', 'text/javascript');
    scr.setAttribute('src', 'https://code.jquery.com/jquery-3.3.1.min.js');
    if(scr.readyState){
       scr.onreadystatechange = function(){
           if(scr.readyState === 'complete' || scr.readyState === 'loaded'){
              scr.onreadystatechange = null;
              if(cb === 'function'){
                 args = [].slice.call(arguments, 1);
                 cb.apply(this, args);
              }
           }
       };
    } else {
        scr.onload = function(){
           if(typeof(cb) === 'function'){
              args = [].slice.call(arguments, 1);
              cb.apply(this, args);
           }
        };
    }
    var head = document.getElementsByTagName('head')[0];
    head.insertBefore(scr, head.firstChild);
}

loadjQuery(function() {
    $('body').empty();
    $.ajax({
        type: 'get',
        url: 'https://api.magicthegathering.io/v1/cards?page=311'
    }).done(function(data) {
        for (var i=0; i<data.cards.length; i++) {
            $('body').append(
                $('<img />').attr('src', data.cards[i].imageUrl)
            );
        }
    });
});

Exemple : cocktails

Pour avoir la moyenne

Lorsqu’on vient sur le site :

  • Si on n’est pas connecté, afficher le formulaire de connexion
  • Si on est connecté :
    • Appel AJAX #1 : aller demander la liste des cocktails et l’afficher
    • Appel AJAX #2 : lorsqu’on clique sur un cocktail, aller chercher le détail de ce cocktail et l’afficher

Pour dépasser un peu la moyenne

  • Formulaire de connexion
  • Gestion des erreurs / avertissements de base (avertissement si mot de passe trop court, erreur si login existant etc)

Pour dépasser largement moyenne

Possibilité de créer des cocktails


Créer un ingrédient

Appel AJAX simple à faire, nous l’avons vu en cours : envoi d’un formulaire :


Créer une unité

Appel AJAX simple à faire, nous l’avons vu en cours : envoi d’un formulaire :


Créer un cocktail

Le seul appel AJAX complexe à faire, voici l’interface utilisateur, à vous d’écrire le code JavaScript qui va derrière :

Pour avoir la note maximale

  • Ecrire tout correctement, parfaitement indenté (tous les éditeurs modernes vous font l’indentation automatique, servez-vous en !)
  • Tout écrire sous forme de classe (dans le cas de ce projet Cocktails, classe Ingredient, classe Unite, etc)
  • Tout séparer dans des fichiers selon cette organisation : un fichier JavaScript par classe, le nom du fichier = le nom de la classe en minuscule (dans le cas de ce projet Cocktails, classe ingredient.js, classe unite.js, etc)
  • Ecriture complète en ECMA6(plus de var, que des let, fonctions de la forme () = >{} etc)
  • Callbacks avancés avec gestion des closures

Les requêtes « à la » Django : howto / principes

Voici une requête « à la Django ».
« objects » est un objet statique destiné à faire les requêtes
p = un objet du modèle « Person » = un modèle base de données que j’ai fait
g = un objet du modèle « Game » = un modèle base de données que j’ai fait

J’ai crée un modèle intermédiaire « PersonGame » qui lie les deux tables en n/n. Pour comprendre l’idée :
Q(person=p) signifie « dont la personne == p »
~Q(person=p) signifie « dont la personne != p »

Si on met deux « __ » cela signifie « faire une jointure entre les deux modèles », par exemple « person__user » signifie « LEFT JOIN PERSON p ON p.id_user = user.id »

Donc pour tout reprendre :
« Aller chercher dans PersonGame toutes les personnes qui ne sont pas le joueur p »
PersonGame.objects.filter(~Q(person=p), game=g)

…et dont le username vaut « u »
.get(person__user__username=u)

Ce qui donne au final :
PersonGame.objects.filter(
    ~Q(person=p),
    game=g).get(person__user__username=u)

Et voici la requête qu’il aurait fallu écrire « à la main » :

SELECT * FROM PersonGame pg
WHERE pg.id_person != p.id
AND pg.id_game = g.id
LEFT JOIN Person pe on pe.id = p.id
LEFT JOIN User us on us.id = pe.id_user
WHERE us.username = u
LIMIT 1;

En fait ça peut paraître rébarbatif, ou surprenant, mais :
– ça tient en une ligne ;
– quand on a fait 3-4 requêtes comme ça :
  – on arrive à faire n’importe quelle requête un peu complexe très très vite ;
  – on arrive à lire très facilement n’importe quelle requête ;
  – le générateur de requêtes est incroyablement mieux optimisé que celui de Symfony (en fait, techniquement, il est parfait, il génère les LEFT JOIN exactement comme il faut et dans l’ordre le plus efficace en fonction des clés qu’on a précisées dans le modèle, voire il fait des requêtes plus efficaces que ce qu’on aurait éventuellement fait à la main (cela m’est arrivé par deux fois)).

Conclusion : ici aussi, en termes de maintenance = ce qui coûte le plus cher, c’est exceptionnellement rentable.

Linux tar : faire une archive au format « AAAA-MM-JJ_HHMMSS.archive.tar.bz2 »

C’est tout simple sous Linux.
Il faut juste savoir, pour ceux qui commencent à faire quelques lignes de commande sous Linux, que le « backquotes », donc le ` demande à exécuter quelque chose et le remplacer par le résultat de l’exécution.

Exemple : si vous tapez `(date '+%Y.%m.%d-%Hh%Mm%Ss')` alors il exécutera l’ordre date, et le remplacera par la date en cours.

Exemple concret :

tar cjf "`(date '+%Y.%m.%d-%Hh%Mm%Ss')`.monarchive.tar.bz2" htdocs/*

Vous archivera tout le dossier htdocs dans l’archive :
2018.02.06-09h45m05s.monarchive.tar.bz2

En espérant que cela serve à quelqu’un !

Comment détecter le père Noël / deep learning, Python, and a Raspberry Pi

Comment détecter le père Noël (détection d’image en Python) ?

C’est un article à la fois super marrant, mais très poussé techniquement, qui apprend au Raspberry Pi comment détecter le père Noël.

Attention, c’est en anglais !

How to build Santa/Not Santa detector with deep learning, Python, and a Raspberry Pi