pyQt und Qtable anzeige der aktuellen Tabellenzeile

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo zusammen,

vielleicht kann mir jemand von euch bei meinemProblem mit pQt und
einem Qtablewidget helfen.
Ich fülle aus einem Formular main Daten mittels dictionary in ein
tablewidget des dialogs customer.

tab_1 Vorname, Nachname usw...

Das befüllen des tablewidgets funktioniert auch schon wie folgt:

Code: Alles auswählen

for dbKey, dbAttrib in self.Dict['Adressen'].items():
    rowNr =dbKey
    self.customerGUI.customerGUI.tab_1.setRowCount(rowNr)         
    item = QtGui.QTableWidgetItem(dbAttrib["Vorname"])
    self.customerGUI.customerGUI.tab_1.setItem(dbKey-1, 0, item)
    item = QtGui.QTableWidgetItem(dbAttrib["Nachname"])
    self.customerGUI.customerGUI.tab_1.setItem(dbKey-1, 1, item)
self.customerGUI.customerGUI.tab_1.sortItems(0)
self.customerGUI.show()    
Nun will ich aber per doppelclick auf die Werte der aktuellen Zeile,
wie Vorname, Nachname usw. in einzelne Editfelder kopieren.

Das Signal für den doppelclick habe ich schon. Auch die def existiert
schon.
Was ich noch nicht herausgwefunden habe, ist, wie ich den aktuellen Wert
der Zeile heraushole. Ich finde weder das richtige Attribut noch die richtige
Technik dafür.

Code: Alles auswählen

self.connect(self.customerGUI.tab_1, QtCore.SIGNAL("cellClicked(int,int)"), self._getTableData)


def _getTableData(self):        
    print self.customerGUI.tab_1.rowCount()   #Anzahl der Datensätze        
    item3 = self.customerGUI.tab_1.item(1,1)
    data = item3.data(QtCore.Qt.DisplayRole)
    print data.toString()
Ich hoffe ich konnte mein Problem deutlich genug schildern.

Vielen Dank schon mal
vorlautboy
User
Beiträge: 38
Registriert: Sonntag 7. Dezember 2008, 18:43

MDGo88 hat geschrieben:Das Signal für den doppelclick habe ich schon
das signal, das du suchst ist eher QTableWidget.itemDoubleClicked - das verbindest du mit einem slot, der in etwa so aussieht und fertig!

Code: Alles auswählen

    def printItemText(self, item):
        print item.text()
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Vielen Dank,

hat funktioniert. Konnte ja nicht funktionieren, wenn man das falsche
Signal wählt.

Tschüss
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Welchen Trigger muss ich denn nutzen, wenn ich mir denn angeclickten
Index der Zeile ausgeben lassen will und wie kann ich den dann anzeigen.

Bisher kann ich immer nur auf die aktuelle Zelle zugreifen.

Ich habe vor, die Zeiel anzuclicken um dann mit dem Index
alle zu dem Record gehörenden Felder auszulesen und in die entsprechenden Editfelder zu übertragen.

Viele Grüße
MDGo88
Benutzeravatar
mkesper
User
Beiträge: 919
Registriert: Montag 20. November 2006, 15:48
Wohnort: formerly known as mkallas
Kontaktdaten:

Meinst du

Code: Alles auswählen

QTableWidgetItem.row()
?
Siehe http://doc.trolltech.com/4.5/qtablewidgetitem.html
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Ja, wahrscheinlich. Bin mir aber n icht sicher.
Leider finde ich mich auf der Trolltech-Seite noch nicht so gut zurecht, da alles auf C++ gemünzt ist und ich mich auch noch nicht so gut mit pyQt auskenne.

Welches Signal müsste ich denn setzen und wie kann cih darauf zugreifen.

Danke
MDGo88
vorlautboy
User
Beiträge: 38
Registriert: Sonntag 7. Dezember 2008, 18:43

MDGo88 hat geschrieben:Welchen Trigger muss ich denn nutzen, wenn ich mir denn angeclickten
Index der Zeile ausgeben lassen will und wie kann ich den dann anzeigen.

Bisher kann ich immer nur auf die aktuelle Zelle zugreifen.

Ich habe vor, die Zeiel anzuclicken um dann mit dem Index
alle zu dem Record gehörenden Felder auszulesen und in die entsprechenden Editfelder zu übertragen.

Viele Grüße
MDGo88
Hallo,

wenn du nur an der Zeile interessiert bist, dann solltest du evtl. doch wieder auf QTableWidget.cellDoubleClicked umsteigen; dieses Signal hat nämlich als ersten Parameter die Zeile, in der sich das Item befindet. Außerdem bietet sich warscheinlich an, das SelectionBehavior auf QAbstractItemView.SelectRows zu setzen.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo,

