1
votes

activer ou désactiver QDialogButtonBox en fonction du texte de plusieurs QComboBox

Je voudrais activer ou désactiver QDialogButtonBox ou de préférence uniquement le bouton OK dans QDialog, en fonction du texte sélectionné de deux QComboBox (s).

Mon exemple est comme ci-dessous. Il ne fonctionne pas actuellement et les deux ComboBox fonctionnent indépendamment l'un de l'autre lors de l'activation ou de la désactivation de QDialogButtonBox.

class SheetColumns(QDialog):
    def __init__(self, column_header):
        super().__init__()

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.column_headers.insert(0, ' ')
        self.setWhatsThis('Please match columns in your data sheet names'
                          ' with the right side labels')

        col_names = ["Student Name:", "Student ID:", "Cohort:", "Gender:",
                     "College:", "Department:", "Major:", "Minor", "Email:",
                     "Adviser", "Adviser Email"]
        self.form_group_box = QGroupBox("Specify Column Names")
        self.layout = QFormLayout()
        for col_name in col_names:
            combo = QComboBox()
            combo.addItems(self.column_headers)
            self.layout.addRow(QLabel(col_name), combo)
        self.form_group_box.setLayout(self.layout)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.button_box.setEnabled(False)

        self.mapper = QSignalMapper(self)

        comb_bx1 = self.layout.itemAt(0, 1).widget()
        comb_bx2 = self.layout.itemAt(1, 1).widget()

        comb_bx1.currentTextChanged.connect(self.mapper.map)
        comb_bx2.currentTextChanged.connect(self.mapper.map)

        self.mapper.setMapping(comb_bx1, comb_bx1.currentText())
        self.mapper.setMapping(comb_bx2, comb_bx2.currentText())

        self.mapper.mapped.connect(self.check_validity)

        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.button_box)
        self.setLayout(main_layout)

    def check_validity(self, text):
        print(text)
        if text == ' ':
            self.button_box.setEnabled(False)
        else:
            self.button_box.setEnabled(True)

Je voudrais que QDialogButtonBox soit activé lorsque le (s) currentText (s) dans les deux ComboBox, il y a autre chose que «», alors qu'il est désactivé quand ils sont tous les deux ».

J'ai essayé d'utiliser QSignalMapper .

Cependant, je n'ai pas réussi à le faire fonctionner.

import sys

from PyQt5.QtCore import QSignalMapper, pyqtSlot
from PyQt5.QtWidgets import (QGroupBox, QFormLayout, QLabel, QComboBox,
                             QApplication, QDialog, QDialogButtonBox,
                             QVBoxLayout)


class SheetColumns(QDialog):
    def __init__(self, column_header):
        super().__init__()

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.column_headers.insert(0, ' ')
        self.setWhatsThis('Please match columns in your data sheet names'
                          ' with the right side labels')

        col_names = ["Student Name:", "Student ID:", "Cohort:", "Gender:",
                     "College:", "Department:", "Major:", "Minor", "Email:",
                     "Adviser", "Adviser Email"]
        self.form_group_box = QGroupBox("Specify Column Names")
        self.layout = QFormLayout()
        for col_name in col_names:
            combo = QComboBox()
            combo.addItems(self.column_headers)
            self.layout.addRow(QLabel(col_name), combo)
        self.form_group_box.setLayout(self.layout)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.button_box.setEnabled(False)

        self.layout.itemAt(0, 1).widget().currentTextChanged.connect(
            self.check_validity)
        self.layout.itemAt(1, 1).widget().currentTextChanged.connect(
            self.check_validity)

        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.button_box)
        self.setLayout(main_layout)

    def check_validity(self, text):
        print(text)
        if text == ' ':
            self.button_box.setEnabled(False)
        else:
            self.button_box.setEnabled(True)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    dialog = SheetColumns(['name student', 'id', 'cohort', 'test 1'])
    sys.exit(dialog.exec_())

Quelqu'un pourrait-il me dire ce que je fais mal, ou y a-t-il une meilleure approche ?

Merci d'avance


0 commentaires

3 Réponses :


1
votes

Personnellement, je trouve le mappeur de signaux extrêmement déroutant et l'évite comme la peste. Voici comment je procéderais (je vais utiliser une classe factice pour rendre la vie plus facile):

class DummyDialog(QDialog):
    def __init__(self):
        self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        self.combo_box_1 = QComboBox()
        self.combo_box_2 = QComboBox()
        self.combo_1_text = ""  # This could also be self.combo_box_1.text()
        self.combo_2_text = ""
        self.combo_box_1.currentTextChanged.connect(self.combo_one_changed)
        self.combo_box_2.currentTextChanged.connect(self.combo_two_changed)

    def combo_one_changed(self, text):
        self.combo_1_text = text

    def combo_two_changed(self, text):
        self.combo_2_text = text

    def check_validity(self):
        if self.combo_1_text and self.combo_2_text:
            self.button_box.setEnabled(True)
        else:
            self.button_box.setEnabled(False)

