Qt.LeftButton kein Event in eventFilter

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

Hallo zusammen,
zur Bearbeitung eines Datensatzes aus einer Tabelle, verwende ich eine vertikalen Ausgabe.
Bestimmte Positionen, dürfen nicht verändert werden, dazu verwende ich als Beispiel ein Dictionary mit Namen 'writeLock'. Mit den Richtungstasten Up, Down und der Tab-Taste, werden die betreffenden Felder übersprungen.
Mit der linken Maustaste, habe ich noch keine Lösung gefunden, da beim Auslösen der Maustaste im eventFilter, kein Event stattfindet.
Ich hoffe, Ihr könnt mir bei Lösung dieses Problems helfen!

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# For Python3.x

import sys
from PyQt5.QtCore import (Qt, QEvent, QSize)
from PyQt5.QtGui import (QMouseEvent)
from PyQt5.QtWidgets import (QApplication, QTableWidget, QTableWidgetItem, 
    QLineEdit, QMainWindow, QMdiArea, QVBoxLayout, QWidget)


title = 'Backlight management'
header = ['Pos', 'Supplier', 'Artikel', 'Benennung']
dataset = ['001', '<?>', '<?>', 'Bratwurst weiss, 125 g / Stück, 50% Schweinefleisch, 50% Rindfleisch']
write_lock = {}
write_lock = {0 : True}
write_lock = {0 : True, 2 : True}


class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("MDI demo")
        self.mdi = QMdiArea()
        self.setCentralWidget(self.mdi)
        self.mdi.pool2dict = {}
        self.write_lock = write_lock
        sub = Dataset(self, dataset, header)
        self.mdi.addSubWindow(sub)
        sub.show()


class Dataset(QWidget): 

    def __init__(self, parent, dataset, header): 
        super().__init__()
        self.parent = parent
        self.mdi = parent.mdi
        self.dataset = dataset
        self.header = header
        self.write_lock = parent.write_lock
        self.setObjectName('DATASET')
        self.setFocusPolicy(Qt.StrongFocus)
        self.setMouseTracking(True)
        self.width = self.columnWidth()
        self.createDataset()
        # Layout
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.tableWidget)
        self.setLayout(self.layout)
        self.selectionModel = self.tableWidget.selectionModel()
        # eventFilter
        self.tableWidget.installEventFilter(self)
        
        self.checkDataset()

    def sizeHint(self):
        try:
            self.width_qs
        except AttributeError: 
            # and the margins which include the frameWidth and the extra
            # margins that would be set via a stylesheet or something else
            margins = self.contentsMargins()
            # y
            height = self.fontMetrics().height()
            height += height * len(self.header)
            height += self.tableWidget.horizontalScrollBar().height()
            height += margins.left() + margins.right()
            # x
            width = self.tableWidget.verticalHeader().length()
            width += self.tableWidget.horizontalHeader().length()
            width += self.tableWidget.verticalScrollBar().width()
            width += margins.left() + margins.right()
            self.width_qs = round(width * 1.1)
            self.height_qs = round(height * 1.7)
        return QSize(self.width_qs, self.height_qs) 
    
    def columnWidth(self):
        md = dict([(len(value), value) for value in self.dataset])
        mv = md[max(md)]
        width = round(
            QLineEdit().fontMetrics().boundingRect(mv).width() * 1.1)
        if width < 500:
            width = 500
        return width

    def eventFilter(self, source, event):
        """
        View events and check for taskstype.
        """
        keyWork = False
        try:    
            if event.type() == QEvent.MouseButtonRelease:
                print('##### MouseButtonRelease')
            if event.button() == Qt.LeftButton:
                print('##### LeftButton')
                if event.type() == QEvent.MouseButtonPress:
                    pass
        except AttributeError:
            pass
        try:
            if event.type() == QEvent.KeyRelease:
                if event.key() == Qt.Key_Tab:
                    keyWork = 'key_tab'
                elif event.key() == Qt.Key_Up:
                    keyWork = 'key_up'
                elif event.key() == Qt.Key_Down:
                    keyWork = 'key_down'
                if keyWork:
                    self.writeLockCheck(keyWork)
        except AttributeError:
            pass
        if event.type() == QEvent.FocusIn:
            keyWork = 'focus_in'
            self.writeLockCheck(keyWork)
        return super().eventFilter(source, event)
        
    def writeLockCheck(self, keyWork):
        """
        Set the FOCUS to the next input field without write protection. 
        """
        i = self.tableWidget.currentRow()
        while True:
            tableItem = self.tableWidget.cellWidget(i, 0)
            try:
                tableItem.setFocus()
            except AttributeError:
                pass
            try:
                tableItem.selectAll()
            except AttributeError:
                pass
            if not self.write_lock.get(i):
                break
            if i < (len(self.header) - 1):
                if keyWork == 'key_up':
                    i -= 1
                    if i < 0:
                        while True:
                            i += 1
                            if not self.write_lock.get(i):
                                break
                else:
                    i += 1
            elif i == (len(self.header) - 1):
                i = 0
        return
    
    def createDataset(self):
        """
        Create data record vertical. 
        """
        self.tableWidget = QTableWidget()
        # Row count
        self.tableWidget.setRowCount(len(self.dataset)) 
        # Column count
        self.tableWidget.setColumnCount(1)  

        for i, value in enumerate(self.dataset):
            self.column_name = self.header[i].lower()
            value = self.dataset[i]
            self.tableWidget.setVerticalHeaderItem(i, 
                QTableWidgetItem(self.header[i]))
            tableItem = QLineEdit()
            tableItem.setText(value)
            tableItem.setFixedWidth(self.width)
            self.tableWidget.setCellWidget(i, 0, tableItem)
        return

    def checkDataset(self):
        """
        Check record and recognize mandatory fields. 
        """
        dataset = [self.tableWidget.cellWidget(i, 0).text()
            for i in range(len(self.header))]
        for i in range(len(self.header)):
            self.column_name = self.header[i].lower()
            value = self.dataset[i]
            tableItem = QLineEdit()
            tableItem.setText(value)
            tableItem.setFixedWidth(self.width)
            self.tableWidget.setCellWidget(i, 0, tableItem)
        error_sign = '<?>'
        if set((error_sign,)) & set(dataset):
            for i, value in enumerate(dataset):
                if value == error_sign:
                    widget = self.tableWidget.cellWidget(i, 0)
                    widget.setFocus()
                    widget.selectAll()
                    self.mdi.duty_error = True
                    work_info = 'Eingabefeld muss ausgefüllt werden!'
                    self.mdi.work_info = work_info
                    print(work_info)
                    break
        self.dataset = dataset
        return

