Catégorie : développement – divers

ZSA Moonlander : ma configuration

Philosophie de Ma Configuration

Ma configuration s’articule autour de trois principes fondamentaux :

🎯 Efficacité

Minimiser les mouvements des mains et optimiser l’accès aux touches les plus utilisées

🧠 Logique

Grouper les fonctions similaires et créer des patterns mémorisables

⚡ Productivité

Adapter spécifiquement aux workflows de développement web et système

🔄 Evolution Constante

Cette configuration est le résultat de 18 mois d’itérations. Elle continue d’évoluer selon mes besoins et découvertes.

Choix du Layout : Colemak-DH

Pourquoi Abandonner AZERTY ?

Après analyse de mes patterns de frappe, AZERTY s’est révélé particulièrement inefficace pour la programmation :

  • Surcharge de l’auriculaire gauche
  • Sous-utilisation des index
  • Mouvements latéraux excessifs
  • Accès difficile aux symboles de programmation

Avantages du Colemak-DH

  • Répartition équilibrée entre les mains (52% droite, 48% gauche)
  • Optimisation pour les bigrammes fréquents en anglais
  • Placement intelligent des voyelles
  • Courbe d’apprentissage plus douce que Dvorak

⚠️ Période d’Adaptation

Le passage d’AZERTY à Colemak-DH m’a pris 6 semaines pour retrouver ma vitesse initiale. Les 2 premières semaines sont frustrantes, mais ça vaut le coup !

Architecture des Couches

Couche 0 : BASE (Colemak-DH)

Layout principal avec les lettres, chiffres de base et modificateurs essentiels

Couche 1 : NAVIGATION

Flèches directionnelles, page up/down, home/end, sélection de texte

Couche 2 : SYMBOLES

Tous les symboles de programmation, parenthèses, crochets, opérateurs

Couche 3 : NUMÉRIQUE

Pavé numérique optimisé, opérations mathématiques, touches fonction

Couche 4 : MÉDIA

Contrôles audio, luminosité, RGB, macros spéciales

Couche BASE – Le Fondement

Optimisations Clés

Ma couche de base intègre plusieurs optimisations par rapport au Colemak-DH standard :

Position des Pouces (les plus importantes) :
Gauche : Space (tap) / Shift (hold)
Droite : Enter (tap) / Navigation Layer (hold)

Modificateurs Principaux :
Ctrl : Caps Lock position (plus accessible)
Alt : Pouce gauche secondaire
Cmd/Win : Près des pouces

Mod-Tap Keys

J’utilise extensivement les touches Mod-Tap pour réduire les contorsions :

  • Espace/Shift : Espace en tap, Shift maintenu
  • Enter/Nav : Enter en tap, couche navigation maintenue
  • Escape/Ctrl : Escape en tap, Ctrl maintenu
  • Tab/Alt : Tab en tap, Alt maintenu

🎯 Timing Optimal

J’ai configuré le TAPPING_TERM à 200ms après plusieurs essais. Plus court créait des activations accidentelles, plus long ralentissait la frappe.

Couche NAVIGATION – Fluidité

Placement Logique

La couche navigation replique un pavé directionnel sous la main droite :

Main Droite Navigation :

← ↓ →

Position exacte :
I = ↑ U = Home O = Page Up
J = ← K = ↓ L = → ; = End ‘ = Page Down

Avec Shift ajouté = sélection de texte
Avec Ctrl ajouté = navigation par mots

Actions Complémentaires

  • Main gauche : Ctrl, Shift, Alt pour les combinaisons
  • Delete/Backspace : Accessibles sans quitter la couche
  • Mouse keys : Contrôle basique de la souris au clavier

💡 Découverte Importante

Placer les flèches sous IJKL (comme Vim) plutôt que WASD a été un game-changer. Plus naturel pour les développeurs et cohérent avec de nombreux outils.

Couche SYMBOLES – Développement

Optimisation pour le Code

Cette couche est entièrement pensée pour la programmation JavaScript/Python :

Groupes Logiques

  • Parenthèses () sur les index
  • Crochets [] sur les majeurs
  • Accolades {} sur les annulaires
  • Chevrons <> sur les auriculaires

Symboles Fréquents

  • = et == facilement accessibles
  • !== et !== en combos
  • // et /* */ pour commentaires
  • => pour les fonctions flèches

Macros Intégrées

Mes macros les plus utilisées :
→ console.log() [Shift + C + L]
→ ${} (template literal) [Alt + $]
→ // TODO: [Leader + T]
→ debugger; [Leader + D + B]

Couche NUMÉRIQUE – Calculs

Pavé Numérique Optimisé

J’ai recréé un pavé numérique sous la main droite avec quelques améliorations :

Layout du pavé numérique :
7 8 9 /
4 5 6 *
1 2 3 –
0 . = +

Bonus :
– Calculs directement intégrés
– Conversion rapide (hex, bin)
– Fonctions mathématiques courantes

Touches Fonction

F1-F12 accessibles via cette couche, particulièrement utile pour :

  • Debugging (F5, F10, F11)
  • Outils de développement (F12)
  • Shortcuts IDE spécifiques

Combos et Séquences

Combos pour Actions Rapides

Les combos permettent d’activer des actions en pressant plusieurs touches simultanément :

Mes combos essentiels :
QW = Escape (alternative rapide)
IO = Delete
DF = Tab
JK = Enter
SD = Ctrl+C (copier)
FG = Ctrl+V (coller)

Leader Key Sequences

Inspiré de Vim, le leader key ouvre des séquences de commandes :

Leader + G + C

Git commit avec message template

Leader + G + P

Git push vers origin

Leader + R + S

Reset du clavier en mode bootloader

Leader + T + M

Toggle entre différents modes RGB

Personnalisation Visuelle

RGB par Couche

Chaque couche a sa couleur distinctive pour un feedback visuel immédiat :

🔵 BASE – Bleu

Couleur calme pour la frappe normale

🟢 NAVIGATION – Vert

Couleur de mouvement pour les déplacements

🟠 SYMBOLES – Orange

Couleur d’attention pour la programmation

🟣 NUMÉRIQUE – Violet

Couleur distinctive pour les calculs

Indicateurs Visuels

  • Caps Lock : Clignotement rouge
  • Mode Bootloader : Animation rainbow
  • Macro Recording : Pulsation jaune
  • Battery Low : Clignotement rouge lent

Optimisations Spécifiques

Auto-Shift

Maintenir une lettre plus longtemps produit sa version majuscule automatiquement. Élimine le besoin de Shift pour les majuscules isolées.

One Shot Mods

Les modificateurs restent actifs pour la prochaine touche même après relâchement. Parfait pour les combinaisons rapides sans contorsions.

Permissive Hold

Configuration fine du timing des Mod-Tap pour éviter les activations accidentelles pendant la frappe rapide.

// Configuration dans config.h
#define TAPPING_TERM 200
#define PERMISSIVE_HOLD
#define TAPPING_FORCE_HOLD
#define AUTO_SHIFT_TIMEOUT 150

Workflow Quotidien

Session de Développement Type

Démarrage

Leader+T+M pour activer le mode RGB productivité, ouverture IDE avec raccourcis custom

