Gleiche Werte in QtWidgets erkennen und zurücksetzen

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Atalanttore
User
Beiträge: 324
Registriert: Freitag 6. August 2010, 17:03

Samstag 26. Januar 2019, 19:11

Hallo

Es kam bei mir jetzt vor, dass als Wert bei jeder von 2 ComboBoxen der gleiche ausgewählt werden kann, aber nicht sollte. Ich habe mir dazu etwas programmiert, was den Wert bei Gleichheit auf den jeweiligen Ausgangswert zurücksetzt.

Geht dasselbe Verhalten auch einfacher mit einer entsprechenden Methode von Qt anstatt meinem Code unten?

main.py

Code: Alles auswählen

import sys
from PyQt5.QtWidgets import QApplication, QMainWindow, QMessageBox
from PyQt5.uic import loadUi


class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super().__init__(parent)
        loadUi("color.ui", self)

        self.background_color = self.comboBox_background_color.currentText()
        self.foreground_color = self.comboBox_foreground_color.currentText()

        self.comboBox_background_color.currentIndexChanged.connect(lambda: self.check_color("b"))
        self.comboBox_foreground_color.currentIndexChanged.connect(lambda: self.check_color("f"))


    def check_color(self, position):
        if self.comboBox_background_color.currentText() == self.comboBox_foreground_color.currentText():
            QMessageBox.warning(self, "Warnung", "Hintergrundfarbe und Vordergrundfarbe sollten nicht gleich sein.")
            if position == "b":
                self.comboBox_background_color.setCurrentText(self.background_color)
                self.background_color = self.comboBox_background_color.currentText()
            elif position == "f":
                self.comboBox_foreground_color.setCurrentText(self.foreground_color)
                self.foreground_color = self.comboBox_foreground_color.currentText()


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

color.ui

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <widget class="QWidget" name="layoutWidget">
    <property name="geometry">
     <rect>
      <x>260</x>
      <y>80</y>
      <width>287</width>
      <height>71</height>
     </rect>
    </property>
    <layout class="QGridLayout" name="gridLayout">
     <item row="0" column="0">
      <widget class="QLabel" name="label_background_color">
       <property name="text">
        <string>Hintergrundfarbe:</string>
       </property>
      </widget>
     </item>
     <item row="0" column="1">
      <widget class="QComboBox" name="comboBox_background_color">
       <item>
        <property name="text">
         <string>Rot</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Blau</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Grün</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Gelb</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Schwarz</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Weiß</string>
        </property>
       </item>
      </widget>
     </item>
     <item row="1" column="0">
      <widget class="QLabel" name="label_foreground_color">
       <property name="text">
        <string>Vordergrundfarbe:</string>
       </property>
      </widget>
     </item>
     <item row="1" column="1">
      <widget class="QComboBox" name="comboBox_foreground_color">
       <item>
        <property name="text">
         <string>Blau</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Rot</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Grün</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Gelb</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Schwarz</string>
        </property>
       </item>
       <item>
        <property name="text">
         <string>Weiß</string>
        </property>
       </item>
      </widget>
     </item>
    </layout>
   </widget>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>28</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>
Gruß
Atalanttore
__deets__
User
Beiträge: 5518
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 26. Januar 2019, 19:27

Jein. Wenn man das gleiche Verhalten haben will wie du, dann geht das nicht viel anders.

Die Alternative ist ein QAbstractListItemModel zu machen. Und darin kannst du dann neben den immer zu ueberladenden data- und rowCount-Methoden auch die flags-Methode ueberladen. Mit der kannst du das Item unanwaehlbar machen, das von der anderen Combo-Box ausgewaehlt wurde.

Code dafuer habe ich zum einen nur in C++, und zum anderen nur von einem Kunden, dessen Code ich nicht einfach hier posten darf. Da musst du also selbst ran. Aber das Ergebnis ist halt etwas schicker als dein Vorgehen, denn statt einen Fehler zu bemaengeln kann man ihn erst gar nicht machen.
Atalanttore
User
Beiträge: 324
Registriert: Freitag 6. August 2010, 17:03