def main():
    app = QApplication(sys.argv)
    ex = MainWindow()
    ex.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du hast das quote statt code tag genutzt. Bitte drauf achten.

Und statt mit QTableItem arbeitet man eigentlich besser mit MVC, und da kann man auch festlegen, welche Zellen editierbar sind, ueber die Flags: https://doc.qt.io/qt-5/qabstractitemmodel.html#flags
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

__deets__ hat geschrieben: Donnerstag 29. April 2021, 16:31 Du hast das quote statt code tag genutzt. Bitte drauf achten.
Danke für den Hinweis, habe es irgendwie im Eifer des Gefechts verwechselt.
__deets__ hat geschrieben: Donnerstag 29. April 2021, 16:31 Und statt mit QTableItem arbeitet man eigentlich besser mit MVC, und da kann man auch festlegen, welche Zellen editierbar sind, ueber die Flags: https://doc.qt.io/qt-5/qabstractitemmodel.html#flags
Danke für den Link. Leider ist mein Englisch absolut miserabel, daher habe ich Schwierigkeiten, der Dokumentation zu folgen.
Hast Du mir vielleicht einen einfachen Beispiel-Code dazu, oder einen entsprechenden Link?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nicht auf deutsch, nein. Ich gehe immer zum Original, und das ist nun mal English.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

__deets__ hat geschrieben: Freitag 30. April 2021, 12:20 Nicht auf deutsch, nein. Ich gehe immer zum Original, und das ist nun mal English.
Ist mir klar, dass alles auf Englisch ist, daher würde mir ein einfaches Code-Beispiel dazu helfen!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Also ich finde da diverse dinge, wenn ich das suche: https://www.programcreek.com/python/exa ... tItemModel zB
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Mal schauen, ob ich damit was zusammen gepuzzelt bekomme.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe mir vorgenommen, mich zuerst mit MVC zu beschäftigen, dazu habe ich dieses Kurzinfo gefunden:
Das Modell hält die Datenstruktur, mit der die App arbeitet.

View ist eine Darstellung von Informationen, wie dem Benutzer gezeigt, ob Grafiken oder Tabellen. Mehrere Ansichten desselben Datenmodells sind erlaubt.

Der Controller akzeptiert die Eingabe vom Benutzer und verwandelt es in Befehle auf das Modell oder View.
Dazu habe ich folgendes Beispiel gefunden: https://stackoverflow.com/questions/523 ... -pyqt5-mvc

Weitere Links zu MVC-Infos: https://www.mfitzp.com/tutorials/modelv ... hitecture/
https://doc.qt.io/qtforpython/overviews ... mming.html

Ist zwar alles in Englisch, aber es gibt ja auch Translater dafür.
Jedenfalls, werde ich mich da mal durcharbeiten, um zuerst die Grundstruktur für meine Dataset-Ausgabe zu bekommen.

Über Unterstützung dazu, freue ich mich immer!
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bei konkreten Fragen kann man helfen. Prophylaktisch Code schreiben wird eher nicht drin sein.
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Folgenden Code, möchte ich zur Nachvollziehbarkeit posten.

Code: Alles auswählen