Coding

Frappe fluide en Colemak-DH, accès instantané aux symboles via couche SYM, navigation rapide avec couche NAV

Debugging

Macros console.log(), breakpoints, touches F pour step-by-step

Git Workflow

Leader sequences pour add/commit/push, macros pour messages de commit fréquents

Gains Mesurables

  • Vitesse : 85 WPM en Colemak vs 70 WPM en AZERTY
  • Fatigue : Réduction de 60% des douleurs de poignet
  • Productivité : 30% moins de mouvements de mains
  • Confort : Sessions de 8h+ sans inconfort

Évolution Future

Améliorations Prévues

  • Intégration de macros IA pour la génération de code
  • Profiles contextuels selon l’application active
  • Optimisation des combos basée sur l’analyse d’usage
  • Synchronisation avec d’autres claviers split

🔬 Analyse Continue

J’utilise des outils comme WhatPulse pour analyser mes patterns de frappe et identifier les optimisations possibles. La configuration parfaite n’existe pas, elle évolue avec nos besoins !

Conseils pour Débuter

Approche Progressive

Semaine 1-2

Commencer avec une config simple, se concentrer sur le layout de base

Semaine 3-4

Ajouter la couche navigation, s’habituer aux Mod-Tap

Mois 2

Intégrer la couche symboles, premiers combos

Mois 3+

Fonctionnalités avancées, macros, optimisations fines

🎯 L’Erreur à Éviter

Ne pas tout changer d’un coup ! J’ai fait cette erreur au début et j’ai failli abandonner. Changez progressivement, une couche à la fois, et laissez-vous le temps de vous adapter.

Conclusion

Cette configuration représente 18 mois d’itérations et d’optimisations. Elle transforme complètement l’expérience de programmation et a considérablement amélioré mon confort de travail.

Le Moonlander n’est pas qu’un clavier, c’est un outil de productivité personnalisable à l’infini. Investir le temps nécessaire pour créer sa configuration parfaite est l’un des meilleurs ROI qu’un développeur puisse faire.

ZSA Moonlander : QMK : compilation

Pourquoi Compiler Soi-Même ?

Bien que ZSA propose Oryx (leur configurateur en ligne), compiler le firmware QMK directement offre plusieurs avantages :

  • Accès à toutes les fonctionnalités avancées de QMK
  • Possibilité d’ajouter du code personnalisé
  • Contrôle total sur la configuration
  • Apprentissage approfondi du fonctionnement
  • Pas de limitation des couches ou fonctionnalités

🎯 Mon Objectif

Je voulais implémenter des fonctionnalités spécifiques comme des macros complexes pour mon workflow de développement et des combos personnalisés que Oryx ne permettait pas à l’époque.

Prérequis et Installation

Environnement de Développement

IMPORTANT : J’utilise exclusivement une machine Ubuntu 22.04. Oubliez Windows et sa merde – même avec WSL2 c’est galère. Prenez une vraie distribution basée sur Debian (Ubuntu, Debian, Mint, Pop!_OS) ou au pire macOS.

🚫 Windows = Problèmes Garantis

J’ai testé sur Windows 11 avec WSL2 : drivers USB foireux, permissions de merde, bugs aléatoires. Économisez-vous des heures de galère et utilisez un vrai OS Unix-like.

# Installation des dépendances sur Ubuntu
sudo apt update
sudo apt install git python3-pip
python3 -m pip install –user qmk

Clonage du dépôt (repository) QMK

# Cloner le repository QMK officiel
git clone https://github.com/qmk/qmk_firmware.git
cd qmk_firmware

# Configuration initiale
qmk setup

🔗 Liens officiels

Repository QMK : https://github.com/qmk/qmk_firmware
Documentation : https://docs.qmk.fm/

⚠️ Attention Version

Assurez-vous d’utiliser une version stable de QMK. J’ai eu des problèmes avec certaines versions de développement qui cassaient la compilation pour le Moonlander. Vérifiez les releases sur GitHub.

Structure du projet Moonlander

Le Moonlander se trouve dans le dossier keyboards/moonlander/ avec cette structure :

moonlander/
├── config.h # Configuration matérielle
├── moonlander.c # Code spécifique au clavier
├── moonlander.h # Définitions des touches
├── rules.mk # Règles de compilation
└── keymaps/ # Configurations de touches
├── default/ # Configuration par défaut
├── via/ # Configuration pour VIA
└── mon_keymap/ # Ma configuration personnalisée

Création de Ma Configuration

# Créer mon dossier de configuration
mkdir keyboards/moonlander/keymaps/mon_keymap
cd keyboards/moonlander/keymaps/mon_keymap

