Problème d'affichage de la scrollbar d'une ScrollView
RésoluMourad2009B Messages postés 123 Date d'inscription Statut Membre Dernière intervention -
Bonjour à tous,
je suis en train de travailler sur un projet (Qt Quick, Qt, C++), comme IDE j'utilise QtCreator.
Et puis dans mon main.qml, j'ai tout essayé, mais je n'arrive pas à afficher la scrollbar du contrôle "ScrollView"
ou l'ascenseur, vertical, pour pouvoir scroller et essayer d'afficher tous mes contrôles qui sont nombreux.
Je vous mets au dessous le fichier complet, le main.qml, et est-ce que vous pouvez, s'il vous plaît, me donner votre avis, et m'aider à trouver ce qui ne va pas.
Merci d'avance
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQuick.VirtualKeyboard 2.15 import "." // Pour utiliser DatabaseManager (depuis qmlRegisterType) import com.mkapplications.database 1.0 // Pour accéder aux éléments du module QML (déclaré dans CMake) import com.mkapplications.GestionBudgetEmbarque 1.0 ApplicationWindow { id: window //Pour les mobiles minimumWidth: Qt.platform.os === "android" || Qt.platform.os === "ios" ? Screen.width : 800 minimumHeight: Qt.platform.os === "android" || Qt.platform.os === "ios" ? Screen.height : 400 visible: true title: qsTr("Hello World") //Les propriéts property int idSousCategorie: -1 // valeur initiale par défaut // Flag global pour éviter boucle infinie property bool updating: false //Le clavier virtuel InputPanel { id: inputPanel z: 99 x: 0 y: window.height width: window.width states: State { name: "visible" when: inputPanel.active PropertyChanges { target: inputPanel y: window.height - inputPanel.height } } transitions: Transition { from: "" to: "visible" reversible: true ParallelAnimation { NumberAnimation { properties: "y" duration: 250 easing.type: Easing.InOutQuad } } } } //Fin du clavier virtuel //---------------------------Vue principale---------------------------// ScrollView { id: scrollView anchors { left: parent.left right: parent.right top: parent.top bottom: Qt.platform.os === "android" ? parent.bottom : inputPanel.top } clip: true ScrollBar.vertical.policy: ScrollBar.AlwaysOn // Pour faciliter le défilement ScrollBar.vertical.interactive: true // Important pour mobile // La colonne qui contient tous les controles ColumnLayout { id: idLayoutPricipal width: scrollView.availableWidth // Important pour le mobile height: scrollView.availableHeight // Important pour le mobile // Cette ligne est CRUCIALE : elle permet au ScrollView de connaître la hauteur du contenu implicitHeight: childrenRect.height //width: parent.width Layout.fillWidth: true Layout.leftMargin: 20 // Marge à gauche Layout.rightMargin: 20 // Marge à droite Layout.topMargin: 20 // Marge en haut spacing: 20 //---------------------------La colonne qui contient le rectangle et l'icone de l'application---------------------------// RowLayout { id: rootLayout width: scrollView.availableWidth // Important pour le mobile spacing: 10 // --------------------------------- Header --------------------------------- Rectangle { id: idHeaderRect Layout.fillWidth: true Layout.leftMargin: 20 // Marge à gauche Layout.rightMargin: 20 // Marge à droite Layout.topMargin: 20 // Marge en haut height: idHeaderRow.implicitHeight + 20 // height: 60 color: "#cc65cc" radius: 8 // Dégradé de couleur (conservé de votre version originale) gradient: Gradient { GradientStop { position: 0.00; color: "#cc65cc" } GradientStop { position: 0.01; color: "#dd1d1d" } GradientStop { position: 1.00; color: "#ffffff" } } border.color: "#2129dd" //---------------------------La ligne qui contient l'icone de l'application ainsi que le titre---------------------------// RowLayout { id: idHeaderRow anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter spacing: 20 Image { source: "qrc:/Media/Icones/icoPrincipale64.ico" width: 32 height: 32 } Label { text: "Gestion budget" font.pixelSize: 20 } } } } //Fin de La colonne qui contient le rectangle et l'icone de l'application //---------------------------La ligne qui contient les comboBox et leurs Labels---------------------------// RowLayout { width: parent // important pour que le layout s'adapte à la ScrollView anchors.leftMargin: 10 // Augmenté anchors.rightMargin: 20 // Augmenté //anchors.topMargin: 10 //anchors.bottomMargin: 10 // 1-------- Colonne pour les comboBox et leurs Labels ColumnLayout { Layout.fillWidth: true Layout.leftMargin: 10 Layout.rightMargin: 10 //Layout.topMargin: 10 //Layout.bottomMargin: 10 spacing: 10 // Controle pour le Label Session Label { text: "Session:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Session ComboBox { id: idSessionCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_session" valueRole: "id_session" currentIndex: 0 //Slot déclenché quand l'index de la ComboBox Session change onCurrentIndexChanged: { ..................................... } } // Controle pour le Label Catégorie Label { text: "Catégorie:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Catégorie ComboBox { id: idCategorieCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_categorie" valueRole: "id_categorie" currentIndex: -1 //Slot déclenché quand l'index de la ComboBox Catégorie change onCurrentIndexChanged: { ............................ ............................... } } // Controle pour le Label Sous-Catégorie Label { text: "Sous-Catégorie:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Sous-Catégorie ComboBox { id: idSousCategorieCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_sc" valueRole: "id_sous_categorie" currentIndex: 0 onCurrentIndexChanged: { ............................................. } } // Controle pour la Label Depense Label { text: "Dépense:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la ComboBox Depense ComboBox { id: idDepenseCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_depense" valueRole: "id_depense" currentIndex: 0 editable: true } //Controle pour Label Prix unitaire Label { text: "Prix unitaire :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // RowLayout pour le Controle TextField Prix unitaire et le Label euro RowLayout { // Controle TextField Prix unitaire CustomTextField { id: idPrixUnitaireFieldText Layout.fillWidth: true placeholderText: "0.00" inputMethodHints: Qt.ImhDigitsOnly onEditingFinished: { .................................... } // Nettoyage texte avec filtre + restriction (optionnel) onTextChanged: { ....................................... } } //Controle Label pour symbole euro du prix unitaire Label { text: " €" Layout.preferredWidth: 20 Layout.alignment: Qt.AlignVCenter } } // Controle pour Label Quantité Label { text: "Quantité :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour TextField Quantité CustomTextField { id: idQuantiteFieldText Layout.fillWidth: true placeholderText: "1" inputMethodHints: Qt.ImhDigitsOnly onTextChanged: { .......................................... } onEditingFinished: { ................................... } } // Controle pour Label Prix total Label { text: "Prix total :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour le TextField Prix total RowLayout { // Prix total CustomTextField { id: idPrixTotalFieldText Layout.fillWidth: true placeholderText: "0.00" inputMethodHints: Qt.ImhDigitsOnly onTextChanged: { .................................. } onEditingFinished: { ............................. } } //Controle Label pour symbole euro du prix totalo Label { text: " €" Layout.preferredWidth: 20 Layout.alignment: Qt.AlignVCenter } } //99999999999999999999999999999999999999999999999999999999999999999999999 Label { text: "Date:" Layout.preferredWidth: 100 } TextField { id: idDateField Layout.fillWidth: true placeholderText: "JJ/MM/AAAA" inputMethodHints: Qt.ImhDate MouseArea { anchors.fill: parent onClicked: dateDialog.open() } } Popup { id: dateDialog width: 300 height: 300 modal: true focus: true DatePicker { id: calendar anchors.fill: parent onSelectedDateChanged: { idDateField.text = Qt.formatDate(selectedDate, "dd/MM/yyyy") dateDialog.close() } } } // time 8888888888 Label { text: "Heure:" Layout.preferredWidth: 100 } TextField { id: idTimeField Layout.fillWidth: true width: parent.width - 20 placeholderText: "HH:MM" inputMethodHints: Qt.ImhTime MouseArea { anchors.fill: parent onClicked: timeDialog.open() } } Popup { id: timeDialog width: 200 height: 200 modal: true focus: true TimePicker { id: clock anchors.fill: parent Button { text: "Valider" Layout.alignment: Qt.AlignHCenter onClicked: { idTimeField.text = Qt.formatTime(new Date(0, 0, 0, clock.selectedTime.hours, clock.selectedTime.minutes), "hh:mm") timeDialog.close() } } } } } //Fin de Colonne pour les comboBox et leurs Labels } //La fin de La ligne qui contient les comboBox et leurs Labels- } // Espace flexible en bas Item { Layout.fillHeight: true } } //Fin de ScrollView }
Pour éviter que le fichier main.qml soit trop long, j'ai supprimé tout ce qui n'est pas en rapport de la maison page. Et j'ai remplacé par des points.
Windows / Chrome 138.0.0.0
2 réponses
Bonjour Bruno83200_6929,
Un grand merci pour ta réponse rapide et efficace ! Je débute avec QML et j’étais bloqué depuis deux jours sur cette mise en page. Grâce à toi, je vais enfin pouvoir avancer. Merci encore
Bonjour,
Le ScrollView doit connaître la taille de son contenu via contentWidth et contentHeight.
Ne pas contraindre artificiellement la taille du layout principal avec width et height.
Utiliser implicitWidth et implicitHeight pour que Qt calcule automatiquement les dimensions;
S'assurer qu'il y a suffisamment de contenu pour dépasser la hauteur visible.
import QtQuick 2.15 import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQuick.VirtualKeyboard 2.15 import "." // Pour utiliser DatabaseManager (depuis qmlRegisterType) import com.mkapplications.database 1.0 // Pour accéder aux éléments du module QML (déclaré dans CMake) import com.mkapplications.GestionBudgetEmbarque 1.0 ApplicationWindow { id: window //Pour les mobiles minimumWidth: Qt.platform.os === "android" || Qt.platform.os === "ios" ? Screen.width : 800 minimumHeight: Qt.platform.os === "android" || Qt.platform.os === "ios" ? Screen.height : 400 visible: true title: qsTr("Hello World") //Les propriéts property int idSousCategorie: -1 // valeur initiale par défaut // Flag global pour éviter boucle infinie property bool updating: false //Le clavier virtuel InputPanel { id: inputPanel z: 99 x: 0 y: window.height width: window.width states: State { name: "visible" when: inputPanel.active PropertyChanges { target: inputPanel y: window.height - inputPanel.height } } transitions: Transition { from: "" to: "visible" reversible: true ParallelAnimation { NumberAnimation { properties: "y" duration: 250 easing.type: Easing.InOutQuad } } } } //Fin du clavier virtuel //---------------------------Vue principale---------------------------// ScrollView { id: scrollView anchors { left: parent.left right: parent.right top: parent.top bottom: Qt.platform.os === "android" ? parent.bottom : inputPanel.top } clip: true ScrollBar.vertical.policy: ScrollBar.AsNeeded // Changé de AlwaysOn à AsNeeded ScrollBar.horizontal.policy: ScrollBar.AsNeeded ScrollBar.vertical.interactive: true ScrollBar.horizontal.interactive: true // CORRECTION PRINCIPALE : Utiliser contentWidth et contentHeight contentWidth: idLayoutPricipal.implicitWidth contentHeight: idLayoutPricipal.implicitHeight // La colonne qui contient tous les controles ColumnLayout { id: idLayoutPricipal // CORRECTION : Ne pas définir width/height explicitement // Laisser le layout calculer sa taille naturellement anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top // Marges appliquées au layout principal anchors.leftMargin: 20 anchors.rightMargin: 20 anchors.topMargin: 20 spacing: 20 //---------------------------La colonne qui contient le rectangle et l'icone de l'application---------------------------// RowLayout { id: rootLayout Layout.fillWidth: true spacing: 10 // --------------------------------- Header --------------------------------- Rectangle { id: idHeaderRect Layout.fillWidth: true height: idHeaderRow.implicitHeight + 20 color: "#cc65cc" radius: 8 // Dégradé de couleur (conservé de votre version originale) gradient: Gradient { GradientStop { position: 0.00; color: "#cc65cc" } GradientStop { position: 0.01; color: "#dd1d1d" } GradientStop { position: 1.00; color: "#ffffff" } } border.color: "#2129dd" //---------------------------La ligne qui contient l'icone de l'application ainsi que le titre---------------------------// RowLayout { id: idHeaderRow anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter spacing: 20 Image { source: "qrc:/Media/Icones/icoPrincipale64.ico" width: 32 height: 32 } Label { text: "Gestion budget" font.pixelSize: 20 } } } } //Fin de La colonne qui contient le rectangle et l'icone de l'application //---------------------------La ligne qui contient les comboBox et leurs Labels---------------------------// ColumnLayout // Changé de RowLayout à ColumnLayout pour plus de hauteur { Layout.fillWidth: true spacing: 15 // 1-------- Colonne pour les comboBox et leurs Labels ColumnLayout { Layout.fillWidth: true spacing: 10 // Controle pour le Label Session Label { text: "Session:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Session ComboBox { id: idSessionCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_session" valueRole: "id_session" currentIndex: 0 //Slot déclenché quand l'index de la ComboBox Session change onCurrentIndexChanged: { // Code à implémenter } } // Controle pour le Label Catégorie Label { text: "Catégorie:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Catégorie ComboBox { id: idCategorieCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_categorie" valueRole: "id_categorie" currentIndex: -1 //Slot déclenché quand l'index de la ComboBox Catégorie change onCurrentIndexChanged: { // Code à implémenter } } // Controle pour le Label Sous-Catégorie Label { text: "Sous-Catégorie:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la comboBox Sous-Catégorie ComboBox { id: idSousCategorieCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_sc" valueRole: "id_sous_categorie" currentIndex: 0 onCurrentIndexChanged: { // Code à implémenter } } // Controle pour la Label Depense Label { text: "Dépense:" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour la ComboBox Depense ComboBox { id: idDepenseCombo Layout.fillWidth: true Layout.minimumWidth: 250 textRole: "nom_de_depense" valueRole: "id_depense" currentIndex: 0 editable: true } //Controle pour Label Prix unitaire Label { text: "Prix unitaire :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // RowLayout pour le Controle TextField Prix unitaire et le Label euro RowLayout { Layout.fillWidth: true // Controle TextField Prix unitaire - Remplacer CustomTextField par TextField TextField { id: idPrixUnitaireFieldText Layout.fillWidth: true placeholderText: "0.00" inputMethodHints: Qt.ImhDigitsOnly onEditingFinished: { // Code à implémenter } onTextChanged: { // Code à implémenter } } //Controle Label pour symbole euro du prix unitaire Label { text: " €" Layout.preferredWidth: 20 Layout.alignment: Qt.AlignVCenter } } // Controle pour Label Quantité Label { text: "Quantité :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour TextField Quantité - Remplacer CustomTextField par TextField TextField { id: idQuantiteFieldText Layout.fillWidth: true placeholderText: "1" inputMethodHints: Qt.ImhDigitsOnly onTextChanged: { // Code à implémenter } onEditingFinished: { // Code à implémenter } } // Controle pour Label Prix total Label { text: "Prix total :" Layout.preferredWidth: 100 Layout.alignment: Qt.AlignVCenter } // Controle pour le TextField Prix total RowLayout { Layout.fillWidth: true // Prix total - Remplacer CustomTextField par TextField TextField { id: idPrixTotalFieldText Layout.fillWidth: true placeholderText: "0.00" inputMethodHints: Qt.ImhDigitsOnly onTextChanged: { // Code à implémenter } onEditingFinished: { // Code à implémenter } } //Controle Label pour symbole euro du prix total Label { text: " €" Layout.preferredWidth: 20 Layout.alignment: Qt.AlignVCenter } } // Date Label { text: "Date:" Layout.preferredWidth: 100 } TextField { id: idDateField Layout.fillWidth: true placeholderText: "JJ/MM/AAAA" inputMethodHints: Qt.ImhDate MouseArea { anchors.fill: parent onClicked: dateDialog.open() } } Popup { id: dateDialog width: 300 height: 300 modal: true focus: true // Remplacer DatePicker par un calendrier simple ou un placeholder Rectangle { anchors.fill: parent color: "#f0f0f0" Text { anchors.centerIn: parent text: "Date Picker\n(à implémenter)" } MouseArea { anchors.fill: parent onClicked: { idDateField.text = Qt.formatDate(new Date(), "dd/MM/yyyy") dateDialog.close() } } } } // Heure Label { text: "Heure:" Layout.preferredWidth: 100 } TextField { id: idTimeField Layout.fillWidth: true placeholderText: "HH:MM" inputMethodHints: Qt.ImhTime MouseArea { anchors.fill: parent onClicked: timeDialog.open() } } Popup { id: timeDialog width: 200 height: 200 modal: true focus: true // Remplacer TimePicker par un placeholder Rectangle { anchors.fill: parent color: "#f0f0f0" Column { anchors.centerIn: parent spacing: 10 Text { text: "Time Picker\n(à implémenter)" anchors.horizontalCenter: parent.horizontalCenter } Button { text: "Valider" anchors.horizontalCenter: parent.horizontalCenter onClicked: { idTimeField.text = Qt.formatTime(new Date(), "hh:mm") timeDialog.close() } } } } } // Ajout d'éléments supplémentaires pour forcer le scroll Repeater { model: 10 Label { text: "Élément test " + (index + 1) Layout.fillWidth: true Layout.preferredHeight: 40 background: Rectangle { color: index % 2 ? "#e0e0e0" : "#f0f0f0" } } } } //Fin de Colonne pour les comboBox et leurs Labels } //La fin de La ligne qui contient les comboBox et leurs Labels // Ajout d'un spacer en bas Item { Layout.fillWidth: true Layout.preferredHeight: 50 } } //Fin du ColumnLayout principal } //Fin de ScrollView }