Qt:如何处理 QAbstractItemView 的焦点变化

Posted

技术标签:

【中文标题】Qt:如何处理 QAbstractItemView 的焦点变化【英文标题】:Qt: How do you handle focus changes for a QAbstractItemView 【发布时间】:2020-09-07 09:09:26 【问题描述】:

我想做的就是知道用户何时按下 ctrl + 向上箭头/向下箭头来聚焦列表中的项目。我希望我可以区分用户关注项目和选择项目。

我有一个 QAbstractItemView 的实现,我已经覆盖了它

::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) 以及

::currentChanged(const QModelIndex& current, const QModelIndex& previous)

阅读https://doc.qt.io/archives/qtjambi-4.5.2_01/com/trolltech/qt/model-view-selection.html 后我的理解是,如果我用ctrl + up arrow 聚焦一个项目,我将得到一个currentChanged 事件。如果我通过up arrow 选择某些内容,我将获得selectionChanged 事件。

但是,事实并非如此。按ctrl + up arrow 会导致selectionChangedcurrentChanged 都被解雇。我不知道为什么一旦我进入这些功能就会被解雇。当我在selectionChanged 时,我不知道我为什么在那里。是因为专注吗?是因为选择吗?

有件事可能应该告诉我QItemSelectionModel.SelectionFlag,但我显然也无权访问这些。

那么发生了什么?这是一个错误吗?同样,我想做的只是列表中的焦点项目。

qt 版本 5.9

【问题讨论】:

建议:不要把你的不满转移到你的帖子上,因为它会转移到我们这些想要帮助你的人身上,而且它会分散我们的注意力并且不会带来任何信息。 @eyllanesc,此建议制定得非常好,值得以某种方式纳入 SO。 它的中肯建议,感谢。 我只想指出您的文档链接看起来非常非常旧。也许它仍然是准确的,但也许不是?我会开始here。 【参考方案1】:

我试图重现 OP 描述的问题。

不幸的是,OP 没有提供MCVE。所以,我自己做了一个:

// Qt header:
#include <QtWidgets>

class ListWidget: public QListWidget 

  public:
    ListWidget(QWidget *pQParent = nullptr): QListWidget(pQParent)  
    virtual ~ListWidget() = default;
    ListWidget(const ListWidget&) = delete;
    ListWidget& operator=(const ListWidget&) = delete;

    virtual void currentChanged(
      const QModelIndex &current, const QModelIndex &previous) override
    
      qDebug() << "currentChanged():" << previous << "->" << current;
      QListWidget::currentChanged(current, previous);
    

    virtual void selectionChanged(
      const QItemSelection &selected, const QItemSelection &deselected) override
    
      qDebug() << "selectionChanged():" << selected << "->" << deselected;
      QListWidget::selectionChanged(selected, deselected);
    
;

void populate(QListWidget &qLst)

  const int n = 10;
  for (int i = 1; i <= n; ++i) 
    new QListWidgetItem(QString("item %1").arg(i), &qLst);
  


// main application
int main(int argc, char **argv)

  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QApplication app(argc, argv);
  // setup GUI
  ListWidget qLst;
  qLst.setSelectionMode(QListWidget::ExtendedSelection);
  populate(qLst);
  qLst.show();
  // runtime loop
  return app.exec();

在VS2017中用Qt5.13编译:

输出:

Qt Version: 5.13.0
currentChanged(): QModelIndex(-1,-1,0x0,QObject(0x0)) -> QModelIndex(0,0,0x2adc4f38610,QListModel(0x2adc4f3a3a0))

我用 Shift 选择了前 5 个项目:

currentChanged(): QModelIndex(0,0,0x2adc4f38610,QListModel(0x2adc4f3a3a0)) -> QModelIndex(1,0,0x2adc4f38a00,QListModel(0x2adc4f3a3a0))
selectionChanged(): (QItemSelectionRange(QModelIndex(0,0,0x2adc4f38610,QListModel(0x2adc4f3a3a0)),QModelIndex(0,0,0x2adc4f38610,QListModel(0x2adc4f3a3a0))), QItemSelectionRange(QModelIndex(1,0,0x2adc4f38a00,QListModel(0x2adc4f3a3a0)),QModelIndex(1,0,0x2adc4f38a00,QListModel(0x2adc4f3a3a0)))) -> ()
currentChanged(): QModelIndex(1,0,0x2adc4f38a00,QListModel(0x2adc4f3a3a0)) -> QModelIndex(2,0,0x2adc4f38a70,QListModel(0x2adc4f3a3a0))
selectionChanged(): (QItemSelectionRange(QModelIndex(2,0,0x2adc4f38a70,QListModel(0x2adc4f3a3a0)),QModelIndex(2,0,0x2adc4f38a70,QListModel(0x2adc4f3a3a0)))) -> ()
currentChanged(): QModelIndex(2,0,0x2adc4f38a70,QListModel(0x2adc4f3a3a0)) -> QModelIndex(3,0,0x2adc4f38ae0,QListModel(0x2adc4f3a3a0))
selectionChanged(): (QItemSelectionRange(QModelIndex(3,0,0x2adc4f38ae0,QListModel(0x2adc4f3a3a0)),QModelIndex(3,0,0x2adc4f38ae0,QListModel(0x2adc4f3a3a0)))) -> ()
currentChanged(): QModelIndex(3,0,0x2adc4f38ae0,QListModel(0x2adc4f3a3a0)) -> QModelIndex(4,0,0x2adc4f38f40,QListModel(0x2adc4f3a3a0))
selectionChanged(): (QItemSelectionRange(QModelIndex(4,0,0x2adc4f38f40,QListModel(0x2adc4f3a3a0)),QModelIndex(4,0,0x2adc4f38f40,QListModel(0x2adc4f3a3a0)))) -> ()

然后我使用 Ctrl 将焦点移动到item 10

currentChanged(): QModelIndex(4,0,0x2adc4f38f40,QListModel(0x2adc4f3a3a0)) -> QModelIndex(5,0,0x2adc4f3f990,QListModel(0x2adc4f3a3a0))
currentChanged(): QModelIndex(5,0,0x2adc4f3f990,QListModel(0x2adc4f3a3a0)) -> QModelIndex(6,0,0x2adc4f3f6f0,QListModel(0x2adc4f3a3a0))
currentChanged(): QModelIndex(6,0,0x2adc4f3f6f0,QListModel(0x2adc4f3a3a0)) -> QModelIndex(7,0,0x2adc4f3f1b0,QListModel(0x2adc4f3a3a0))
currentChanged(): QModelIndex(7,0,0x2adc4f3f1b0,QListModel(0x2adc4f3a3a0)) -> QModelIndex(8,0,0x2adc4f3f840,QListModel(0x2adc4f3a3a0))
currentChanged(): QModelIndex(8,0,0x2adc4f3f840,QListModel(0x2adc4f3a3a0)) -> QModelIndex(9,0,0x2adc4f3f7d0,QListModel(0x2adc4f3a3a0))

这在我看来是合理的 - 正如 OP 所报告的那样,没有对 selectionChanged() 的不必要调用。

可以肯定的是,我在cygwin中编译了相同的代码并再次尝试:

$ qmake-qt5 
Info: creating stash file /cygdrive/d/ds32737/Entwicklung/tests/Qt/QAbstractItemViewCurrentChanged/.qmake.stash

