Author Archives: renaud

TFTP file not found

Un serveur TFTP est un serveur de transfert de fichier assez stupide. Il utilise de udp. Clairement, il ne peut être utiliser que dans un réseau local.
Dans une configuration de base, vous devez définir un répertoire comme étant surveillé par le serveur. Le serveur pourra envoyer et recevoir des fichiers dans ce dossier.
Il ne fournit aucun mécanisme de listage, il faut donc connaitre le nom du fichier à l’avance.

Si vous obtenez souvent une erreur de type (1) : File not found. Alors que vous êtes sur de donner le bon chemin. C’est peut-être à cause de l’option -s de votre serveur. Cette option change la racine du serveur. Ainsi si vous faisiez un

tftp 255.255.255.255 -c get /tftproot/monfichier.img

/tftproot est devenu / . Il faut donc faire

tftp 255.255.255.255 -c get monfichier.img

Tout simplement.

JFreeChart dans vos applications Java

Bonjour, ce petit tutorial va tenter de vous expliquer comment faire de jolie diagramme dans une application JAVA/Swing.
Il est souvent agréable de montrer à ses utilisateurs un diagramme, un camembert ou bien un histogramme, plutôt qu’un gros tableau plein de chiffres. Il existe de nombreuses méthodes pour créer ce genre de graphique. Je vais vous présenter une méthode utilisant JFreeChart. JFreeChart est une bibliothèque java libre (malheuresement la documentation est payante). Elle est assez sexy et facile à utiliser.

Installation

Comme toutes les bibliothèques java, il faut télécharger le .jar et le placer dans un dossier de votre projet. Pour le téléchargement aller à la page [Download] et choississez la version qui correspond à votre système.

Décompressez le contenu de l’archive. Vous devriez obtenir une arborescence de fichier de la forme:

JFreeChart-*.*
    ant
    ChangeLog
    checkstyle
    docfiles
    experimental
    jfreechart-1.0.13-demo.jar
    lib
    licence-LGPL.txt
    maven-jfreechart-project.xml
    NEWS
    README.txt
    source
    swt
    tests

Ce qui est important, c’est le dossier lib. En effet, il contient les 3 archives jar nécessaire. À savoir: jfreechart-1.0.13.jar, jcommon-1.0.16.jar et iText-2.1.5.jar

Il faut en premier lier, ces jars avec votre projet java. La première étape est de copier les jars dans le répertoire principal de votre projet. Ensuite, dans eclipse faite un click droit sur votre projet, choississez : Properties. Sélectionnez Java Build Path puis l’onglet libraries

Appuyer sur Add External JARs… et sélectionnez les 3 jars de la JFreeChart que nous avons déplacés. Appuyer sur Ok

Normalement une partie “Referenced Libraries” s’ajoute à l’arborescence de votre projet. Ainsi eclipse inclura jfreechart à la compilation de votre projet.

Création du panel d’affichage des diagrammes.

Ce n’est pas le sujet de ce tutorial donc je ne vais pas m’attarder dessus. Les charts sont tous contenu dans un JPanel. Il suffit donc de créer un JPanel avec un layout adéquat pour mettre en place votre disposition. N’oubliez pas d’encapsuler votre panel dans un JScrollPane.

Premier Diagramme

JFreeChart permet de créer une multitude de diagramme, je ne traiterais que des PieCharts pour le moment. La première chose à faire est de construire un ensemble de valeurs: Pour un PieChart (camenbert), utilisez [DefaultPieDataset], c’est une interface simple et pratique pour stocker des paires valeur/clé. Pour un histogramme, utilisez plutôt une implémentation de [CategoryDataset]

.

DefaultPieDataset union = new DefaultPieDataset();
//remplir l'ensemble
union.setValue("Pierre",20.0);
union.setValue("Marie",20.0);
union.setValue("Jean",20.0);
union.setValue("Hime",20.0);
union.setValue("Sarah",20.0);

Il est tout à fait possible de faire une boucle for/while pour parcourrir un ensemble de données pour remplir celui du pieChart. Vous n’êtes pas tenu de remplir l’ensemble avant de créer le chart. Si vous ajoutez des valeurs dans l’ensemble après coup, le chart sera automatiquement mis à jour.

Pour créer le diagramme lui même, il suffit de faire appel à l'”usine à diagramme” [pattern factory].

JFreeChart repart = 
    ChartFactory.createPieChart3D("Répartition par personne",
    union, true, true, false);
ChartPanel crepart = new ChartPanel(repart);
add(crepart);

Le premier paramètre est, bien sûr, le titre, suivit de l’ensemble de valeurs. Les valeurs booléennes qui suivent sont respectivement : l’affichage de la légende, l’affichage d’un tooltips et la création d’une URL). Une fois le JFreeChart créé, vous devez récuperér l’encapsuler dans un ChartPanel et ajouter ce dernier dans votre Interface Homme Machine (IHM).

Aller plus loin

Personnaliser les couleurs de chaque valeur

Par défaut, JFreeChart utilise des couleurs aléatoire pour coloriser les éléments. Il existe un moyen pour forcer l’utilisation d’une couleur. On reprends l’exemple du dessus et on le modifie un petit peu pour modifier les couleurs

JFreeChart repart= 
    ChartFactory.createPieChart3D("Répartition par personne",
    union, true, true, false);
ChartPanel crepart = new ChartPanel(repart);
plot = repart.getPlot();
((PiePlot)plot).setSectionPaint("Pierre", new Color(255,0,0));
((PiePlot)plot).setSectionPaint("Marie", new Color(255,255,0));
((PiePlot)plot).setSectionPaint("Jean", new Color(255,0,255));
((PiePlot)plot).setSectionPaint("Hime", new Color(0,0,255));
((PiePlot)plot).setSectionPaint("Sarah", new Color(0,255,255));
add(crepart);

