Scallierung von QtWidgets

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

QTableView, habe ich im Designer auch schon mal in der Hand gehabt, da habe ich aber noch keine Ahnung wie das umgesetzt wird.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Na wattn Glueck, dass es so viele Beispiele dafuer gibt, an denen man sich das anschauen kann.

ZB http://www.python-forum.org/viewtopic.php?f=11&t=16817
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ja das ist es wirklich ...., schönes Beispiel. :wink:
Das mit "import *" sollte man aktualisieren.

Code: Alles auswählen

from PyQt4.QtCore import Qt, QAbstractTableModel, SIGNAL
from PyQt4.QtGui import QWidget, QTableView, QApplication, QVBoxLayout
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@__deets__, hast Du evtl. noch ein Beispiel für Binding?
Die Tabelle in dem Beispiel ist ja schreibgeschützt, was ja nicht verkehrt ist.
Wenn man nun sich einen Datensatz auswählt und mit Enter bestätigt, könnte man ja den Datensatz als Dataset in einer GUI ausgeben lassen, um diesen zu editieren.
Wenn es da etwas in der Richtung schon gebe, wäre ein oder mehrere Beispiele nicht schlecht.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe mich mit dem Code-Beispiel http://www.python-forum.org/viewtopic.php?f=11&t=16817 auseinander gesetzt.
Manches beginne ich zu verstehen, bei manch anderem fehlt noch der Funke. :wink:

PyQt4 wurde ja durch PyQt5 ersetzt. Gibt es evtl., ein in etwa gleiches Code-Beispiel mit PyQt5?

Wie ich gesehen habe, gibt es kein Binding wie bei tk.
Dazu habe ich folgende Infos gefunden:
http://pyqt.sourceforge.net/Docs/PyQt5/
http://pyqt.sourceforge.net/Docs/PyQt5/ ... event.html
https://doc.qt.io/qt-5/qkeyevent.html
Hier habe ich erhebliche Probleme, vielleicht könnte jemand von Euch mir mit ein paar Beispielen helfen, dies beeser zu verstehen?
BlackJack

@Nobuddy: Was willst Du denn damit machen? Nicht das Du am Ende auf dieser Ebene Sachen löst die man eigentlich auf einer höheren Ebene wie `QAction` und dem `shortcut` dort lösen würde.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, da hast Du recht, Qt ist sehr umfangreich ....

Als Grundlage nehme ich das Tabellen-Beispiel von __deets__.
In der Tabelle kann ich mit TAB und den Richtungstasten, den Focus auf eine Zelle setzen. Klicke ich mit der Maus auf eine bestimmte Zelle, wird der Zelleninhalt ausgegeben.

Ich würde nun gerne, den kompletten Datensatz mir ausgeben lassen, wenn ich mit Enter bzw. KP_Enter die ausgewählte Zelle bestätige.
Wie es mit tk funktioniert ist mir bekannt, aber mit Qt habe ich momentan keine Ahnung.
BlackJack

@Nobuddy: Ich würde sagen da musst Du wohl von `QTableView` erben und beispielsweise `keyReleaseEvent()` überschreiben. Enter kann übrigens mit der vorhandenen Tastenverarbeitung in Konflikt geraten wenn Du die Tabelle bearbeitbar machst.

Edit: Und sollte das letztendlich darauf hinauslaufen, dass Du für die ausgewählte Zeile einen Dialog zum bearbeiten öffnen möchtest, dann würde ich doch über ein `QAction` gehen, denn normalerweise bieten GUIs für so etwas ja eine Schaltfläche und ein Kontextmenü (und meistens auch einen Doppelklick).
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, ja der Gedanke ist einen Dialog zu öffnen, in dem der ausgewählte Datensatz bearbeitet werden kann.
In dem Tabellen-Beispiel sind ja die Zellen schreibgeschützt und das ist auch gut so, denn Änderungen würde ich gerne über einen Dialog machen.

Momentan setzte ich zum Bearbeiten eines Datensatzes aus einer Tabelle in tk, dies mit tk.Checkbutton um. Mit dem tk.Checkbutton wähle ich den betreffenden Datensatz aus und wähle über ein tk.Button, den betreffenden Workshop aus. Ich weiss nicht, ob das mit Qt und Checkbutton auch möglich wäre?
Die Bearbeitung einzelner Zellen erfolgt durch Selektion der Zelle und einem Enter. Dadurch öffnet sich ein Dialog zum Bearbeiten der Zelle.

Ich habe da schon für QAction nach Infos gegoogelt. Die Infos sind für einen Qt-Aussenstehenden nicht ganz verständlich.
Hier habe ich ein Beispiel mit QAction gefunden, mir ist nur noch nicht klar, wo ich da ansetzen muss.

