当用户选择一个项目时 QListWidget 会发生变化

Posted

技术标签:

【中文标题】当用户选择一个项目时 QListWidget 会发生变化【英文标题】:QListWidget shifts when user selects an item 【发布时间】:2017-06-06 15:09:26 【问题描述】:

我有一个特殊的问题。我有一个 1000 QListWidgetItem 的 QListWidget。 我有一个搜索文本框,只要有人开始输入,就会触发以下代码。

QRegExp regExSearch(searchText, Qt::CaseInsensitive, QRegExp::RegExp);
for (QListWidgetItem * currIt : allItems)

    currIt->setHidden( !(currIt->text().contains(regExSearch)) );

最终结果是,一旦我执行了搜索,我将拥有一个隐藏 600 个项目并显示 400 个的 QListWidget。每当我点击一个项目时,整个列表只会向下跳几行,因此我的选择在屏幕上什至不可见。

我已经确认似乎是 setHidden 造成的。如果我只是突出显示找到的行,而不隐藏/显示项目,则选择不会导致列表向下滚动。

因此,我想知道我错过了什么?我调用哪个函数来确保我的 QListWidget 在我选择一个项目时不会移动?

下面的工作示例:

#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QListWidget>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

    ui->setupUi(this);

    QListWidget * myListWidget = new QListWidget();

    for (int i = 0; i <= 1000; ++i)
    
        QListWidgetItem * myItem = new QListWidgetItem(myListWidget);
        QString text("");
        for (int i = 0; i <= 100; ++i)
        
            text.append("W");
        
        myItem->setText(text + QString::number(i));
    

    for (int i = 0; i <= 1000; ++i)
    
        if (i%2)
            myListWidget->item(i)->setHidden(true);
    

    setCentralWidget(myListWidget);


MainWindow::~MainWindow()

    delete ui;


我基本上已经复制并隔离了水平滚动条的问题。在我的原始应用程序和这个 mcve 中,如果您选择一个项目(例如在索引 314 处),列表将跳下。但是,如果我将列表大小调整为 有水平滚动条,那么它不会移动。

所以现在我知道问题出在水平滚动条上,但我仍然不确定如何防止发生跳转。

更新:

我尝试过这样使用 SIGNAL-SLOT:

connect(myListWidget, SIGNAL(itemClicked(QListWidgetItem*)), myListWidget, 
            SLOT(scrollToItem(const QListWidgetItem*,QAbstractItemView::ScrollHint)));

我还尝试创建自己的插槽,我明确指定该项目应该是可见的:

void MainWindow::scrollToItem(QListWidgetItem * item)

    std::cout << "Scrolling to item." << std::endl;
    myListWidget->scrollToItem(item, QAbstractItemView::EnsureVisible);

但是,它仍然会跳下并消失在视野之外!如果我将 ScrollHint 设置为 PositionAtCenter,它确实有效!但是,从用户体验的角度来看,每次单击某个项目时都让列表移动并不是一种理想的行为(即使该项目现在位于屏幕的中心)。我还有其他选择吗?

【问题讨论】:

确保您没有连接到列表的插槽,这些插槽可能会在单击项目或选择更改时执行某些操作。 @RonaldoNazarea 是的!据我所知,我唯一的事件是项目双击。有趣的是,这很有效。我在双击时打开了一个新的小部件。在这种情况下,QListWidget 不会滚动... 如果跳向下,你确定整个列表吗?没有mcve 很难得出结论,但听起来您的选择正在向上 跳跃,因为QListWidgetItems above 您选择的项目被隐藏了,因此导致可见列表变短,因此您选择的项目与顶部的距离更短,并跳出您当前滚动到的可见区域 @SteveLorimer 我也觉得这很令人惊讶,但是是的,它跳了下来。如果我选择项目 399,列表会滚动,因此我会看到项目 500-600。如果我向上滚动,我会看到我的选择(仍然处于活动状态和所有内容)。我正在争论是否应该尝试在这一点上拦截鼠标事件。但后来我不确定我可以调用什么函数来保持列表的位置......更糟糕的是,我尝试编写一个 mcve,它只生成一个 1-1000 的列表并隐藏 index%2 处的项目 - 这很有效美好的。没有任何跳跃:/ @SteveLorimer 感谢您提供的工作示例。抱歉,我还不能运行它(出于某种原因,vcxserv 拒绝将粘贴复制到我正在处理的远程机器上)。但是,我确实使用自己的代码示例进行了更新。在我的情况下,问题似乎与水平滚动条有关。我已经能够证明这两者 - 我的原始应用程序和 mcve。一旦我调整大小以没有水平滚动 - 一切正常。现在我需要以某种方式解决这个问题。 【参考方案1】:

使用QListWidget::scrollToItem,您可以重新滚动您的列表,以便您选择的项目可见。

scrollToItem 还采用ScrollHint,它允许您指定QListWidget 滚动到的位置(默认为EnsureVisible

EnsureVisible:滚动以确保项目可见。 PositionAtTop:滚动以将项目定位在视口的顶部。 PositionAtBottom:滚动以将项目定位在视口底部。 PositionAtCenter:滚动以将项目定位在视口的中心。

下面的完整工作示例:

#include <QApplication>
#include <QMainWindow>
#include <QVBoxLayout>
#include <QPushButton>
#include <QListWidget>
#include <QLineEdit>
#include <QRegExp>

int main(int argc, char** argv)

    QApplication* app = new QApplication(argc, argv);
    QMainWindow*  window = new QMainWindow();

    QWidget widget;
    QVBoxLayout layout(&widget);

    QLineEdit edit;
    layout.addWidget(&edit);

    QListWidget list;
    layout.addWidget(&list);

    for (int i = 0; i < 1000; ++i)
    
        QListWidgetItem* item = new QListWidgetItem(QString::number(i));
        list.addItem(item);
    

    QObject::connect(&edit, &QLineEdit::textChanged, [&](const QString& text)
        
            QRegExp re(text, Qt::CaseInsensitive, QRegExp::RegExp);
            for(int i = 0; i < list.count(); ++i)
            
                QListWidgetItem* item = list.item(i);
                item->setHidden(!(item->text().contains(re)));
            

            auto selected = list.selectedItems();
            if (selected.size() == 1)
            
                list.scrollToItem(selected.front());
            
        );

    window->setCentralWidget(&widget);
    window->show();
    return app->exec();

【讨论】:

非常感谢您的帮助,史蒂夫!我会将其标记为答案,因为我相信您在技术上确实帮助我解决了这个问题。但是,请阅读我在原始问题中的最新更新。我使用信号槽和PositionAtBottom 让它工作,但是,我首选的解决方案是让列表首先不移动。似乎出于某种原因,应用程序认为该项目是可见的,即使它不在视图中!【参考方案2】:

我有同样的问题。 禁用自动滚动解决了这个问题:

list.setAutoScroll(false);

其中 list 是 QListWidget 类型(如您的示例中所示)。 看: https://doc.qt.io/qt-5/qabstractitemview.html#autoScroll-prop

【讨论】:

以上是关于当用户选择一个项目时 QListWidget 会发生变化的主要内容,如果未能解决你的问题,请参考以下文章

如何控制 QListWidget 选择

QlistWidget如何一一选择项目

PyQt5:当列表失去焦点时设置 QListWidget 选择颜色

QListWidget 发送没有项目的 doubleClicked 信号

如何在 PyQt5 中返回 QlistWidget 中项目的值

如果选择了多个项目,如何在 QListWidget 中打印多个项目