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

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: 14493
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: 14493
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.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

"MyTreeView.keyPressEvent(self, QKeyEvent)" ist weg!

Ist mein zuletzt geposteter Code richtig, oder doch falsch?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

"Falsch". Wie ich schon sagte - der Filter ist eine Eigenschaft des TreeView, nicht der TreeView selbst ist ein Filter.

Ich lege dir http://doc.qt.io/qt-4.8/eventsandfilters.html an's Herz - darin ist das alles schoen beschrieben.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Bestimmt eine schöne Doku, leider hilft mir das trotz allem nicht weiter.
Ohne ein Code-Beispiel, zum besseren Verständnis, muss ich im Moment die Flügel streichen. :K
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Gibt es ausser clicked, doubleClicked und pressed noch anderes, das sich auf die Tastatur bezieht?
BlackJack

@Nobuddy: `clicked()` und `doubleClicked()` bezieht sich auf die Maus und nicht auf die Tastatur.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

@BlackJack, klar das weiss ich. ich wollte wissen, ob es so was auch für die Tastatur gibt?
BlackJack

@Nobuddy: Als Signale nicht soweit ich weiss. Es gibt eben auf `QWidget` die Methoden `keyPressEvent()` und `keyReleaseEvent()` die man bei abgeleiteten Klassen überschreiben kann, oder man leitet von `QObject` ab und implementiert die `eventFilter()`-Methode und installiert so ein Objekt dann mittels `installEventFilter()` für das Objekt wo man für Tasten etwas spezielles machen möchte. Da gibt es in der Qt-Dokumentation auch jeweils Beispiele in C++ für, die man auf Python übertragen kann/muss.

Andererseits kann man, wie schon gesagt, auch eine `QAction` um eine Tastatursequenz anreichern und die dann auch gleich für eine Schaltfläche und einen Menüpunkt in einem Kontextmenü verwenden. Das ist das was man wohl heute von einer Anwendung erwarten würde.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Leider komme ich trotz Bemühung nicht zum Erfolg.
Ich poste nochmals hier den aktuellen gekürzten Code.

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
        print('event', event)
        print('Qt.Key_Enter', Qt.Key_Enter)
        if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
            self.enterPressed.emit()
            print('enter key pressed')


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

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

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

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

    def events(self):
        MyTreeView.keyPressEvent(self, self.filter)


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 main():
    # 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)

if __name__ == '__main__':
    main()
Fehlermeldung zu obigem Code:

Code: Alles auswählen

Bus::open: Can not get ibus-daemon's address. 
IBusInputContext::createInputContext: no connection to ibus-daemon 
event <__main__.MyTreeView object at 0x7fd39c2a2af8>
Qt.Key_Enter 16777221
Traceback (most recent call last):
  File "table_test2.py", line 44, in events
    MyTreeView.keyPressEvent(self, self.filter)
  File "table_test2.py", line 16, in keyPressEvent
    if event.key() == Qt.Key_Enter or event.key() == Qt.Key_Return:
AttributeError: 'MyTreeView' object has no attribute 'key'
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ach du liebe Güte. Ein paar Hinweise:

- dein Filter gehört nur von Qobject abgelitten. Und besser benannt. Es ist ein Filter. Kein view.
- dein Fehler tritt auf, weil du im clicked einfach die MyTreeView keyPressEvent aufrufst. Als ob es eine classmethod wäre. Warum? Weder ist es eine solche, noch sollst DU die aufrufen, die wird schon automatisch aufgerufen.
- der Filter gehört auf den TableView, nicht aufs MainWindow
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Hallo __deets__, Danke für Deine Unterstützung!

Habe versucht, nach bestem Verständnis, Deine Worte umzusetzen.
Hier die Kurzversion.

Code: Alles auswählen

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

        self.table_model = MyTableModel(self, dataList, header)
        
        table_view = QTableView()
        # bind cell click to a method reference
        table_view.pressed.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):
        cellContent = item.data()
        sf = "You clicked on {}".format(cellContent)
        # display in title bar for convenience
        self.setWindowTitle(sf)
Fehlermeldung, kommt nun keine mehr!

Bin für weitere Hilfe dankbar!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Hallo Nobuddy,

ich muss gestehen ich weiss da nicht mehr so recht weiter. Du hast irgendwelche Dinge getan, aber das ist doch ganz schoen weit entfernt von dem, was ich angemerkt habe. Und mich beschleicht das Gefuehl, dass du gar nicht nachvollziehst, was ich meine. Wenn ich davon rede, dass "dein Filter gehört nur von Qobject abgelitten. Und besser benannt. Es ist ein Filter. Kein view.", dann bezieht sich das auf deine Klasse "MyTreeView", die von QTreeView ableitet. Aber stattdessen 'MyEventFilter' heissen sollte, und von QObject ableiten. Weil sie kein View ist, sondern ein Filter, und darum weder die Funktionalitaet noch den Namen braucht. Etc.

Wenn man so sehr aneinander vorbeiredet hat das glaube ich nicht wirklich Sinn, das weiter zu fuehren.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Schade ..., ein einfaches Beispiel hätte genügt, dann hätte ich auch verstanden, was Du mir mit Deinem Geschriebenem sagen willst.
Dazu musst Du noch nicht mal den Code neu schreiben. Copy & Paste, die von Dir beschriebenen Stellen editiert und fertig.
Ich habe schon zigmal Deine Links verfolgt, leider habe ich das Problem, kein Englisch zu beherrschen. Mit dem Google-Übersetzer ist es oft schwierig sinngemäss, das eigentliche zu verstehen. Bitte komm mir jetzt nicht mit "dann lerne erst mal E...", dazu fehlt mir einfach die Sprachbegabung und mein Alter ...
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Nochmal ein Versuch.

Code: Alles auswählen

class MyEventFilter(QObject):

    enterPressed = pyqtSignal()

    def __init__(self):
        QObject.__init__(self)

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


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

        self.table_model = MyTableModel(self, dataList, header)
        
        table_view = QTableView()
        
        # bind cell click to a method reference
        table_view.pressed.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):
        cellContent = item.data()
        sf = "You clicked on {}".format(cellContent)
        # display in title bar for convenience
        self.setWindowTitle(sf)
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Ist die Klasse "MyEventFilter", so richtig?
Antworten