Code: Alles auswählen

import sys
from PyQt4.QtCore import *
from PyQt4.QtGui import *

class menudemo(QMainWindow):
   def __init__(self, parent = None):
        super(menudemo, self).__init__(parent)
        
        layout = QHBoxLayout()
        bar = self.menuBar()
        file = bar.addMenu("File")
        file.addAction("New")
        
        save = QAction("Save",self)
        save.setShortcut("Ctrl+S")
        file.addAction(save)
        
        edit = file.addMenu("Edit")
        edit.addAction("copy")
        edit.addAction("paste")
        
        quit = QAction("Quit",self) 
        file.addAction(quit)
        file.triggered[QAction].connect(self.processtrigger)
        self.setLayout(layout)
        self.setWindowTitle("menu demo")
        
   def processtrigger(self,q):
        print(q.text()+" is triggered")
        
def main():
    app = QApplication(sys.argv)
    ex = menudemo()
    ex.show()
    sys.exit(app.exec_())
    
if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Statt abzuleiten empfehlen sich bei Qt inzwischen die EventFilter. Zusammen mit den Signalen aus der Selection des Views solltest du da etwas basteln koennen.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Wenn ich

Code: Alles auswählen

        # bind cell click to a method reference
        table_view.clicked.connect(self.showSelection)
verwende, kann ich mit

Code: Alles auswählen

    def showSelection(self, item):
        mylist = self.table_model.sorted_list()
        dataset = mylist[item.row()]
        print(dataset)  # test
mir den mit der Maus ausgewählten Zelle den Datensatz ausgeben lassen.

Nur wie ich das mit QAction oder mit EventFilter berwerkstelligen soll ... :K

Ein Beispiel dazu, wäre vielleicht hilfreich.
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du musst doch nur einen installieren, und wenn ein RETURN kommt in der Selection nachschauen, ob es da eine angewaehlte Zeile gibt.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Alles schön und gut, allerdings sehe ich den Wald vor lauter Bäumen nicht! :K
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich habe kein fertiges Beispiel, und auch nicht die Zeit, eines zu bauen. Aber wie BlackJack nie muede wird (ich verneige mich vor seiner Geduld) zu betonen: ein grosses Problem zerlegt man in kleine, bis man welche hat, die man versteht.

Also haeng doch ersmal einen EventFilter an deinen QTableView. Dazu kannst du zB dem hier folgen:

https://stackoverflow.com/questions/330 ... ent-filter
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Werde den Link durchgehen.

Danke und Grüße
Nobuddy
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Mein Code sieht momentan so aus (Baustelle)

Code: Alles auswählen

import operator  # used for sorting
from PyQt4.QtCore import Qt, QAbstractTableModel, SIGNAL, pyqtSignal
from PyQt4.QtGui import QWidget, QTableView, QApplication, QVBoxLayout, QTreeView, QKeyEvent

class MyTreeView(QTreeView):

    enterPressed = pyqtSignal()

    def __init__(self):
        QTreeView.__init__(self)

    def keyPressEvent(self, event): #QKeyEvent
        if event.key() == Qt.Key_Enter:
            self.enterPressed.emit()
            print('enter key pressed')


class MyWindow(QWidget):
    def __init__(self, dataList, header, *args):
        QWidget.__init__(self, *args)
        self.filter = MyTreeView()
        self.installEventFilter(self.filter)
        MyTreeView.keyPressEvent(QKeyEvent()) #<<<<<<<<<< Fehlermeldung
        # setGeometry(x_pos, y_pos, width, height)
        self.setGeometry(70, 150, 420, 250)
        self.setWindowTitle("Click on the header to sort table")

        self.table_model = MyTableModel(self, dataList, header)
        table_view = QTableView()
        # bind cell click to a method reference
        table_view.clicked.connect(self.showSelection)

        table_view.setModel(self.table_model)
        # enable sorting
        table_view.setSortingEnabled(True)

        layout = QVBoxLayout(self)
        layout.addWidget(table_view)
        self.setLayout(layout)

    def showSelection(self, item):
        # Hole Liste
        mylist = self.table_model.sorted_list()
        # Ausgabe des ausgewählten Datensatzes
        dataset = mylist[item.row()]
        # Editiere Datensatz
        dataset = list(dataset) # Test
        dataset[0] = 'aaa test' # Test
        dataset = tuple(dataset) # Test
        # Update Liste
        mylist[item.row()] = dataset
        self.table_model.edit_list(mylist)
        #
        cellContent = item.data()
        print(cellContent)  # test
        sf = "You clicked on {}".format(cellContent)
        # display in title bar for convenience
        self.setWindowTitle(sf)


