Qt实现从列表中选择项目到其他列表(双列表,累加器,列表生成器,TwoListSelection ...)

Posted

技术标签:

【中文标题】Qt实现从列表中选择项目到其他列表(双列表,累加器,列表生成器,TwoListSelection ...)【英文标题】:Qt Implementation of selecting items from list into other list (Dual List, Accumulator, List builder, TwoListSelection ...) 【发布时间】:2018-01-18 17:56:50 【问题描述】:

我想从任意长度的列表中选择任意数量的项目。 下拉列表 (QComboBox) 不允许检查项目。可检查的项目列表会因为项目太多而变得笨拙。

我在 User Experience SE 子站点中找到了 this question,this answer 似乎是最适合我需求的解决方案。它有很多名字,正如所说的答案评论中的评论:Dual List, Accumulator, List builder, TwoListSelection ...

上面链接的答案中显示的来自OpenFaces.org 的版本:

不过,我在 Qt 中找不到实现。我应该自己实现它还是 Qt 中有可用的实现?有推荐的方法吗?

【问题讨论】:

对 Qt 示例进行调查...我在一个示例中看到过这种情况.. 实在想不起来是哪个! 在问这个问题之前完成它。找不到类似的东西。你还记得这个例子吗?也许我错过了。 您可以从左侧 QListWidget 获取当前项目并使用 addItem(new QListWidgetItem()) 添加右侧,您可以从其他 QListWidgetItem 创建 QListWidgetItem。如果要从左侧取出所有项目,可以使用 while(ui->leftList->count() > 0) and take a item from a index,有什么问题? @Taz742,这似乎是一种常见的模式,不想重新发明***。我将自己实现它并将其添加为我和其他人将来参考的答案。谢谢! 如果您有任何问题,我在这里。我已经为自己做了确切的事情。 【参考方案1】:

这个小部件在Qt中默认没有,但是构建它并不复杂,首先你应该列出需求:

如果左侧列表不为空,则启用带有文本>>的按钮,如果按下它必须移动所有项目。

带有文本的按钮与上一个类似,但右侧有列表

如果左侧列表中的项目被选中,则带有文本 > 的按钮被启用,如果按下,则所选项目应移动到右侧。

带有文本的按钮相同,但在右侧。

如果选择了一个项目并且这不是列表中的第一项,则启用带有“向上”文本的按钮。按下时,您必须将当前项目移动到更高的位置。

如果选择了一个项目并且这不是列表中的最后一个项目,则会启用带有“向下”文本的按钮。按下时,您必须将当前项目移动到较低的位置。

正如我们所见,大部分逻辑取决于项目的选择,为此我们将信号 itemSelectionChanged 与决定按钮是否启用的插槽连接。

要移动项目,我们必须使用takeItem()addItem(),第一个删除项目,第二个添加它。

向上或向下移动相当于删除项目然后插入它,为此我们再次使用takeItem()insertItem()

以上所有内容都在以下小部件中实现:

#ifndef TWOLISTSELECTION_H
#define TWOLISTSELECTION_H

#include <QHBoxLayout>
#include <QListWidget>
#include <QPushButton>
#include <QWidget>

class TwoListSelection : public QWidget

    Q_OBJECT
public:
    explicit TwoListSelection(QWidget *parent = nullptr):QWidgetparent
        init();
        connections();
    

    void addAvailableItems(const QStringList &items)
        mInput->addItems(items);
    

    QStringList seletedItems()
        QStringList selected;
        for(int i=0; i<mOuput->count(); i++)
            selected<< mOuput->item(i)->text();
        return selected;
    
