ViewModel-Klasse und ComboBox / ListView

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Olbob
User
Beiträge: 4
Registriert: Samstag 8. Februar 2020, 12:12

Hallo zusammen,

ich bin neu hier im Forum. War bisher als stiller Beobachter hier schon öfters unterwegs und wurde einfach durch Suchen und Finden unterstützt. Doch jetzt habe ich ein Problem, bei dem ich auch mit Tante Google nicht weiter komme. Bin zwar in der Programmierwelt nicht ganz neu. Bisher lag mein Fokus allerdings eher auf VB.NET bzw. VBA, früher auch C++, aber das ist schon eine Ewigkeit her.. Da mir aber nun VBA einfach zu unflexibel ist (ja, habe eine große Schmerztoleranz ;-) ), arbeite ich mich in Python ein.

Randdaten: ich verwende Python 3.7.6, PyQt 5.14.1, PyQt5 sip 12.71, versch. IDEs (PyCharm, VSCode, IDLE (X), Eric6, QtCreator [hier mit PySide2])

Bitte keine Vorschläge, wie nimm doch ... o.ä. Mir geht es nicht um die schnellstmögliche, sondern einer angepassten Lösung, zu einem Problem, auf das ich hier nicht näher eingehe.

Ich möchte eine QComboBox oder auch QListView über eine von QAbstractListModel direkt oder indirekt abgeleitete Klasse füttern.
Einzig und allein ein unverändertes Objekt der QStringListModel-Klasse scheint im Stande zu sein, diese Aufgabe zu erfüllen. Zu meinem Zweck müsste ich aber die data()-Methode überschreiben. Und da fängt das Problem an: Die QComboBox wird zwar befüllt, aber das PopUp enthält keine Daten, wird auch nicht aufgeklappt. Über den Code mit setCurrentIndex() bzw. per Hoch-/Runtertaste kann ich den aktuellen Eintrag wählen. Ich kann auch per Text()-Methode alle Einträge auslesen, aber es wird kein PopUp angezeigt, aus dem der Anwender den entsprechenden Eintrag auswählen kann.

Das ListView bringt es noch eine Stufe heftiger: hier wird überhaupt gar nichts angezeigt. Ob es Einträge überhaupt speichert habe ich bisher aus lauter Verzweiflung noch gar nicht ausprobiert.

Hier mal der Code der zumindest Einträge darstellen lässt:

Code: Alles auswählen

from PyQt5 import QtCore as qtc

class myStringListModel(qtc.QStringListModel):
    def __init__(self):
        super().__init__(None)
        self.setStringList([f"Eintrag {x}" for x in range(1, 11)])

    def rowCount(self, parent):
        ret = len(self.stringList())
        return ret

Mit diesem Code wird die Anzahl der Einträge bereits festgesetzt, aber "natürlich" keine Einträge dargestellt.

Code: Alles auswählen

from PyQt5 import QtCore as qtc

class myStringListModel(qtc.QStringListModel):
    def __init__(self):
        super().__init__(None)
        self.setStringList([f"Eintrag {x}" for x in range(1, 11)])

    def rowCount(self, parent):
        ret = len(self.stringList())
        return ret

    def data(self, index, role):
        pass

Wenn ich nun die data()-Methode so abändere, dass auch sinnvolle Daten zurückgegeben werden, dann enthält das PopUp keine Einträge mehr (Änderungen kursiv dargestellt):

Code: Alles auswählen

from PyQt5 import QtCore as qtc

class myStringListModel(qtc.QStringListModel):
    def __init__(self):
        super().__init__(None)
        self.setStringList([f"Eintrag {x}" for x in range(1, 11)])

    def rowCount(self, parent):
        ret = len(self.stringList())
        return ret

    def data(self, index, role):
        try:
            ret = self.stringList()[index.row()]
            return ret
        except IndexError:
            return qtc.QVariant()
Irgendetwas scheint an den Datentypen, welche über die data()-Methode zurückgegeben werden nicht zu passen. Hat jemand schon dieses Verhalten beobachtet und eine Lösung dazu?

Olbob
Benutzeravatar
__blackjack__
User
Beiträge: 13100
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Olbob: Das Du da jetzt von QStringListModel ableitest ist komisch. Ich denke Du wolltest das mit QAbstractListModel machen. Da musst Du dann mindestens `rowCount()` und `data()` implementieren. Du berücksichtigst das `role`-Argument überhaupt nicht, sondern lieferst für jede Rolle die Zeichenkette, was nicht wirklich Sinn macht.