# Copier la base depuis default
cp ../default/* .

Mon Processus de Développement

Étape 1 : Analyse des Besoins

J’ai d’abord analysé mes patterns de frappe pendant une semaine avec un keylogger pour identifier les touches et combinaisons les plus utilisées.

Étape 2 : Design des Layers

Création du schéma de mes couches sur papier avant de commencer le code, en optimisant pour mes besoins de développeur Python/JavaScript.

Étape 3 : Implémentation Progressive

Développement couche par couche, en testant chaque modification avant de passer à la suivante.

Étape 4 : Optimisation

Ajustements fins basés sur l’usage réel et l’ajout de fonctionnalités avancées.

Configuration de Base

Le fichier keymap.c

Voici la structure de base de mon fichier de configuration :

#include QMK_KEYBOARD_H
#include « version.h »

// Définition des couches
enum layers {
BASE, // Couche de base (Colemak-DH)
NAV, // Navigation et flèches
SYM, // Symboles et ponctuation
NUM, // Pavé numérique et fonctions
ADJ // Ajustements (RGB, reset, etc.)
};

// Définition des keycodes personnalisés
enum custom_keycodes {
RGB_SLD = ML_SAFE_RANGE,
MY_MACRO_1,
MY_MACRO_2
};

Gestion des Couches

J’utilise des Mod-Tap keys et des Layer-Tap keys pour un accès fluide :

// Mod-Tap : Shift quand maintenu, Espace quand tapé
MT(MOD_LSFT, KC_SPC)

// Layer-Tap : Couche NAV quand maintenu, Enter quand tapé
LT(NAV, KC_ENT)

// Hold-Tap avec timing personnalisé
LCTL_T(KC_ESC) // Ctrl maintenu, Escape tapé

Fonctionnalités Avancées Implémentées

Macros Personnalisées

J’ai créé des macros pour mes workflows de développement les plus fréquents :

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
switch (keycode) {
case MY_MACRO_1:
if (record->event.pressed) {
SEND_STRING(« console.log() » SS_TAP(X_LEFT));
}
break;
case MY_MACRO_2:
if (record->event.pressed) {
SEND_STRING(« git add . && git commit -m \ »\ » » SS_TAP(X_LEFT));
}
break;
}
return true;
}

Combos

Implémentation de combos pour des actions rapides :

// Définition des combos
const uint16_t PROGMEM combo_esc[] = {KC_Q, KC_W, COMBO_END};
const uint16_t PROGMEM combo_del[] = {KC_O, KC_P, COMBO_END};

combo_t key_combos[COMBO_COUNT] = {
COMBO(combo_esc, KC_ESC),
COMBO(combo_del, KC_DEL),
};

Leader Key Sequences

Séquences de touches inspirées de Vim pour des actions complexes :

LEADER_EXTERNS();

void matrix_scan_user(void) {
LEADER_DICTIONARY() {
leading = false;
leader_end();

SEQ_ONE_KEY(KC_R) {
reset_keyboard();
}

SEQ_TWO_KEYS(KC_G, KC_C) {
SEND_STRING(« git commit -m \ »\ » » SS_TAP(X_LEFT));
}
}
}

Compilation et Flash

Commandes de Compilation

# Compilation seule
qmk compile -kb moonlander -km mon_keymap

# Compilation et flash en une fois
qmk flash -kb moonlander -km mon_keymap

# Compilation avec verbose pour debug
qmk compile -kb moonlander -km mon_keymap –verbose

Processus de Flash

Pour flasher le firmware sur le Moonlander :

1. Mise en Mode Bootloader

Appuyer sur le petit bouton reset à l’arrière du clavier ou utiliser la séquence de touches configurée.

2. Vérification du Device

Le clavier apparaît comme un device USB en mode DFU. Vérifier avec lsusb sur Linux.

3. Flash du Firmware

La commande qmk flash détecte automatiquement le clavier et lance le processus.

4. Redémarrage

Le clavier redémarre automatiquement avec la nouvelle configuration.

⚠️ Problèmes Courants

Si le flash échoue, vérifiez les permissions USB avec sudo usermod -a -G dialout $USER et reconnectez-vous. Parfois il faut utiliser sudo pour la première fois. Guide complet : QMK Linux udev rules

Debugging et Optimisation

Console de Debug

QMK offre une console de debug très utile pour le développement :

# Activer le debug dans config.h
#define CONSOLE_ENABLE

# Dans le code, utiliser
dprintf(« Debug: Layer = %d\n », get_highest_layer(layer_state));

Monitoring en Temps Réel

# Voir les messages de debug en temps réel
qmk console

# Ou avec hid_listen
sudo hid_listen

Optimisation de la Mémoire

Le Moonlander a des limitations de mémoire. J’ai dû optimiser :

  • Désactiver les fonctionnalités non utilisées dans rules.mk
  • Réduire le nombre de RGB modes
  • Optimiser les macros et combos
  • Utiliser PROGMEM pour les données constantes
# Dans rules.mk – désactiver ce qui n’est pas nécessaire
MOUSEKEY_ENABLE = no # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
AUDIO_ENABLE = no # Audio output
RGBLIGHT_ENABLE = yes # Enable WS2812 RGB underlight
SLEEP_LED_ENABLE = no # Breathing sleep LED during USB suspend

Configuration Avancée

Fichier config.h Personnalisé

Mon fichier config.h avec les optimisations importantes :

#pragma once

// Timing optimisé pour les Mod-Tap
#define TAPPING_TERM 200
#define PERMISSIVE_HOLD
#define TAPPING_FORCE_HOLD

// Auto-shift pour les majuscules
#define AUTO_SHIFT_TIMEOUT 150
#define NO_AUTO_SHIFT_ALPHA

// Combos configuration
#define COMBO_COUNT 10
#define COMBO_TERM 40

// Leader key
#define LEADER_TIMEOUT 300
#define LEADER_PER_KEY_TIMING

// RGB optimization
#define RGBLIGHT_LIMIT_VAL 120
#define RGBLIGHT_HUE_STEP 10
#define RGBLIGHT_SAT_STEP 17
#define RGBLIGHT_VAL_STEP 17

Rules.mk Optimisé

# Mon rules.mk final
BOOTMAGIC_ENABLE = yes # Enable Bootmagic Lite
MOUSEKEY_ENABLE = yes # Mouse keys
EXTRAKEY_ENABLE = yes # Audio control and System control
CONSOLE_ENABLE = no # Console for debug
COMMAND_ENABLE = no # Commands for debug and configuration
NKRO_ENABLE = yes # Enable N-Key Rollover
BACKLIGHT_ENABLE = no # Enable keyboard backlight functionality
RGBLIGHT_ENABLE = yes # Enable keyboard RGB underglow
AUDIO_ENABLE = yes # Audio output
AUTO_SHIFT_ENABLE = yes # Auto Shift
COMBO_ENABLE = yes # Key combos
LEADER_ENABLE = yes # Leader key sequences

Gestion des Versions

Git Workflow

J’ai créé un repository séparé pour ma configuration :

# Initialiser le repository pour ma config
cd keyboards/moonlander/keymaps/mon_keymap
git init
git remote add origin https://github.com/mon-user/moonlander-config

# Workflow quotidien
git add .
git commit -m « Ajout macro pour debugging JavaScript »
git push origin main

Branches pour Expérimentations

# Créer une branche pour tester de nouvelles fonctionnalités
git checkout -b feature/nouveau-layout
# … modifications …
git commit -m « Test nouveau layout Colemak-DH »

# Revenir à la config stable si problème
git checkout main

💡 Conseil Pro

Gardez toujours une version stable fonctionnelle ! Rien de pire que de casser sa config un vendredi soir et de ne plus pouvoir travailler le weekend.

Automatisation du Build

Script de Compilation

J’ai créé un script pour automatiser le processus :

#!/bin/bash
# build.sh

set -e # Arrêter en cas d’erreur

echo « 🔧 Compilation du firmware Moonlander… »

# Nettoyer les builds précédents
qmk clean

# Compiler
qmk compile -kb moonlander -km mon_keymap

# Copier le fichier .bin vers un dossier de backup
cp moonlander_mon_keymap.bin ~/moonlander-firmwares/$(date +%Y%m%d_%H%M%S)_moonlander.bin

echo « ✅ Compilation terminée ! »
echo « 📁 Firmware sauvegardé dans ~/moonlander-firmwares/ »

Script de Flash Automatisé

#!/bin/bash
# flash.sh

echo « 🚀 Flash du firmware Moonlander… »

# Compiler et flasher
qmk flash -kb moonlander -km mon_keymap

# Attendre que le clavier revienne
echo « ⏳ Attente du redémarrage du clavier… »
sleep 3

# Test rapide
echo « ✅ Flash terminé ! Testez votre clavier. »

GitHub Actions

Pour automatiser les builds sur GitHub :

# .github/workflows/build.yml
name: Build Moonlander Firmware

on: [push, pull_request]

jobs:
build:
runs-on: ubuntu-latest
steps:
– uses: actions/checkout@v3
– name: Install QMK
run: |
python3 -m pip install qmk
qmk setup -y
– name: Build firmware
run: qmk compile -kb moonlander -km mon_keymap
– name: Upload firmware
uses: actions/upload-artifact@v3
with:
name: moonlander-firmware
path: « *.bin »

🔗 Exemple Complet

Voir un exemple complet de CI/CD pour QMK : QMK Official Workflow

Tests et Validation

Procédure de Test

Avant chaque flash en production, je suis une procédure de test rigoureuse :

Test de Compilation

Vérifier que le firmware compile sans erreurs ni warnings

Test des Couches

Vérifier que chaque couche s’active correctement

Test des Macros

Valider toutes les macros et séquences personnalisées

Test d’Usage Réel

Session de programmation de 30 minutes minimum

Outils de Test

  • QMK Configurator : Validation visuelle du layout
  • Key Tester : Vérification que toutes les touches fonctionnent
  • Via/Vial : Test rapide des modifications en live
  • Custom script : Validation automatisée des macros

Troubleshooting

Problèmes Courants et Solutions

❌ Compilation Failed

  • Vérifier la syntaxe du keymap.c
  • Contrôler les #include
  • Valider les enum et defines

❌ Flash Failed

  • Vérifier les permissions USB
  • Réessayer le mode bootloader
  • Utiliser sudo si nécessaire

❌ Touches qui ne Répondent Pas

  • Vérifier la matrix dans moonlander.h
  • Contrôler les keycodes
  • Tester avec un layout simple

❌ Macros Dysfonctionnelles

  • Vérifier la syntaxe SEND_STRING
  • Contrôler les delays
  • Tester isolément

Recovery Procedures

# En cas de firmware cassé, procédure de récupération :

1. Forcer le mode bootloader (bouton physique)
2. Flash un firmware de base connu fonctionnel
3. Identifier le problème dans la config personnalisée
4. Corriger et re-tester progressivement

Optimisations Avancées

Réduction de la Latence

Optimisations pour réduire la latence du clavier :

# Dans config.h
#define USB_POLLING_INTERVAL_MS 1 // Polling à 1000Hz
#define QMK_KEYS_PER_SCAN 4 // Limite le nombre de touches par scan
#define DEBOUNCE 5 // Réduire le debounce si pas de problème

Économie d’Énergie

Pour prolonger l’autonomie en mode sans fil :

# Configuration d’économie d’énergie
#define RGBLIGHT_SLEEP // Éteindre RGB en veille
#define AUTO_SHIFT_TIMEOUT 150 // Réduire les timeouts
#define COMBO_TERM 40 // Timeout combos plus court

Personnalisation du Bootloader

# Message de boot personnalisé
void keyboard_post_init_user(void) {
rgblight_enable_noeeprom();
rgblight_sethsv_noeeprom(HSV_PURPLE);
wait_ms(500);
rgblight_sethsv_noeeprom(HSV_BLUE);
}

Performance et Monitoring

Analyse des Performances

Outils pour analyser les performances de votre firmware :

  • Memory usage : Vérifier l’utilisation RAM/Flash
  • Scan rate : Fréquence de scan de la matrice
  • Latency testing : Mesurer la latence totale
  • Power consumption : Monitoring de la consommation
# Commandes utiles pour le monitoring
qmk console –verbose # Debug détaillé
qmk doctor # Diagnostic de l’environnement
qmk list-keyboards # Liste des claviers supportés

Profiling du Code

// Profiling simple dans le code
static uint16_t timer;

void matrix_scan_user(void) {
timer = timer_read();
// … code à profiler …
if (timer_elapsed(timer) > 1) {
dprintf(« Slow operation: %dms\n », timer_elapsed(timer));
}
}

Documentation et Maintenance

Documentation de ma Config

J’ai créé une documentation complète pour ma configuration :

moonlander-config/
├── README.md # Documentation principale
├── CHANGELOG.md # Historique des modifications
├── keymap.c # Code principal
├── config.h # Configuration
├── rules.mk # Règles de compilation
├── diagrams/ # Schémas des layouts
└── backup/ # Versions précédentes

Routine de Maintenance

Hebdomadaire

Backup de la configuration actuelle, review des modifications

Mensuelle

Mise à jour QMK, optimisations basées sur l’usage

Trimestrielle

Review complète de la config, nouvelles fonctionnalités

Annuelle

Refonte majeure basée sur l’évolution des besoins

Leçons Apprises et Conseils

Erreurs à Éviter

  • Ne pas tester suffisamment avant de flasher une grosse modification
  • Oublier de sauvegarder la config qui marche
  • Vouloir tout changer d’un coup au lieu d’y aller progressivement
  • Négliger la documentation des modifications
  • Ignorer les warnings de compilation

Bonnes Pratiques Développées

  • Toujours commenter le code personnalisé
  • Tester chaque fonctionnalité isolément
  • Garder un changelog des modifications
  • Avoir plusieurs versions de backup
  • Documenter les keymaps avec des diagrammes
  • Utiliser des branches Git pour les expérimentations

🎯 Conseil Final

La compilation QMK peut sembler intimidante au début, mais c’est un processus très logique. Commencez simple, ajoutez progressivement les fonctionnalités, et n’hésitez pas à consulter la documentation QMK qui est excellente.

Ressources et Communauté

Documentation Essentielle

Outils Utiles

QMK Toolbox

Interface graphique pour flasher les firmwares
Télécharger QMK Toolbox

Via/Vial

Configuration en temps réel sans recompilation
Via | Vial

KLE

Keyboard Layout Editor pour visualiser les layouts
keyboard-layout-editor.com

QMK Configurator

Interface web pour générer des configurations
config.qmk.fm

Conclusion

Compiler son propre firmware QMK est un voyage passionnant qui ouvre des possibilités infinies de personnalisation. Ce processus m’a non seulement permis de créer un clavier parfaitement adapté à mes besoins, mais aussi de comprendre en profondeur le fonctionnement des claviers programmables.

Bénéfices Obtenus

  • Contrôle total : Chaque aspect du clavier correspond exactement à mes besoins
  • Évolutivité : Possibilité d’ajouter de nouvelles fonctionnalités à tout moment
  • Apprentissage : Compréhension approfondie de QMK et des claviers mécaniques
  • Communauté : Accès à une communauté passionnée et entraide

ROI du Temps Investi

Les 40+ heures investies dans l’apprentissage et la configuration se sont révélées être l’un des meilleurs investissements de productivité de ma carrière. Chaque jour, je gagne du temps et du confort grâce aux optimisations mises en place.

🚀 Pour Aller Plus Loin

La compilation n’est que le début. Dans l’article suivant, je détaille ma configuration finale avec tous les choix de design et les optimisations qui rendent mon workflow de développement si efficace.

Mon Setup Final

Après plusieurs mois d’itérations, j’ai obtenu un firmware parfaitement adapté à mes besoins de développeur. Le processus de compilation m’a permis de comprendre en profondeur le fonctionnement de QMK et d’implémenter des fonctionnalités impossibles avec les configurateurs graphiques.

Statistiques Finales

📊 Firmware

28KB utilisés / 32KB disponibles
85% de la mémoire flash utilisée

⌨️ Fonctionnalités

5 couches principales
12 macros personnalisées
8 combos optimisés

🎯 Performance

Latence < 1ms
1000Hz polling rate
N-Key Rollover complet

🔧 Maintenance

Update mensuelle
Backup automatique
CI/CD sur GitHub

Dans l’article suivant, je détaille ma configuration finale avec les choix de layout et les raisons derrière chaque décision.

Les défauts des claviers

Le problème fondamental

Nos claviers actuels ont été conçus dans les années 1870 pour les machines à écrire mécaniques. Plus de 150 ans plus tard, nous utilisons toujours le même design inadapté pour un usage intensif sur ordinateur.

🚨 Réalité Alarmante

Les troubles musculo-squelettiques (TMS) représentent 87% des maladies professionnelles en France, dont une grande partie liée à l’utilisation prolongée de claviers inadaptés . Source : 2024, santepubliquefrance

Problèmes ergonomiques majeurs

1. Position non-naturelle des mains

Les claviers traditionnels forcent nos mains dans une position complètement artificielle :

  • Rotation interne des avant-bras (pronation excessive)
  • Déviation cubitale des poignets
  • Extension dorsale des poignets
  • Rapprochement artificiel des mains

Position Naturelle

Mains écartées à la largeur des épaules, avant-bras parallèles, poignets dans l’axe des avant-bras

Position Forcée par les Claviers

Mains rapprochées, avant-bras en rotation, poignets en tension permanente

2. Tensions musculaires constantes

Cette position non-naturelle génère des tensions permanentes dans :

  • Les muscles de l’avant-bras (fléchisseurs et extenseurs)
  • Les tendons des poignets
  • Les muscles du cou et des épaules
  • Les muscles du dos (compensation posturale)

Pathologies Courantes

Syndrome du canal carpien

Compression du nerf médian dans le poignet, causée par la position en extension prolongée. Symptômes : fourmillements, engourdissements, douleurs irradiant dans la main et le bras.

Tendinites

Inflammation des tendons due aux mouvements répétitifs et aux tensions constantes. Particulièrement fréquent au niveau des extenseurs du poignet.

Syndrome de la souris

Douleurs de l’épaule, du cou et du bras dues à la position asymétrique pour atteindre la souris depuis un clavier traditionnel.

⚠️ Mon Expérience Personnelle

Après 30 ans de développement intensif, j’ai développé des douleurs chroniques aux poignets, des tensions cervicales permanentes et une fatigue excessive en fin de journée. C’est ce qui m’a poussé à chercher des alternatives.

Problèmes de productivité

Disposition AZERTY Inefficace

La disposition AZERTY (ou QWERTY) a été conçue pour ralentir la frappe sur les machines à écrire mécaniques afin d’éviter les bourrages. Cette logique n’a plus aucun sens aujourd’hui.

  • Répartition inégale de la charge entre les doigts
  • Mouvements excessifs des mains
  • Utilisation sub-optimale des doigts les plus forts
  • Enchaînements de touches difficiles

Touches modifiatrices mal placées

L’auriculaire gauche est surchargé avec Ctrl, Shift, Alt, alors que les pouces (les doigts les plus forts) sont sous-utilisés.

Accès difficile aux symboles

Pour les développeurs, l’accès aux symboles de programmation nécessite des contorsions constantes avec AltGr et autres combinaisons complexes.

L’illusion du clavier « ergonomique »

Beaucoup de claviers se prétendent « ergonomiques » avec des modifications superficielles :

Faux Ergonomiques

  • Repose-poignets en gel
  • Légère incurvation
  • Design « vague »

Bien que les repose-poignets en gel constituent la première solution à mettre en œuvre et demeurent très efficaces pour soulager les tensions immédiates, ces améliorations restent des palliatifs qui ne résolvent pas le problème fondamental de la position des mains. Un clavier ergonomique séparé reste indispensable pour une solution complète.

Vraie Ergonomie

  • Séparation physique (split)
  • Inclinaison ajustable
  • Répartition optimisée des touches

Respect de l’anatomie naturelle des mains et des bras.

Le coût caché

Impact sur la carrière

Les TMS peuvent sérieusement affecter une carrière dans le développement ou tout métier nécessitant une frappe intensive. J’ai vu des collègues contraints de réduire leur temps de travail ou changer de métier.

Coûts médicaux

Consultations, kinésithérapie, parfois chirurgie… Les TMS représentent un coût énorme pour la sécurité sociale et les individus.

Perte de productivité

  • Fatigue plus rapide
  • Pauses plus fréquentes
  • Concentration diminuée par la douleur
  • Vitesse de frappe réduite à long terme

💡 La Solution

C’est exactement pour résoudre ces problèmes que des claviers comme le Moonlander ont été développés. Ils ne sont pas qu’un gadget de geek, mais une vraie solution de santé au travail.

Pourquoi peu de gens font le changement ?

Résistance au changement

Nous sommes habitués à nos mauvaises habitudes. Changer de clavier implique une période d’adaptation douloureuse que beaucoup ne veulent pas affronter.

Méconnaissance des alternatives

La plupart des gens ignorent qu’il existe des alternatives ergonomiques viables aux claviers traditionnels.

Investissement Initial

Un bon clavier ergonomique coûte plus cher qu’un clavier standard, mais c’est un investissement dérisoire comparé aux coûts potentiels des TMS.

🎯 Mon conseil

N’attendez pas d’avoir mal pour agir ! La prévention est beaucoup plus facile que la guérison. Si vous passez plus de 4h par jour à taper, un clavier ergonomique n’est pas un luxe, c’est une nécessité.

La prise de conscience

Réaliser les défauts des claviers traditionnels a été le premier pas vers ma transition au Moonlander. Une fois qu’on comprend les enjeux de santé et de productivité, investir dans un vrai clavier ergonomique devient évident.

Dans l’article suivant, je détaille comment j’ai compilé le firmware QMK pour créer une expérience parfaitement adaptée à mes besoins.

ZSA Moonlander : le clavier ultime

Qu’est-ce que le Moonlander ?

Le Moonlander est un clavier mécanique ergonomique split conçu par ZSA Technology Labs. Il représente l’évolution ultime du clavier traditionnel, offrant une expérience de frappe révolutionnaire grâce à sa conception innovante et sa personnalisation infinie.

🚀 Innovation Clé

Le Moonlander n’est pas juste un clavier – c’est un outil de productivité qui s’adapte parfaitement à votre façon de travailler et évolue avec vos besoins.

Contrairement aux claviers traditionnels qui nous forcent à adapter nos mains à leur forme rigide, le Moonlander s’adapte à l’anatomie naturelle de nos mains et à notre posture de travail.

Split Design révolutionnaire

La caractéristique la plus frappante du Moonlander est sa conception split (divisée). Chaque moitié du clavier peut être positionnée indépendamment, permettant :

  • Un alignement naturel des épaules
  • Une réduction des tensions dans les poignets
  • Une posture plus ergonomique
  • Une adaptation à différentes morphologies

Clavier Traditionnel

Force une position fixe des mains, créant des tensions et des déformations posturales à long terme.

Moonlander Split

Permet un positionnement naturel des mains, respectant l’anatomie et réduisant la fatigue.

Personnalisation totale avec QMK

Le Moonlander utilise le firmware QMK (Quantum Mechanical Keyboard), offrant des possibilités de personnalisation pratiquement infinies :

Layers (couches)

Créez plusieurs couches de touches pour accéder à différentes fonctions. Imaginez avoir plusieurs claviers en un seul !

Couche Base

Votre disposition principale pour la frappe quotidienne (AZERTY dans mon cas mais vous pouvez faire du QWERTY, de l’AZERTY, du Colemak, etc.)

Couche Symboles

Accès rapide aux symboles de programmation et caractères spéciaux

Couche Navigation

Touches directionnelles, page up/down, home/end optimisées

Couches Personnalisées

Macros, raccourcis spécifiques à vos applications favoris

Fonctionnalités avancées

  • Tap Dance : une touche peut avoir plusieurs fonctions selon le nombre de pressions
  • Mod-Tap : une touche agit comme modificateur quand maintenue, comme caractère quand tapée
  • Combos : combinaisons de touches personnalisées
  • Macros : séquences automatisées de touches
  • Leader Key : séquences de commandes comme dans Vim

Caractéristiques techniques

⌨️ Switches

Hot-swap Kailh sockets – changez vos switches sans soudure

🔌 Connectivité

USB-C avec câbles TRRS pour connecter les deux moitiés

💡 RGB

Éclairage RGB par touche entièrement personnalisable

🎯 Précision

Polling rate élevé pour les gamers et professionnels exigeants

Pourquoi choisir le Moonlander ?

Pour les développeurs

Programmation plus efficace avec des couches dédiées aux symboles, navigation optimisée dans le code, et macros pour les snippets fréquents.

Pour la productivité

Réduction drastique des mouvements de mains, accès instantané aux raccourcis, et workflow personnalisé selon vos applications.

Pour la santé

Prévention des troubles musculo-squelettiques, réduction de la fatigue, et amélioration de la posture de travail.

⚠️ Courbe d’apprentissage

Le passage au Moonlander demande un investissement en temps et patience. Comptez 2-4 semaines pour retrouver votre vitesse de frappe habituelle, mais les bénéfices à long terme en valent largement la peine !

Mon expérience personnelle

Après des années sur des claviers traditionnels et l’apparition de douleurs aux poignets, le passage au Moonlander a été une révélation. La personnalisation poussée m’a permis de créer un environnement de travail parfaitement adapté à mes besoins de développeur.

Dans les articles suivants, je détaille mon parcours complet : les problèmes rencontrés avec les claviers classiques, le processus de compilation du firmware, et ma configuration personnalisée avec les raisons derrière chaque choix.

Commentaires fermés sur ZSA Moonlander : le clavier ultime Publié dans Mots-clé

Automatiser le nettoyage des branches Git avec un alias : Guide pratique

Automatiser le nettoyage des branches Git avec un alias : Guide pratique

Travailler avec Git implique souvent de gérer de nombreuses branches, locales et distantes. Au fil du temps, il est courant que des branches distantes soient supprimées, laissant des branches locales obsolètes. Pour simplifier le nettoyage de ces branches, vous pouvez créer un alias Git qui automatise ce processus. Dans cet article, nous vous expliquons comment faire, ainsi que les avantages et les inconvénients de cette méthode.

Créer un alias Git pour nettoyer les branches locales

Voici la commande pour créer un alias Git nommé prune-all qui nettoie automatiquement les branches locales obsolètes :

git config --global alias.prune-all '!git fetch --prune && git branch -vv | grep ": gone]" | sed "s/^[[:space:]]*\([^[:space:]]*\).*/\1/" | xargs -r git branch -d'