vielleicht hilft es weiter, wenn ich schreibe ws ich vor habe, denn irgendwie komme ich nicht weiter.

1) Ich habe eine SqLite-Tabelle mit der Struktur
key Vorname Name
A1 Herbert Test
A2 Peter Mustermann
.. .. ..

2) Diese lese ich in ein dictionary ein, sortiere es und übertrage die
Datensätze in ein Qtable widget.

3) Ich möchte nun, daß ich durch anclicken der eines Datensatzes den
jeweiligen Wert key erhalten und anhand des keys mittels SQL die
zugehörenden Wertes aus der DB holen und diese in entsprechende
Edit-Felder zu übertragen (Detailansicht eines Datensatzes.


Bisher kann ich immer nur entweder einen einzelnen Inhalt einer Zelle ausgeben lassen, oder die aktuelle Zeilennummer bestimmen, welche aber nicht unbedingt dem key der Daten enstpricht.Irgendwie bekomme ich hier den Dreh nicht.
In anderen Programmiersprachen ist dies eher einfacher...
Vielen Dank für Eure Geduld ;)

Viele Grüße
MDGo88
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Es geht 100% mit Qt viel leichter als mit anderen Frameworks...
1) Verwende doch bitte ein existierendes Model für DBs, z.B. reicht in deinem Fall ein QSqlTableModel
2) Anstelle des QTableWidget nimmst du natürlich jetzt ein QTableView
3) Für Anzeige der Daten in einem eigenen Formular verwendest du am einfachsten den QDataWidgetMapper.

Achtung, ungetesteter Code!

Code: Alles auswählen

model = QSqlTableModel() # wir verwenden die Default-DB
view = QTableView()
view.setModel(model)
# diese 3 Zeilen reichen, um eine Tabelle mit den DB-Werten zu füllen!

# keyLineEdit usw. sind Widgets in deiner Form zum Anzeigen der Daten
mapper = QDataWidgetMapper()
mapper.setModel(model)
mapper.addMapping(keyLineEdit, 0)
mapper.addMapping(vornameLineEdit, 1)
mapper.addMapping(nameLineEdit, 2)
Und schau dir dann auch gleich die Doku zu setCurrentModelIndex an. Da ist ein Beispiel, wie du automatisch bei Ändern der currentRow() deines Model neu Daten in deine Widgets bekommst.

So brauchst du echt nur gute 10 Zeilen um
*) Eine Db in einer Tabelle zu visualisieren
*) Daten in ein eigenes Formular zu mappen
*) Diese Daten zu ändern und gleich in der Datenbank wieder zu speichern .

Grüße
Franz
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,
vielen Dank für Deine Antwort. Ich denke das geht genau in die Richtung,
die ich einschlagen will.
Ich weiß zwar im Moment noch nicht, wie ich das ganze in meinem Script umsetzen soll, aber (Wo finde ich das richtige Objekt, wie binde iche es ein
und wie rufe ich es in meiner eigenen Class auf...).
Da werde ich wohl erst einmal basteln müssen.
Es kann sein, daß ich mich dann noch einmal melden werde.

Gruß
MDGo88
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo,

ich hab's mal mit der Methode von Franz versucht, bin dann aber auf
große Probleme schon am Anfang gestossen.
Also, ich erstellte mit dem Assistenten ein Dialog mit dem Namen ui_customer.py (Darin enthalten ein tableView mit dem Namen tab_1).

Ich bin mir aber nicht sicher welches tableview ich nutzen soll (itembased oder modelbased).
Ich tippe mal auf modellbased.
Wenn ich aber modelbased auswähle, habe ich nicht die Möglichkeit Elements anzugeben, bzw. die Datenquelle anzugeben (z.B.Sqlite3 DB)
Ich nehme an, dies geschieht nachher im Code oder auch nicht?
Wo und wie gebe ich denn das model an.
Ich habe folgenden Klassen bzw. Objekte in meinem Projekt

main ui_main main.ui
customer ui_customer.py customer.ui

Damit beim übersetzen der main.ui bzw. der customer.ui nicht immer alle Änderungen überschrieben werden, habe ich Instanzen der Klassen ui_main bzw. ui_customer gebildet.

Das tableview sollte nachher im Formular customer.py liegen.

