QListView signals

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo,

wenn ich in einer QListView einen anderen Eintrag auswähle und damit das "clicked"-Signal sende, wie komme ich an den Index des Eintrags im empfangenen Slot (z.B. im einfachsten Fall, um den Taxt eines Labels zu ändern, etc...)?

Stehe irgendwie auf dem Schlauch gerade.

Danke und Gruß.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das steht doch in der Doku: https://doc.qt.io/qt-6/qabstractitemview.html#clicked - du bekommst den Index.
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

__deets__ hat geschrieben: Freitag 19. April 2024, 10:47 Das steht doch in der Doku: https://doc.qt.io/qt-6/qabstractitemview.html#clicked - du bekommst den Index.
Ja, danke. Ich habe aber das Problem, dass ich an den Eintrag, den ich ausgewählt habe, nicht heran komme. D.h. dahinter liegt ja mein model, welches ist auch mit .model erreiche. Jedoch kann ich keinerlei funktionen darauf aufrufen. D.h. example_list.model.some_function() ist nicht möglich. Wie komme ich also tatsächlich an die Listeneinträge dran? Finde in der Doku auch nichts bzgl. item(index) oder sonstwas. Auch in den vererbenden Klassen nicht.
Benutzeravatar
__blackjack__
User
Beiträge: 13131
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Du suchst wohl die `data()`-Methode.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

__blackjack__ hat geschrieben: Freitag 19. April 2024, 13:22 @mechanicalStore: Du suchst wohl die `data()`-Methode.
Hallo __blackjack___,
ja, die habe ich auch schon gesucht. Und nicht erreichbar über die QListView.model, sondern nur über das model selbst, das hatte ich übersehen. Funktioniert jetzt. Besten Dank.
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo,

jetzt habe ich doch noch ein Problem. Da im u.g. (stark gekürzten) Beispiel id der primary key ist, kann es natürlich beliebig viele internal_number geben. Durch die Auswahl in der QListView gelange ich über index und die data methode wiederum an die id. Sind aber mehrere gleiche Einträge drin, bekomme ich wegen .first() immer die erst gefundene id zurück. Gibt es einen Weg, über den Index der QListView auch direkt wieder an die id zu kommen?

Code: Alles auswählen

#sqlmodel
class Part(Base):
    __tablename__ = "part_basedata"

    id: Mapped[int] = mapped_column(primary_key=True)
    measurings: Mapped[List["Measuring"]] = relationship(back_populates="part")
    internal_number: Mapped[int]
    ...

    @classmethod
    def get_all_internal_numbers(cls, session) -> int:
        return session.execute(select(cls.internal_number)).scalars().all()

    @classmethod
    def get_id_by_internal_number(cls, session, internal_number) -> int:
        return session.scalars(select(cls.id).where(cls.internal_number == internal_number)).first()

#gridmodel
class PartListModel(QAbstractListModel):
    def __init__(self, session):
        super().__init__()

        self.session = session
        self.part_data = Part.get_all_internal_numbers(self.session)

    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
            return self.part_data[index.row()]

#gridview
class MainWindow(QMainWindow):
    def __init__(self, session):

        self.parts_view = QListView()
        self.parts_model = PartListModel(self.session)
        self.parts_view.setModel(self.parts_model)
...
        self.parts_view.clicked.connect(self.set_part_description_labels)
...
    @Slot()
    def set_part_description_labels(self, index):
        internal_number = self.parts_model.data(index, Qt.ItemDataRole.DisplayRole)
        part_id = Part.get_id_by_internal_number(self.session, internal_number)
        ...
        self.part_internal_number_description.setText(f'Part-Number: = {str(internal_number)}')
        ...
        print(part_id)
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

...Gibt es einen Weg, über den Index der QListView auch direkt wieder an die id zu kommen?
Jemand eine Idee dazu?
Benutzeravatar
__blackjack__
User
Beiträge: 13131
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mechanicalStore: Ich würde da einfach beides von der Datenbank abfragen. Also eigentlich würde ich sogar einfach ganze `Part`-Objekte abfragen. Denn wozu ein ORM wenn man dann doch immer nur einzelne Werte abfragt und nicht die Objekte. Vielleicht würde es sogar Sinn machen ein Model zu erstellen das mehr als nur diese eine Spalte der Datenbank liefert. Man kann bei QListView ja festlegen welche Spalte verwendet werden soll.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

@__blackjack__: Es werden quasi die Autofilter einer Excel Liste nachgebildet (natürlich nur ,was die Auswahl betrifft). In Part gibt es Einträge, zu jedem dieser Einträge gibt es in Measuring eine Teilmenge. Ebenso gibt es zu jedem Eintrag in Measuring eine Teilmenge in MeasuringPiece. Also quasi surjektive Abbildungen. Wähle ich aus Part einen Eintrag aus, soll die entsprechende Teilmenge in Measuring (d.h. ebenfalls wieder eine QListView) aufgelistet werden (und vice versa nach Unten, wieder QListView). Da Part auch gleiche Einträge haben kann, wird eben der "Rückweg", die id anhand des (doppelt oder mehrfachen) Eintrags zu finden, nicht mehr möglich.
Du meinst also, dass für jede der 3 Tabellen jeweils alles abgefragt und nur die entsprechende Spalte angezeigt wird?!
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