Une fois cet alias configuré, vous pouvez simplement exécuter :

git prune-all

Cette commande va :

  1. Mettre à jour les références locales et supprimer les branches distantes supprimées (git fetch --prune).
  2. Identifier les branches locales qui n’ont plus de branche distante associée (git branch -vv | grep ": gone]").
  3. Extraire les noms de ces branches (sed).
  4. Supprimer les branches locales (xargs -r git branch -d).

Pourquoi utiliser cet alias ?

Cet alias offre plusieurs avantages :

  • Gain de temps : Plus besoin d’exécuter manuellement plusieurs commandes pour nettoyer les branches locales.
  • Automatisation : Le processus est entièrement automatisé, ce qui réduit les erreurs humaines.
  • Propreté du dépôt : Vous gardez votre dépôt local propre et synchronisé avec le dépôt distant.

Les précautions à prendre

Bien que cet alias soit très utile, il est important de comprendre ses limites et les risques potentiels :

  • Utilisation de git branch -d : L’alias utilise git branch -d pour supprimer les branches locales. Cela signifie que Git refusera de supprimer une branche si elle contient des commits non fusionnés. C’est une sécurité pour éviter de perdre du travail.
  • Risque de suppression accidentelle : Si vous utilisez git branch -D (avec un D majuscule) à la place de -d, les branches seront supprimées de force, même si elles contiennent des commits non fusionnés. Soyez prudent si vous modifiez l’alias pour utiliser -D.
  • Vérification manuelle : Avant d’exécuter l’alias, il peut être utile de vérifier les branches qui seront supprimées en exécutant :