private:
    void init()
        QHBoxLayout *layout = new QHBoxLayout(this);
        mInput = new QListWidget;
        mOuput = new QListWidget;

        mButtonToSelected = new QPushButton(">>");
        mBtnMoveToAvailable= new QPushButton(">");
        mBtnMoveToSelected= new QPushButton("<");
        mButtonToAvailable = new QPushButton("<<");

        layout->addWidget(mInput);

        QVBoxLayout *layoutm = new QVBoxLayout;
        layoutm->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
        layoutm->addWidget(mButtonToSelected);
        layoutm->addWidget(mBtnMoveToAvailable);
        layoutm->addWidget(mBtnMoveToSelected);
        layoutm->addWidget(mButtonToAvailable);
        layoutm->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));

        layout->addLayout(layoutm);
        layout->addWidget(mOuput);

        mBtnUp = new QPushButton("Up");
        mBtnDown = new QPushButton("Down");

        QVBoxLayout *layoutl =  new QVBoxLayout;
        layoutl->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));
        layoutl->addWidget(mBtnUp);
        layoutl->addWidget(mBtnDown);
        layoutl->addItem(new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding));

        layout->addLayout(layoutl);
        setStatusButton();
    

    void connections()
        connect(mOuput, &QListWidget::itemSelectionChanged, this, &TwoListSelection::setStatusButton);
        connect(mInput, &QListWidget::itemSelectionChanged, this, &TwoListSelection::setStatusButton);
        connect(mBtnMoveToAvailable, &QPushButton::clicked, [=]()
           mOuput->addItem(mInput->takeItem(mInput->currentRow()));
        );

        connect(mBtnMoveToSelected, &QPushButton::clicked, [=]()
           mInput->addItem(mOuput->takeItem(mOuput->currentRow()));
        );

        connect(mButtonToAvailable, &QPushButton::clicked, [=]()
            while (mOuput->count()>0) 
                 mInput->addItem(mOuput->takeItem(0));
            
        );

        connect(mButtonToSelected, &QPushButton::clicked, [=]()
            while (mInput->count()>0) 
                 mOuput->addItem(mInput->takeItem(0));
            
        );

        connect(mBtnUp, &QPushButton::clicked, [=]()
            int row = mOuput->currentRow();
            QListWidgetItem *currentItem = mOuput->takeItem(row);
            mOuput->insertItem(row-1, currentItem);
            mOuput->setCurrentRow(row-1);
        );

        connect(mBtnDown, &QPushButton::clicked, [=]()
            int row = mOuput->currentRow();
            QListWidgetItem *currentItem = mOuput->takeItem(row);
            mOuput->insertItem(row+1, currentItem);
            mOuput->setCurrentRow(row+1);
        );
    

    void setStatusButton()
        mBtnUp->setDisabled(mOuput->selectedItems().isEmpty() || mOuput->currentRow() == 0);
        mBtnDown->setDisabled(mOuput->selectedItems().isEmpty() || mOuput->currentRow() == mOuput->count()-1);
        mBtnMoveToAvailable->setDisabled(mInput->selectedItems().isEmpty());
        mBtnMoveToSelected->setDisabled(mOuput->selectedItems().isEmpty());
    

    QListWidget *mInput;
    QListWidget *mOuput;

    QPushButton *mButtonToAvailable;
    QPushButton *mButtonToSelected;

    QPushButton *mBtnMoveToAvailable;
    QPushButton *mBtnMoveToSelected;

    QPushButton *mBtnUp;
    QPushButton *mBtnDown;
;

#endif // TWOLISTSELECTION_H

在下面的link你会找到一个完整的例子。

【讨论】:

+ 这是一个很好的例子,但从 cmets 看来,他自己也想这样做。 我尝试编译它(在 Qt Creator 和 qmake && make 中)并且它引发了太多错误来修复我自己。但理论上它与我最终做的非常相似,所以 +1 并接受了。 @Alechan 你的 Qt 版本是什么? @eyllanesc,QMake 3.0 版。在 /usr/lib/x86_64-linux-gnu 中使用 Qt 5.2.1 版 它的 Qt 版本很老,从 5.6 或 5.7 Qt 开始支持 C++ 11,您可以使用新的连接方式:wiki.qt.io/New_Signal_Slot_Syntax,以及其他功能。这就是为什么它与其版本不兼容的原因。我已经用 Qt 5.10 对其进行了测试,但我认为它与 Qt 5.7 向前兼容。如果我有时间,我会支持以前的版本。

以上是关于Qt实现从列表中选择项目到其他列表(双列表,累加器,列表生成器,TwoListSelection ...)的主要内容,如果未能解决你的问题,请参考以下文章

RecyclerView双列表联动效果开发细节

双列表拖放控件[关闭]

更改双列表框中单击选项的背景颜色

如何在 Qt 中自定义列表视图

Qt实现列表显示功能的是哪一个类?

双列的选择条件(ORA-00904:标识符无效)