Search code examples
pythonpyqtpyqt5qlistwidget

How to avoid QListWidget list item being reset when adding new item?


I have a GUI with a QListWidget that starts with no entries. Entries are added using an "add" button. The problem I have is that when you edit the text of a list item, if you click the add button again before hitting enter or clicking away, the text you enter gets erased (see gif for reference)

screenshot

Also, another gif to show that the code is otherwise working:

screenshot

The problem is that it doesn't save what you are typing with each keystroke. It instead waits until you are finished and have changed the selection or pressed enter.

Can anyone suggest a way to fix this?

Code:

I have the following signals declared in the init function of my GUI class:

self.w_client_list.itemChanged.connect(self.edit_client_name)
self.w_client_list.itemSelectionChanged.connect(self.switching_clients)
self.b_add_client.clicked.connect(self.add_client)

These are the Slot functions that signals are connected to:

    def get_index(self):
        """Gets index number of selected client for client details functions"""
        for i in range(self.w_client_list.count()):
            if self.w_client_list.item(i).isSelected():
                index = i
                return index
        index = None
        return index

    @Slot()
    def switching_clients(self):
        index = self.get_index()
        if index == None:
            self.l_email.clear()
            self.c_main_email.setCheckState(Qt.Unchecked)
            self.c_secondary_email.setCheckState(Qt.Unchecked)
            self.w_phone.clear()
            self.l_preferred_name.clear()
            self.w_title.setCurrentText('Mr')
        else:
            # Email
            self.l_email.setText(self.client.individual[index]['email'][0])
            self.c_main_email.setChecked(self.client.individual[index]['email'][1])
            self.c_secondary_email.setChecked(self.client.individual[index]['email'][2])
            # Phone
            self.update_phone_list()
            # Preferred Name
            self.l_preferred_name.setText(self.client.individual[index]['preferred_name'])
            # Title
            self.w_title.setCurrentText(self.client.individual[index]['title'])

    @Slot()
    def edit_client_name(self):
        index = self.get_index()
        self.client.individual[index]['full_name'] = self.w_client_list.item(index).text().strip()
        self.switching_clients()

    @Slot()
    def add_client(self):
        self.client.individual.append({'title': 'Mr', 'first_name': '', 'middle_name': '', 'last_name': '',
                                        'full_name': 'Enter full name',
                                        'preferred_name': '', 'salutation': '', 'postal_salutation': '',
                                        'email': ['', 0, 0], 'address': [], 'phone': [],
                                        'preferred_name_connected': True})
        self.update_client_list()  # Updates the client form to show new address row

    def update_client_list(self):
        self.w_client_list.clear()
        client_list = []
        for client in self.client.individual:
            item = QtWidgets.QListWidgetItem()
            item.setText(client['full_name'])
            item.setFlags(
                QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEditable | QtCore.Qt.ItemIsEnabled)
            self.w_client_list.addItem(item)
        item.setSelected(True)



Solution

  • You can fix this by setting the focus policy of the buttons to NoFocus. This allows the item-editor to stay open when the buttons are clicked (because they won't steal the focus). The list-widget's isPersistentEditorOpen method can then be used to prevent unwanted operations whilst the user is still editing.

    UPDATE:

    If you want to commit the current edit when adding a new item, you can just call setFocus on the list-widget (since the button doesn't steal the focus). This also means there's no longer any need to check whether the item-editor is open as suggested above.


    Here is a working demo based on your code:

    import sys
    from PyQt5 import QtCore, QtWidgets
    
    class Client:
        individual = []
    
    class Window(QtWidgets.QWidget):
        def __init__(self):
            super(Window, self).__init__()
            self.client = Client()
            self.b_add_client = QtWidgets.QPushButton('Add')
            self.b_add_client.setFocusPolicy(QtCore.Qt.NoFocus)
            self.w_client_list = QtWidgets.QListWidget()
            layout = QtWidgets.QVBoxLayout(self)
            layout.addWidget(self.w_client_list)
            layout.addWidget(self.b_add_client)
            self.w_client_list.itemChanged.connect(self.edit_client_name)
            self.b_add_client.clicked.connect(self.add_client)
    
        def get_index(self):
            selection = self.w_client_list.selectedItems()
            if selection:
                return self.w_client_list.indexFromItem(selection[0]).row()
    
        def switching_clients(self):
            pass
    
        def edit_client_name(self):
            index = self.get_index()
            if index is not None:
                text = self.w_client_list.item(index).text().strip()
                if text:
                    self.client.individual[index]['full_name'] = text
            self.switching_clients()
    
        def add_client(self):
            self.w_client_list.setFocus()
            self.client.individual.append({
                'title': 'Mr', 'first_name': '', 'middle_name': '',
                'last_name': '', 'full_name': 'Enter full name',
                'preferred_name': '', 'salutation': '',
                'postal_salutation': '', 'email': ['', 0, 0],
                'address': [], 'phone': [],
                'preferred_name_connected': True,
                })
            self.update_client_list()
    
        def update_client_list(self):
            if len(self.client.individual):
                self.w_client_list.clear()
                for client in self.client.individual:
                    item = QtWidgets.QListWidgetItem()
                    item.setText(client['full_name'])
                    item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
                    self.w_client_list.addItem(item)
                item.setSelected(True)
                self.w_client_list.editItem(item)
    
    if __name__ == '__main__':
    
        app = QtWidgets.QApplication(sys.argv)
        window = Window()
        window.setGeometry(600, 100, 300, 200)
        window.show()
        sys.exit(app.exec_())