Das Du da immer alles an den Namen `ret` bindest bevor Du es mit ``return`` zurückgibst macht keinen Sinn, und `ret` ist auch ein schlechter Name. Die Vorsilbe `my` macht auch keinen Sinn wenn es nicht auch `our` und/oder `their` als Vorsilbe gibt, wogegen man sich abgrenzen will.

`QtCore` sollte man nicht abkürzen. Grundsätzlich sollte man keine kryptischen Abkürzungen verwenden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Olbob
User
Beiträge: 4
Registriert: Samstag 8. Februar 2020, 12:12

Ok, danke für die Hinweise. Ich bin mit dem Thema am Verzweifeln, so dass da schon einiges für Außenstehende komisch aussehen mag.

Die Hinweise bringen mich aber bei meinem eigentlichen Problem nicht weiter.
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Wieso bringt dich der Hinweis auf QAbstractItemModel statt string-model nicht weiter?
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hier mal ein Beispiel, wie man das ueberlaedt: https://github.com/awacha/cct/blob/mast ... rymodel.py
Olbob
User
Beiträge: 4
Registriert: Samstag 8. Februar 2020, 12:12

Hi deets,

es gab gar kein Hinweis auf QAbstractItemModel. Nur auf QAbstractListModel, das ich a) bereits selbst erwähnte und b) auch von QAbstractItemModel ableitet.

Dieses habe ich in meinem Fehlersuchvorgang ebenfalls schon ausprobiert und bin dann eben bis zum QStringListModel gekommen, welches in Reinform funktioniert.

Die Hinweise von BlackJack waren die Implementierung von rowCount() und data(). Diese habe ich ja schon in meinen CodeSnippets erwähnt.

Grüße

P.S.: mittlerweile wurde von dir der Link erwähnt. In sbgewandelter Form habe ich es doch auch gemacht. Nur dass mir für die Fehlersuche die Rolle egal ist.

Aber ja: in der Endform wird die Rolle eine Rolle spielen 😆😉
__deets__
User
Beiträge: 14539
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Code den du zeigst leitet *nicht* von QAbstractListModel ab. Und das du das mal irgendwann irgendwie gemacht hast mag ja sein - darauf koennen wir uns aber nicht beziehen. Und das die Rolle einfach ignoriert werden kann, mag sein. Muss aber nicht. Wenn es nicht geht, waere es vielleicht angezeigt, es mal auszuprobieren.

Und hast du den im Link gezeigten Code mal ausprobiert?
Olbob
User
Beiträge: 4
Registriert: Samstag 8. Februar 2020, 12:12

Hmmm. Asche auf mein Haupt.

Scheinbar wertet Qt die Abfrage nach der Rolle nochmal separat aus. Wenn überhaupt keine Abfrage "role == QtCore.Qt.DisplayRole" oder ähnliches stattfindet, dann scheint der Zugriff auf data() von vornherein schon ungültig zu sein.

Ich hatte die Abfrage nach der Rolle anfangs tatsächlich drin. Da es aber da schon nicht funktionierte, reduzierte ich nach und nach alle für mich unnötigen Abfragen und Codefragmente. Und es funktioniert mit allen Derivaten, welche ursprünglich von QAbstractItemModel ableiten.

Code: Alles auswählen

class myStringListModel(qtc.QAbstractItemModel):
    def __init__(self):
        super().__init__(None)
        self._list = [f"Eintrag {x}" for x in range(1, 11)]

    def rowCount(self, parent):
        ret = len(self._list)
        return ret

    def columnCount(self, parent):
        return 1

    def index(self, row, column, parent):
        return self.createIndex(row, column)

    def data(self, index, role):
        try:
            if role == qtc.Qt.DisplayRole:
                return self._list[index.row()]
            else:
                return qtc.QVariant()
        except IndexError:
            return qtc.QVariant()

Danke für eure Hilfe. Mal wieder habt ihr mir geholfen. Ich hatte die Hoffnung schon aufgegeben. Sorry, wegen meines Tons zuvor. Wie gesagt: ich war verzweifelt :roll:
Antworten