3
\$\begingroup\$

The goal is to make a word generator. The program takes a word list (typically, it may be the full dictionary of a language, or a list of names), analyses it, and then invents word that sound alike the one in the list.
There is multiple parts of the program, which is embedded in a Qt GUI. I used Qt Creator 4.9.2 (based on Qt 5.12.4, GCC 5.3.1, 64 bits).

It is all mostly in french (variable names and UI texts), sorry about that. I commented in English, I hope it's enough.

About how the program works :
Analyzing the word means noting an occurrence for each succession of character present in this word. If a succession of characters appear in multiple words, multiple occurrences are noted and hence it is more probable to generate this succession.
In the fonction.h, analysis is made by 'analyseWord' (but it only handles ASCII characters), or by another version 'QanalyseWord' using Qchar instead of char (hence any utf-8 character).
Depending on the type of analysis made, word generation is made by 'generateur' or 'Qgenerateur'

For the program itself, the main.cpp is only what's needed to launch the mainwindow ("FenetrePrincipale")

#include <QtWidgets>
#include "fenetreprincipale.h"
#include "fonctions.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    srand( static_cast<unsigned int>(time(NULL)));

    /*Traduction of Qt texts to Fr*/
    QString locale = QLocale::system().name().section('_', 0, 0);
    QTranslator translator;
    translator.load(QString("qt_") + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath));
    app.installTranslator(&translator);
    /*end*/

    FenetrePrincipale fenetre;
    fenetre.show();

    return app.exec();

}