Pour modifier les couleurs, il faut récuperer une instance de Plot. Cela gère l’ensemble des règles de dessins. Dans chaque section, vous définissez la couleur à utiliser. Voici une capture d’écran du résultat final. Capture d'écran JFreeChart

Je mettrais à disposition le projet eclipse, d’ici quelques jours. En attendant, je donne le code source du panel principal:

import java.awt.Color;
import javax.swing.JPanel;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PiePlot;
import org.jfree.chart.plot.Plot;
import org.jfree.data.general.DefaultPieDataset;

public class Charts extends JPanel
{
    public Charts() 
    {
        super();
        DefaultPieDataset union = new DefaultPieDataset();
        union.setValue("Pierre",20.0);
        union.setValue("Marie",20.0);
        union.setValue("Jean",20.0);
        union.setValue("Hime",20.0);
        union.setValue("Sarah",20.0);
        JFreeChart repart = 
            ChartFactory.createPieChart3D("Répartition par personne",
            union,true, true, false);
        ChartPanel crepart = new ChartPanel(repart);
        Plot plot = repart.getPlot();
        ((PiePlot)plot).setSectionPaint("Pierre", new Color(255,0,0));
        ((PiePlot)plot).setSectionPaint("Marie", new Color(255,255,0));
        ((PiePlot)plot).setSectionPaint("Jean", new Color(255,0,255));
        ((PiePlot)plot).setSectionPaint("Hime", new Color(0,0,255));
        ((PiePlot)plot).setSectionPaint("Sarah", new Color(0,255,255));
        
        this.add(crepart);
    }
}

Plus d’Information

GROUP BY qui respecte l’ordre numérique Avec MySQL

Triez un champ avec un tri numérique et non sur les codes des caractères en MySQL. Il est parfois utile de trier une liste d’enregistrement grâce à un champ. Si ce champ contient des chiffres mais qu’il est défini comme un varchar. La fonction ORDER BY fera un mauvais tri. Cas classique et idéal: Type: Integer(11) Data:

+———-+
| Nombres |
+———-+
| 10 |
| 500 |
| 1 |
| 3000 |
| 20 |
| 50 |
| 30 |
| 1000 |
+———-+

Requête nomale.
Requête: SELECT numbers from table order by numbers
Nous obtenons ce résultat:

+———-+
| Nombres |
+———-+
| 1 |
| 10 |
| 20 |
| 30 |
| 50 |
| 500 |
| 1000 |
| 3000 |
+———-+

Le tri est fait de façon correcte. Si nous changeons le type de la donnée en texte, et que nous trions à nouveau.
Requête: SELECT left(numbers, 11) as numbersSTR from table order by numbersSTR
Nous obtenons ce résultat:

+————+
| NombresSTR |
+————+
| 1 |
| 10 |
| 1000 |
| 20 |
| 30 |
| 3000 |
| 50 |
| 500 |
+————+

Ce résultat ne suit pas l’ordre mathématique des nombres. Dans le même ordre d’idée, si vous avez des nombres stockés dans une champ text ou varchar, et que vous souhaitez les trier dans l’ordre croissant. Ce résultat sera surement mauvais.
Requête: select number from (table) order by number;

+——–+
| Nombre |
+——–+
| 1 |
| 10 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+——–+

Pour palier ce problème, vous pouvez utiliser cette astuce.
Requête: select number from (table) order by (number+0);

+——–+
| Nombre |
+——–+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+——–+

Le résultat est celui attendu. (champ + 0 ) convertit le texte/le caractère en nombre. Ainsi le tri est fait correctement.

Qt4 et le design pattern Command

Bonjour,  ce petit tutorial va tenter de vous expliquer comment réaliser une opération longue tout en restant réactif dans une application Qt4.
Il existe plusieurs moyens mais je ne vais en décrire qu’un seul qui à mon sens est le plus propre. Comme cas pratique, j’ai choisi d’implémenter le Design patterns “Command”.
La première étape utilise des thread.

Communication entre un thread et une application Qt

Pas de solution miracle, pour faire “deux choses à la fois” dans une application, il faut passer par du multi-threading. Nous allons créer une classe thread qui hérite de QThread. Cette classe sera le support du traitement long à effectuer. Voyez ça comme une sorte encapsulation.

//.h de la classe

#ifndef THREAD_H
#define THREAD_H
#include <QThread>
#include "command.h"
class Thread : public QThread
{
    Q_ObJECT
        COMMAND* mycommand;
        bool undo;
public:

    Thread(COMMAND* mycommand,bool undo);

    protected:
        void run();
};

#endif // THREAD_H

Comme vous le voyez, c’est très simple. Nous héritons de QThread, nous avons deux membres dans cette classe: un pointeur vers une instance de la classe COMMAND (utile pour l’implémentation du patterns du même nom) et un booléen qui nous permet de déterminer le sens de l’action “annuler” (undo) ou “normal” (undo == false). bien entendu, il est possible d’avoir autant de paramètre que vous le souhaitez. Il ne faut pas oublié de redéfinir la fonction run().

// l’implémentation

#include "thread.h"

Thread::Thread(COMMAND* _mycommand,bool _undo)
        : mycommand(_mycommand),undo(_undo)
{

}
void Thread::run()
{
    if(undo)
        mycommand->undo();
    else
        mycommand->doCommand();
}

Dans le constructeur, je définis les données membres. La fonction run exécute la commande en fonction du paramètre undo. Il faut savoir que tout le code appelé dans run sera exécuté dans un thread différent.
À ce stade, nous avons une classe Thread qui est prête à recevoir et exécuter une “command”.

Nous allons voir maintenant comment créer un design pattern command.

Implémenter le design pattern command