git fetch --prune && git branch -vv | grep ": gone]"

Quand utiliser cet alias ?

Cet alias est particulièrement utile dans les situations suivantes :

  • Vous travaillez sur un projet avec de nombreuses branches, et vous voulez garder votre dépôt local propre.
  • Vous collaborez avec une équipe et les branches distantes sont fréquemment supprimées après fusion.
  • Vous voulez automatiser une tâche répétitive pour gagner du temps.

Conclusion

Créer un alias Git pour nettoyer les branches locales est une excellente façon d’automatiser une tâche fastidieuse et de garder votre dépôt propre. En utilisant git branch -d, vous ajoutez une couche de sécurité pour éviter de perdre du travail non fusionné. Cependant, soyez conscient des risques si vous décidez d’utiliser git branch -D à la place.

N’hésitez pas à essayer cet alias et à l’adapter à vos besoins. Bonne gestion de branches !

Vous avez des questions ou des suggestions ? Laissez un commentaire ci-dessous !

JavaScript hack update

Mise à jour en pur JavaScript hacks

Voici une petite mise à jour en vanilla JS qui fait la même chose que ce que j’ai mis ici.

function emptyBody() {
    document.body.innerHTML = '';
}
function getRandomInt(max) {
    return Math.floor(Math.random() * Math.floor(max));
}
function addNewAvatar() {
    let curr = getRandomInt(5000);
    return function() {
        const img = document.createElement('img');
        img.src = `https://avatars.githubusercontent.com/u/${curr}`;
        img.style.maxWidth = '50px';
        img.style.display = 'inline-block';
        img.style.float = 'left';
        img.style.margin = '0';
        img.style.padding = '0';
        document.body.appendChild(img);
        curr += (1 + getRandomInt(3));
        setTimeout(addNewAvatar(), 100);
    };
}
emptyBody();
setTimeout(addNewAvatar(), 100);