class Dataset(QWidget): 

    def __init__(self, parent, dataset, header, columnWidths): 
        super(Dataset, self).__init__()
        self.parent = parent
        self.mdi = parent.mdi
        self.dataset = dataset
        self.old_dataset = dataset.copy()
        self.header = header
        try:
            self.width = max(columnWidths.values())
        except (AttributeError, TypeError):
            self.width = columnWidths
        self.listname = parent.listname
        self.setObjectName('DATASET')
        self.installEventFilter(self)
        self.setFocusPolicy(Qt.StrongFocus)
        self.view_dataset = QTableView(self)
        self.view_dataset.clicked.connect(self._item_clicked)
        # GridLayout
        grid = QGridLayout() 
        grid.addWidget(self.view_dataset, 0, 0)
        self.setLayout(grid)
        self.model_dataset = QStandardItemModel(self)
        # Load dataset
        [self.model_dataset.invisibleRootItem().appendRow(
            QStandardItem(column)) for column in self.dataset]
        # Complete items
        ItemCompleter(self)
        # Vertical header
        [self.model_dataset.setHeaderData(i, Qt.Vertical, column)
            for i, column in enumerate(header)]
        self.proxy = QSortFilterProxyModel(self) 
        self.proxy.setSourceModel(self.model_dataset)
        self.view_dataset.setModel(self.proxy)

    def _item_clicked(self, index):
        # numeric position of dataset
        pos2dataset = index.row()
        # in front of item
        self.item_after = index.sibling(1,0)
        # in front of item
        item_before = index.sibling(-1,0)

    def eventFilter(self, source, event):
        """
        View events and check for taskstype.
        """
        keyWork = False
        try:
            if event.type() == QEvent.KeyRelease:
                if event.key() == Qt.Key_Tab:
                    keyWork = 'key_tab'
                elif event.key() == Qt.Key_Up:
                    keyWork = 'key_up'
                elif event.key() == Qt.Key_Down:
                    keyWork = 'key_down'
                if keyWork:
                    self.writeLockCheck(keyWork)
        except AttributeError:
            pass
        if event.type() == QEvent.FocusIn and keyWork:
            keyWork = 'focus_in'
            self.writeLockCheck(keyWork)
        return super().eventFilter(source, event)
        
    def writeLockCheck(self, keyWork):
        """
        Set the FOCUS to the next input field without write protection. 
        """
        i = self.view_dataset.currentIndex().row() + 1
        flags = QItemSelectionModel.SelectionFlags()
        while True:
            item = self.model_dataset.invisibleRootItem().child(i)
            try:
                #item.setFocus() ???????????
            except AttributeError:
                pass
            try:
                #item.selectAll() ???????????
            except AttributeError:
                pass
            if not self.write_lock.get(i):
                print('write_lock', i)
                break
            if i < (len(self.header) - 1):
                if keyWork == 'key_up':
                    i -= 1
                    if i < 0:
                        while True:
                            i += 1
                            if not self.write_lock.get(i):
                                break
                else:
                    i += 1
            elif i == (len(self.header) - 1):
                i = 0
        return
        
class ItemCompleter(object):

    def __init__(self, parent): 
        """
        Check and update column value.
         - column format
         - mandatory fields
         - if write protection on column, set flags 
        """
        self.listname = parent.listname
        self.write_lock = WriteLock(self.listname).write_lock
        for i, value in enumerate(parent.dataset):
            item = parent.model_dataset.invisibleRootItem().child(i)
            self.column_name = parent.header[i].lower()
            self.value = item.text()
            # column format, mandatory fields
            SetValue(self)
            item.setText(self.value)
            try:
                self.write_lock[i]
                item.setFlags(Qt.ItemIsSelectable |  Qt.ItemIsEnabled)
            except KeyError:
                pass
            item.setData(i, Qt.UserRole)
            item.data(Qt.UserRole)
            parent.model_dataset.setItem(i, 0, item) 
Der Schreibschutz funktioniert.
Was ich noch nicht hinbekommen habe:
- Focus setzen auf das erste Feld ohne Schreibschutz, bei Start
- In der Funktion "writeLockCheck", den Focus setzen, bei Betägigen der Tab-, Up-, Down-Taste

Da könnte ich doch ein wenig Hilfe benötigen, um auf den richtigen Weg zu kommen!
Nobuddy
User
Beiträge: 994
Registriert: Montag 30. Januar 2012, 16:38

Habe eine Lösung, für mein Problem gefunden.
Statt den "eventFilter" dafür zu nutzen, nutze ich dies:

Code: Alles auswählen

class Dataset(QLineEdit):

    def __init__(self, parent, dataset, header):
        super(Dataset, self).__init__()
        ....
        ...        
        # selection model
        self.selection_model = self.view.selectionModel()
        self.selection_model.selectionChanged.connect(self.on_selectionChanged)
    
    @pyqtSlot('QItemSelection', 'QItemSelection')
    def on_selectionChanged(self, selected, deselected):
        for ix in selected.indexes():
            print("selected: ", ix.row())
            self.selectedItem = ix.row()
        for ix in deselected.indexes():
            print("deselected: ", ix.row())
            self.deselectedItem = ix.row()
Ob mit den Tasten Up/Down, mit Tab oder das Selectieren mit der linken Maustaste, alles funktioniert!
Antworten