Ce pattern est très utile pour gèrer l’annulation d’une action ou par exemple refaire la dernière action. Il consiste à créer une classe pour chaque action (du moins toutes les actions que vous voulez pouvoir modifier ou dans notre cas, exécuter dans un thread). Qt fournit des outils pour faire ça. Dans un cadre formateur, je préfère l’implémenter entièrement.
Il faut tout d’abord écrire la classe abstraite qui définit une commande. Cela permettra à notre classe thread de bien intéragir avec la commande.

#ifndef COMMAND_H
#define COMMAND_H
#include <QObject>
class COMMAND : public QObject
{
        Q_ObJECT

public:
    virtual void doCommand()=0;
    virtual void undo()=0;
signals:
        void Maximum(int M);
        void Minimum(int m);
        void valueChanged(int v);
        void done();
};

#endif // COMMAND_H

Il y a deux méthodes abstraites pures: l’une pour faire la commande, l’autre pour l’annuler. J’ai ajouté quelques signaux pour que la commande communique avec le thread principal pour informer l’utilisateur de l’avancée du la tâche en cours. Toutes les futures commandes de notre application doivent être des sous-classes de COMMAND. Vous l’avez deviné, il faut implémenter une commande, maintenant.
Notre commande sera vraiment basique, c’est une commande d’attente (so useless).

//wait.h

#ifndef WAIT_H
#define WAIT_H
#include "command.h"
class WAIT : public COMMAND
{
public:
    WAIT();

    virtual void doCommand();
    virtual void undo();

};

#endif // WAIT_H

Rien de particulier, juste la re-définition des fontions virtuelles pures. Voici leurs implémentations

#include "wait.h"
WAIT::WAIT()
{

}

void WAIT::doCommand()
{

int step = 10;
        emit Maximum(100);
        int i = 0;
        int k = 0;
        emit Minimum(i);
    for(int j = 0 ; j< 1000 ; j++)
    {
        sleep(0.5); //fake statement
        if(i>=step)
        {
                emit valueChanged(++k);
                i = 0;
         }
        i++;

    }
    emit valueChanged(++k);
    emit done();
}

void WAIT::undo()
{

}

Dans notre “doCommand”, nous calculons le pas de la notification de l’application principale. Ici, j’ai arbitrairement choisi 10 mais dans un contexte utile, le pas est égal à la taille de vos données à traiter divisé par le nombre de notification que vous voulez.
Nous émettons la valeur maximale. Initialisation des variables temporaires (i et k). On émet i (0). Nous faisons une bouble sur chaque élément à traiter, on fait le traitement et on calcule un peu pour savoir s’il faut ou pas prévenir l’application principale.
Il est bon de ne pas prévenir à chaque fois, car si vous travaillez sur plusieurs milliers ou millions de données le traitement des signaux ralentira un peu votre application. Comme exemple, imaginez que vous travaillez sur chaque pixel d’une grosse photo.

Il ne reste plus qu’a écrire la fenêtre principale qui affichera à l’utisateur l’avancée de notre traitement.

QProgressBar et QThread.

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include <QProgressBar>
#include <QDockWidget>
#include "thread.h"
namespace Ui
{
    class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
    QProgressBar *workinprogress;
    QDockWidget *Progressdock;
    Thread* myThread;
};

#endif // MAINWINDOW_H

Rien d’original, une mainwindow avec juste trois membres. Une QProgressBar qui affichera la progression.
Un DockWidget pour afficher la progesse bar, je ne l’ai pas implémenté dans cet exemple mais il peut être amusant d’afficher le dock quand une opération est en cours et la cacher quand c’est fini. Le dernier membre est une instance de notre classe Thread.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "wait.h"



MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent), ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    workinprogress = new QProgressBar;
    Progressdock = new QDockWidget(tr("progress panel"));
    workinprogress->setValue(0);
    Progressdock->setWidget(workinprogress);
    Progressdock->setAllowedAreas(Qt::BottomDockWidgetArea);
    addDockWidget(Qt::BottomDockWidgetArea,Progressdock);
    WAIT* mywait = new WAIT();

    myThread = new Thread(mywait,false);
    connect(mywait,SIGNAL(Maximum(int)),workinprogress,SLOT(setMaximum(int)));
    connect(mywait,SIGNAL(Minimum(int)),workinprogress,SLOT(setMinimum(int)));
    connect(mywait,SIGNAL(valueChanged(int)),workinprogress,SLOT(setValue(int)));

    connect(ui->pushButton,SIGNAL(clicked()),myThread,SLOT(start()));

}

MainWindow::~MainWindow()
{
    delete ui;
}

Nous initialisons la QProgressbar et le QDockwidget. En suite, je paramètre un peu nos deux instances. Je crée alors une instance de WAIT et j’initialise le thread avec en paramètre l’adresse de notre commande.
Je connecte maintenant les signaux de la commande au slot de la QProgressBar.
Finalement, je connecte le clique sur “pushButton” sur le démarrage du Thread.
Ainsi, à chaque clique, il demarrera la commande wait.

Aller plus loin

Il serait préférable de créer une instance de WAIT à chaque clique et de l’ajouter dans une pile (dans la mainwindow). Cela est nécessaire pour finir le design pattern command. En haut de la pile, se trouve la dernière commande, si elle est annulée alors il faut la dépiler et exécuter undo().

Je proposerai en téléchargement ce petit exemple, d’ici quelques jours. En attendant, une petite capture d’écran. Capture d'écran Design Pattern Command, QThread et Qt.

Utiliser TagLib dans vos applications

Introduction

TagLib est une bibliothèque open-source écrite en C++ qui permet la lecture, l’écriture et la manipulation de Tags (étiquettes) de fichiers sons (Mp3, FLAC, MPC, Speex, WavPack and TrueAudio).
Ce tutorial doit vous donner les bases pour utiliser TagLib dans vos applications.

Installer TagLib