Panda vs Numpy

Ce qu’il faut retenir

Numpy et Pandas n’ont pas exactement les mêmes objectifs.

Dans la plupart des cas, NumPy peut être légèrement plus rapide que pandas, car NumPy est plus bas niveau et a moins de surcharge. Cependant, pandas offre des structures de données et des fonctionnalités plus avancées, ce qui peut faciliter le travail avec des ensembles de données complexes. Les performances relatives de NumPy et pandas dépendent également des opérations spécifiques effectuées sur les données, de sorte que les différences de performances peuvent varier en fonction des tâches spécifiques. Certaines fonctions n’existent qu’avec pandas, et qui n’ont pas d’équivalents NumPy sont : read_csv, read_excel, groupby, pivot_table, merge, concat, melt, crosstab, cut, qcut, get_dummies et applymap.

Résultats

Résultat : image générée : notez bien que j’ai appelé des fonctions « bas niveau » pour qu’on voie ce que NumPy a dans le ventre et des fonctions qui n’existent que dans pandas, que ré-implémentées en Python pur + NumPy.

Résultats pandas vs NumPy

Code source

Voici le code source que j’ai fait, qui appelle quelques fonctions connues de NumPy et de pandas.

import numpy as np
import pandas as pd
import time
import matplotlib.pyplot as plt

