FocusIn, Item-Position in Dataset-Tabelle ermitteln

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

Samstag 2. Februar 2019, 10:53

Die Tab-Geschichte, brauche ich vorerst nicht, später ja.
Aber da kann ich mich ja nochmals melden.

Bin gerade dabei meinen bisherigen Qt4-Code in Qt5-Code umzuwandeln.
Puh, da hat sich wirklich was getan und verändert...

Grüße und Danke für Deine Hilfe!
Nobuddy
Nobuddy
User
Beiträge: 833
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 6. Februar 2019, 11:29

Komme nochmals auf das Thema zurück.
Der bisherige Code, mit focusEvent funktioniert, geändertes Item wird ohne Fkus setzen auf ein anderes Item übernommen.
Da bei dem bisherigen Test-Code, kein Close-Button integriert war, konnten wir auch dies nicht testen.
Die geänderten Daten des Items ohne FocusOut, werden nicht übernommen.
Deshalb habe ich mich bemüht, dies zu ändern.
Dank Deiner bisherigen nicht benötigten Funktion "_item_clicked", habe ich eine Lösung gefunden.
Ich poste mal den Teil-Code dazu, siehe bei Funktion "close" die erste Zeile.

Code: Alles auswählen


    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 exec_(self):
        self.view.setCurrentIndex(self.item_after)
        self.dataset = [self.model.invisibleRootItem().child(i).text()
            for i in range(len(self.header))]
        if self.dataset != self.old_dataset:
            r = MessageBox(QMessageBox.Yes, 'Änderung speichern?')
            if r.result() != QMessageBox.Yes:
                self.dataset = self.old_dataset
        print('self.dataset', self.dataset)
__deets__
User
Beiträge: 6651
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 6. Februar 2019, 12:51

Ich kann nur raten was du wirklich meinst: das man das Hauptfenster schliessen kann, ohne das es zu einer Uebernahme der Daten kommt? Das loest man mE anders. Du solltest das auf globaler Ebene verankern. Dein gesamtes Programm sollte nicht beendet werden koennen, wenn irgendwo Daten eingegeben wurden. Mir ist so, als ob es dazu einen Mechanismus gibt, muss den mal recherchieren.
Benutzeravatar
__blackjack__
User
Beiträge: 4496
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 6. Februar 2019, 14:38

Das ist ja auch genau das Verhalten was Benutzer von anderen Programmen her kennen, dass ein Programm nachfragt ob das Schliessen abgebrochen werden soll, oder die veränderten Daten vor dem Schliessen gespeichert oder verworfen werden sollen. Wenn man die Daten einfach ungefragt speichert, kann das Benutzer auch sehr verärgern, weil man damit nicht rechnet das so etwas einfach ungefragt passiert.
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
Nobuddy
User
Beiträge: 833
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 6. Februar 2019, 14:40

Ich habe alle Buttons zentral in der MDI angelegt, die steuen dann das activeSubWindow.
Die benötigten Buttons für das activeSubWindow werden entsprechend aktualisiert.
Die Buttons, sind in einem ToolBar verankert, ich sehe das als einfachste Lösung.

Zuerst hatte ich die Buttons in den jeweiligen activeSubWindow integriert, bin dann aber zu der Auffassung gekommen, dies zu zentralisieren. Einen Datensatz zu editieren, löschen oder einen Neuen hinzufügen, bleibt letztednlich immer das Gleiche.

Nun nochmals auf meinen letzten Post zurück zu kommen.
Beim Beenden des Dataset-Windows durch den Close-Button, werden Änderungen nicht übernommen, wenn man nicht das Item verlässt. Ganz anderst ist es ja, wenn ich das Dataset-Window durch den closeEvent beende. Das hat mich auch zu letzterer Lösung gebracht. Das sichert die gemachte Änderung, die dann durch eine MessageBox abgefragt wird (Bestätigung positiv/negativ).
Wenn es dafür noch eine einfachere Lösung gibt, kannst Du mir das gerne aufzeigen, ich lerne immer gerne dazu.
Benutzeravatar
__blackjack__
User
Beiträge: 4496
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Mittwoch 6. Februar 2019, 14:45