La première étape pour utiliser TagLib est bien sûr de l’installer sur son systême. Pour de nombreuses distributions GNU/Linux, TagLib existe dans le gestionnaire de paquets. Il est très important d’installer la bibliothèque ainsi que ses en-têtes (souvent un paquet avec un suffixe -dev ou -devel).

Sous Ubuntu 8.04:

sudo apt-get install libtag1c2a libtag1-dev

Bien sûr, vous pouvez compiler vous-même la bibliothèque. En téléchargeant le code source sur le site:

TagLib 1.5Pour les utilisateurs de Windows, il existe deux possibilités:

Télécharger et compiler les sources

ici

Une version binaire de la bibliothèque est disponible

ici

.

Compiler avec TagLib

Pour des environnements linux, il suffit d’installer la bibliothèque et à l’édition des liens, ajouter

-ltag

à la ligne de compilation. Attention: il est obligatoire d’ajouter le chemin des includes dans le includepath.

#include <taglib/tag.h>

seul ne marchera pas.

Exemple d’un petit makefile:


all:main

main: main.o MP3_File.o
        g++ -Wall -g -o main main.o MP3_File.o -ltag

main.o: main.cpp
        g++ -Wall -g -c main.cpp

MP3_File.o:MP3_File.cpp MP3_File.h
        g++ -Wall -g -c MP3_File.cpp

Lire des tags

Dans ce chapitre, nous allons voir comment TagLib doit être utilisé pour lire des tags. La bibliothèque est entièrement contrôlée par la classe

TagLib::FileRef

(Design pattern: Façade). L’initialisation commence par la création d’une instance

TagLib::FileRef

. Il faut ensuite la lier directement avec un fichier audio.


#include <tagli/tag.h>
#include <taglib/audioproperties.h>


 myTaggger = new TagLib::FileRef(filename);//filename est un char* contenant l'adresse du fichier
TagLib::String artist = myTaggger->tag()->artist ();

Comme vous le voyez, depuis l’objet FileRef, on appelle tag(). Pour ensuite appeler la fonction de la donnée désirée. Ici le nom de l’interprète de la chanson. Pour plus d’information voir

TagLib API documentation

).

Le format de chaîne de caractères renvoyé par TagLib est un format “à leur sauce” (TagLib::String) pour la conversion vers des formats de chaînes plus courrant voir FAQ.

Écrire des tags

L’écriture de tag avec TagLib est l’exacte réciproque de la lecture. En effet, la bibliothèque fournit des fonctions prennant en paramètre une chaîne de caractères.


#include <taglib/tag.h>
#include <taglib/audioproperties.h>


myTaggger = new TagLib::FileRef("chemin_du_mp3.mp3");

myTaggger->tag()->setTitle(TagLib::String("9ème Symphonie"),TagLib::String::UTF8);

myTaggger->save();

Il y a principalement 3 étapes :

  • créer une instance de TagLib::FileRef liée à un fichier.
  • Modifier les tags souhaités.
  • Sauvegarder les changements

Accès aux propriétés audio

Certaines informations comme le “bitrate” et la durée du fichier son ne sont pas contenu dans des tags. Il caractérise plus le flux audio que la chanson. Les auteurs de TagLib appellent ce genre de données des propriétés audio. Un petit exemple pour récupérer le bitrate.


#include <taglib/tag.h>
#include <taglib/audioproperties.h>


myTaggger = new TagLib::FileRef("chemin_du_mp3.mp3");
myTaggger->audioProperties()->bitrate();

Il est également possible de récuperer la longueur en milliseconde par l’appel à “audioProperties()->length()”

Conclusion

Ce tutorial vous a montré comment utiliser la bibliothèque

TagLib

. Ce tutorial ne couvre bien sûr pas toutes les fonctionnées que peut fournir cette bibliothèque. En ce qui conserne les exemples de ce tutorial, c’est à vous de créer l’architecture de votre application et d’intégrer les exemples au bon endroit.(renseignez vous sur le design pattern : adapter)

Personnellement, je l’utilise pour lire et modifier les tags de tous les formats excepté les mp3 car je trouve id3lib plus complète pour le mp3.

Utiliser Id3lib dans vos applications

Installer id3lib

La première étape pour utiliser id3lib est bien sûr de l’installer sur son systême. Pour de nombreuses distributions GNU/Linux, Il existe dans le gestionnaire de paquets, un paquet d’id3lib. Il est très important d’installer la bibliothèque ainsi que ses en-têtes (souvent un paquet avec un suffixe -dev ou -devel).

Sous Ubuntu 8.04:

sudo apt-get install libid3-3.8.3c2a libid3-3.8.3-dev

Sous fedora 9:

yum install id3lib-3.8.3-20.fc9 id3lib-devel-3.8.3-20.fc9

Bien sûr, vous pouvez compiler vous-même la bibliothèque. En téléchargeant le code source sur le site:

DownloadPour les utilisateurs de Windows, il existe deux possibilités:

Si vous utilisez Visual Studio, alors télécharger

ici

la version binaire de la bibliothèque. Ajoutez la, dans la liste des dépendences de votre projet et tout ira bien.

Si vous utilisez le compilateur libre mingw. Je vous conseille alors d’utiliser cette version de la bibliothèque. Sinon, il ne vous restera qu’à la compiler.

Compiler avec id3lib

Pour des environnements linux, il suffit d’installer la bibliothèque et d’ajouter

-lid3

à la ligne d’édition des liens.

Exemple d’un petit makefile:


all:main

main: main.o MP3_File.o
        g++ -Wall -g -o main main.o MP3_File.o -lid3

main.o: main.cpp
        g++ -Wall -g -c main.cpp

MP3_File.o:MP3_File.cpp MP3_File.h
        g++ -Wall -g -c MP3_File.cpp

Pour windows, le principe est le même.
Si vous utilisez visual studio. Modifier les paramètres de votre projet. Si vous utilisez mingw (gcc). Il sera surêment nécessaire de donner en paramètre l’emplacement de la bibliothèque et de ses en-têtes:"-I"lib\include".