# Générer un grand ensemble de données
data_np = np.random.rand(30_000_000)
data_pd = pd.DataFrame({"values": data_np})

operations = (
    "sum",
    "mean",
    "filter",
    "cum_sum",
    "sort",
    "complex",
    "pivot",
    "group_by",
    "rolling",
)
time_np = []
time_pd = []


# Définir une fonction pour chronométrer et stocker les temps d'exécution
def measure_time(start_time, end_time, time_list):
    time_list.append(end_time - start_time)


# Effectuer les différentes opérations et mesurer les temps d'exécution
for operation in operations:
    # print(f"operation: {operation}")
    print(f"{operation}")
    if operation == "sum":
        start_time_np = time.time()
        result_np = np.sum(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        result_pd = data_pd["values"].sum()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "mean":
        start_time_np = time.time()
        mean_np = np.mean(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        mean_pd = data_pd["values"].mean()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "filter":
        start_time_np = time.time()
        filtered_np = data_np[data_np > 0.5]
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        filtered_pd = data_pd[data_pd["values"] > 0.5]
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "cum_sum":
        start_time_np = time.time()
        cum_sum_np = np.cumsum(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        cum_sum_pd = data_pd["values"].cumsum()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "sort":
        start_time_np = time.time()
        sorted_np = np.sort(data_np)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        start_time_pd = time.time()
        sorted_pd = data_pd["values"].sort_values()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)
    elif operation == "complex":
        # Générer des données structurées
        data_1 = np.random.randint(0, 1_000_000, (2_000, 2))
        data_2 = np.random.randint(0, 1_000_000, (2_000, 2))

        # Créer des DataFrames pandas
        df_1 = pd.DataFrame(data_1, columns=["id", "value_1"])
        df_2 = pd.DataFrame(data_2, columns=["id", "value_2"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype([("id", int), ("value", int)])
        numpy_data_1 = np.array(
            list(map(tuple, data_1)), dtype=d_type
        )
        numpy_data_2 = np.array(
            list(map(tuple, data_2)), dtype=d_type
        )

        # Jointure avec NumPy
        def numpy_join(data1, data2):
            result = []
            for row1 in data1:
                for row2 in data2:
                    if row1["id"] == row2["id"]:
                        result.append(
                            (row1["id"], row1["value"], row2["value"])
                        )
            return np.array(
                result,
                dtype=[
                    ("id", int),
                    ("value_1", int),
                    ("value_2", int),
                ],
            )

        start_time_np = time.time()
        numpy_result = numpy_join(numpy_data_1, numpy_data_2)
        end_time_np = time.time()
        measure_time(
            start_time_np, end_time_np, time_np
        )  # Ajoutez cette ligne

        # Jointure avec pandas
        start_time_pd = time.time()
        pandas_result = df_1.merge(df_2, on="id")
        end_time_pd = time.time()

        measure_time(start_time_pd, end_time_pd, time_pd)
    elif operation == "pivot":
        # Générer des données structurées
        unique_ids = np.arange(0, 60_000)
        unique_groups = np.arange(0, 3)
        id_col = np.repeat(unique_ids, len(unique_groups))
        group_col = np.tile(unique_groups, len(unique_ids))
        value_col = np.random.randint(0, 100, len(id_col))
        data = np.column_stack((id_col, group_col, value_col))

        # Créer des DataFrames pandas
        df = pd.DataFrame(data, columns=["id", "group", "value"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype(
            [("id", int), ("group", int), ("value", int)]
        )
        numpy_data = np.array(list(map(tuple, data)), dtype=d_type)

        # Pivot avec NumPy
        def numpy_pivot(_data, _id_col, _group_col, _value_col):
            _unique_ids = np.unique(_data[_id_col])
            _unique_groups = np.unique(_data[_group_col])

            pivot_table = np.zeros(
                (len(_unique_ids), len(_unique_groups))
            )


            for row in _data:
                id_index = np.where(_unique_ids == row[_id_col])[0][0]
                group_index = np.where(
                    _unique_groups == row[_group_col]
                )[0][0]
                pivot_table[id_index, group_index] = row[_value_col]

            return pivot_table

        start_time_np = time.time()
        numpy_pivot_table = numpy_pivot(
            numpy_data, "id", "group", "value"
        )
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Pivot avec pandas
        start_time_pd = time.time()
        pandas_pivot_table = df.pivot(
            index="id", columns="group", values="value"
        )
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "group_by":
        # Générer des données structurées
        data = np.random.randint(0, 10_000_000, (100_000, 2))

        # Créer des DataFrames pandas
        df = pd.DataFrame(data, columns=["id", "value"])

        # Créer des arrays structurés NumPy
        d_type = np.dtype([("id", int), ("value", int)])
        numpy_data = np.array(list(map(tuple, data)), dtype=d_type)

        # Group_by avec NumPy
        def numpy_group_by_mean(_data):
            _unique_ids, counts = np.unique(
                _data["id"], return_counts=True
            )
            sums = np.zeros_like(_unique_ids, dtype=float)
            for row in _data:
                sums[np.where(_unique_ids == row["id"])[0][0]] += row[
                    "value"
                ]
            return _unique_ids, sums / counts

        start_time_np = time.time()
        numpy_result = numpy_group_by_mean(numpy_data)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Group_by avec pandas
        start_time_pd = time.time()
        pandas_result = df.groupby("id")["value"].mean()
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

    elif operation == "rolling":
        # Générer un grand ensemble de données
        data_np = np.random.rand(100_000_000)
        data_pd = pd.DataFrame({"values": data_np})

        window = 100

        def numpy_rolling_mean(arr, _window):
            _cum_sum = np.cumsum(np.insert(arr, 0, 0))
            return (
                _cum_sum[_window:] - _cum_sum[:-_window]
            ) / _window

        start_time_np = time.time()
        numpy_result = numpy_rolling_mean(data_np, window)
        end_time_np = time.time()
        measure_time(start_time_np, end_time_np, time_np)

        # Rolling avec pandas
        start_time_pd = time.time()
        pandas_result = (
            data_pd["values"].rolling(window=window).mean()
        )
        end_time_pd = time.time()
        measure_time(start_time_pd, end_time_pd, time_pd)

# Créer un graphique de comparaison
x = np.arange(len(operations))
width = 0.35

fig, ax = plt.subplots()

rects1 = ax.bar(
    x - width / 2,
    time_np,
    width,
    label="NumPy",
    color="#c9daf8",
    edgecolor="black",
    hatch="//",
    linewidth=1,
)
rects2 = ax.bar(
    x + width / 2,
    time_pd,
    width,
    label="pandas",
    color="#c2e8b8",
    edgecolor="black",
    hatch=".",
    linewidth=1,
    alpha=0.5,
)


# Modification de la taille des marqueurs dans rects2
for rect in rects2:
    rect.set_linewidth(2)

ax.set_yscale("log")
ax.set_ylabel("Temps d'exécution (s) - Échelle logarithmique")
ax.set_title(
    "Comparaison des temps d'exécution entre NumPy et pandas"
)
ax.set_xticks(x)
ax.set_xticklabels(operations)
ax.legend()


def autolabel(rects):
    for _rect in rects:
        height = _rect.get_height()
        ax.annotate(
            "{:.2f}".format(height),
            xy=(_rect.get_x() + _rect.get_width() / 2, height),
            xytext=(0, 3),  # 3 points vertical offset
            textcoords="offset points",
            ha="center",
            va="bottom",
        )


autolabel(rects1)
autolabel(rects2)

fig.tight_layout()
plt.savefig("pandas_vs_numpy.png")

Expressions régulières

Regexp hints / aide

Récupéré ici, traduction à finir

Caractères

Caractères Explication Exemple Chaînes qui « matchent »
[abc]
[a-c]
Fait correspondre les caractères / la plage de caractères donné(e)s abc[abc] abca
abcb
abcc
[^abc]
[^ac]
Négation des caractères / la plage de caractères donnés abc[^abc] abcd
abce
abc1
...
. Tout caractère sauf saut de ligne bc. bca
bcd
bc1
b.
...
\d Tout caractère numérique (équivalent à [0-9]) c\d c1
c2
c3
...
\D Tout caractère non numérique (équivalent à [^0-9]) c\D cA
cB
c*
...
\w Tout caractère alphanumérique (équivalent à [A-Za-z0-9_]) a\w aa
a1
a_
...
\W Tout caractère non alphanumérique (équivalent à [^A-Za-z0-9_]) a\W a)
a$
a?
...
\s Généralement utilisé pour les espaces blancs, mais peut être utilisé pour les nouvelles lignes, tabulations, etc. a\s [espace]
\S Tout sauf un espace blanc ou nouvelle ligne ou tabulation. a\S aa
\t Tabulation horizontale t\txy t[tab]xy
\r Correspond à un retour chariot AB\r\nCD AB[\return]CD
\n Correspond à un saut de ligne AB\r\nCD AB[\return]CD
| Correspond à « x » ou « y » aa|bb aa
bb
Chars Définition Ex match
Chars Définition Ex match
Chars Définition Ex match
Commentaires fermés sur Expressions régulières Publié dans

Godot

Objets

Avoir le noeud « racine » owner
Nombre de points (« vertices ») utilisés  
print(
    Performance.get_monitor(
        Performance.RENDER_VERTICES_IN_FRAME
    )
)
Attendre une frame  
yield(get_tree(), "idle_frame")

ou (peut prendre plus d’une frame) :

yield(get_tree(), "physics_frame")

Déplacer
un objet
dans un autre
func _on_barillet_body_shape_entered(
    body_rid, body,
    body_shape_index, local_shape_index
):
    # distant object:
    # body.shape_owner_get_owner(body_shape_index)
    # local object:
    # shape_owner_get_owner(local_shape_index)
    var me = shape_owner_get_owner(
        local_shape_index
    )
    body.get_parent().remove_child(body)
    get_parent().add_child(body)
queue_free()
vs
free()
queue_free() est un raccourci pour détruire un nœud et le supprimer de l’arborescence en toute sécurité à la fin de la frame : le nœud est mis dans une « file d’attente » qui est une liste de « choses à supprimer lorsque le jeu a fini de traiter à cette frame« .. Cela n’arrive pas immédiatement pour éviter certaines situations délicates qui feraient autrement planter le jeu. Pour garder les choses simples, il est recommandé d’utiliser queue_free() pour les nœuds de l’arborescence.

Supprimer les plus vieux fichiers d’un dossier tant qu’on dépasse une certaine taille


Exemples de lancement du script

Notez qu’il faut lancer en utilisant « source« 

  • Supprimer les plus vieux fichiers du dossier courant (./) tant qu’il prend plus de 96Mo :
    source ./clean_custom.sh --path ./ -l 9600000
  • Supprimer les plus vieux fichiers du dossier temporaire (/tmp/) tant qu’il prend plus de 2Go :
    source ./clean_custom.sh --path /tmp/ -l 2000000000

Code du script

#!/usr/bin/env bash                                                              
PATH_TO_CLEAN=                                                                   
NUMBER_FILES_TO_DELETE_EACH_LOOP=1                                               
SIZE_LIMIT=2000000000                                                            
                                                                                 
# ----------------------------------------------------------------------------   
# usage:                                                                         
usage()                                                                          
{                                                                                
    echo "Clean directory: while size of a dir > limit, oldest files first."
    echo "Usage: ${filename} [-p|--path path] [-s|--max-size size] | [-h]"
    echo "    -p|--path: path to clean"            
    echo "    -l|--limit: max size for the folder (must be > 0)"
    echo "    -h|--help this help"                 
}                                                                                
                                                                                 
# ----------------------------------------------------------------------------   
# handling arguments:                                                            
args=("$@")                                                            
filename=$(basename -- "$0" | sed 's/\(.*\)\..*/\1/')        
while [ "$1" != "" ]; do                                     
    case $1 in                                               
        -p | --path ) shift              
                      # stop if path doesn't exist:
                      if [ ! -d "$1" ]; then
                          echo "Path not found: '$1'"
                          usage
                          return 1
                      fi
                      PATH_TO_CLEAN=$1
                      ;;
        -l | --limit ) shift             
                       SIZE_LIMIT=$(echo $1 | bc)
                       if [ $SIZE_LIMIT -le 0 ]
                       then
                           usage
                           return 1
                       fi
                       ;;
        -h | --help ) usage              
                      return
                      ;;
        * ) usage                        
            return 1 
    esac                                                     
    shift                                                    
done                                                                             
[ -z "$PATH_TO_CLEAN" ] && echo "Path empty" && usage && return 1
echo "Cleanin dir: '$PATH_TO_CLEAN', size limit=$SIZE_LIMIT" 
# ----------------------------------------------------------------------------   
# handling arguments:                                                            
while [ 1 ]                                                                      
do                                                                               
    s=$(du -sb $PATH_TO_CLEAN | cut -f1 | bc)                
    if [ $s -gt $SIZE_LIMIT ]                                
    then                                                     
        find $PATH_TO_CLEAN -type f -printf '%T+ %p\n' | \
            sort -nr | \
            tail -$NUMBER_FILES_TO_DELETE_EACH_LOOP | \
            cut -d' ' -f 2- | \
            xargs -I {} rm -f {}
    else                                                     
        break                            
    fi                                                                                                                                                                                                                                                      
done                                                                             
return 0