@Nobuddy: Ich würde das ja in die Fenster integrieren und ja, richtige Fenster. Für MDI muss man IMHO in Zeiten wo mehr als ein Monitor nichts exotisches mehr ist, schon eine gute Begründung haben. MDI empfinde ich immer als nervig und einschränkend. Und dass das letztlich immer das Gleiche bleibt: Warum ist das ein Argument es nicht in die Fenster zu integrieren? Auch dann muss man den immer gleichen Code ja nur *einmal* schreiben. Man hat ja nicht den Code für jedes Fenster einzeln und kopiert‽
“Give a man a fire and he's warm for a day, but set fire to him and he's warm for the rest of his life.”
— Terry Pratchett, Jingo
Nobuddy
User
Beiträge: 833
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 6. Februar 2019, 15:08

An mehrere Monitore, habe ich jetzt nicht gedacht, da wäre dann auf jeden Fall das Integrieren der Buttons in die einzelnen Fenster wichtig. Diese Szenario brauche ich jetzt für mich nicht. Eine anschließende Änderung für mehrere Monitore, wäre dann schon eine Herausforderung.

Ich habe mich MDI angefreundet, obwohl es da nicht gerade übermäßig viele Resultate beim Googeln gibt.
Mir fehlt einfach das Gesamtwissen über Py und PyQt, um andere Lösungen schnell und einfach umsetzen zu können, was Ihr mit "links" macht. Für mich ist es wichtig, meinen Code zu verstehen.
Ich lerne gerne dazu. Was ich auch verstanden habe und Ihr mir versucht habt nahezulegen, den Code so einfach wie möglich zu halten, bzw. den Code in einzelne Funktionen aufzuteilen. Da bin ich heute schon ein kleinen Schritt dem näher gekommen.
Nobuddy
User
Beiträge: 833
Registriert: Montag 30. Januar 2012, 16:38

Mittwoch 6. Februar 2019, 15:27

Ich habe hier mal den Test-Code für das Dataset-Window, der lauffähig ist für Euch zum Testen, damit Ihr die Funktionsweise nachvollziehen könnt.

Code: Alles auswählen

import os
import sys
import operator  # used for sorting
from PyQt5 import QtCore
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import (Qt, QSize, QEvent, QObject, 
    QSortFilterProxyModel)
from PyQt5.QtGui import QStandardItem, QStandardItemModel
from PyQt5.QtWidgets import (QMainWindow, QMdiArea, QWidget, QMessageBox, 
    QToolBar, QTableView, QGridLayout, QHBoxLayout, QAction, QApplication,
    qApp)

title = 'Backlight management'
header = ['Pos', 'Supplier', 'Artikel', 'Benennung']
mylist = [['001', 'Meyer', '47110', 'Bratwurst weiss, 125 g'],
['002', 'Meyer', '47111', 'Bratwurst weiss, 425 g'],
['003', 'Meyer', '47112', 'Bratwurst weiss, 1225 g']]
columnWidths = [20, 15, 20, 40]


class MainWindow(QMainWindow):

    def __init__(self):
        super(MainWindow, self).__init__()
        self.setWindowTitle("MDI demo")
        self.mdi_area = QMdiArea()
        self.setCentralWidget(self.mdi_area)
        ## Toolbar
        screen_position = 'below'
        buttons = [
            ['close', 'Ctrl+Q', self.close],
            ]
        ToolBar(self, screen_position, buttons)
        self.mylist = mylist
        self.index = 0
        sub = Dataset(self, mylist[self.index], header, columnWidths)
        sub.setWindowTitle(title)
        self.mdi_area.addSubWindow(sub)
        sub.show()

    def close(self):
        print('CLOSE')
        if self.mdi_area.activeSubWindow():
            self.mdi_area.activeSubWindow().widget().exec_()
            self.mdi_area.removeSubWindow(self.mdi_area.activeSubWindow())
            return
        qApp.quit()

