Search code examples
pythonpyqtpyqt5qlistwidgetqlistwidgetitem

PyQt5 QListWidget delete mouse hover QListItem


I am stuck into a PyQt5 Qlistwidget probelm. below is the current code I have:

from PyQt5 import QtWidgets, QtCore, QtGui
import sys

class FlatUIListWidget(QtWidgets.QListWidget):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setStyleSheet("""
                QScrollBar:vertical {              
            border: none;
            background:white;
            width:3px;
            margin: 0px 0px 0px 0px;
        }
        QScrollBar::handle:vertical {
            background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
            stop: 0 rgb(32, 47, 130), stop: 0.5 rgb(32, 47, 130), stop:1 rgb(32, 47, 130));
            min-height: 0px;
        }
        QScrollBar::add-line:vertical {
            background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
            stop: 0 rgb(32, 47, 130), stop: 0.5 rgb(32, 47, 130),  stop:1 rgb(32, 47, 130));
            height: 0px;
            subcontrol-position: bottom;
            subcontrol-origin: margin;
        }
        QScrollBar::sub-line:vertical {
            background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
            stop: 0  rgb(32, 47, 130), stop: 0.5 rgb(32, 47, 130),  stop:1 rgb(32, 47, 130));
            height: 0 px;
            subcontrol-position: top;
            subcontrol-origin: margin;
        }
    """)


    def add_custom_widget(self, widget):
        myQListWidgetItem = QtWidgets.QListWidgetItem(self)
        # Set size hint
        myQListWidgetItem.setSizeHint(widget.sizeHint())
        # Add QListWidgetItem into QListWidget
        self.addItem(myQListWidgetItem)
        self.setItemWidget(myQListWidgetItem, widget)


class DummyWidget(QtWidgets.QWidget):
    delete_signal = QtCore.pyqtSignal(str)
    def __init__(self, text):
        super().__init__()
        self.text = text
        self.layout = QtWidgets.QVBoxLayout(self)
        self.delete_button = QtWidgets.QPushButton(text)
        self.delete_button.clicked.connect(self.on_delte_button_clicked)
        self.layout.addWidget(self.delete_button)


    def on_delte_button_clicked(self):
        self.delete_signal.emit(self.text)


class Container(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.layout = QtWidgets.QVBoxLayout(self)
        self.list_widget = FlatUIListWidget()
        self.layout.addWidget(self.list_widget)
        for i in range(10):
            self.add_widget(str(i))

    def add_widget(self, text):
        item = DummyWidget(text)
        item.delete_signal.connect(self.on_item_delete)
        self.list_widget.add_custom_widget(item)

    def on_item_delete(self, string):
        try:
            # cursor = QtGui.QCursor()
            # print(cursor.pos())
            # print(self.list_widget.itemAt(cursor.pos()))
            # print(self.list_widget.selectedItems())
            # print(self.sender().parent().parent())
            # print(self.list_widget.currentRow())
            # self.list_widget.setCurrentItem(self.sender().parent())
            # self.list_widget.takeItem(self.sender().parent())
            print(string)
            self.list_widget.takeItem(self.list_widget.currentRow())
        except Exception as e:
            print(e)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    mw = Container()
    mw.show()
    sys.exit(app.exec_())

basically, I want to create a QListWidget with all customized items. In each of those item there is a button allowed for user to delete itself. I have tried the following:

  1. connect button clicked signal to self.sender().deleteLater(). This does not work since it only delete the widget inside the QListWidgetItem and will leave a blank box in the QListWidget.

  2. after click the button, send out a signal to Container widget and delete the current row. This is also not working because you actually need to select on the widget first and click delete, otherwise it will delete the very first row.

  3. gave each item widget a name tag and after click button, looping through the listwidget and use takeItem() to delete the one have the same tag. I just think this approach is not elegant...

I think it would make most of sense that if I can somehow get the row number for the widget which button have been clicked and use takeItem() on self.list_widget.row(row_number) to delete. I just couldn't figure out how.

I have read the official document of QListWidget and QListWidgetItems, which didn't give me an answer. I would be really appreciated it if somebody can provide some ideas.

Adding customized widget to QListWidget is re-written from this post: PyQt QListWidget custom items


Solution

  • To eliminate the item using the QWidget the procedure is as follows:

    • Obtain the position of the widget with respect to the viewport(), in this case it is easy since the widget(DummyWidget) is the children of the viewport(), so we should only use self.sender().pos().

    • Using the position we obtain the item through itemAt().

    • Using the item we get the row through row().

    • And then we eliminate it with takeItem().

    The implementation is as follows:

    def on_item_delete(self, string):
        it = self.list_widget.itemAt(self.sender().pos())
        row = self.list_widget.row(it)
        item = self.list_widget.takeItem(row)
        del item