Faites-moi savoir si vous avez des problèmes avec cela, je viens de trouver cela rapidement.

EDIT: Je remarque également que vous définissez vos combos comme des locaux dans votre méthode init, ce qui peut également être un problème à cause de la propriété de pyqt. Si vous ne stockez pas de référence à l'objet, vous n'êtes pas assuré qu'il restera en existence et python pourrait le ramasser des déchets, ce qui signifie que vous risquez de perdre complètement vos connexions de signaux. Je ne peux pas dire que c'est ce qui se passe, mais en général, il est plus sûr de leur conserver une instance dans votre méthode init en les définissant plutôt comme des variables d'instance.


2 commentaires

Votre "EDIT" est incorrect car lorsque vous ajoutez un widget à une mise en page, la propriété du widget sera prise par le widget où le widget a été défini, dans votre cas, la propriété de la QComboBox sera prise par la QGroupBox donc il n'y a pas problèmes avec le GC.


Ah c'est vrai que je ne faisais pas attention au fait qu'ils faisaient partie de la mise en page.



2
votes

L'utilisation de QSignalMapper est surdimensionnée pour ce dont vous avez besoin, dans votre cas, il vous suffit d'itérer sur la QComboBox et de vérifier qu'elle n'a pas l'option appropriée et, en conséquence, d'activer le bouton comme indiqué ci-dessous:

class SheetColumns(QDialog):
    def __init__(self, column_header, parent=None):
        super().__init__(parent)

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.setWhatsThis(
            "Please match columns in your data sheet names"
            " with the right side labels"
        )

        col_names = [
            "Student Name:",
            "Student ID:",
            "Cohort:",
            "Gender:",
            "College:",
            "Department:",
            "Major:",
            "Minor",
            "Email:",
            "Adviser",
            "Adviser Email",
        ]

        self.combos = []

        flay = QFormLayout()
        for i, col_name in enumerate(col_names):
            combo = QComboBox()
            combo.addItems([""] + self.column_headers)
            flay.addRow(col_name, combo)
            if i in (0, 1):
                combo.currentIndexChanged.connect(self.check_validity)
                self.combos.append(combo)

        self.form_group_box = QGroupBox("Specify Column Names")
        self.form_group_box.setLayout(flay)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        main_layout = QVBoxLayout(self)
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.button_box)

    @pyqtSlot()
    def check_validity(self):
        is_enabled = True
        for combo in self.combos:
            if not combo.currentText():
                is_enabled = False
                break
        button = self.button_box.button(QDialogButtonBox.Ok)
        button.setEnabled(is_enabled)


0 commentaires

0
votes

Merci à @eyllanesc pour sa réponse qui fonctionne parfaitement bien.

Le code ci-dessous convient mieux à mon cas d'utilisation et il est assez similaire à la réponse donnée par @eyllanesc suivant son concept .

class SheetColumns(QDialog):
    def __init__(self, column_header, parent=None):
        super().__init__(parent)

        self.setMinimumWidth(300)
        self.setWindowTitle("Input Column Names")

        self.column_headers = column_header

        self.setWhatsThis(
            "Please match columns in your datasheet names"
            " with the right side labels"
        )

        col_names = [
            "Student Name:",
            "Student ID:",
            "Cohort:",
            "Gender:",
            "College:",
            "Department:",
            "Major:",
            "Minor",
            "Email:",
            "Adviser",
            "Adviser Email",
        ]

        self.combos = []

        flay = QFormLayout()
        for i, col_name in enumerate(col_names):
            combo = QComboBox()
            combo.addItems([""] + self.column_headers)
            flay.addRow(col_name, combo)
            if i in (0, 1):
                combo.currentIndexChanged.connect(self.check_validity)
                self.combos.append(combo)

        self.form_group_box = QGroupBox("Specify Column Names")
        self.form_group_box.setLayout(flay)

        self.button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
        )
        self.button = self.button_box.button(QDialogButtonBox.Ok)
        self.button.setEnabled(False)
        self.button_box.accepted.connect(self.accept)
        self.button_box.rejected.connect(self.reject)

        self.label = QLabel()
        self.label.setText('Student Name and ID cannot be empty')

        main_layout = QVBoxLayout(self)
        main_layout.addWidget(self.form_group_box)
        main_layout.addWidget(self.label)
        main_layout.addWidget(self.button_box)

    @pyqtSlot()
    def check_validity(self):
        if all([combo.currentText() for combo in self.combos]):
            self.button.setEnabled(True)
            self.label.setText('')
        else:
            self.button.setEnabled(False)
            self.label.setText('Student Name and ID cannot be empty')


1 commentaires

changer self.label.setText ('') en self.label.clear () , est plus lisible.