You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5中QListWidget上移项需手动repaint?求优化方案

QListWidget移动项后选中状态不自动刷新的问题解决

我碰到过类似的情况,这其实是Qt的视图更新机制在item移动后的一个小同步问题。让我给你拆解一下原因和更优雅的解决办法:

问题原因

当你用takeItem移除项再用insertItem插入新位置时,QListWidget内部的选中状态跟踪和视图渲染之间出现了短暂的不同步。虽然你用setCurrentItem指定了选中项,但控件并没有自动触发选中状态的UI重绘——这就是为什么必须手动调用repaint才能看到蓝色选中条带的原因。

无需repaint的解决方案

有两种更符合Qt设计思路的方法,完全可以去掉手动重绘的操作:

方法1:用setCurrentRow替代setCurrentItem

这是最推荐的方式。当你把项插入到row-1位置后,直接通过行号来设置选中状态,而不是直接操作item对象。这样QListWidget会直接定位到目标行,并自动触发视图更新,选中条带会正常显示:

修改你的__up槽函数:

@pyqtSlot()
def __up(self):
    """Move the item up by one row."""
    row = self.__fruit_list.currentRow()
    if row >= 1:
        # Remove the currently selected item.
        item = self.__fruit_list.takeItem(row)
        self.__fruit_list.insertItem(row - 1, item)
        # 用行号设置选中,替代setCurrentItem
        self.__fruit_list.setCurrentRow(row - 1)
        # 删掉self.__fruit_list.repaint()

方法2:用update()替代repaint()

如果坚持要用setCurrentItem,可以把repaint()换成update()。update()会让Qt在事件循环空闲时安排重绘,比强制立即重绘的repaint()更符合Qt的事件处理机制,性能也更好:

@pyqtSlot()
def __up(self):
    """Move the item up by one row."""
    row = self.__fruit_list.currentRow()
    if row >= 1:
        item = self.__fruit_list.takeItem(row)
        self.__fruit_list.insertItem(row - 1, item)
        self.__fruit_list.setCurrentItem(item)
        # 用update替代repaint
        self.__fruit_list.update()

为什么setCurrentItem会失效?

简单来说,当你把item从列表中移除再插入时,QListWidget内部对这个item的位置关联暂时没有同步,setCurrentItem虽然标记了item为选中状态,但控件没有意识到这个item已经在新位置上,所以没有触发对应的UI更新。而setCurrentRow直接操作行号,控件会直接处理该行的选中渲染,避免了这个同步问题。

下面是修改后的完整可运行代码:

"""Demonstrates QListWidget and adding and modifying values using a dialog."""
# Importing the standard python libraries.
import sys
# Importing the PyQt5 libraries.
from PyQt5.QtWidgets import QApplication, QAbstractItemView, QMessageBox
from PyQt5.QtWidgets import QMainWindow, QWidget, QVBoxLayout, QHBoxLayout
from PyQt5.QtWidgets import QListWidget, QPushButton
from PyQt5.QtCore import pyqtSlot

class FruitListApp(QMainWindow):
    """Fruit List Management Application class."""
    def __init__(self, parent=None):
        """Initialize the Fruit List Application."""
        super().__init__(parent)
        # Setup the widgets.
        self.__fruit_list = QListWidget()
        self.__fruit_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.__button_up = QPushButton("&Up")
        # Layout the widgets.
        button_box = QWidget()
        button_box_layout = QVBoxLayout()
        button_box_layout.addWidget(self.__button_up)
        button_box.setLayout(button_box_layout)
        central_widget = QWidget()
        layout = QHBoxLayout()
        layout.addWidget(self.__fruit_list)
        layout.addWidget(button_box)
        central_widget.setLayout(layout)
        # Setup the main application window.
        self.setCentralWidget(central_widget)
        self.setWindowTitle("Edit Fruit List")
        # Setup signals and slots.
        self.__button_up.clicked.connect(self.__up)

    def add_items(self, item_list):
        """Add items provided as a list to the fruit list."""
        # Item is a list.
        if isinstance(item_list, list):
            if len(item_list) > 0:
                # Add items to the list after determining the previous count
                # of items in the list.
                previous_count = self.__fruit_list.count()
                self.__fruit_list.addItems(item_list)
                # Set the first item added from a list as the current
                # selection.
                self.__fruit_list.setCurrentRow(previous_count)
            else:
                QMessageBox.warning(self, "Error", "The fruit names list "
                                    "provided is empty.")
                return
        else:
            QMessageBox.warning(self, "Error", "The fruit names are not "
                                "provided as a list.")
            return

    @pyqtSlot()
    def __up(self):
        """Move the item up by one row."""
        row = self.__fruit_list.currentRow()
        if row >= 1:
            # Remove the currently selected item.
            item = self.__fruit_list.takeItem(row)
            self.__fruit_list.insertItem(row - 1, item)
            self.__fruit_list.setCurrentRow(row - 1)

if __name__ == "__main__":
    def main():
        """Main method to test the application."""
        # The fruit list.
        fruit = ["Banana", "Apple", "Elderberry", "Clementine", "Fig",
                 "Guava", "Mango", "Honeydew Melon", "Date", "Watermelon",
                 "Tangerine", "Ugli Fruit", "Juniperberry", "Kiwi", "Lemon",
                 "Nectarine", "Plum", "Raspberry", "Strawberry", "Orange"]
        # Instantiate the application.
        app = QApplication(sys.argv)
        # Instantiate the Fruit List Main Window.
        main_window = FruitListApp()
        # Add the fruit list.
        main_window.add_items(fruit)
        # Display the main application window.
        main_window.show()
        # Start the event loop.
        app.exec_()
    # Start the application.
    main()

内容的提问来源于stack exchange,提问作者Sanket Rajgarhia

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 22:57:34