Samstag 26. Januar 2019, 20:26

@__deets__: Das man dem Nutzer gar nicht erst die Chance gibt einen Fehler zu machen, der bemängelt wird, habe ich noch gar nicht gedacht. Ich habe jetzt mit deinem Vorschlag begonnen.

Folgende Erweiterungen habe ich aus einem Codebeispiel gemacht:

Code: Alles auswählen

from PyQt5.QtCore import QAbstractListModel, QModelIndex, Qt


class ColorModel(QAbstractListModel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.COLORS = ["Rot", "Blau", "Gelb", "Grün", "Schwarz", "Weiß"]

    def data(self, index, role=Qt.DisplayRole):
        row = index.row()

    def rowCount(self, parent=QModelIndex()):
        return len(self.COLORS)

    def flags(self):
        pass
Zur `flags()`-Methode habe ich nur etwas in der Doku gefunden, aber kein Codebeispiel. Wie geht das im Detail?

Gruß
Atalanttore
__deets__
User
Beiträge: 5518
Registriert: Mittwoch 14. Oktober 2015, 14:29

Samstag 26. Januar 2019, 21:51

Probier doch mal rum. Lies die Doku, was es für werte gibt. Überlass die Methode und gibt mal abhängig vom Index verschiedene Flags zurück. Oder auch im Internet nach Beispielen.
Atalanttore
User
Beiträge: 324
Registriert: Freitag 6. August 2010, 17:03

Sonntag 27. Januar 2019, 16:07

Ich habe die Methoden nach einem weiteren Beispiel umgebaut. Wie man ein Item unanwählbar macht, wenn es in der jeweils anderen ComboBox ausgewählt ist, habe ich nicht herausgefunden.

Code:

Code: Alles auswählen

class ColorModel(QAbstractListModel):
    COLORS = ["Rot", "Blau", "Gelb", "Grün", "Schwarz", "Weiß"]

    def __init__(self, parent=None):
        super().__init__(parent)

    def data(self, index, role=Qt.DisplayRole):
        return index.row()

    def rowCount(self, parent=QModelIndex()):
        return len(self.COLORS)

    def flags(self, index):
        flags = super().flags(index)
        if index.isValid():
            flags |= Qt.ItemIsSelectable
            flags |= Qt.ItemIsDragEnabled
        else:
            flags = Qt.ItemIsDropEnabled
        return flags
Gruß
Atalanttore
__deets__
User
Beiträge: 5518
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 27. Januar 2019, 16:28

Dazu brauchst du zwei Modelle. Jedes hat einen Index der eben verboten ist. Und wenn das andere DropDown seinen Index wechselt, setzt das Signal dem Index im anderen Modell.
Atalanttore
User
Beiträge: 324
Registriert: Freitag 6. August 2010, 17:03

Sonntag 27. Januar 2019, 16:44

@__deets__: Dazu habe ich mehrere Fragen:
  1. Meinst du mit "zwei Modelle", dass es zwei `ColorModel`-Objekte geben muss?
  2. Soll der verbotene Index den Indexwert der Farbe in `COLORS` enthalten, die in der zugehörigen ComboBox gerade ausgewählt ist?
  3. Meinst du mit "Signal setzen" den Zugriff auf eine Methode zum Index wechseln im jeweils anderen `ColorModel`-Objekt?
Gruß
Atalanttore
__deets__
User
Beiträge: 5518
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sonntag 27. Januar 2019, 16:51

Ja, ein Modell pro ComboBox. Und jede combobox hat doch ein Signal das sie sendet wenn der User die Auswahl geändert hat. Dieses Signal verknüpfst du mit dem Modell der ANDEREN ComboBox, und änderst damit den verbotenen Index.
Antworten