class MyTableModel(QAbstractTableModel):
    """
    keep the method names
    they are an integral part of the model
    """
    def __init__(self, parent, mylist, header, *args):
        QAbstractTableModel.__init__(self, parent, *args)
        self.mylist = mylist
        self.header = header

    def rowCount(self, parent):
        return len(self.mylist)

    def columnCount(self, parent):
        return len(self.mylist[0])

    def data(self, index, role):
        if not index.isValid():
            return None
        elif role != Qt.DisplayRole:
            return None
        return self.mylist[index.row()][index.column()]

    def headerData(self, col, orientation, role):
        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
            return self.header[col]
        return None

    def sort(self, col, order):
        """sort table by given column number col"""
        self.emit(SIGNAL("layoutAboutToBeChanged()"))
        self.mylist = sorted(self.mylist,
            key=operator.itemgetter(col))
        if order == Qt.DescendingOrder:
            self.mylist.reverse()
        self.emit(SIGNAL("layoutChanged()"))

    def sorted_list(self):
        return self.mylist

    def edit_list(self, mylist):
        self.mylist = mylist
        return


# you could process a CSV file to create this data
header = ['First Name', 'Last Name', 'Age', 'Weight']
# a list of (fname, lname, age, weight) tuples
dataList = [
    ('Ben', 'Dover', 36, 127),
    ('Foster', 'Krampf', 27, 234),
    ('Barry', 'Chaurus', 19, 315),
    ('Sede', 'Anowski', 59, 147),
    ('Carolus', 'Gabel', 94, 102),
    ('Michel', 'Zittus', 21, 175),
    ('Annie', 'Waters', 31, 114)
    ]

app = QApplication([])
win = MyWindow(dataList, header)
win.show()
result = app.exec_()
print(result)
Dabei erhalte ich folgende Fehlermeldung:

Code: Alles auswählen

Traceback (most recent call last):
  File "table_test.py", line 132, in <module>
    win = MyWindow(dataList, header)
  File "table_test.py", line 32, in __init__
    MyTreeView.keyPressEvent(QKeyEvent()) #<<<<<<<<<< Fehlermeldung
TypeError: arguments did not match any overloaded call:
  QKeyEvent(QEvent.Type, int, Qt.KeyboardModifiers, str text='', bool autorep=False, int count=1): not enough arguments
  QKeyEvent(QKeyEvent): not enough arguments
was diese Zeile betrifft:

Code: Alles auswählen

        MyTreeView.keyPressEvent(QKeyEvent()) #<<<<<<<<<< Fehlermeldung
Was mache ich falsch?
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du sollst den EventFilter *auf dem QTableView* setzen. Nicht ihn zum EventFilter eines Fensters machen. Der Filter ist nur von QObject abgeleitet (wie in meinem Beispiel vorgemacht), und er wird dann auf dem QTableView installiert. Im Beispiel ist es ein TreeView, aber das ist ja nun egal.

Und warum rufst du keyPressEvent dann auf? Das soll doch passieren, wenn du einen Key *drueckst*. Wenn du das selbst aufrufst, ist das doch sinnlos :K
BlackJack

@Nobuddy: Alles. Die ganze Zeile macht keinen Sinn und Du vermischt hier jetzt auch das überschreiben der Methode und das Verwenden eines Eventfilters. Entweder das *eine* oder das *andere*. Beides macht keinen Sinn.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Also, dann habe ich ja die volle Punktzahl .... :K

Ich habe die zwei ersten Zeilen hier her positioniert

Code: Alles auswählen

    # you could process a CSV file to create this data
    header = ['First Name', 'Last Name', 'Age', 'Weight']
    # a list of (fname, lname, age, weight) tuples
    dataList = [
        ('Ben', 'Dover', 36, 127),
        ('Foster', 'Krampf', 27, 234),
        ('Barry', 'Chaurus', 19, 315),
        ('Sede', 'Anowski', 59, 147),
        ('Carolus', 'Gabel', 94, 102),
        ('Michel', 'Zittus', 21, 175),
        ('Annie', 'Waters', 31, 114)
        ]

    app = QApplication([])
    filter = MyTreeView()
    app.installEventFilter(filter)
    win = MyWindow(dataList, header)
    win.show()
    result = app.exec_()

Nur wohin mit diesem?

Code: Alles auswählen

        MyTreeView.keyPressEvent(self, QKeyEvent)
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich weiss nicht, wieviel klarer ich das ausdruecken soll: der View ist nicht der Filter. Der View HAT einen Filter. Und MyTreeView.keyPressEvent(self, QKeyEvent) muss weg, nicht wohin.
Antworten