QAbstractTableModel mit Icons sehr langsam

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

Hallo,

ich gebe eine SQL-Abfrage mit Hilfe eines QAbstractTableModels aus. In der Datenbank sind zur Zeit nur 10 Reihen drin.

Bild

Die Werte für "Aktiv" und "Maker" sind als 0 und 1 (bool) abgespeichert.

Rufe ich Liste ohne Icons - also mit den Werten 0 und 1 auf - ist die Liste sofort auf dem Bildschirm zu sehen.
Ersetze ich die 1 durch Icons (siehe #7 und #8) braucht diese Ansicht ca. 2-3 Sekunden zum Aufbau.

Geht das auch schneller?

Code: Alles auswählen

   
def data(self, index, role):
        # 0 KD-nr
        if index.column() == 0:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return " " + str(value) + " "
            if role == Qt.ItemDataRole.TextAlignmentRole:
                return Qt.AlignmentFlag.AlignCenter
            return None
        # 1 Anrede
        if index.column() == 1:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return " " + str(value) + " "
            return None
        # 2 Titel
        if index.column() == 2:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return str(" " + value + " ")
            return None
        # 3 Vorname
        if index.column() == 3:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return str(" " + value + " ")
            return None
        # 4 Nachname
        if index.column() == 4:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return str(" " + value + " ")
        # 5 PLZ
        if index.column() == 5:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return str(" " + value + " ")
            if role == Qt.ItemDataRole.TextAlignmentRole:
                return Qt.AlignmentFlag.AlignCenter
            return None
        # 6 Stadt
        if index.column() == 6:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DisplayRole:
                return str(" " + value + " ")
            return None
        # 7 Aktiv
        if index.column() == 7:
            value = self._sql_data.record(index.row()).value(index.column())
            if value == 1 and role == Qt.ItemDataRole.DecorationRole:
                return QIcon(PIC + "Tick_kn.png")
            return None
        # 8 Maker
        if index.column() == 8:
            value = self._sql_data.record(index.row()).value(index.column())
            if role == Qt.ItemDataRole.DecorationRole and value == 1:
                return QIcon(PIC + "Tick_km.png")
            return None
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@GiJay: So wie es da steht wird jedes mal wieder eine Bilddatei geladen. Schau mal ob es sich besser verhält wenn man diese beiden Icons *einmal* erstellt, und nicht jedes mal wenn Qt danach fragt.

Jetzt nicht direkt zur Frage, aber bei ``str(" " + value + " ")`` ist das `str()` sinnfrei weil das hier schon eine Zeichenkette bekommt, die wird dadurch nicht noch ”zeichenkettiger” oder so. An sich ist das aber auch falsch da überall ein Leerzeichen an beiden Seiten an die Daten zu pappen wenn man eigentlich die *Optik* beeinflussen will.

Das ist ziemlich viel Code der sehr ähnlich aussieht. Das könnte im Grunde auf so etwas schrumpfen (ungetestet):

Code: Alles auswählen

    def __init__(self):
        ...
        self._column_index_to_icon = {
            7: QIcon(str(ICON_PATH / "Tick_kn.png")),
            8: QIcon(str(ICON_PATH / "Tick_km.png")),
        }

    def data(self, index, role):
        if role == Qt.ItemDataRole.TextAlignmentRole:
            return Qt.AlignmentFlag.AlignCenter if column in {0, 5} else None

        if role == Qt.ItemDataRole.DisplayRole and 0 <= index.column() <= 6:
            return str(
                self._sql_data.record(index.row()).value(index.column())
            )

        if role == Qt.ItemDataRole.DecorationRole:
            return self._column_index_to_icon.get(index.column())

        return None
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

Super, das war ein entscheidender Tipp. Die Geschwindigkeit hat sich erheblich verbessert, eine wirklich kleine Verzögerung ist nur noch beim Start der Tabelle spürbar. Soweit ist das Problem erst einmal gelöst. (Mal sehen, wenn 200 Datensätze vorliegen ...)

Ja, das "str()" ist absolut sinnfrei. So etwas passiert, wenn man diverse Dinge ausprobiert und dann an einer "Lösung" hängen bleibt. (Es ist meine erste Qt Anwendung, da feht es noch an vielen Ecken und Kanten).
Mit dem "Anpappen" bin ich recht zufrieden. Das Erscheinungsbild ist nicht so gedrungen. Für mich (und der Anwenderin) stimmt alles - Ziel also zunächst erreicht!
Aber eine ehrliche Frage dazu: Was spricht wirklich gegen so eine Lösung? Geht es hier nicht "nur" um die Programmierer-Ehre? Ein Programmierer, der etwas auf sich hält, löst dies eleganter / souveräner ... Ist dies so? Bewerte ich es falsch?

Vielen Dank für Deinen Code-Entwurf. Es ist interessant zu sehen, wie ein Profi so etwas löst.
Für mich als Anfänger ist es jedoch erst einmal wichtig, einen Code in neuer Materie sauber zum Laufen zu bekommen.
(Und der Code ist noch nicht fertig, da er mal für diverse SQL-Abfragen dienen soll. Ich stelle mir für den weiteren Code vor, dass ich nicht nur den Header übergebe, sonder auch die Beschreibung "role". Schaun wir mal!)

Auf jeden Fall vielen Dank für's "lösen" :D
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

PS: Eine Idee, wie ich die Icons zentriert - also "mittig" - bekomme?
Benutzeravatar
__blackjack__
User
Beiträge: 13117
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@GiJay: Das erweitern mit Leerzeichen ist halt eine Änderung der Daten um indirekt eine Änderung der Darstellung zu erreichen, statt an der Stelle der Darstellung anzusetzen. Aus dem (Daten)Modell würden sich ja beispielsweise auch Editoren die Werte holen. Das heisst sobald man Zellen auch bearbeitbar macht, hat man diese zusätzlichen Leerzeichen auch im Editor. Und muss dann Code schreiben der das auf dem Rückweg wieder gerade biegt. Was erst mal nur nervig ist, aber spätestens dann zum Problem wird, wenn führende oder angehängte Leerzeichen nicht einfach entfernt werden dürfen, sondern auch tatsächlich genau so wie eingegeben in der Datenbank landen sollen.

Einfacher Ansatz der bei der Darstellung ansetzt, könnte vielleicht das implementieren von `sizeHintForColumn()` auf dem View sein, das die originale Methode aufruft und dann da einfach noch ein bisschen drauf addiert, um die Spalten etwas breiter als den reinen Inhalt zu machen.

Hast Du zum zentrieren der Icons das Alignment wie bei den anderen zentrierten Zellen schon ausprobiert?

Muss es eigentlich ein Icon sein? So ein Haken mit Kreis gibt es in Unicode glaube ich nicht, aber Haken ohne Umrahmung und ich glaube in einem Kästchen gibt es als Zeichen. Das liesse sich dann auch wie Text zentrieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

Kapiert !!! Ok, die Daten hier bei mir werden nur angezeigt und nicht bearbeitet. Durch einen Doppelclick wird bei mir ein Datensatz ausgewählt und in einem anderen Bildschirm zur Bearbeitung freigegeben. Aber Dein Argument ist natürlich sinnvoll, wieder dazugelernt 8)