So, mehr weiss ich nicht, wie kann ich es so ansprechen, dass es mir Daten auswirft.
Ich würde auch gerne bei Bedarf meinen Code zur Verfügung stellen (wenn's hilft)

Viele Grüsse
MDGo88
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

MDGo88 hat geschrieben:Ich tippe mal auf modellbased.
Genau die. Das andere ist nämlich ein Table Widget und das ist für QT was anderes.
Wenn ich aber modelbased auswähle, habe ich nicht die Möglichkeit Elements anzugeben, bzw. die Datenquelle anzugeben (z.B.Sqlite3 DB)
Das machst du auch nicht über den Designer. Der ist nicht dazu gedacht, ein komplettes Programm samt content und Logik ohne zu coden zu erstellen sondern lediglich ein User-Interface-Designer.
Ich nehme an, dies geschieht nachher im Code oder auch nicht?
Wo und wie gebe ich denn das model an.
Les dir mal die Tutorials zum Designer durch.
Du hast meist eine Klasse, welche dein per Designer erstelltes ui anzeigt. Wahrscheinlich ist das bei dir customer. Die Klasse erbt von QWidget, und hat wahrscheinlich das ui_customer als Member. Im Konstruktor (__init__) jener Klasse machst du ein self.ui_customer.setupUi(self). Jetzt existiert dein tab_1 und kann per

Code: Alles auswählen

self.model = QSqlTableModel()
self.ui_customer.tab_1.setModel(model)
mit Daten gefüttert werden. Du musst natürlich vorher noch dein Datenbankobjekt per QSqlDatabase.addDatabase() erstellen, sonst wird dein Model leer sein.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,

1) Hab verstanden Qtableview modelbased benutzen (Hab ich)
2) Im __init__ Teil von customer model initialisieren (Hab ich)
3) Datenbank initialisieren kann er nicht??? Evtl nicht alles importiert oder so?

Fehlermeldungen:

QSqlDatabase: D:\MyData\PyProgs\db\test.db driver not loaded
QSqlDatabase: available drivers: QSQLITE QODBC3 QODBC
QSqlDatabase: QSQLITE QODBC3 driver not loaded
QSqlDatabase: available drivers: QSQLITE QODBC3 QODBC


Code: Alles auswählen

# -*- coding: utf-8 -*-

import sys
import os
import time
from PyQt4 import QtCore, QtGui, QtSql
import Ui_customer

class CustomerGUI(QtGui.QDialog):
    def __init__(self, parent=None):
        self.customerGUI = QtGui.QDialog()
        QtGui.QDialog.__init__(self, parent)

        self.customerGUI = Ui_customer.Ui_Dialog()
        self.customerGUI.setupUi(self)
        
        self.model = QtSql.QSqlTableModel()
        self.customerGUI.tab_1.setModel(self.model)

        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName(os.getcwd() + os.sep + 'db\\test.db')
        self.db.open()

        view = QtGui.QTableView()
        view.setModel(self.model)         
        
        
        
        
        ## Events Main ##
        self.connect(self.customerGUI.pushButton, QtCore.SIGNAL("clicked()"), self._beforeExit)
        self.connect(self.customerGUI.tab_1, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.printItemText)
        
        ## Events Main ##
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

MDGo88 hat geschrieben:3) Datenbank initialisieren kann er nicht??? Evtl nicht alles importiert oder so?

Fehlermeldungen:

QSqlDatabase: D:\MyData\PyProgs\db\test.db driver not loaded
Der Fehler kommt aber nicht vom model erstellen, sondern vom DB erstellen. Wie bekommst du dein DB-Objekt?

Ansonsten steht es eigentlich in der Doku:
Du kannst dem QSqlTableModel-Konstruktor einen Parameter mitgeben, nämlich eine Instanz von QSqlDatabase. Wenn du dies nicht tust, wird die Deafult-Database genommen. So eine erhältst du, wenn du QSqlDatabase::addDatabase() keinen connectionName mitgibst.
Das schaut dann in etwa so aus:

Code: Alles auswählen

db = QSqlDatabase.addDatabase("QSQLITE")
db.setDatabaseName("D:\MyData\PyProgs\db\test.db") # hier bin ich mir nicht sicher, aber eigentlich solltest du da keinen "" sondern einen "/" als Separator nehmen...
# hab nur ein Linux

# wichtig: db öffnen, bevor du das Model erstellst...
model = QSqlTableModel() # hier wird die default-connection genommen
Wenn das so schon bei dir aussieht und immer noch nicht geht, dann reduzier deinen Code so weit, wie das Problem noch auftritt, und poste diesen komplett.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,

ich hab den Code so umgebaut, daß ich zuerst die DB-Connection aufgebaut habe. Fehlermeldung ist jetz weg und ich habe ein ok = db.open() eingebaut, welches mir ein True liefert.
Ich denke, daß ich die DB jetzt geöffnet habe.
Allerdings ist die Tableview immer noch leer.
Fehlt denn nicht normalerweise noch die Angabe welche Tabelle und Felder ich in der view anzeigen möchte?
Z.B. Tabelle Adresse : id, Vorname, Nachname usw.

Viele Grüße
MDGo88

Code: Alles auswählen

# -*- coding: utf-8 -*-

import sys
import os
import time

