Import initial du projet shipheart_cm5

This commit is contained in:
2026-08-13 09:19:38 +02:00
commit 53e47729fb
533 changed files with 127946 additions and 0 deletions
+741
View File
@@ -0,0 +1,741 @@
//========================================================================================================================================================================
// CONFIG_PAGE_POINT.QML
// Ce fichier traite la configuration d'un point dans une page. La page permet notamment de :
// - Modifier la config du point (ou de le créer la première fois),
// - Supprimer le point
// La page est appelée depuis la page config_page_edition.qml, en cliquant sur le point à modifier (ou sur le '+' en vue d'une création)
//========================================================================================================================================================================
import QtQuick 2.12
import QtQml.Models
import qt.koriolan.shipheart 1.0
import"fonctions.js" as Fonctions
Shipheart_page {
id: page_config_page_point
// Mise en forme - Largeur :
readonly property double pourcentage_largeur_colonne_gauche: 0.75
readonly property double espace_colonnes_principales: 20
readonly property double pourcentage_largeur_labels_formulaire: 0.5
readonly property double pourcentage_largeur_unites_formulaire: 0.1
readonly property int largeur_bouton_suite: 15
readonly property double pourcentage_bandeau_bas_largeur_zone_retour: 1
readonly property int nombre_widgets_heure_par_ligne: 12
// Mise en forme - Hauteur :
readonly property double pourcentage_hauteur_ligne_haut: 1
readonly property int hauteur_ligne_formulaire: 60
readonly property int hauteur_bouton_droit: 80
readonly property int espace_lignes_principales: 20
readonly property double affichage_hauteur_utile: shipheart_hauteur_page - shipheart_hauteur_bandeau_haut - shipheart_hauteur_bandeau_bas
// Timings :
readonly property int periode_init_raffraichissement_parametres: 1 // En ms
readonly property int periode_raffraichissement_parametres: 1000 // En ms
readonly property int step_timer_appui_long: 30 // en ms
// Affichage :
property bool flag_init: true // Permet de bloquer certains affichage tant que l'init n'est pas terminée
// Données :
property int valeur_timer_appui_long: 0
property bool flag_point_existant: false
property int type_source: 0
property int id_source: -1
property int type_point: 0
property int taille_point: 25
// Modèles :
// Note : les modèles chargés dans 'main.qml' n'ont pas à être rechargés dans les pages suivantes
//-------------------------------------------------
// Signaux :
//-------------------------------------------------
signal enregistrer_config_page_point(bool new_flag_point_existant, int new_type_source, int new_id_source, int type_point, int taille_point) // Valeurs à lire lorsque le "OnEnregistrer' est activé
//===========================================================
// Timer periode raffraichissement parametres :
//===========================================================
Timer {
id: timer_raffraichissement_parametres
interval: periode_init_raffraichissement_parametres
triggeredOnStart: false
running: true
repeat: true
onTriggered: {
if (interval == periode_init_raffraichissement_parametres) { // Premier passage, 1ms après le premier chargement
interval = periode_raffraichissement_parametres;
init_page();
}
else {
}
}
}
//===========================================================
// Timer pour gérer les appuis longs :
//===========================================================
Timer {
id: timer_appui_long
interval: step_timer_appui_long
triggeredOnStart: false
repeat: true
onTriggered: {
if (valeur_timer_appui_long < shipheart_mouse_press_and_hold_interval_tres_long) valeur_timer_appui_long+= step_timer_appui_long;
else // Le fait de ne pas utilise le OnPressandHold permet d'être sût que la sortie se fait au même moment que la fin du comptage (sinon, on peut avoir de petits décalages)
{
timer_appui_long.running = false;
valeur_timer_appui_long = 0;
if (mousearea_bouton_supprimer.pressed)
{
shipheart_clic();
page_config_page_point.enregistrer_config_page_point(false, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
}
}
}
// Titre de la page :
Shipheart_titre_1 {titre: (type_source === -1) ? qsTr("Création d'un point") : qsTr("Configuration du point"); hauteur: shipheart_hauteur_bandeau_haut}
//-------------------------------------------------------
// Ecran Haut - Gauche :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_haut_gauche
x: 0
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: (affichage_hauteur_utile * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
ListView {
id: grille_principale
width: parent.width
height: parent.height
// snapMode: GridView.SnapToRow // L'arret de l'affichage se fera sur une rangée entière.
clip: true // Nécessaire pour que les Delegates s'arretent à l'intérieur de la GridView
model: modele_grille_principale
footer: Rectangle { // Permet de dégager la place suffisante en fin de GridView pour accéder même au dernier élément lorsque que clavier est déplié
height: affichage_hauteur_utile - hauteur_ligne_formulaire // Cette valeur permet de bien remonter les drniers champs (utile dans le cas de Textarea notamment) en laissant toujours une hauteur de lugne de formulaire au moins (évite qu'on "perde" l'affichage qui serait trop décalé vers le haut)
color: "transparent"
}
ObjectModel {
id: modele_grille_principale
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type_source
Rectangle {
id: ligne_type_source
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
Shipheart_formulaire_label {
text: qsTr("Type de la source")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Type_source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Type_source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
if (type_source !== champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur)
{
type_source = champ_formulaire_Type_source.model.get(champ_formulaire_Type_source.currentIndex).valeur;
id_source = -1;
type_point = 0;
if (type_source > 0) mise_a_jour_source();
}
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : ID_Source
Rectangle {
id: ligne_source
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
Shipheart_formulaire_label {
text: qsTr("Source")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: (champ_formulaire_Source.currentIndex !== 0)
? shipheart_formulaire_couleur_texte_par_defaut
: shipheart_formulaire_couleur_texte_en_erreur
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Source
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
id_source = champ_formulaire_Source.model.get(champ_formulaire_Source.currentIndex).valeur;
mise_a_jour_type_point();
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Type_point
Rectangle {
id: ligne_type_point
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
&& (id_source !== -1)
&& (type_source !== shipheart_type_source_modules)
// && (champ_formulaire_Type_point.model.count > 1)
Shipheart_formulaire_label {
text: qsTr("Type de point")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: shipheart_formulaire_couleur_texte_par_defaut
}
Shipheart_formulaire_combobox_icones {
id: champ_formulaire_Type_point
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
type_point = champ_formulaire_Type_point.model.get(champ_formulaire_Type_point.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
//-------------------------------------------------------------------------------
// Ligne de formulaire : Taille_point
Rectangle {
id: ligne_taille_point
width: (shipheart_largeur_page * pourcentage_largeur_colonne_gauche) - (espace_colonnes_principales / 2)
height: visible ? hauteur_ligne_formulaire : 0
color: "transparent"
visible: (type_source !== 0)
&& (id_source !== -1)
// && (champ_formulaire_Taille_point.model.count > 1)
Shipheart_formulaire_label {
text: qsTr("Taille du point")
width: parent.width * pourcentage_largeur_labels_formulaire
couleur: shipheart_formulaire_couleur_texte_par_defaut
}
Shipheart_formulaire_combobox {
id: champ_formulaire_Taille_point
model: ListModel { }
width: parent.width * (1 - pourcentage_largeur_labels_formulaire)
onActivated: {
shipheart_clic();
taille_point = champ_formulaire_Taille_point.model.get(champ_formulaire_Taille_point.currentIndex).valeur;
}
}
Shipheart_formulaire_ligne_separation {
haut_bas_hautbas: shipheart_formulaire_ligne_separation_basse
}
}
} // Fin de Grille principale
}
}
//-------------------------------------------------------
// Ecran Droite :
//-------------------------------------------------------
Rectangle {
id: zone_affichage_droite
x: zone_affichage_haut_gauche.width + espace_colonnes_principales
y: shipheart_hauteur_bandeau_haut;
width: (shipheart_largeur_page * (1 - pourcentage_largeur_colonne_gauche)) - (espace_colonnes_principales / 2)
height: (affichage_hauteur_utile * pourcentage_hauteur_ligne_haut) - (espace_lignes_principales / 2);
color: "transparent"
Column {
anchors.fill: parent
spacing: 10
//.......................................................
// Bouton Enregistrer/créer :
//.......................................................
Rectangle {
id: bouton_enregistrer_creer
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: (champ_formulaire_Type_source.currentIndex !== 0)
&& (champ_formulaire_Source.currentIndex !== 0)
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: flag_point_existant ? qsTr("Enregistrer") : qsTr("Créer le point")
color: couleur_blanc_casse
}
MouseArea {
anchors.fill: parent
onClicked: {
shipheart_clic();
page_config_page_point.enregistrer_config_page_point(true, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
}
}
//.......................................................
// Bouton de suppression
//.......................................................
Rectangle {
width: parent.width
height: hauteur_bouton_droit
color: shipheart_couleur_fond_bouton
radius: shipheart_rayon_bouton
visible: flag_point_existant
Rectangle { // Barre grandissante pour montrer durée d'appui vs tempo nécessaire :
width: (valeur_timer_appui_long / shipheart_mouse_press_and_hold_interval_tres_long) * parent.width
height: parent.height
color: couleur_rouge_transparent
radius: shipheart_rayon_bouton
visible: mousearea_bouton_supprimer.pressed
}
Text {
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
font.pixelSize: 20
text: qsTr("Supprimer")
color: couleur_blanc_casse
}
MouseArea {
id: mousearea_bouton_supprimer
anchors.fill: parent
onPressed: { // Déclenché lors de l'appui inital
timer_appui_long.running = true;
valeur_timer_appui_long = 0;
shipheart_clic();
}
onClicked: { // Déclenché si l'appui n'a pas été suffisement long pour avoir un Press&Hold :
timer_appui_long.running = false;
valeur_timer_appui_long = 0;
}
onExited: { // Déclenché si le doigt sort de la zone du bouton (sinon, on continue à compter alors qu'on est sorti de la zone et même relaché le doigt !!)
timer_appui_long.running = false;
valeur_timer_appui_long = 0;
}
}
}
//.......................................................
// Timer de reconfiguration :
//.......................................................
Timer {
id: timer_tempo_reconfiguration
interval: 250 // Cette tempo n'est utilisée que pour le premier cycle
triggeredOnStart: false
running: false
repeat: true
onTriggered: {
if (bdd.check_fin_configuration())
{
timer_tempo_reconfiguration.stop();
page_config_page_point.enregistrer_config_page_point(flag_point_existant, type_source, id_source, type_point, taille_point);
stackView.pop(); // Retour à la page d'édition de la page
}
else timer_tempo_reconfiguration.interval = 100 // Tempo plus courte pour les lectures suivantes
}
}
}
}
//-------------------------------------------------------
// Bandeau bas :
//-------------------------------------------------------
Row {
x: 0
y: shipheart_hauteur_page - shipheart_hauteur_bandeau_bas;
width: shipheart_largeur_page;
height: shipheart_hauteur_bandeau_bas;
spacing: 5
padding: 5
//.................................................
// Bouton retour
//.................................................
Rectangle {
id: bandeau_bas_zone_retour
width: (parent.width * pourcentage_bandeau_bas_largeur_zone_retour) - (2 * parent.padding)
height: shipheart_hauteur_bandeau_bas - parent.topPadding - parent.bottomPadding;
radius: shipheart_rayon_bouton
color: shipheart_couleur_fond_bouton
Image {
source: "/images/icone_retour.png"
sourceSize.height: 30
anchors {
verticalCenter: parent.verticalCenter
horizontalCenter: parent.horizontalCenter
}
}
MouseArea { // Zone d'interaction. On est obligé de la mettre dans la grille pour que Rectangle et MouseArea soient frères ou parent/enfant ET qu'on n'occupe qu'un espace dans la grille.
id: mouse_area_bandeau_bas_zone_retour
anchors.fill: parent
onClicked: {
shipheart_clic();
stackView.pop()
}
}
}
}
//-------------------------------------------------------
// Boite de validation :
//-------------------------------------------------------
Shipheart_boite_dialogue {
id: boite_information
}
//===========================================================
// Busy Indicator :
//===========================================================
Shipheart_busy_indicator {
id: busy_indicator
running: timer_tempo_reconfiguration.running
}
// Note : il est inutile de prévoir un Mousearea pour sortir de l'économiseur sur cette page puisqu'il
// est systématiquement bloqué pour toutes les pages lancées depuis 'Paramètres Avancés'.
//=============================================================================================================
// Init de la page
//=============================================================================================================
function init_page()
{
var req, res;
var i;
// Remplissage des différents modèles, dans l'ordre des priorités :
mise_a_jour_type_source();
// Mise à jour du modèle de taille sans attendre car elle est indépendante du type de source ou de point :
mise_a_jour_taille_point();
flag_init = false; // Indique que l'init est terminée
}
//=============================================================================================================
// MAJ du type de source
//=============================================================================================================
function mise_a_jour_type_source()
{
var i;
// Effacement du modèle et premier enregistrement avec 'Non défini' :
champ_formulaire_Type_source.model.clear();
champ_formulaire_Type_source.model.append( {
"valeur": -1,
"texte": qsTr("Non défini"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_capteurs,
"texte": qsTr("Capteur"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_circuits,
"texte": qsTr("Circuit"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_reservoirs,
"texte": qsTr("Réservoir"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_batteries,
"texte": qsTr("Batterie"),
"actif": true });
champ_formulaire_Type_source.model.append( {
"valeur": shipheart_type_source_modules,
"texte": qsTr("Modules"),
"actif": true });
// Mise à jour de l'index courant du modèle champ_formulaire_Type_source :
i = recherche_index_valeur_modele(type_source, champ_formulaire_Type_source.model, "Type_source");
if (i === -1) champ_formulaire_Type_source.currentIndex = recherche_index_valeur_modele(-1, champ_formulaire_Type_source.model, "Type_source");
else champ_formulaire_Type_source.currentIndex = i;
// Appel du remplissage des modèles de niveau inférieur si cela est possible :
if (type_source > 0) mise_a_jour_source();
}
//=============================================================================================================
// MAJ de la source
//=============================================================================================================
function mise_a_jour_source()
{
var req, res;
var nb;
var i;
// Effacement du modèle et premier enregistrement avec 'Non défini' :
champ_formulaire_Source.model.clear();
champ_formulaire_Source.model.append( {
"valeur": -1,
"texte": qsTr("Non défini"),
"actif": true });
switch(type_source)
{
case shipheart_type_source_circuits:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Circuits, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_circuits + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_capteurs:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Capteurs, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_capteurs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_reservoirs:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Reservoirs, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_reservoirs + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_batteries:
req = 'SELECT Cle_primaire, ';
req += 'Nom, ';
req += 'Nom_famille ';
req += 'FROM TB_Batteries, ';
req += ' (SELECT Cle_primaire AS Cle_famille, Nom AS Nom_famille, Priorite_affichage AS Priorite_famille FROM TB_Familles WHERE Type_source=' + shipheart_type_source_batteries + ') AS TB_Familles ';
req += ' WHERE ID_Famille=Cle_famille ';
req += ' ORDER BY Priorite_famille, ABS(Priorite_affichage_accueil)';
break;
case shipheart_type_source_modules:
req = 'SELECT Adresse_k_bus, ';
req += 'Type, ';
req += '"" AS Nom_famille '; // Permet de passer le traitement des familles sans erreur et ajout de ligne inutile (les batteries n'ont pas de notion de famille)
req += 'FROM TB_Modules_config ';
req += ' ORDER BY Adresse_k_bus';
break;
default:
table = "";
break;
}
if (req !== "")
{
//console.log("Requête = [ " + req + "; ]");
nb = bdd.requete(req);
var prec_famille = "";
for (i = 0; i < nb; i ++)
{
res = bdd.lecture_ligne();
if (!res.isEmpty)
{
// Ajout de le ligne de famille :
if (prec_famille !== res[2])
{
champ_formulaire_Source.model.append( {
"groupe": true,
"valeur": 0,
"texte": res[2],
"actif": false });
prec_famille = res[2];
}
// Ajout de la ligne normale :
var texte;
if (type_source === shipheart_type_source_modules)
{
texte = Fonctions.nom_type_module(Number(res[1]));
if (Number(res[0]) !== 0) texte += " @" + res[0];
}
else texte = res[1];
champ_formulaire_Source.model.append( {
"groupe": false,
"valeur": Number(res[0]),
"texte": texte,
"actif": true });
}
}
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(id_source, champ_formulaire_Source.model, "id_source");
if (i === -1) champ_formulaire_Source.currentIndex = recherche_index_valeur_modele(0, champ_formulaire_Source.model, "id_source");
else champ_formulaire_Source.currentIndex = i;
// Appel du remplissage des modèles de niveau inférieur si cela est possible :
if (id_source !== -1) mise_a_jour_type_point();
}
//=============================================================================================================
// MAJ du type de point
//=============================================================================================================
function mise_a_jour_type_point()
{
var req, res;
var i;
var libelle;
// Effacement du modèle :
champ_formulaire_Type_point.model.clear();
// Boucle sur toutes les entrées du tableau des types de points correspondant au 'type_source' (cf dans 'main.qml') :
for (i = 0; i < shipheart_page_personnalisee_type_point_icones[type_source].length; i ++)
{
if ((type_source === shipheart_type_source_circuits) && (i === 0)) continue; // Pour les circuits, on n'autorise pas le premier élément qui correspond à l'icone OFF
champ_formulaire_Type_point.model.append( {
"valeur": i,
"texte": "",
"icone": shipheart_page_personnalisee_type_point_icones[type_source][i],
"actif": true });
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(type_point, champ_formulaire_Type_point.model, "type_point");
if (i === -1) champ_formulaire_Type_point.currentIndex = recherche_index_valeur_modele(1, champ_formulaire_Type_point.model, "type_point");
else champ_formulaire_Type_point.currentIndex = i;
// Cas particulier des modules : on ne laisse pas le choix de l'icone à afficher...
if (type_source === shipheart_type_source_modules)
{
req = 'SELECT Type FROM TB_Modules_config WHERE Adresse_k_bus=' + id_source + ' ';
//console.log("Requête = [ " + req + "; ]");
res = bdd.requete_unique(req);
type_point = Number(res[0]); // Pour les modules, on a autant de fichiers que de type, classés dans l'ordre des Type.
}
}
//=============================================================================================================
// MAJ de la taille de point
//=============================================================================================================
function mise_a_jour_taille_point()
{
var i;
// Effacement du modèle ::
champ_formulaire_Taille_point.model.clear();
for (i = 2; i <= 16; i ++)
{
champ_formulaire_Taille_point.model.append( {
"valeur": (i * 5),
"texte": (i * 5),
"actif": true });
}
// Mise à jour de l'index courant du modèle :
i = recherche_index_valeur_modele(taille_point, champ_formulaire_Taille_point.model, "taille_point");
if (i === -1) champ_formulaire_Taille_point.currentIndex = recherche_index_valeur_modele(25, champ_formulaire_Taille_point.model, "taille_point");
else champ_formulaire_Taille_point.currentIndex = i;
}
//=============================================================================================================
// Recherche de l'index d'une certaine valeur dans un modele
//=============================================================================================================
function recherche_index_valeur_modele(cible, modele, nom_modele)
{
var i;
for (i=0; i<modele.count; i ++)
{
if (modele.get(i).valeur === cible) break; // Cas où la valeur cible a été trouvée parmi les valeurs du modèle
}
if (i === modele.count)
{
console.log("Aucune valeur trouvée pour cible = [" + cible + "] du modèle = " + nom_modele);
i = -1; // Cas où la valeur n'a pas été trouvée parmi les valeurs du modèle
}
//else console.log("Valeur trouvée : [" + i + "] pour cible = [" + cible + "] soit texte = [" + modele.get(i).texte + "]");
return(i);
}
}