@__blackjack__:

stehe irgendwie auf dem Schlauch. Wie Du geschrieben hast, ist es besser, das ganze Object abzufragen, und in der QListView nur die entsprechende Spalte anzuzeigen. Das funktioniert auch, aber wie komme ich wieder an das Object selbst dran? Das muss doch über den Index gehen, aber ich habe hier offenbar nicht die Struktur, die ich brauche?!

Gekürztes Beispiel:

Code: Alles auswählen

# sqlqlchemy
class Part(Base):
    __tablename__ = "part_basedata"

    id: Mapped[int] = mapped_column(primary_key=True)
    measurings: Mapped[List["Measuring"]] = relationship(back_populates="part")
    internal_number: Mapped[int]
    internal_name: Mapped[Optional[str]]
    customer_number: Mapped[Optional[str]]
    customer_name: Mapped[Optional[str]]
    ...
    @classmethod
    def get_all_objects(cls, session):
        return session.execute(select(cls)).scalars().all()
    
# qt model
class PartListModel(QAbstractListModel):
    def __init__(self, session):
        super().__init__()

        self.session = session
        self.part_data = Part.get_all_objects(self.session)
    ...
    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
            return self.part_data[index.row()].internal_number

# qt view
    ...
    self.parts_view = QListView()
    self.parts_model = PartListModel(self.session)
    self.parts_view.setModel(self.parts_model)
    ...
    self.parts_view.clicked.connect(self.set_part_description_labels)
    ...
    @Slot()
    def set_part_description_labels(self, index):

        internal_number = self.parts_model.data(index, Qt.ItemDataRole.DisplayRole)
        print(type(internal_number))
        print(internal_number)
        print(type(self.parts_model.data))
        print(self.parts_model.data)
        print(self.parts_model.data[index.row()])
        ....
Ausgabe:

Code: Alles auswählen

<class 'int'>
80757
<class 'method'>
<bound method PartListModel.data of <gridmodel.PartListModel(0x61d86b62c240) at 0x72c01b2a9900>>
Traceback (most recent call last):
  File "/home/******/develop/git/measuring/gridview.py", line 83, in set_part_description_labels
    print(self.parts_model.data[index.row()])
          ~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
TypeError: 'method' object is not subscriptable    
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Der Parameter index ist kein QIndex. Warum auch immer. Gib dir aus, was es eigentlich ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13131
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

`data` ist eine Methode und [ ] sind die falschen Klammern für einen Methodenaufruf.
“There will always be things we wish to say in our programs that in all known languages can only be said poorly.” — Alan J. Perlis
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Ja, und damit komme ich dann zu:

Code: Alles auswählen

print(self.parts_model.data())
Ausgabe:

Code: Alles auswählen

    print(self.parts_model.data())
          ^^^^^^^^^^^^^^^^^^^^^^^
TypeError: PartListModel.data() missing 2 required positional arguments: 'index' and 'role'
Womit ich dann wieder hier bin:

Code: Alles auswählen

internal_number = self.parts_model.data(index, Qt.ItemDataRole.DisplayRole)
Was mir nur den Integerwert der Auswahl zurück gibt. Daher war ja meine Frage, wie ich wieder an das eigentliche Object komme, denn der Weg scheint ja komplett ins Leere zu führen.
mechanicalStore
User
Beiträge: 124
Registriert: Dienstag 29. Dezember 2009, 00:09

Hallo Zusammen,

habe es jetzt mit einem Workaround lösen können...

Code: Alles auswählen

class PartListModel(QAbstractListModel):
    def __init__(self, session):
        super().__init__()
  
        self.session = session
        self._part_data = Part.get_all_objects(self.session)
	...
    def data(self, index, role):
        if role == Qt.ItemDataRole.DisplayRole or role == Qt.ItemDataRole.EditRole:
            return self._part_data[index.row()].internal_number

    @property
    def datamodel(self):
        return self._part_data
        
  class MainWindow(QMainWindow):
    def __init__(self, session):
        super().__init__(windowTitle = "Main Window")
        self.resize(QSize(1500, 800))
        self.session = session

        self.setStatusBar(QStatusBar(self))

        self.parts_view = QListView()
        self.parts_model = PartListModel(self.session)
        self.parts_view.setModel(self.parts_model)
        .....
        
               
        self.parts_view.clicked.connect(self.set_part_description_labels)
        ...
        
    	@Slot()
    	def set_part_description_labels(self, index):
		# internal_number = self.parts_model.data(index, Qt.ItemDataRole.DisplayRole)
		object_choice = self.parts_model.datamodel
		part_id = object_choice[index.row()].id
		internal_number = object_choice[index.row()].internal_number
		internal_name = object_choice[index.row()].internal_name
		customer_number = object_choice[index.row()].customer_number
		customer_name = object_choice[index.row()].customer_name
Ist es legitim, nicht die data() methode des models zu benutzen (s. auskommentierte Zeile) und stattdessen das model sozusagen nochmal zuzuweisen (an object_choice)? Funktioniert so zwar, kommt mir aber immer noch umständlich vor. Der index der QLIstView lässt sich über die data() methode irgendwie nicht verwenden.

(Einrückung kommt am Schluss nicht richtig rüber)
Antworten