$ make && ./testQAbstractItemViewCurrentChanged
g++ -c -fno-keep-inline-dllexport -D_GNU_SOURCE -pipe -O2 -Wall -W -D_REENTRANT -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -I. -isystem /usr/include/qt5 -isystem /usr/include/qt5/QtWidgets -isystem /usr/include/qt5/QtGui -isystem /usr/include/qt5/QtCore -I. -I/usr/lib/qt5/mkspecs/cygwin-g++ -o testQAbstractItemViewCurrentChanged.o testQAbstractItemViewCurrentChanged.cc
g++  -o testQAbstractItemViewCurrentChanged.exe testQAbstractItemViewCurrentChanged.o   -lQt5Widgets -lQt5Gui -lQt5Core -lGL -lpthread 
Qt Version: 5.9.4
currentChanged(): QModelIndex(-1,-1,0x0,QObject(0x0)) -> QModelIndex(0,0,0x60014b260,QListModel(0x6000df6b0))
currentChanged(): QModelIndex(0,0,0x60014b260,QListModel(0x6000df6b0)) -> QModelIndex(1,0,0x60014ba60,QListModel(0x6000df6b0))
selectionChanged(): (QItemSelectionRange(QModelIndex(0,0,0x60014b260,QListModel(0x6000df6b0)),QModelIndex(0,0,0x60014b260,QListModel(0x6000df6b0))), QItemSelectionRange(QModelIndex(1,0,0x60014ba60,QListModel(0x6000df6b0)),QModelIndex(1,0,0x60014ba60,QListModel(0x6000df6b0)))) -> ()
currentChanged(): QModelIndex(1,0,0x60014ba60,QListModel(0x6000df6b0)) -> QModelIndex(2,0,0x60014bb20,QListModel(0x6000df6b0))
selectionChanged(): (QItemSelectionRange(QModelIndex(2,0,0x60014bb20,QListModel(0x6000df6b0)),QModelIndex(2,0,0x60014bb20,QListModel(0x6000df6b0)))) -> ()
currentChanged(): QModelIndex(2,0,0x60014bb20,QListModel(0x6000df6b0)) -> QModelIndex(3,0,0x60014bc30,QListModel(0x6000df6b0))
selectionChanged(): (QItemSelectionRange(QModelIndex(3,0,0x60014bc30,QListModel(0x6000df6b0)),QModelIndex(3,0,0x60014bc30,QListModel(0x6000df6b0)))) -> ()
currentChanged(): QModelIndex(3,0,0x60014bc30,QListModel(0x6000df6b0)) -> QModelIndex(4,0,0x60014bd20,QListModel(0x6000df6b0))
selectionChanged(): (QItemSelectionRange(QModelIndex(4,0,0x60014bd20,QListModel(0x6000df6b0)),QModelIndex(4,0,0x60014bd20,QListModel(0x6000df6b0)))) -> ()
currentChanged(): QModelIndex(4,0,0x60014bd20,QListModel(0x6000df6b0)) -> QModelIndex(5,0,0x60014be10,QListModel(0x6000df6b0))
currentChanged(): QModelIndex(5,0,0x60014be10,QListModel(0x6000df6b0)) -> QModelIndex(6,0,0x60014bf00,QListModel(0x6000df6b0))
currentChanged(): QModelIndex(6,0,0x60014bf00,QListModel(0x6000df6b0)) -> QModelIndex(7,0,0x60014bbb0,QListModel(0x6000df6b0))
currentChanged(): QModelIndex(7,0,0x60014bbb0,QListModel(0x6000df6b0)) -> QModelIndex(8,0,0x60014c120,QListModel(0x6000df6b0))
currentChanged(): QModelIndex(8,0,0x60014c120,QListModel(0x6000df6b0)) -> QModelIndex(9,0,0x60014c210,QListModel(0x6000df6b0))

所以,我再次无法复制。报告的事件与我使用 VS2017 得到的事件相当。

请注意,我已经在 cygwin 上安装了 Qt5.9.4——OP 声称拥有的版本。

【讨论】:

啊,我明白了。所以它真的应该只在焦点改变时调用 currentChanged 。我应该提到我没有使用 QListWidgets。我不确定这是否重要。我正在使用 QStyledItemDelegate。不确定这是否重要。我想我会尝试在一个小例子中设计一些东西 @JoshSanders 尝试使用尽可能小的minimal reproducible example 重现您的问题。我使用QListWidget 将代码 fpr 填充等保持在最低限度。如果这是QAbstractItemView 的问题,它不应该有所作为。我不确定QStyledItemDelegate 在什么情况下起作用(但恕我直言,它不应该)。 @JoshSanders 你在什么操作系统/平台上?

以上是关于Qt:如何处理 QAbstractItemView 的焦点变化的主要内容,如果未能解决你的问题,请参考以下文章

"Qt Qtwebengineprocess已停止工作",该如何处理

Qt Qtwebengineprocess已停止工作,该如何处理

STM32单片机如何处理QT上位机串口中发过来的数据?

如何处理 QDialogBu​​ttonBox RestoreDefaults 按钮

如何处理 C++ 重定向进程的输出

Qt之QAbstractItemView视图项拖拽