class Dataset(QWidget):

    def __init__(self, parent, dataset, header, columnWidths):
        super(Dataset, self).__init__()
        self.parent = parent
        self.old_dataset = self.dataset = dataset
        self.old_list = self.parent.mylist
        self.header = header
        try:
            self.width = max(columnWidths)
        except TypeError:
            self.width = columnWidths
        self.setObjectName('DATASET')
        self.installEventFilter(self)
        self.setFocusPolicy(Qt.StrongFocus)
        self.view = QTableView(self)
        self.view.clicked.connect(self._item_clicked)
        # GridLayout
        grid = QGridLayout()
        grid.addWidget(self.view, 0, 0)
        self.setLayout(grid)
        self.model = QStandardItemModel()
        # Load dataset
        [self.model.invisibleRootItem().appendRow(
            QStandardItem(column)) for column in self.dataset]
        # Vertical header
        [self.model.setHeaderData(i, Qt.Vertical, column)
            for i, column in enumerate(header)]
        self.proxy = QSortFilterProxyModel(self)
        self.proxy.setSourceModel(self.model)
        self.view.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 closeEvent(self, event):
        self.exec_()
        event.accept()

    def exec_(self):
        try:
            self.view.setCurrentIndex(self.item_after)
        except AttributeError:
            pass
        self.dataset = [self.model.invisibleRootItem().child(i).text()
            for i in range(len(self.header))]
        if self.dataset != self.old_dataset:
            r = MessageBox(QMessageBox.Yes, 'Änderung speichern?')
            if r.result() != QMessageBox.Yes:
                self.dataset = self.old_dataset
        print('self.dataset', self.dataset)
        return self.parent.mylist


class ToolBar(QToolBar): 

    def __init__(self, parent, position, buttons):
        QToolBar.__init__(self)
        # Change Font
        font = self.font()
        font.setFamily("Arial") 
        font.setPointSize(18)

        self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        #self.setToolButtonStyle(QtCore.Qt.ToolButtonTextUnderIcon)
        positions = {
            'up' : QtCore.Qt.TopToolBarArea,
            'left' : QtCore.Qt.LeftToolBarArea,
            'right' : QtCore.Qt.RightToolBarArea,
            'below' : QtCore.Qt.BottomToolBarArea,
            }

        for button, shortcut, comand in buttons:
            path = ''
            action = QAction(QIcon(path), button.capitalize(), self)
            try:
                action.triggered.connect(comand)
            except TypeError:
                pass
            action.setShortcut(shortcut)
            action.setFont(font)
            if path == '':
                action.setCheckable(True)
            widget = QWidget()
            widget.setLayout(QHBoxLayout())
            self.addWidget(widget)
            self.addAction(action)
            if position == 'right':
                self.addSeparator()
        parent.addToolBar(positions[position.lower()], self)

        self.setAllowedAreas(
            QtCore.Qt.TopToolBarArea
            | QtCore.Qt.BottomToolBarArea
            | QtCore.Qt.LeftToolBarArea
            | QtCore.Qt.RightToolBarArea
            )  
        self.setStyleSheet(
            "color: blue;"
            "background-color: yellow;"
            "selection-color: red;"
            "selection-background-color: blue;"
            ) 

 
class MessageBox(QMessageBox): 

    def __init__(self, buttonReply, text):
        QMessageBox.__init__(self)
        self.setText(text)
        if buttonReply == self.Ok:
            self.setIcon(self.Warning)
            self.setWindowTitle('Info')
            self.setStandardButtons(self.Ok)
            self.setEscapeButton(self.Cancel)
            default = self.Cancel
        elif buttonReply == self.Yes:
            self.setIcon(self.Warning)
            self.setWindowTitle('Question')
            self.setStandardButtons(self.Yes | self.No)
            default = self.No
        elif buttonReply == self.Cancel:
            self.setIcon(self.Question)
            self.setWindowTitle('Question')
            self.setStandardButtons(self.Yes | self.No | self.Cancel)
            self.setEscapeButton(self.Cancel)
            default = self.Cancel
        elif buttonReply == self.Save:
            self.setIcon(self.Question)
            self.setWindowTitle('Save and Exit')
            self.setStandardButtons(self.Save | self.Discard | self.Cancel)
            self.setInformativeText('Änderung speichern?')
            self.setEscapeButton(self.Cancel)
            default = self.Cancel
        self.setDefaultButton(default)
        self.exec_()

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


if __name__ == '__main__':
    main()
Antworten