Lire des tags

Dans ce chapitre, nous allons voir comment Id3lib doit être utilisé pour la lecture de tags. La bibliothèque est entièrement contrôlée par la classe

ID3_Tag

(Design pattern: Façade) L’initialisation commence par la création d’une instance

ID3_Tag

puis il faut la lier à un fichier mp3.


#include <id3/tag.h>

ID3_Tag mytag;
mytag.Link("chemin_du_mp3.mp3",ID3TT_ALL);
ID3_Frame* myFrame = NULL;
myFrame= myTag.Find(ID3FID_TITLE);
if(myFrame!=0)
{
	char* title = new char[1024];
	myframe->Field(ID3FN_TEXT).Get(title,1024);
	cout << title << endl;
}

Ensuite, nous déclarons un pointeur sur une

ID3_Frame

initialisé à NULL. Nous demandons le titre de mp3 par l’appel de la fonction Find(ID3_FrameID). Cette fonction prend en paramètre une valeur d’une énumération (pour connaitre toutes les valeurs de l’énumeration: voir

Id3lib API documentation

).

Si la frame n’est pas vide alors nous créons un tableau de caractères. Et nous demandons à la frame de remplir ce tableau avec le champs ID3FN_TEXT. Un tag correspond à une frame et une frame stocke l’information sous plusieurs formes. Ainsi il est évident que le titre est une donnée textuelle. Alors que le genre de musique est une valeur entière.

Écrire des tags

Il existe deux méthodes pour ajouter/modifier des tags. La première est l’inverse de la lecture. Elle nécessite la création et l’initialisation d’une

ID3_FRAME

puis l’ajout de cette frame à une instance de

ID3_Tag

. Cette méthode est longue et compliquée. C’est pourquoi, Id3lib fournit des méthodes plus simple d’utilisation


#include <id3/tag.h>
#include <id3/misc_support.h>


ID3_Tag mytag;
mytag.Link("chemin_du_mp3.mp3",ID3TT_ALL);
char* nouveautitre = new char[100];
sprintf(nouveautitre,"9eme symphonie");
ID3_AddTitle (&mytag, nouveautitre, true);
mytag->Update();

Comme toujours, vous devez créer et initialiser une instance de

ID3_Tag

. Puis, faite appel à la fonction ID3_AddTitle(). Cette fonction prend 3 paramètres: un pointeur sur un

ID3_Tag

pour identifier quel fichier doit être modifié, un tableau de caractères contenant la nouvelle valeur et un booléen, true pour écraser si le tag existe déja et false pour ne rien faire si le tag existe déja.

Accès aux headers

Certaines informations comme le “bitrate” et la durée du mp3 ne sont pas contenu dans des frames. Il existe bien une frame “Time” mais elle est rarement remplie. Ainsi, il vaut mieux utiliser les informations calculées dans les headers. Elles sont plus justes. Voici la liste des 4 champs principaux d’une instance Mp3_Headerinfo:


bitrate = m_mp3header->bitrate;
bitrate_si_variable = m_mp3header->vbr_bitrate;
frequence = m_mp3header->frequency;
durée   = m_mp3header->time;

Un petit exemple pour récupérer le bitrate.


#include <id3/tag.h>
ID3_Tag* mytag = new ID3_Tag;
mytag->Link("chemin_du_mp3.mp3",ID3TT_ALL);
Mp3_Headerinfo* header = mytag->GetMp3HeaderInfo();

if((header != NULL)&&(header->bitrate>0))
fields->Bitrate = header->bitrate;
else if(header != NULL)
fields->Bitrate = header->vbr_bitrate;

Il existe d’autres champs. Voici un exemple de transformation de ces champs en données lisible pour l’homme.


szMpegLayer = new char[10];
//MPEG layer
switch(m_mp3header->layer)
{
case MPEGLAYER_UNDEFINED: sprintf(szMpegLayer, "UNDEFINED"); break;
case MPEGLAYER_III:       sprintf(szMpegLayer, "Layer III"); break;
case MPEGLAYER_II:        sprintf(szMpegLayer, "Layer II");  break;
case MPEGLAYER_I:         sprintf(szMpegLayer, "Layer I");   break;
default:                  szMpegLayer = 0;
}

// MPEG Version 
szMpegVersion = new char[10];
switch(m_mp3header->version)
{
case MPEGVERSION_2_5: sprintf(szMpegVersion, "MPEG 2.5"); break;
case MPEGVERSION_2:   sprintf(szMpegVersion, "MPEG 2");   break;
case MPEGVERSION_1:   sprintf(szMpegVersion, "MPEG 1");   break;
default:              szMpegVersion = 0;
}

// Channel Mode
szChannelMode = new char[15];
switch(m_mp3header->channelmode)
{
case MP3CHANNELMODE_STEREO:         sprintf(szChannelMode, "Stereo");         break;
case MP3CHANNELMODE_JOINT_STEREO:   sprintf(szChannelMode, "Joint Stereo");   break;
case MP3CHANNELMODE_DUAL_CHANNEL:   sprintf(szChannelMode, "Dual Channel");   break;
case MP3CHANNELMODE_SINGLE_CHANNEL: sprintf(szChannelMode, "Single Channel"); break;
default:                            szChannelMode = 0;
}

Conclusion

Ce tutorial vous a montré comment utiliser la bibliothèque

id3lib

. Ce tutorial ne couvre bien sûr pas toutes les fonctionnées que peut fournir cette bibliothèque. N’hesiter pas à consulter le code source (et surtout les fichiers .h) d’id3lib pour l’utiliser au mieux.

La documention d’id3lib est dépassée mais elle peut vous être utile, notament les différents manuels.

En ce qui conserne les exemples de ce tutorial, c’est t’a vous de créer l’architecture de votre application et d’intégrer les exemples au bon endroit.