from PyQt4 import QtCore, QtGui, QtSql
import Ui_customer

class CustomerGUI(QtGui.QDialog):
    def __init__(self, parent=None):
        self.customerGUI = QtGui.QDialog()
        QtGui.QDialog.__init__(self, parent)

        self.customerGUI = Ui_customer.Ui_Dialog()
        self.customerGUI.setupUi(self)
        
        self.db = QtSql.QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName(os.getcwd() + os.sep + 'db\\firma.db')
        ok = self.db.open()
        print ok            
        self.model = QtSql.QSqlTableModel()
        self.customerGUI.tab_1.setModel(self.model)

        view = QtGui.QTableView()
        view.setModel(self.model)         
        
        ## Events Main ##
        self.connect(self.customerGUI.pushButton, QtCore.SIGNAL("clicked()"), self._beforeExit)
        self.connect(self.customerGUI.tab_1, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.printItemText)        
        ## Events Main ##

        
    def printItemText(self, item):
        pass
        
    # Wird ausgefürt, wenn im Menü Datei "beenden" gewählt wird
    def _beforeExit(self):
        print "Kundenform wird geschlossen"
        QtGui.QMainWindow.close(self)


if __name__ == "__main__":
    
    app = QtGui.QApplication(sys.argv)
    customerGUI = CustomerGUI()
    customerGUI.show()
    sys.exit(app.exec_())
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

http://www.riverbankcomputing.co.uk/sta ... ml#details
Solltest du mittlerweile gelesen haben (hoffe ich). Dann weißt du dass du natürlich noch deine Tabelle setzen musst die du anzeigen willst.

Code: Alles auswählen

model.setTable("personen") # oder wie deine Tabelle heißt
Denn jede DB kann mehrere Tabellen haben, und das TableModel ist halt zur Anzeige von einer Tabelle (mehr macht irgendwie in einem TableView wenig Sinn) ausgelegt.

Wenn du die Tabellen verbindest (foreign keys), ist das hier natürlich nix mehr, dann brauchst du QSqlRelationalTableModel.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,

hab's schon eigebaut. So, die Überschriften werden angezeigt, aber leider keine Daten.
Hast Du eine Idee?

Gruß
MDGo88
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

MDGo88 hat geschrieben:hab's schon eigebaut. So, die Überschriften werden angezeigt, aber leider keine Daten.
Hast Du eine Idee?

Code: Alles auswählen

model.select()
Steht aber so auch in dem Beispiel aus dem link oben ;)

// edit: Dein Beispiel ist immer noch zu komplex. Du brauchst das ganze .ui-Gedöhns samt Klasse gar nicht. Für so kleine Tests nehm ich immer die Python-Konsole her und experimentier einfach bissl rum. So schaut es jetzt bei mir aus:

Code: Alles auswählen

>>> from PyQt4.Qt import *
>>> app = QApplication([])
>>> db = QSqlDatabase.addDatabase("QSQLITE")
>>> db.setDatabaseName("/pfad/zur/test1.db")
>>> model = QSqlTableModel()
>>> model.setTable("person")
>>> view = QTableView()
>>> view.setModel(model)
>>> model.select()
True
>>> view.show()
Sind genau 10 Zeilen Code incl. dem Import.
Zuletzt geändert von franzf am Montag 5. Oktober 2009, 19:24, insgesamt 1-mal geändert.
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,

endschuldige, ich hab den Link zu spät gesehen. Jatzt hats funktioniert.
Ist ja echt nicht all zuviel Code.

Vielen, vielen Dank für Deine Hilfe.

Gruß
MDGo88
MDGo88
User
Beiträge: 18
Registriert: Sonntag 20. September 2009, 13:39

Hallo Franz,

wenn es geht, würde ich Dich doch nochmal bemühen. Ich bin im Prinzip
wieder an der gleichen Stelle, wie am Anfang meines Problems.
Ich würde gerne die Daten einer Tabellenzeile in die einzelnen
LineEditFelder schreiben. Dazu hast Du mir schon mal das Coding für das Mapping gesendet.

Dieses habe ich über ein Sgnla versucht aufzurufen (Ohne Erfolg)

Viele Grüße
MDGo88

Code: Alles auswählen

        ## Events Main ##
        self.connect(self.customerGUI.pushButton, QtCore.SIGNAL("clicked()"), self._beforeExit)
        self.connect(self.customerGUI.tab_1, QtCore.SIGNAL("doubleClicked(QModelIndex)"), self.printItemText) 
        ## Events Main ##

        
    def printItemText(self, item):
        mapper = QtGui.QDataWidgetMapper()
        mapper.setModel(self.model)
        mapper.addMapping(self.customerGUI.le_Vorname, 1)
        mapper.addMapping(self.customerGUI.le_Nachname, 2)        
        
Antworten