Das Formatieren über TextAlign funktioniert leider nicht. Im Internet habe ich eine Lösung über "delegate" gefunden. In etwa so:

Code: Alles auswählen

class IconDelegate(QStyledItemDelegate):
    def initStyleOption(self, option, index):
        super(IconDelegate, self).initStyleOption(option, index)
        option.displayAlignment = Qt.AlignmentFlag.AlignCenter
        
Habe das aber nicht verstanden und werde mich daher erst mit anderen "Problemen" beschäftigen.

Dein Ansatz mit Schriftzeichen statt Icon ist dabei vielleicht gar nicht so verkehrt ... werde ich mal weiter verfolgen.

DANKE !!!
Ernie1412
User
Beiträge: 161
Registriert: Freitag 10. Januar 2020, 20:38

QIcon(str(ICON_PATH / "Tick_kn.png")) könnte man durch *.qrc (resource collection files) ersetzen. Sieht dann in etwas so aus: QIcon(":/icons/Tick_kn.png") Vorteil: du hast nur eine *.py datei die du importieren musst, kein pathlib mehr nötig und kürzer, übersichtlicher. Durch anführendes ":/" erkennt Python, das es eine qrc ist.
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

Oh, kannte ich nicht. Vielen Dank! Deinen Tipp werde ich aber erst in der Zukunft einsetzen. Ich habe mich hier nun für Unicode-Zeichen entschieden. Diese mittig zu setzen ist recht einfach (da Text) und es gibt keine "Ladehemmungen".
GiJay
User
Beiträge: 36
Registriert: Freitag 5. März 2021, 14:40
Wohnort: Ratingen
Kontaktdaten:

Bild
Antworten