FenetrePrincipale handles all the interaction of the program. It has two tabs (one for analysis, one for generating words). Its .ui file is :

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>FenetrePrincipale</class>
 <widget class="QMainWindow" name="FenetrePrincipale">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>702</width>
    <height>539</height>
   </rect>
  </property>
  <property name="font">
   <font>
    <pointsize>10</pointsize>
   </font>
  </property>
  <property name="windowTitle">
   <string>GeMots</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="0" column="1">
     <widget class="QTabWidget" name="tabWidget">
      <property name="currentIndex">
       <number>0</number>
      </property>
      <widget class="QWidget" name="onglet_analyse">
       <attribute name="title">
        <string>Analyse d'une liste de mots</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_4">
        <item row="6" column="0">
         <widget class="QProgressBar" name="progr_Analyse">
          <property name="sizePolicy">
           <sizepolicy hsizetype="Expanding" vsizetype="Minimum">
            <horstretch>0</horstretch>
            <verstretch>0</verstretch>
           </sizepolicy>
          </property>
          <property name="minimumSize">
           <size>
            <width>0</width>
            <height>15</height>
           </size>
          </property>
          <property name="value">
           <number>0</number>
          </property>
         </widget>
        </item>
        <item row="3" column="0">
         <spacer name="verticalSpacer_2">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="4" column="0">
         <widget class="QPushButton" name="bouton_analyser">
          <property name="font">
           <font>
            <pointsize>12</pointsize>
           </font>
          </property>
          <property name="text">
           <string>Lancer l'analyse</string>
          </property>
         </widget>
        </item>
        <item row="2" column="0">
         <widget class="QFrame" name="frame_mode">
          <property name="frameShape">
           <enum>QFrame::Panel</enum>
          </property>
          <property name="frameShadow">
           <enum>QFrame::Sunken</enum>
          </property>
          <layout class="QGridLayout" name="gridLayout_2">
           <item row="3" column="1" colspan="2">
            <widget class="QRadioButton" name="radio_speciaux">
             <property name="toolTip">
              <string>C'est beau l'utf-8, mais c'est chiant à utiliser !</string>
             </property>
             <property name="text">
              <string>Gère tout les caractères spéciaux</string>
             </property>
             <property name="checked">
              <bool>true</bool>
             </property>
            </widget>
           </item>
           <item row="0" column="4" rowspan="4">
            <widget class="Line" name="line_details">
             <property name="orientation">
              <enum>Qt::Vertical</enum>
             </property>
            </widget>
           </item>
           <item row="4" column="0">
            <widget class="QLabel" name="label_lcoh">
             <property name="text">
              <string>Longueur de cohérence :</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
             </property>
            </widget>
           </item>
           <item row="4" column="1">
            <widget class="QSpinBox" name="spin_lcoh">
             <property name="accelerated">
              <bool>true</bool>
             </property>
             <property name="suffix">
              <string> lettres</string>
             </property>
             <property name="minimum">
              <number>2</number>
             </property>
             <property name="value">
              <number>3</number>
             </property>
            </widget>
           </item>
           <item row="0" column="3">
            <widget class="QLabel" name="Qualite">
             <property name="font">
              <font>
               <weight>75</weight>
               <bold>true</bold>
              </font>
             </property>
             <property name="text">
              <string>Qualité</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="3" column="5">
            <widget class="QLabel" name="Bof">
             <property name="toolTip">
              <string>Pour ce que le programme fait (lire des suites de lettres. Noter le nombre d'occurence),
cette méthode est honteusement longue</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(223, 177, 19);</string>
             </property>
             <property name="text">
              <string>Bof</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="1" column="5">
            <widget class="QLabel" name="Parfait_2">
             <property name="toolTip">
              <string>C'est presque instantanné. Normal, y a vraiment pas besoin d'un supercalculateur pour faire ça</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(44, 161, 40);</string>
             </property>
             <property name="text">
              <string>Parfait</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="2" column="3">
            <widget class="QLabel" name="Bof_2">
             <property name="toolTip">
              <string>On génère pas des mots accentués. Et en plus, ça gère pas les tirets</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(223, 177, 19);</string>
             </property>
             <property name="text">
              <string>Bof</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="1" column="3">
            <widget class="QLabel" name="Minable">
             <property name="toolTip">
              <string>J'voulais pas faire un truc pour les anglais, moi !</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(255, 0, 0);</string>
             </property>
             <property name="text">
              <string>Minable</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="0" column="5">
            <widget class="QLabel" name="Rapidite">
             <property name="font">
              <font>
               <weight>75</weight>
               <bold>true</bold>
              </font>
             </property>
             <property name="text">
              <string>Rapidité</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="3" column="3">
            <widget class="QLabel" name="Parfait">
             <property name="toolTip">
              <string>Testez avec le Japonnais ! Promis, ça marche !</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(44, 161, 40);</string>
             </property>
             <property name="text">
              <string>Parfait</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="2" column="5">
            <widget class="QLabel" name="Minable_2">
             <property name="toolTip">
              <string>En même temps, c'est codé avec les pieds</string>
             </property>
             <property name="styleSheet">
              <string notr="true">color: rgb(255, 0, 0);</string>
             </property>
             <property name="text">
              <string>Minable</string>
             </property>
             <property name="alignment">
              <set>Qt::AlignCenter</set>
             </property>
            </widget>
           </item>
           <item row="1" column="1" colspan="2">
            <widget class="QRadioButton" name="radio_ignore">
             <property name="toolTip">
              <string>Les lettres accentuées sont supprimée par l'analyse : on regarde le mot &quot;ttt&quot; au lieu de &quot;étêtât&quot;
Il vaut donc mieux que la liste de mots originale soit sans accents</string>
             </property>
             <property name="text">
              <string>Ignore les accents</string>
             </property>
            </widget>
           </item>
           <item row="2" column="1" colspan="2">
            <widget class="QRadioButton" name="radio_minable">
             <property name="toolTip">
              <string>Ces caractères spéciaux sont remplacé par leur valeur non-accentué :
ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÌÍÎÏìíîïÙÚÛÜùúûüÿÑñÇç
Mais ce traitement est long et nul ! Oui, mais je me suis fait chier à le coder, je vais pas le supprimer...</string>
             </property>
             <property name="text">
              <string>Gère très mal les accents</string>
             </property>
            </widget>
           </item>
           <item row="4" column="5">
            <widget class="QPushButton" name="bouton_details">
             <property name="font">
              <font>
               <pointsize>8</pointsize>
              </font>
             </property>
             <property name="text">
              <string>(+ détails)</string>
             </property>
             <property name="flat">
              <bool>true</bool>
             </property>
            </widget>
           </item>
           <item row="0" column="0">
            <widget class="QLabel" name="label_mode">
             <property name="text">
              <string>Mode de traitement :</string>
             </property>
            </widget>
           </item>
           <item row="4" column="2" colspan="3">
            <spacer name="horizontalSpacer">
             <property name="orientation">
              <enum>Qt::Horizontal</enum>
             </property>
             <property name="sizeHint" stdset="0">
              <size>
               <width>40</width>
               <height>20</height>
              </size>
             </property>
            </spacer>
           </item>
          </layout>
         </widget>
        </item>
        <item row="5" column="0">
         <spacer name="verticalSpacer_3">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="1" column="0">
         <spacer name="verticalSpacer">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="9" column="0">
         <spacer name="verticalSpacer_5">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="0" column="0">
         <widget class="QFrame" name="frame_choixListe">
          <property name="frameShape">
           <enum>QFrame::Panel</enum>
          </property>
          <property name="frameShadow">
           <enum>QFrame::Sunken</enum>
          </property>
          <layout class="QGridLayout" name="gridLayout_3">
           <item row="1" column="0" colspan="2">
            <widget class="QRadioButton" name="radio_langDef">
             <property name="text">
              <string>Utiliser la langue par défaut (////Nom liste défaut////)</string>
             </property>
             <property name="checked">
              <bool>true</bool>
             </property>
            </widget>
           </item>
           <item row="2" column="0">
            <widget class="QRadioButton" name="radio_langPerso">
             <property name="text">
              <string>Choisir une liste de mot</string>
             </property>
            </widget>
           </item>
           <item row="0" column="0" colspan="2">
            <widget class="QLabel" name="label_choixLang">
             <property name="text">
              <string>Choix de la liste de mots à analyser :</string>
             </property>
            </widget>
           </item>
           <item row="2" column="1">
            <widget class="QPushButton" name="bouton_selecFichier">
             <property name="text">
              <string>(Aucun)</string>
             </property>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
       </layout>
      </widget>
      <widget class="QWidget" name="onglet_generation">
       <property name="enabled">
        <bool>false</bool>
       </property>
       <attribute name="title">
        <string>Génération de mots</string>
       </attribute>
       <layout class="QGridLayout" name="gridLayout_5">
        <item row="4" column="1">
         <spacer name="verticalSpacer_6">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="2" column="0">
         <widget class="QLabel" name="label_tailleMots">
          <property name="text">
           <string>Taille max des mots</string>
          </property>
         </widget>
        </item>
        <item row="9" column="2">
         <spacer name="horizontalSpacer_3">
          <property name="orientation">
           <enum>Qt::Horizontal</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>40</width>
            <height>20</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="10" column="2">
         <widget class="QPushButton" name="bouton_trier">
          <property name="text">
           <string>Trier par taille</string>
          </property>
         </widget>
        </item>
        <item row="11" column="2">
         <widget class="QPushButton" name="bouton_nettoyer">
          <property name="text">
           <string>Nettoyer</string>
          </property>
         </widget>
        </item>
        <item row="5" column="0" colspan="2">
         <widget class="QCheckBox" name="check_forcerTaille">
          <property name="layoutDirection">
           <enum>Qt::RightToLeft</enum>
          </property>
          <property name="text">
           <string>Forcer la taille des mots</string>
          </property>
         </widget>
        </item>
        <item row="2" column="1">
         <widget class="QSpinBox" name="spin_tailleMax">
          <property name="suffix">
           <string> lettres</string>
          </property>
          <property name="minimum">
           <number>2</number>
          </property>
          <property name="singleStep">
           <number>2</number>
          </property>
          <property name="value">
           <number>20</number>
          </property>
         </widget>
        </item>
        <item row="0" column="3" rowspan="13">
         <widget class="QTextEdit" name="text_mots"/>
        </item>
        <item row="1" column="1">
         <widget class="QSpinBox" name="spin_nbMots">
          <property name="minimum">
           <number>1</number>
          </property>
          <property name="value">
           <number>1</number>
          </property>
         </widget>
        </item>
        <item row="6" column="0" colspan="2">
         <widget class="QCheckBox" name="check_troll">
          <property name="layoutDirection">
           <enum>Qt::RightToLeft</enum>
          </property>
          <property name="text">
           <string>Génération de mots parfaits</string>
          </property>
         </widget>
        </item>
        <item row="1" column="0">
         <widget class="QLabel" name="label_nbMots">
          <property name="text">
           <string>Nombre de mots</string>
          </property>
         </widget>
        </item>
        <item row="10" column="0" rowspan="2" colspan="2">
         <widget class="QFrame" name="fram_resume">
          <property name="frameShape">
           <enum>QFrame::Box</enum>
          </property>
          <property name="frameShadow">
           <enum>QFrame::Sunken</enum>
          </property>
          <layout class="QVBoxLayout" name="verticalLayout">
           <item>
            <widget class="QLabel" name="label_resume_analyse">
             <property name="text">
              <string>Type d'analyse : //type//</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QLabel" name="label_resume_lcoh">
             <property name="text">
              <string>Longueur de cohérence : //lcoh//</string>
             </property>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item row="3" column="0" colspan="3">
         <widget class="QPushButton" name="bouton_generer">
          <property name="font">
           <font>
            <pointsize>12</pointsize>
           </font>
          </property>
          <property name="text">
           <string>Inventer des mots</string>
          </property>
         </widget>
        </item>
        <item row="8" column="1">
         <spacer name="verticalSpacer_7">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="7" column="1">
         <spacer name="verticalSpacer_8">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
        <item row="9" column="0">
         <widget class="QLabel" name="label">
          <property name="text">
           <string>Analyse utilisée :</string>
          </property>
          <property name="alignment">
           <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
          </property>
         </widget>
        </item>
        <item row="9" column="1">
         <spacer name="verticalSpacer_4">
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>20</width>
            <height>40</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>702</width>
     <height>23</height>
    </rect>
   </property>
   <widget class="QMenu" name="menuMenu">
    <property name="title">
     <string>Menu</string>
    </property>
    <widget class="QMenu" name="menuQuitter">
     <property name="title">
      <string>Quitter</string>
     </property>
     <widget class="QMenu" name="menuQuitter_2">
      <property name="title">
       <string>Quitter</string>
      </property>
      <addaction name="actionNe_pas_quitter"/>
      <addaction name="actionNe_pas_quitter_2"/>
      <addaction name="actionNe_pas_quitter_3"/>
      <addaction name="actionQuitter_3"/>
      <addaction name="actionNe_pas_quitter_4"/>
     </widget>
     <addaction name="menuQuitter_2"/>
    </widget>
    <addaction name="separator"/>
    <addaction name="actionUtiliser_les_valeur_actuelles_par_defaut"/>
    <addaction name="separator"/>
    <addaction name="menuQuitter"/>
   </widget>
   <widget class="QMenu" name="menuA_propos">
    <property name="title">
     <string>A propos</string>
    </property>
    <addaction name="actionAide"/>
    <addaction name="action_propos_de_ce_programme"/>
   </widget>
   <addaction name="menuMenu"/>
   <addaction name="menuA_propos"/>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
  <action name="actionQuitter_3">
   <property name="text">
    <string>Ha oui, quitter</string>
   </property>
  </action>
  <action name="actionNe_pas_quitter">
   <property name="text">
    <string>Ne pas quitter</string>
   </property>
  </action>
  <action name="actionNe_pas_quitter_2">
   <property name="text">
    <string>Ne pas quitter</string>
   </property>
  </action>
  <action name="actionNe_pas_quitter_3">
   <property name="text">
    <string>Ne pas quitter</string>
   </property>
  </action>
  <action name="actionNe_pas_quitter_4">
   <property name="text">
    <string>Ne pas quitter</string>
   </property>
  </action>
  <action name="actionUtiliser_les_valeur_actuelles_par_defaut">
   <property name="text">
    <string>Changer les valeurs par défaut</string>
   </property>
  </action>
  <action name="actionCharger_automatiquement_la_liste_de_mot_par_d_faut">
   <property name="checkable">
    <bool>true</bool>
   </property>
   <property name="text">
    <string>Charger la liste par défaut au lancement</string>
   </property>
  </action>
  <action name="actionAnalyser_la_liste_par_d_faut_au_lancement">
   <property name="checkable">
    <bool>true</bool>
   </property>
   <property name="text">
    <string>Analyser la liste par défaut au lancement</string>
   </property>
  </action>
  <action name="actionAide">
   <property name="text">
    <string>Aide</string>
   </property>
  </action>
  <action name="action_propos_de_ce_programme">
   <property name="text">
    <string>À propos de ce programme</string>
   </property>
  </action>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>radio_speciaux</sender>
   <signal>toggled(bool)</signal>
   <receiver>spin_lcoh</receiver>
   <slot>setEnabled(bool)</slot>
   <hints>
    <hint type="sourcelabel">
     <x>318</x>
     <y>320</y>
    </hint>
    <hint type="destinationlabel">
     <x>255</x>
     <y>351</y>
    </hint>
   </hints>
  </connection>
 </connections>
</ui>

then, the .h file :

#ifndef FENETREPRINCIPALE_H
#define FENETREPRINCIPALE_H

#include <QMainWindow>
#include <QProgressBar>
#include "fonctions.h"
#include "fenaide.h"
#include "infos.h"

namespace Ui {
class FenetrePrincipale; //=Main Window
}

class FenetrePrincipale : public QMainWindow
{
    Q_OBJECT

public:
    explicit FenetrePrincipale(QWidget *parent = 0);
    ~FenetrePrincipale();

    enum type_Trait {aucun, ascii, asciiplus, utf_8};
    type_Trait selectedTrait();
    type_Trait stringToType(QString stringType);

public slots:
    //Analyses a wordlist
    void on_bouton_analyser_clicked();
    //Generates a word based on previously analysed wordlist
    void on_bouton_generer_clicked();

    //Choose path to the wordlist
    void on_bouton_selecFichier_clicked();
    //Adds detail on the 3 possible analyses
    void on_bouton_details_clicked();

    //Removes all generated words
    void on_bouton_nettoyer_clicked();
    //Sort generated words by lenght
    void on_bouton_trier_clicked();

    //Menu icon 'Aide'
    void on_actionAide_triggered();
    //Menu icon 'About'
    void on_action_propos_de_ce_programme_triggered();
    //Change the default values of the Analyse tab to the current values.
    void on_actionUtiliser_les_valeur_actuelles_par_defaut_triggered();

    // Just a test function : unckecks the box 0.4 second after it has been clicked
    void on_check_troll_clicked();
    void unchecking();

    //Message when quitting through menu
    void quit_troll();

private slots:
    //Indicates if the current parameters of the 'Analyse tab' have been modified since previous analisys
    void check_analyse_changed();
    //Checks if the current coherence lengh have been modified since previous analisys
    void on_spin_lcoh_valueChanged(int value);
    //Checks if the current analysis method have been modified since previous analisys
    void traitement_modifie();
    //Checks if the current wordlist have been modified since previous analisys
    void liste_modifie();

private:
    Ui::FenetrePrincipale *ui;

//    QString nomListeMotsDefaut;
    QString nomListeMotsDefaut="WordLists/Mots_FR_full.txt";
    double probatab[27][27][27] = {{{0}}}; //result of the analysis of the wordlist (for 'ascii' and 'ascii+')
    std::map<std::vector<QChar>, std::pair<int,double>> charmap; //result of the analysis (for 'utf-8')

    //Parameter of the previous analysis:
    QString nomListeMots; //name of the wordlist
    type_Trait analyse = aucun; //indicates what analysis has previously been done. Acun = nothing
    uint lcoh; //coherence lengh of previous analyse

    //Indicates if a change has been made on the 'Analysis' tab
    bool listeMots_changed=false;
    bool traitement_changed=false;
    bool lcoh_changed=false;
    QString nomListeAnalysePrecedente;

    FenAide *m_FenAide;
    Infos *m_Infos;
};

#endif // FENETREPRINCIPALE_H

and the .cpp file

#include <QFileDialog>
#include <QProgressBar>
#include <QTimer>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <QMessageBox>
#include <QShortcut>
#include <thread>
#include "fenetreprincipale.h"
#include "ui_fenetreprincipale.h"

FenetrePrincipale::FenetrePrincipale(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::FenetrePrincipale)
{
    ui->setupUi(this);

    //Indication des tooltip longs
    QString ListeMotsInstruction("La liste de mot doit être au format .txt, avec 1 seul mot par ligne\n"
                                 "Si plusieurs mots par lignes, seul le premier est pris en compte");
        ui->radio_langPerso->setToolTip(ListeMotsInstruction);
        ui->bouton_selecFichier->setToolTip(ui->radio_langPerso->toolTip());

    //Adaptation de la progressbar
    ui->progr_Analyse->setAlignment(Qt::AlignCenter);

    //Éléments de la statusbar
    ui->statusbar->setFixedHeight(20);
    ui->statusbar->showMessage("En attente de lancement...");

    //Lecture du fichier des valeurs par défaut:
    QFile file("WordLists/Parametres.xml");
    if(file.open(QIODevice::ReadOnly)) {
        QXmlStreamReader xmlReader(&file);
        if(xmlReader.readNextStartElement()) {
            if (xmlReader.name() == "valeursDef") {
                while(xmlReader.readNextStartElement()) {
                    if (xmlReader.name() == "nomListe")
                        nomListeMotsDefaut = xmlReader.readElementText();
                    else if (xmlReader.name() == "modeTraitement") {
                        type_Trait wantedAnalyse = stringToType(xmlReader.readElementText());
                        if(wantedAnalyse==ascii)
                            ui->radio_ignore->setChecked(true);
                        else if(wantedAnalyse==asciiplus)
                            ui->radio_minable->setChecked(true);
                        else if(wantedAnalyse==utf_8)
                            ui->radio_speciaux->setChecked(true);
                    }
                    else if (xmlReader.name() == "lcoh")
                        ui->spin_lcoh->setValue(xmlReader.readElementText().toInt());
                    else if (xmlReader.name() == "nbMots")
                        ui->spin_nbMots->setValue(xmlReader.readElementText().toInt());
                    else if (xmlReader.name() == "tailleMax")
                        ui->spin_tailleMax->setValue(xmlReader.readElementText().toInt());
                    else
                        xmlReader.skipCurrentElement();
                }
            }
            xmlReader.skipCurrentElement();
        }
    }
    else{
        ui->statusbar->showMessage("Problème lors de la lecture des valeurs par défaut");
    }

    //Indication de la liste par défaut
    QFileInfo fi(nomListeMotsDefaut);
    QString textLangDef = "Utiliser la langue par défaut ("+fi.fileName()+")";
    ui->radio_langDef->setText(textLangDef);

    //Efface tous les éléments affiché par le bouton "détail"
    ui->Bof->setVisible(false);
    ui->Bof_2->setVisible(false);
    ui->Minable->setVisible(false);
    ui->Minable_2->setVisible(false);
    ui->Parfait->setVisible(false);
    ui->Parfait_2->setVisible(false);
    ui->Qualite->setVisible(false);
    ui->Rapidite->setVisible(false);
    ui->line_details->setVisible(false);

    //Selectionne l'onglet de départ
    ui->tabWidget->setCurrentIndex(0);

    //Connexion signal/slot pour detection changement
    QObject::connect(ui->radio_ignore, SIGNAL(clicked()), this, SLOT(traitement_modifie()));
    QObject::connect(ui->radio_minable, SIGNAL(clicked()), this, SLOT(traitement_modifie()));
    QObject::connect(ui->radio_speciaux, SIGNAL(clicked()), this, SLOT(traitement_modifie()));

    QObject::connect(ui->radio_langDef, SIGNAL(clicked()), this, SLOT(liste_modifie()));
    QObject::connect(ui->radio_langPerso, SIGNAL(clicked()), this, SLOT(liste_modifie()));
    QObject::connect(ui->bouton_selecFichier, SIGNAL(clicked()), this, SLOT(liste_modifie()));

    //Raccourci clavier
    //new QShortcut(QKeySequence(Qt::Key_Return || Qt::Key_Enter), this, SLOT(on_bouton_generer_clicked()));
    //Quitter via le menu
    QObject::connect(ui->actionQuitter_3, SIGNAL(triggered()), this, SLOT(quit_troll()));
}

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

FenetrePrincipale::type_Trait FenetrePrincipale::selectedTrait() {
    if(ui->radio_ignore->isChecked())
        return ascii;
    else if(ui->radio_minable->isChecked())
        return asciiplus;
    else if(ui->radio_speciaux->isChecked())
        return utf_8;
    else
        return aucun;
}

FenetrePrincipale::type_Trait FenetrePrincipale::stringToType(QString stringType) {
    if (stringType == "ascii")
        return FenetrePrincipale::ascii;
    else if (stringType == "asciiplus")
        return FenetrePrincipale::asciiplus;
    else if (stringType == "utf_8")
        return FenetrePrincipale::utf_8;
    else
        return FenetrePrincipale::aucun;
}

void FenetrePrincipale::on_bouton_analyser_clicked() {
    ui->centralwidget->setCursor(Qt::WaitCursor);
    ui->onglet_analyse->setEnabled(false);
    ui->progr_Analyse->setValue(0); //inutile.
    ui->statusbar->setToolTip("");
    int avRecup(5), avAnal(90), avProbatab(5); //doit sommer a 100. Avancement (%) de chaque étape

    charmap.clear();

//Partie 1 : Récupération de la liste de mots
    ui->statusbar->showMessage("Récupération de la liste de mots...");
    //Choix liste mot (selon valeur radio)
    if (ui->radio_langDef->isChecked()) {
        nomListeMots = nomListeMotsDefaut;
    }
    else if (ui->radio_langPerso->isChecked()) {
        QFileInfo fi(nomListeMots);
        if (fi.fileName() != ui->bouton_selecFichier->text()) {
            ui->statusbar->showMessage("Problème sur le nom de la liste de mot. Veuillez reselectionner un liste de mot");
            ui->onglet_analyse->setEnabled(true);
            ui->centralwidget->unsetCursor();
            return;
        }
    } //vérification qu'on ne va pas refaire l'analyse par défaut
    else { //Trololol inutile
        ui->progr_Analyse->setValue(42);
        ui->statusbar->showMessage("How did you do that ? En plus maintenant, t'as un pointeur de merde :p");
        return; //Si aucun bouton coché : arrêter tout
    }
    //Importation liste mots
    std::vector<std::string> mots;
    std::vector<float> proba;
    std::ifstream Liste_mots(nomListeMots.toStdString(), std::ios::in);
    if(Liste_mots) {
        extractWords(Liste_mots, mots, proba);
        ui->progr_Analyse->setValue(avRecup);
    }
    else {
        ui->statusbar->showMessage("Impossible de lire la liste de mots (voir ici pour plus de détails)");
        ui->statusbar->setToolTip("La liste de mot suivante :\n"+nomListeMots+
                                  "\nn'a pas pu être lue. Vérifiez qu'elle existe bien et est une liste de mots au format .txt, 1 mot par ligne");
        ui->onglet_analyse->setEnabled(true);
        ui->centralwidget->unsetCursor();
        return;
    }

    ui->tabWidget->setTabIcon(0,QIcon()); //L'icone 'warning' nest retirée que si la première étape marche (pas de return dans les étape 2 et 3

//Partie 2 et 3 : Analyse liste mots / construction des proba
    ui->statusbar->showMessage("Analyse de la liste de mots...");
    int nb=0; //nombre de lettres traitées
//Partie 2 version A :
    if(ui->radio_speciaux->isChecked()) { //Traitement moderne
        lcoh = ui->spin_lcoh->value();
        for(uint i=0; i<mots.size(); i++) {
            QString qmot=QString::fromStdString(mots[i]);
            QanalyzeWord(qmot,charmap, lcoh, nb);
            if (i%500==0) { //Possibilité : 10000 (haché), 100000 (3 étapes)
                ui->progr_Analyse->setValue(avRecup+i/(float)mots.size()*avAnal);
                QCoreApplication::processEvents(); //permet l'actualisation du gui. Ralenti les calcul...
            }
        }

//Partie 3 vA : Création des proba de chaque enchaînement de lettre
        ui->statusbar->showMessage("Liste de mots analysée. Analyse de l'analyse en cours...");
        std::map<std::vector<QChar>, std::pair<int,double>>::iterator it = charmap.begin() , cePrDebut , cePrFin;
        //cePrDebut et cePrFin sont des itérateur indiquant le début et la fin de l'enchaînement de lettre examiné actuellement

        while(it!=charmap.end()) {
            //pr = sous vecteur de it->first de [0] #FFBF00à [lcoh-2] : contient lcoh-1 éléments
            //c'est l'enchainement de lettres précédente --> utilisé pour déterminer la proba de l'actuelle
            std::vector<QChar> pr(&it->first[0], &it->first[lcoh-1]);
            cePrDebut = it;
            int nbtlsuiv = 0; //nb tot de lettre suivant l'enchainement 'pr[0]pr[1]...'

            //1er parcours : compter occurence de chaque pr
            // /!\ à l'odre des condition du while : il ne faut pas appeler it si it=end() --> vérification end en premier
            while( (it!=charmap.end()) && (pr==std::vector<QChar>(&it->first[0], &it->first[lcoh-1])) ) {
                //tant que l'enchainement des lettres précédentes (défini dans pr) ne change pas, on additionne le nombre d'occurence
                nbtlsuiv += it->second.first; //it->second = la pair / .first -> le 'int'=nb d'occurence
                it++; //passage au membre suivant
            }
            cePrFin = it;

            //2e  parcours : diviser enchainement/occurence total du pr + additionner (pour proba cumul)
            //on repasse sur la partie déjà vue : de cePrDebut à cePrFin
            for(std::map<std::vector<QChar>, std::pair<int,double>>::iterator repasse = cePrDebut; repasse!=cePrFin && repasse!=charmap.end(); ++repasse) {
                repasse->second.second = (double)repasse->second.first / nbtlsuiv;
                if (repasse != cePrDebut && repasse!=charmap.begin() )
                    repasse->second.second += prev(repasse)->second.second; //prev() = élément précédent
            }
        }
//Traitement terminé. Indication lié au traitement utf_8
        analyse = utf_8;
        ui->check_forcerTaille->setEnabled(false);
        ui->check_forcerTaille->setToolTip("Un bug très (très) idiot empêche de forcer la taille des mots\n"
                                           "avec la méthode gérant tous les type de caractères\n"
                                           "Oui, désolé, j'ai eu la flemme de corriger ça");
        ui->label_resume_analyse->setText("Type d'analyse : tous caractères spéciaux");
        ui->label_resume_lcoh->setText("Longueur de cohérence : "+QString::number(lcoh));
    }
//Patie 2 version B :
    else { //Traitement à l'ancienne (radio Ignore et radio gère très mal
        int lettertab[27][27][27] = {{{0}}}; //--> lettertab[2][1][3] = nombre d'occurence de "cab" ("3","1","2")
        int nb=0; //nombre total de lettres traités
        bool clearAccent = ui->radio_minable->isChecked(); //ascii (false) ou asciiplus (true)
        for(unsigned int i=0; i<mots.size(); i++) {
            nb += analyzeWord(mots[i], lettertab, clearAccent);
            if (i%500==0) { //Possibilité : 10000 (haché), 100000 (3 étapes)
                ui->progr_Analyse->setValue(avRecup+i/(float)mots.size()*avAnal);
                QCoreApplication::processEvents(); //permet l'actualisation du gui. Ralenti les calculs...
            }
        }

//Partie 3 vB: Création des proba de chaque enchaînement de lettre
        ui->statusbar->showMessage("Liste de mots analysée. Analyse de l'analyse en cours...");
        for (uint k=0; k<27; k++) {
            for (uint j=0;j<27;j++) {
                int nbtlsuiv = 0; //nb tot de lettre suivant l'enchainement 'k-j'
                for (uint i=0;i<27;i++) {
                    nbtlsuiv += lettertab[i][j][k];
                }
                for (uint i=0;i<27;i++) {
                    probatab[i][j][k] =  (double)lettertab[i][j][k] / nbtlsuiv;
                    if(i!=0)
                        probatab[i][j][k] += probatab[i-1][j][k]; //Transformation en proba cumulative
                }
            }
        }
//Traitement terminé. Indication lié au traitement ascii
        if (clearAccent) {
            ui->label_resume_analyse->setText("Type d'analyse : accents désaccentés");
            analyse = asciiplus; }
        else {
            ui->label_resume_analyse->setText("Type d'analyse : sans accents");
            analyse = ascii; }
        //Autorise le "forcer_taille"
        ui->check_forcerTaille->setEnabled(true);
        ui->check_forcerTaille->setToolTip("");

        lcoh=0; //pas de lcoh ici. lcoh=0 <=> traitement ascii
        ui->label_resume_lcoh->setVisible(false); //cache la valeur lcoh
    }

//Traitement terminé. Indications générales
    ui->progr_Analyse->setValue(avRecup+avAnal+avProbatab-1);
    ui->progr_Analyse->setToolTip("Non, la barre ne va pas à 100%. C'est frustrant, hein ?");

    ui->onglet_generation->setEnabled(true);
    ui->onglet_analyse->setEnabled(true);
    ui->centralwidget->unsetCursor();


    //Supprime les warning de changement
    traitement_modifie();
    on_spin_lcoh_valueChanged(lcoh);
        QFile file(nomListeMots); QFileInfo fileinfo(file);
        nomListeAnalysePrecedente=fileinfo.fileName(); //récupération du nom de la liste de mots
    liste_modifie();

    ui->statusbar->showMessage("Analyse terminée ! Prêt a inventer des mots !");
    ui->tabWidget->setCurrentIndex(1);
}

void FenetrePrincipale::on_bouton_generer_clicked() {
    ui->check_troll->setChecked(false);
    if ((analyse==aucun)) {
        ui->statusbar->showMessage("Vous devez analyser une liste de mots avant de générer des mots");
        return; }

    uint taille_max = ui->spin_tailleMax->value();
    if(taille_max==0)
        taille_max=100;

    std::string mot;
    for (int i=0; i<ui->spin_nbMots->value(); i++) {
        if(analyse==utf_8)
            mot = Qgenerateur(charmap,lcoh, ui->check_forcerTaille->isChecked(), taille_max);
        if(analyse==ascii || analyse==asciiplus)
            mot = generateur(probatab, ui->check_forcerTaille->isChecked(), taille_max);
        ui->text_mots->append(QString::fromStdString(mot));
    }
}

void FenetrePrincipale::on_bouton_selecFichier_clicked() {
    nomListeMots = QFileDialog::getOpenFileName(this, "Selectionner une liste de mot .txt", "WordLists", "Fichier texte (*.txt)");
    QFile file(nomListeMots);
    QFileInfo fileinfo(file);
    liste_modifie();
    ui->bouton_selecFichier->setText(fileinfo.fileName());
    if(ui->bouton_selecFichier->text()!="")
        ui->radio_langPerso->setChecked(true);
    else
        ui->bouton_selecFichier->setText("(Aucun)");
}

void FenetrePrincipale::on_bouton_details_clicked() {
    ui->Bof->setVisible(!ui->Bof->isVisible());
    ui->Bof_2->setVisible(!ui->Bof_2->isVisible());
    ui->Minable->setVisible(!ui->Minable->isVisible());
    ui->Minable_2->setVisible(!ui->Minable_2->isVisible());
    ui->Parfait->setVisible(!ui->Parfait->isVisible());
    ui->Parfait_2->setVisible(!ui->Parfait_2->isVisible());
    ui->Qualite->setVisible(!ui->Qualite->isVisible());
    ui->Rapidite->setVisible(!ui->Rapidite->isVisible());
    ui->line_details->setVisible(!ui->line_details->isVisible());
}

void FenetrePrincipale::on_bouton_nettoyer_clicked() {
    ui->text_mots->clear();
}

void FenetrePrincipale::on_bouton_trier_clicked() {
    QString text = ui->text_mots->toPlainText();
    triParTaille(text);
    ui->text_mots->setText(text);
}

void FenetrePrincipale::on_actionAide_triggered() {
    m_FenAide = new FenAide();
    m_FenAide->show();
}

void FenetrePrincipale::on_action_propos_de_ce_programme_triggered() {
    m_Infos = new Infos();
    m_Infos->show();
}

void FenetrePrincipale::on_actionUtiliser_les_valeur_actuelles_par_defaut_triggered() {
    QMessageBox::StandardButton reply;
    reply = QMessageBox::question(this, "Changer valeurs par defaut",
                                  "Voulez vous changer les valeurs par défaut du programme ?\n"
                                  "\nLes valeurs par défaut (càd celle au lancement du programme) seront remplacées par les valeurs actuelles\n"
                                  "Sont concernés :\n"
                                  "    - La liste de mot\n"
                                  "    - La longueur de cohérence\n"
                                  "    - Le nombre de mot à générer\n"
                                  "    - La taille maximale des mots\n",
                                  QMessageBox::Yes|QMessageBox::No);
    //TODO : mieux que ça ! Heuresement qu'il n'y a que 5 paramètres...
    if (reply == QMessageBox::Yes) {
        //préparation pour écriture du chemin de la liste de mot
        QFile file("WordLists/Parametres.xml");
        if(file.open(QIODevice::WriteOnly)) {
            QXmlStreamWriter xmlWriter(&file);
            xmlWriter.writeStartElement("?xml version=\"1.0\" encoding=\"utf-8\"?");
            xmlWriter.writeCharacters("\n");
            xmlWriter.writeStartElement("valeursDef");
            xmlWriter.writeCharacters("\n\t");
                xmlWriter.writeStartElement("nomListe");
                    if(ui->radio_langPerso->isChecked())
                        xmlWriter.writeCharacters(nomListeMots);
                    else
                        xmlWriter.writeCharacters(nomListeMotsDefaut);
                xmlWriter.writeEndElement();
                xmlWriter.writeCharacters("\n");
                xmlWriter.writeComment("Le chemin peut être donné en absolu ou en relatif");
                xmlWriter.writeCharacters("\n\t");
                xmlWriter.writeStartElement("modeTraitement");
                    if(ui->radio_ignore->isChecked())
                        xmlWriter.writeCharacters("ascii");
                    else if(ui->radio_minable->isChecked())
                        xmlWriter.writeCharacters("asciiplus");
                    else if(ui->radio_speciaux->isChecked())
                        xmlWriter.writeCharacters("utf_8");
                    else
                        xmlWriter.writeCharacters("aucun");
                xmlWriter.writeEndElement();
                xmlWriter.writeCharacters("\n");
                xmlWriter.writeComment("Les valeurs possible du mode de traitement sont : aucun, ascii, asciiplus et utf_8");
                xmlWriter.writeCharacters("\n\t");
                xmlWriter.writeStartElement("lcoh");
                    xmlWriter.writeCharacters(QString::number(ui->spin_lcoh->value()));
                xmlWriter.writeEndElement();
                xmlWriter.writeCharacters("\n\t");
                xmlWriter.writeStartElement("nbMots");
                    xmlWriter.writeCharacters(QString::number(ui->spin_nbMots->value()));
                xmlWriter.writeEndElement();
                xmlWriter.writeCharacters("\n\t");
                xmlWriter.writeStartElement("tailleMax");
                    xmlWriter.writeCharacters(QString::number(ui->spin_tailleMax->value()));
                xmlWriter.writeEndElement();
                xmlWriter.writeCharacters("\n");
            xmlWriter.writeEndElement();
            xmlWriter.writeCharacters("\n");
        }
        else{
            ui->statusbar->showMessage("Problème lors de l'écriture des valeurs par défaut");
        }
    }
}

void FenetrePrincipale::on_check_troll_clicked() {
    QTimer::singleShot(80, this, SLOT(unchecking()));
}

void FenetrePrincipale::unchecking() {
    std::this_thread::sleep_for(std::chrono::milliseconds(400));
    ui->check_troll->setChecked(false);
}

void FenetrePrincipale::quit_troll() {
    QMessageBox::information(this, "Vraiment ?",
                             "Sinon, y avait plus simple pour quitter hein...\n"
                             "Pourquoi y a toujours un bouton du menu pour quitter ?\n"
                             "Qui l'utilse ?...");
    this->close();
}

void FenetrePrincipale::check_analyse_changed() {
    bool changed = listeMots_changed || traitement_changed || lcoh_changed;
    if (changed && analyse!=aucun) {
        QIcon p(":/icones/icons/warning.png");
        ui->tabWidget->setTabIcon(0,p);
        ui->statusbar->showMessage("Attention, vous devez refaire l'analyse pour que les changements soient pris en compte");
    }
    else {
        ui->tabWidget->setTabIcon(0,QIcon());
        ui->statusbar->showMessage("");
    }
}

void FenetrePrincipale::on_spin_lcoh_valueChanged(int value) {
    if( (analyse==type_Trait::utf_8) & ((uint)value!=lcoh) ) {
        ui->spin_lcoh->setStyleSheet("background-color: #FFBF00");
        lcoh_changed = true; }
    else {
        ui->spin_lcoh->setStyleSheet("");
        lcoh_changed = false; }
    check_analyse_changed();
}

void FenetrePrincipale::traitement_modifie() {
    if(analyse!=aucun) {
        if(selectedTrait()!=analyse) {
            traitement_changed=true;
            if(ui->radio_ignore->isChecked()) {
                ui->radio_ignore->setStyleSheet("background-color: #FFBF00");
                ui->radio_minable->setStyleSheet("");
                ui->radio_speciaux->setStyleSheet(""); }
            else if(ui->radio_minable->isChecked()) {
                ui->radio_ignore->setStyleSheet("");
                ui->radio_minable->setStyleSheet("background-color: #FFBF00");
                ui->radio_speciaux->setStyleSheet(""); }
            else if(ui->radio_speciaux->isChecked()) {
                ui->radio_ignore->setStyleSheet("");
                ui->radio_minable->setStyleSheet("");
                ui->radio_speciaux->setStyleSheet("background-color: #FFBF00"); }
        }
        else {
            traitement_changed=false;
            ui->radio_ignore->setStyleSheet("");
            ui->radio_minable->setStyleSheet("");
            ui->radio_speciaux->setStyleSheet("");
        }
    }
    check_analyse_changed();
}


void FenetrePrincipale::liste_modifie() {
    if(analyse!=aucun) {
        bool usedLangDef = (nomListeMots==nomListeMotsDefaut);

        if(ui->radio_langPerso->isChecked() && usedLangDef) {
            ui->radio_langDef->setStyleSheet("");
            ui->radio_langPerso->setStyleSheet("background-color: #FFBF00");
            listeMots_changed=true; }
        else if (ui->radio_langDef->isChecked() && usedLangDef) {
            ui->radio_langDef->setStyleSheet("");
            ui->radio_langPerso->setStyleSheet("");
            listeMots_changed=false; }
        else if(ui->radio_langDef->isChecked() && !usedLangDef) {
            ui->radio_langDef->setStyleSheet("background-color: #FFBF00");
            ui->radio_langPerso->setStyleSheet("");
            listeMots_changed=true; }
        else if(ui->bouton_selecFichier->text()!=nomListeAnalysePrecedente&& !usedLangDef) {
            ui->radio_langDef->setStyleSheet("");
            ui->radio_langPerso->setStyleSheet("background-color: #FFBF00");
            listeMots_changed=true; }
        else if(ui->radio_langPerso->isChecked() && !usedLangDef ) {
            ui->radio_langDef->setStyleSheet("");
            ui->radio_langPerso->setStyleSheet("");
            listeMots_changed=false; }
        else {
            ui->radio_langDef->setStyleSheet("");
            ui->radio_langPerso->setStyleSheet("");
            listeMots_changed=false; }
    }
    check_analyse_changed();
}

Addition: Default value are stored in an xml file and loaded in FenetrePrincipale constructor. This file path is "wordlist/Parametres.xml" (from executable folder).

<?xml version="1.0" encoding="utf-8"?>
<valeursDef>
    <nomListe>wordlist/wordlist.txt</nomListe>
<!--Le chemin peut être donné en absolu ou en relatif-->
    <modeTraitement>utf_8</modeTraitement>
<!--Les valeurs possible du mode de traitement sont : aucun, ascii, asciiplus et utf_8-->
    <lcoh>3</lcoh>
    <nbMots>1</nbMots>
    <tailleMax>20</tailleMax>
</valeursDef>

The word analysis/generation in itself is contained in the fonction.h/.cpp files : fonction.h

#ifndef FONCTIONS
#define FONCTIONS

#include <map>
#include <vector>
#include <fstream>
#include <QString>

//Get the words listed in the wordlist (text file)
void extractWords(std::ifstream &liste, std::vector<std::string> &mots, std::vector<float> &proba);

//Remove accentuated letters (for 'analyze word')
std::string retireAccent(std::string &message);

//analyse one word and completes lettertab (of "fenetreprincipale"). ClearAccent=true handles accentuated letter by replacing them by their unaccentuated version
int analyzeWord(std::string &lemot, int lettertab[27][27][27], bool ClearAccent=false);
//analyse on word and completes the charmap (of "fenetreprincipale")
void QanalyzeWord(const QString &lemot, std::map<std::vector<QChar>, std::pair<int,double>> &charmap, uint lcoh, int &nb);


//Generates a word based on the table 'probatab'
std::string generateur(double probatab[27][27][27], bool forcedSize=false, uint maxsize=100);
//Generates a word based on the character map (charmap)
std::string Qgenerateur(std::map<std::vector<QChar>, std::pair<int,double>> &charmap, uint lcoh=3, bool forcedSize=false, uint maxsize=100);


//triParTaille : sort word displayed by size
void triParTaille(QString &liste_mots);
    void Qechanger(QStringList &liste, int a, int b);
    void Qquicksort(QStringList &liste, int debutR, int fin);

#endif // FONCTIONS

fonction.cpp

#include <vector>
#include <sstream>
#include <iostream>
#include "fonctions.h"
#include "fenetreprincipale.h"

//Fonction commencant par Q = fonction pour le traitement gérant tout les type de
//caratère et la longueur de cohérence (Q pour le Q de QString, le moyen le plus
//simple de gérer l'utf-8 (j'aime pas les wchar_t. C'est nul et pas pratique)

using namespace std;

void extractWords(ifstream &liste, vector<string> &mots, vector<float> &proba) {
    int nb_lignes = 0; //number of lines on the wordlist
    string line; //last read line
    for (; getline(liste,line); nb_lignes++) {}; //Get number of lines

    liste.clear();
    liste.seekg(0, ios::beg); //going back to beginning of the file

    getline(liste, line);
    nb_lignes--; //Ignore first line
    //TODO : manage comments on the file, e.g. lines beginning by //

    mots.resize(nb_lignes);
    proba.resize(nb_lignes);
    for (int i=0; i<nb_lignes; i++) {
        getline(liste, line);

        string sproba;
        stringstream ss;
        ss << line;
        ss >> mots[i] >> sproba; // get 1st word = actual word and 2nd word = occurence probability
        if(sproba!="")
            proba[i]=stof(sproba); //conversion to float
        else  //if no word probability is indicated, set it at 0
            proba[i]=0;
    }
    liste.close();
    return;
}

string retireAccent(string &message)
{
    string accent("ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÌÍÎÏìíîïÙÚÛÜùúûüÿÑñÇç");
    string sansAccent("AAAAAAaaaaaaOOOOOOooooooEEEEeeeeIIIIiiiiUUUUuuuuyNnCc");
    int i=0,j=0,k=0,taille;
    taille=message.size();
    for (i=0;i<=taille;i++) {
        for(j=0;j<=104;j=j+2) {
            if((message[i]==accent[j])&&(message[i+1]==accent[j+1])) {
                message[i]=sansAccent[j/2];
                for(k=i+1;k<taille;k++) {
                    message[k]=message[k+1];
                }
                message=message.substr(0,taille-1);
                taille=message.size();
            }
        }
    }
    return message;
}

int analyzeWord(string &lemot, int lettertab[27][27][27], bool ClearAccent) {
    int pr1=0; //previous letter, default is nothing (0)
    int pr2=0; //second-to-last letter, default is nothing (0)
    int curr; //lettre actuelle
    int nb=0; //nombre total de lettre traitées
    for(unsigned int i=0; i<lemot.size(); i++) {
        if(ClearAccent==true)
            lemot=retireAccent(lemot);
        curr=lemot.at(i)-96; // returns ascii code for the letter
        if(curr>0 && curr <27) { //ignoring accentuated letter
            lettertab[curr][pr1][pr2]++;
            pr2=pr1;
            pr1=curr; //current letter is now previous letter
            nb++;
        }
    }
    lettertab[0][pr1][pr2]++; //indicates the word finishes by "...."pr2""pr1"
    return nb;
}

void QanalyzeWord(const QString &lemot, map<vector<QChar>, pair<int,double>> &charmap, uint lcoh, int &nb) {
    vector<QChar> suiteLettres(lcoh,'\0'); //vecteur de longueur lcoh, initialisé à \0

    for(int i=0; i<lemot.size(); i++) {
        suiteLettres[lcoh-1]=lemot.at(i); //lcoh = suiteLettres.size()
        charmap[suiteLettres].first++;
        nb++;
        for(uint j=0; j<lcoh-1; j++) {
            suiteLettres[j]=suiteLettres[j+1];
        }
    }
    //Indication de dernier charactère=vide
    suiteLettres[lcoh-1]='\0';
    charmap[suiteLettres].first++;
    return;
}

string generateur (double probatab[27][27][27], bool forcedSize, uint maxsize) {
    string monmot ="";
    int pr1=0; //lettre précédente
    int pr2=0; //avant-dernière lettre
    int pot; //lettre potentielle
    do {
        pot=0;
        double r = (double)rand() / RAND_MAX;
        while (r > probatab[pot][pr1][pr2] && pot<26) {
            pot++;
        }
        if (pot!=0){ //si pot=0 (eg. fin du mot) MAIS forcedSize, alors on ignore ce caractère
            monmot += (char)(pot+96);
            pr2=pr1;
            pr1=pot;
        }
    } while ((pot!=0 || forcedSize) && monmot.size()<=maxsize); //!\ size=taille en octet=nb carac en ascii seulement

    return monmot;
}

string Qgenerateur(std::map<std::vector<QChar>, pair<int,double>> &charmap, uint lcoh, bool forcedSize, uint maxsize) {
    QString monmot="";
    vector<QChar> cePr(lcoh-1,'\0');

    vector<QChar> cePrMin, cePrMax; //debut et fin des élément de la map ayant ce Pr
    cePrMin.reserve(lcoh); cePrMax.reserve(lcoh);
    map<vector<QChar>, pair<int,double>>::iterator it, itLow, itHigh;
    //itLow : itérateur vers le premier éléments de la map ayant ce Pr
    //itHigh: itérateur vers l'élément suivant le dernier éléments de la map ayant ce Pr

    do {
        cePrMin=cePr;   cePrMin.push_back(QChar::Null);
        cePrMax=cePr;   cePrMax.push_back(QChar::LastValidCodePoint);

        itLow = charmap.lower_bound(cePrMin);
        itHigh = charmap.upper_bound(cePrMax);
        it = itLow;

        double r = double(rand())/ RAND_MAX; // 0 < r < 1
        while (r > it->second.second && it != itHigh) { //places iterator to the 1st caracter having a probability less than r
            it++;
        }
        monmot += QString(it->first.back()); //append this caracter to the word

        for(uint i=0; i<cePr.size()-1; i++) { 
            cePr[i] = cePr[i+1];
        }
        cePr[cePr.size()-1] = it->first.back();
    } while ( (it->first.back()!='\0') && uint(monmot.size()) <= maxsize);
    //TODO : implémenter le forced size
    return monmot.toStdString();
}

void triParTaille(QString &liste_mots) {
    QStringList splitted(liste_mots.split('\n'));
    Qquicksort(splitted, 0, splitted.size()-1);
    liste_mots = splitted.join("\n");
}

void Qechanger(QStringList &liste, int a, int b) {
    //Fonction utilisée par le tri quicksort
    QString temp = liste.at(a);
    liste.replace(a, liste.at(b));
    liste.replace(b,temp);
}

void Qquicksort(QStringList &liste, int debut, int fin) {
    int gauche=debut-1;
    int droite=fin+1;
    const int pivot = liste.at(debut).size();

    if(debut>=fin)
        return;

    while(1) {
        do {droite--; } while(liste.at(droite).size() > pivot);
        do gauche++; while(liste.at(gauche).size() < pivot);

        if(gauche < droite)
            Qechanger(liste, gauche, droite);
        else break;
    }

    Qquicksort(liste, debut, droite);
    Qquicksort(liste, droite+1, fin);
}

Now only remain an 'help' window and a 'about' window. I don't show the .ui here due to number of caraters limitation : fenaide.h

#ifndef FENAIDE_H
#define FENAIDE_H

#include <QDialog>

namespace Ui {
class FenAide; //=Help window
}

class FenAide : public QDialog
{
    Q_OBJECT

public:
    explicit FenAide(QWidget *parent = 0);
    ~FenAide();

public slots:
    void on_bouton_moreInfo_clicked();

private:
    Ui::FenAide *ui;
};

#endif // FENAIDE_H

fenaide.cpp

#include "fenaide.h"
#include "ui_fenaide.h"

FenAide::FenAide(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FenAide)
{
    ui->setupUi(this);

    ui->text_details->setVisible(false);
    this->adjustSize();

#ifdef __linux__
    this->setFixedSize(this->width(),this->height());
#elif _WIN32
    //Taille de fenetre fixe. Windows seulement
    setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
#else

#endif
}

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

void FenAide::on_bouton_moreInfo_clicked() {
    ui->text_details->setVisible(!ui->text_details->isVisible());
    this->adjustSize();
}

infos.h

#ifndef INFOS_H
#define INFOS_H

#include <QDialog>

namespace Ui {
class Infos;
}

class Infos : public QDialog
{
    Q_OBJECT

public:
    explicit Infos(QWidget *parent = 0);
    ~Infos();

private:
    Ui::Infos *ui;
};

#endif // INFOS_H

infos.cpp

#include "infos.h"
#include "ui_infos.h"

Infos::Infos(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Infos)
{
    ui->setupUi(this);

// Make window fixed size
#ifdef __linux__
    this->setFixedSize(this->width(),this->height());
#elif _WIN32
    setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
#else

#endif
}

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

The .pro file if ever it is usefull

TEMPLATE = app
CONFIG += qt
CONFIG += c++11

QT += widgets

SOURCES += main.cpp \
    fonctions.cpp \
    fenaide.cpp \
    fenetreprincipale.cpp \
    infos.cpp

HEADERS += \
    fonctions.h \
    fenaide.h \
    fenetreprincipale.h \
    infos.h

DISTFILES +=

FORMS += \
    fenaide.ui \
    fenetreprincipale.ui \
    infos.ui

And finaly, the wordlist. It can be any .txt file like :

//First line is ignored
This
is
a
wordlist
platypus

Edit: In addition here are screenshots of the application.
On the analyze tab, default behaviour is pre-selected (trhough the xml file), so user just has to click 'launch alaysis' ('lancer l'analyse'). User can select another wordlist using the button (on the top) ; and chooses between analysis type (ascii only characters, ascii + few accentuated letters, or utf-8) Analyze tab On the generate word tab, user chooses how many words he wants to create, and sets up a limit to a maximum number of characters. Word generation tab

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Can you provide a screenshot of the app? \$\endgroup\$ – L. F. Aug 25 '19 at 11:48
2
\$\begingroup\$

That is quite a lot of code, so I won't do a full review, just give some remarks about obvious things.

Try to avoid as much platform-specific code as possible

I see this piece of code:

#ifdef __linux__
    this->setFixedSize(this->width(),this->height());
#elif _WIN32
    //Taille de fenetre fixe. Windows seulement
    setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint);
#else

#endif

Is it really that important to have the MSWindowsFixedSizeDialogHint flag set? Also, you don't actually fix the size of the window in this case. The Qt documentation also advises against using this flag, since it apparently doesn't behave nice in multi-monitor setups.

Even better would be to set a size constraint on the layout, as mentioned in the documentation for setFixedSize().

Accents change the sound of a character

string accent("ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÌÍÎÏìíîïÙÚÛÜùúûüÿÑñÇç");
string sansAccent("AAAAAAaaaaaaOOOOOOooooooEEEEeeeeIIIIiiiiUUUUuuuuyNnCc");

There it looks like you are just mapping every character to one which looks similar but without accents. However, in many languages, accents have a big impact on the sound, for example in German, "ä" sounds more like "eh" than "a". Even in French, "ç" sounds like "s" while where it is used, a "c" would have sounded like a "k".

Mixing languages in source code

You already apologized for using French variable and function names in the code. And from experience, I know many French developers prefer using French names in source code, so it's a culture thing that's probably hard to change. However, it has two drawbacks. First, it makes it harder to collaborate with non-French speaking persons on the same code. Second, since you are using C++ which uses English names for keywords, and the Qt library with is using English names as well, you are getting a horrible mix of languages in your code. For example:

else if(ui->radio_speciaux->isChecked())

But it even happens in variable and function names you completely declared yourself, like:

void FenetrePrincipale::on_bouton_generer_clicked() {

Use a consistent code style

I see both if (foo) and if(foo) in your code, sometimes there are spaces surrounding operators, sometimes not. Keeping a consistent code style makes it easier to navigate the code and to spot errors. The exact code style you use is a matter of taste, but it's best to use something that is in common use.

Avoid using new and delete

I see the following in the implementation of class Fenaide's constructor and destructor:

FenAide::FenAide(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::FenAide)

...

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

If you are always allocating memory for a variable in the constructor, and always freeing it in the destructor, you might just as well have made ui a regular member variable, but I guess in this case the full declaration of Ui::FenAide might be hidden. In that case, use a std::unique_ptr<> to hold the pointer, so it gets cleaned up automatically, even when something throws in the constructor:

class FenAide : public QDialog
{
    ...

private:
    std::unique_ptr<Ui::FenAide> ui;
};

With the above, you can get rid of the destructor of FenAide.

\$\endgroup\$
6
  • \$\begingroup\$ Ui::FenAide is the class that's generated by uic - you can't change that namespace, AFAIK. \$\endgroup\$ – Toby Speight Aug 26 '19 at 13:07
  • \$\begingroup\$ @TobySpeight Ah, well if that's because of the tools used then you can't do anything about it. Personally I avoid all these weird XML-to-C compilers. It's quite possible to write a Qt5 UI in pure C++. \$\endgroup\$ – G. Sliepen Aug 26 '19 at 15:41
  • \$\begingroup\$ Concerning the flag, on windows (7) with setFixedSize you get a resize cursor on the side of the window even if it has no effect (because size is fixed) ; so I found this workaround to delete this cursor. Regarding new, delete and Ui:: namespace: all this code is part of the default template of Qt creator, so I did not change it. Where could I find resources for using Qt with more modern style? \$\endgroup\$ – sayanel Aug 26 '19 at 16:12
  • \$\begingroup\$ About new and delete: if it's autogenerated code, then I guess there's not much you can or should do about it. It will still work. Still, it would be nice if Qt would use unique_ptr<> and that it automatically fixes the cursor issue when you set a fixed size... \$\endgroup\$ – G. Sliepen Aug 26 '19 at 20:35
  • \$\begingroup\$ The solution with unique_ptr you proposed work (apparently, I did test test it extensivly, nor would I know how to do that) \$\endgroup\$ – sayanel Aug 28 '19 at 15:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.