(renseignez vous sur le design pattern : adapter)

Écrire un applet Gnome2

Écrire un applet Gnome2 – Programmation

par Andrew Burton traduit par Renaud Guezennec

Historique
Révision 1.1 6 septembre 2007 RG
Ajout d’une copie de makefile et traduction française V0.5.
Révision 1.0 20 Decembre 2005 ADB
Ajout du lien pour télécharger l’applet.
Revision 0.02 15 Avril 2004 ADB
Version basé sur les retours de la liste de diffution: gnome-doc-list mailing. Utilisation de Docbook 4.2, amélioration de la section sur la compilation, Utilisation apropriée des marqueurs pour la FAQ, Modification du code.
Revision 0.01 13 Avril 2004 ADB
Version initiale.

Mise en bouche

Les applets Gnome sont de petites applications qui s’intègrent à Gnome. Elles fournissent des fonctionnalités simples et utiles pour l’utilisateur. Il y a quelques différences avec l’écriture d’une application Gnome normale. Le but de ce tutorial est d’expliquer; comment créer un petit applet.


Table des matières

Qu’est-ce qu’un Applet?
Construire l’infrastructure
Le rendre intéressant
Création d’un menu contextuel
Foire aux Questions
Conclusion
plus informations

Qu’est-ce qu’un Applet?

Dans Gnome, un applet est une petite application destinée à s’intégrer au tableau de bord de Gnome, donnant un accès rapide et facile sur un contrôle, comme un contrôleur de volume, un indicateur d’activité réseau, ou encore une information météorologique.

Les applets nécessitent la bibliothèque “libpanel-applet” pour s’exécuter et si vous souhaitez en développer un, vous aurez besoin d’installer le paquet de développement (souvent distinguer par -dev ou -devel en fin, cela dépend de votre distribution). En raison de leur symbiose avec Gnome, ils sont souvent moins complexes et plus facile à maîtriser pour un programmeur débutant dans l’environnement Gnome.

Après une installation de Gnome, sur un ordinateur de bureau, vous aurez la date et un applet de contrôle du volume dans le coin supérieur droit. Sur un ordinateur portable, vous trouverez aussi une barre d’état de votre batterie.

screenshot

Construire l’infrastructure

Sur un plan technique, Les applets sont des contrôles “Bonobo” intégrés au tableau de bord de Gnome. Cela signifie qu’il y a plusieurs différences avec un programme Gnome classique. La première différence est que chaque applet nécessite un fichier avec l’extension ‘.server’, qui contient la description pour le rendre compréhensible par “Bonobo”. Si vous ne comprenez pas très bien ceci ne vous inquiétez pas. La seule chose que le développeur a à faire est d’éditer le fichier, et remplacer certains champs par les spécificités de son applet.

Voici un exemple d’un fichier “.server” appellé “sample.server”.

Example 1. Un exemple de fichier .server

<oaf_info>
<oaf_server iid="OAFIID:ExampleApplet_Factory" type="exe"
location="/usr/lib/gnome-panel/myexample">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:Bonobo/GenericFactory:1.0"/>
<item value="IDL:Bonobo/Unknown:1.0"/>
</oaf_attribute>
<oaf_attribute name="name" type="string" value="Example Applet Factory"/>
<oaf_attribute name="description" type="string" 
value="Factory to create the example applet"/>
</oaf_server>
<oaf_server iid="OAFIID:ExampleApplet" type="factory"
location="OAFIID:ExampleApplet_Factory">
<oaf_attribute name="repo_ids" type="stringv">
<item value="IDL:GNOME/Vertigo/PanelAppletShell:1.0"/>
<item value="IDL:Bonobo/Control:1.0"/>
<item value="IDL:Bonobo/Unknown:1.0"/>
</oaf_attribute>
<oaf_attribute name="name" type="string" value="Example Applet"/>
<oaf_attribute name="description" type="string" value="An example applet"/>
<oaf_attribute name="panel:category" type="string" value="Amusements"/>
<oaf_attribute name="panel:icon" type="string" value="myicon.png"/>
</oaf_server>
</oaf_info>

Il y a quelques trucs à noter. Premièrement, il faut définir l’emplacement de l’exécutable, définie dans l’attribut “location” de oaf_server, N’oublier pas d’indiquer le type en “exe”. Dans cet exemple, notre fichier exécutable s’appelle myexample et est placé dans /usr/lib/gnome-panel/ . (Emplacement traditionnel de tous les applets) Secondement, nous définissons le nom de notre ‘applet Factory’ , ExampleApplet_Factory. Nommer votre .serveur et placer le dans /usr/lib/bonobo/servers/.

Après avoir ajouter le .server dans le dossier, nous pouvons commencer à écrire le code source du notre applet. Voici, un exemple simple: un applet “Hello World”.

Exemple 2. Hello World!

#include <string.h>
#include <panel-applet.h>
#include <gtk/gtklabel.h>
static gboolean
myexample_applet_fill (PanelApplet *applet,
const gchar *iid,
gpointer data)
{
GtkWidget *label;
if (strcmp (iid, "OAFIID:ExampleApplet") != 0)
return FALSE;
label = gtk_label_new ("Hello World");
gtk_container_add (GTK_CONTAINER (applet), label);
gtk_widget_show_all (GTK_WIDGET (applet));
return TRUE;
}
PANEL_APPLET_BONOBO_FACTORY ("OAFIID:ExampleApplet_Factory",
PANEL_TYPE_APPLET,
"The Hello World Applet",
"0",
myexample_applet_fill,
NULL);

La compilation ce fait avec cette commande:

bash~$ gcc $(pkg-config --cflags --libs libpanelapplet-2.0) -o myexample my_applet.cPour un Makefile, voici mon exemple:

PKGCONFIG = /usr/bin/pkg-config
CFLAGS = $(shell $(PKGCONFIG) –cflags libpanelapplet-2.0)
LDFLAGS = $(shell $(PKGCONFIG) –libs libpanelapplet-2.0)
OPTION = -g -Wall

all:myexample
myexample: my_applet.c
gcc $(OPTION) $(CFLAGS) $(LDFLAGS) -o myexample my_applet.cinstall:myexample sample.server
cp myexample /usr/lib/gnome-applets/
cp sample.server /usr/lib/bonobo/servers/
clean:
rm /usr/lib/gnome-applets/myexampleNoter que la variable d’environnement PKG_CONFIG_PATH doit contenir le chemin vers libpanelapplet-2.0.pc. Si vous obtenez l’erreur suivante:

Package libpanelapplet-2.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `libpanelapplet-2.0.pc'
to the PKG_CONFIG_PATH environment variable
No package 'libpanelapplet-2.0' found

Vous avez besoin d’exécuter les commandes suivantes:

bash$ PKG_CONFIG_PATH=/usr/lib/pkgconfig
bash$ export $PKG_CONFIG_PATH

Placer l’exécutable dans le répertoire /usr/lib/gnome-panel/ (souvenez-vous que le fichier .server doit être défini). Notre applet sera représenté (dans la fenêtre d’ajout d’applet) par l’icone myicon.png, qui doit être placé dans /usr/share/pixmaps/.

Nous ajoutons notre applet au tableau de bord Gnome par un clique droit sur la barre de Gnome, et choisissez “ajouter au tableau de bord…” dans la rubrique “Amusements”: Example Applet.

Comment Gnome fait pour lier le code C et le fichier .server? Le lien se fait grâce a l’appel de la fonction PANEL_APPLET_BONOBO_FACTORY. Cet appel utilise un nombre important de paramètres, et le prototype de la fonction est:

PANEL_APPLET_BONOBO_FACTORY (iid, type, name, version, callback, data)

Le premier paramètre spécifie le OAFIID, c’est l’identifiant pour Bonobo, et il devrait être le nom que vous avez défini dans le fichier .server, “ExampleApplet_Factory”. Le deuxième paramètre définit le type; pour un applet donc PANEL_TYPE_APPLET. Le troisième paramètre est le nom qui s’affichera lorsque vous exécuterez l’interface Bonobo. Le quatrième est le version. Nous devons aussi donner la fonction à exécuter,La fonction d’entrée dans notre code source est myexample_applet_fill(). Pour le dernier paramètre, nous spécifions l’envoi à la méthode d’entrée n’importe quel type de données. Dans notre exemple,nous ne passons rien ainsi nous mettons “NULL” comme valeur de paramètre.

Dans notre code, la définition de la méthode pour myexample_applet_fill () est:

myexample_applet_fill (applet, iid, data)

Noter que le nom de cette méthode doit être présent dans la définition de la “factory”.

Premièrement, nous testons que l’iid corresponde avec l’iid passé en paramètre. Si ce n’est pas le cas, nous annulons la création de l’applet, dans les autres cas, nous continuons avec le reste de la méthode.

if (strcmp (iid, "OAFIID:ExampleApplet") != 0)
return FALSE;

Il est préférable pour la fonction “main” qu’elle n’aie pas beaucoup de fonctionnalités. Assurez-vous que le OAFIID corresponde avec l’identifiant de Bonobo. Dessiner un ‘label’ dans l’applet, et afficher le. Facile mais pas vraiment utile. Après avoir fait tout cela, notre applet existe. Si vous faites un clique droit dessus, vous avez trois possibilités : Enlever l’applet, le déplacer et le fixer.

Le rendre intéressant

Nous allons changer le ‘widget’ avec le texte “Hello World” par un petit image. D’abord ajoutons une “GtkImage” dans l’applet:

image = gtk_image_new_from_file ("/usr/share/pixmaps/mypicture.png");

Alors, les widget GtkImage ne peuvent recevoir d’évènement (c’est vrai) et ainsi ils ne peuvent pas réagir vis à vis d’un clique de souris, Nous avons besoin de mettre le GtkImage dans GtkEventBox:

event_box = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER (event_box), image);
g_signal_connect (G_OBJECT (event_box), "button_press_event",
G_CALLBACK (on_button_press),
image);

N’oublier pas de supprimer le code pour la création du label avec “Hello World”.

Maintenant, nous avons faire que l’applet réagisse quand nous cliquons sur l’icône du “tableau de bord”. Bien sûr, nous avons besoin d’une fonction qui intercepte le clique de souris:

Example 3. Traiter un clique de souris dans votre applet.

static gboolean
on_button_press (GtkWidget *event_box, GdkEventButton *event,
gpointer data)
{
static int window_shown;
static GtkWidget *window, *box, *image, *label;
/* Don't react to anything other than the left mouse button;
return FALSE so the event is passed to the default handler */
if (event->button != 1)
return FALSE;
if (!window_shown) {
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
box = GTK_BOX (gtk_vbox_new (TRUE, 12));
gtk_container_add (GTK_CONTAINER (window), box);
image = GTK_IMAGE (gtk_image_new_from_file ("/usr/share/pixmaps/mypicture.png"));
gtk_box_pack_start (GTK_BOX (box), image, TRUE, TRUE, 12);
label = gtk_label_new ("Hello World");
gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 12);
gtk_widget_show_all (window);
}
else
gtk_widget_hide (GTK_WIDGET (window));
window_shown = !window_shown;
return TRUE;
}

Dans la fonction on_button_press() ci-dessus, nous créons une nouvelle fenêtre avec un peu de texte. Quand nous cliquons sur le bouton la fenêtre s’affiche si vous recliquez, elle disparaît.

Içi la capture d’écran de la fenêtre affichée quand on clique sur l’applet avec le bouton gauche de la souris:

screenshort

Création d’un menu contextuel

Quand on clique avec le bouton droit sur l’icône d’un applet sur le tableau de bord, un menu par défaut s’affiche avec la possibilité de: supprimer l’applet, le déplacer ou le figer. Il est possible d’ajouter quelques options – un bouton d’aide, une fenêtre “à propos” et une fenêtre de préférences.

Pour créer un menu “pop-up”, nous définissons en premier lieu le menu lui même. Vous pouvez le faire avec le code suivant:

static const char Context_menu_xml [] =
"<popup name=\"button3\">\n"
" <menuitem name=\"Properties Item\" "
" verb=\"ExampleProperties\" "
" _label=\"_Preferences...\"\n"
" pixtype=\"stock\" "
" pixname=\"gtk-properties\"/>\n"
" <menuitem name=\"About Item\" "
" verb=\"ExampleAbout\" "
" _label=\"_About...\"\n"
" pixtype=\"stock\" "
" pixname=\"gnome-stock-about\"/>\n"
"</popup>\n";

ou dans un fichier, charger pendant l’exécution avec la fonction panel_applet_setup_menu_from_file (). Les valeurs utilisées ici devrait être simple à comprendre. Nous ajoutons 2 éléments au menu, le nom apparaîtra dans le menu après un clique droit avec une icône ‘stockée’, défini dans GtkStockItem (les Stock sont des éléments présent dans gnomes permettant une internationnalisation facile des applications).

En suite, nous définissons les actions graphiques Bonobo:

static const BonoboUIVerb myexample_menu_verbs [] = {
BONOBO_UI_VERB ("ExampleProperties", display_properties_dialog),
BONOBO_UI_VERB ("ExampleAbout", display_about_dialog),
BONOBO_UI_VERB_END
};

Ceci lie les actions avec leurs fonctions spécifiées ci-dessus, et exécute les fonctions. Autrement dit, quand l’utilisateur choisit “Préférences…” depuis le menu contextuel, votre applet entre dans la fonction display_properties_dialog ().

En fin, nous avons besoin de construire le menu, rassemblons ensemble ces deux étapes:

panel_applet_setup_menu (PANEL_APPLET (myexample->applet),
Context_menu_xml,
myexample_menu_verbs,
myexample);

Notons que le dernier paramètre est un pointeur, qui peut être utilisé dans la fonction de gestion d’un évènement.

Vos gestionnaires d’évènements ont le prototype suivant:

static void
myexample_applet_properties_dialog (BonoboUIComponent *uic,
struct MultiRes *applet) {
/* Construire la fenêtre des propriétes et l'afficher ici */
...
}

Foire aux questions

1. Comment débogguer mon applet ? J’utilise souvent des appels à printf () pour voir ce que fait mon code, mais je ne peux pas le faire avec un applet!
2. Comment débogguer mon applet ? J’utilise souvent gdb, comment l’utiliser?
3.Je n’ai pas d’icône pour mon applet, je ne peux dont pas l’inclure dans le .server. Je ne peux pas trouver mon applet dans la liste des applets disponibles quand j’essaie de l’ajouter à mon tableau de bord.
4.Mon applet n’est pas visible dans le menu d’ajout au tableau de bord.
1. Comment débogguer mon applet ? J’utilise souvent des appels à printf() pour voir ce que fait mon code, mais je ne peux pas le faire avec un applet!
Exécuter votre applet par la ligne de commande /usr/lib/gnome-applet/my_example, Ensuite ajouter le à votre “tableau de bord”, ainsi tous les appels vers les sorties standards seront visibles dans la console (une astuce de Glynn Foster).
2. Comment débogguer mon applet ? J’utilise souvent gdb, comment l’utiliser!
Exécuter votre applet par la ligne de commande gdb /usr/lib/gnome-applet/my_example, puis faites ‘run’ dans gdb. Ensuite, ajouter à votre “tableau de bord”, ainsi vous pouvez maintenant utiliser gdb comme vous en avait l’habitude.
3. Je n’ai pas d’icône pour mon applet, je ne peux donc pas l’inclure dans le .server. Je ne peux pas trouver mon applet dans la liste des applets disponibles quand j’essaie de l’ajouter à mon tableau de bord.
Les Applets nécessitent une icône pour être afficher dans la listes d’ajout.
4. Mon applet n’est pas visible dans le menu d’ajout au tableau de bord.
L’applet est installé dans /usr/local/ par défaut. Gnome le cherche dans /usr. Vous pouvais exécuter ./configure –prefix=/usr pour installer votre applet dans un autre répertoire. Vous aurez besoin de vous fermer la session Gnome et de la rouvrir pour que les changement soit pris en compte.

Conclusion

Créer un applet n’est pas difficile. Cependant, il peut être aussi complexe que vous le souhaitez. Il est parfois plus raisonnable de créer une application. Exemple: vous désirez ajouter plus de widgets, Utiliser Glade combiné avec libxml sera plus facile pour construire votre IHM(Interface Homme Machine).

Un avertissement important en ce qui concerne l’exemple employé dans ce tutorial. J’ai compilé l’applet par la ligne de commande par simplicité ; pour un vrai applet, employer un fichier makefile. Cela tient compte des particularités matérielles (comme l’emplacement de l’icône), la maintenance n’en sera que plus facile.

Plus d’Information

  • Télécharger l’applet d’exemple Voici l’applet créé dans ce tutorial. L’archive (.tar) inclut aussi les ‘makefiles’ appropriés pour configurer et installer votre applet.
  • Les applets Gnome sont disponible au téléchargement dans le CVS de gnome sous le répertoire gnome-applets.
  • Installer la documentation de la bibliothèque libpanel-applet2 donne une bonne aide (bien qu’incomplète) pour écrire des applets.
  • Voici un vieux tutorial pour réaliser des applets avec GTK1.0
    Le site des développer Gnome.
  • Version originale de ce tutorial: cliquez-ici.