使用 QTreeView 在 QCombobox 中使用键盘控制所选项目

问题描述 投票:0回答:1

使用Qt 6.2.4,Ubuntu环境,我从

QComboBox
导出来设置一个
QTreeView
作为其视图。

它包含一个具有多个父级和子级的树(文件夹和文件),可以有多个级别的文件夹。

一切工作正常,并且按照我想要的方式运行。

现在,当选择一个项目时,我想使用向上和向下箭头键来选择同一父项的上一个或下一个项目。

我尝试了几件事,我得到了正确的同级,但我无法相应地更新组合框视图(组合框始终显示之前选择的项目,没有任何更新)。

main.cpp:

#include <QApplication>

#include "tree-combobox.h"

int main(int argc, char **argv)
{
    QApplication app (argc, argv);

    TreeComboBox combo;
    combo.setGeometry(0, 0, 400, 50);
    combo.ShowFileList("/my/path/", "*");
    combo.show();

    return app.exec();
}

树组合框.h:

#include <QComboBox>
#include <QTreeView>
#include <QFileSystemModel>
#include <QMouseEvent>
#include <QAbstractItemView>

class TreeComboBox : public QComboBox
{
public:
    TreeComboBox(QWidget* parent = 0) : QComboBox(parent)
    {
        QTreeView* tree = new QTreeView(this); // tree view for combobox
        setView(tree); // assign it to combobox
    }

    void ShowFileList(QString path, QString filesFilter) // fill combobox with folders and files, specify path and wildcard(s)
    {
        // block signals
        this->blockSignals(true); // no signals emitted while stuffing the widget

        //// create files model
        QFileSystemModel *fileModel = new QFileSystemModel(this); // file system model to use
        // set options to file model
        fileModel->setReadOnly(true); // set it read-only
        fileModel->setFilter(QDir::AllDirs | QDir::AllEntries |QDir::NoDotAndDotDot); // all folders, all files, no file beginning with a dot
        fileModel->setOption(QFileSystemModel::DontUseCustomDirectoryIcons); // don't use icons from the files
        fileModel->setOption(QFileSystemModel::DontWatchForChanges); // the widget won't track changes on disk
        // set file filter for files model
        QStringList filter; // for files wildcard
        filter << filesFilter;
        fileModel->setNameFilters(filter);
        // set root for files model
        fileModel->setRootPath("");

        //// create view
        // tree view
        QTreeView *view = new QTreeView;
        this->setView(view); // assign tree view to combobox
        // files model
        this->setModel(fileModel); // assign files model to combobox
        // remove columns in tree view, to keep only filenames
        QModelIndex index = fileModel->index(path);
        for (int i = 1; i < fileModel->columnCount(); ++i) // all columns but first one
            view->hideColumn(i);
        // tree view options
        view->setAnimated(true); // animated
        view->setSortingEnabled(true); // sorting enabled by clicking on header
        view->sortByColumn(0, Qt::AscendingOrder); // sort values
        view->expand(index); // expand the view from the given path
        view->scrollTo(index); // set view from given path
        view->setRootIndex(index); // set root index to given path

        // allow signals again
        this->blockSignals(false);
    }

    QString GetFile() // get selected value from list
        // currentItemChanged() is emitted each time an item is clicked, even a parent item
        // this function returns an empty QString if the clicked item is not valid (i.e. a folder)
    {
        QModelIndex index = view()->currentIndex(); // current value index from QTree

        QString path = model()->data(index, QFileSystemModel::FilePathRole).toString(); // get full path value
        QFileInfo info(path); // to test this value
        if (info.isFile()) // if the value is really a file
            return path; // ... return its full path
        else // value is a folder
            return QString(); // ... so return nothing
    }

private:
    virtual void hidePopup() // control popup hiding behaviour
        // for a combobox, each time an item is clicked the popup disappears... but not for a folder this time !
    {
        if (!view()->underMouse()) { // is mouse over QTreeView ?
            QComboBox::hidePopup(); // if not collapse the comboBox
            return;
        }

        QModelIndex index = view()->currentIndex(); // get current index of selected item
        if (!model()->hasChildren(index)) // if it doesn't have children (so it is not a folder)
            QComboBox::hidePopup(); // collapse the comboBox
    }

    virtual void keyPressEvent(QKeyEvent *keyboardEvent) // keyboard event
    {
        if (this->hasFocus()) { // widget has to be active to accept keyboard keys
            if (keyboardEvent->key() == Qt::Key_Up) { // up
                //view()->setCurrentIndex(view()->currentIndex().sibling(view()->currentIndex().row() - 1, 0));
                //view()->selectionModel()->select(view()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Rows);

                QModelIndex index = view()->currentIndex();
                int n = index.row() - 1;
                QModelIndex sibling = index.siblingAtRow(n);
                if (sibling.isValid()) {
                    view()->setCurrentIndex(sibling);
                    view()->scrollTo(sibling);
                    //view()->selectionModel()->setCurrentIndex(sibling, QItemSelectionModel::ClearAndSelect);
                    //view()->selectionModel()->select(sibling, QItemSelectionModel::Select | QItemSelectionModel::Rows);
                    //tree->selectionModel()->select(tree->currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
                    //this->setCurrentIndex(sibling.row());
                }

                keyboardEvent->accept(); // accept keyboard event
            }
            else if (keyboardEvent->key() == Qt::Key_Down) { // down
                view()->setCurrentIndex(view()->currentIndex().sibling(view()->currentIndex().row() - 1, 0));

                keyboardEvent->accept();
            }
        }
    }

};

到目前为止

keyPressEvent()
中有效的方法:
sibling
索引 (
QModelIndex
) 是正确的。

我尝试过的:用

//
注释掉这些行。

期望的结果:组合框选择并显示列表中的上一个或下一个项目,对于同一父级(无需向上或向下到另一个父级)。

c++ qt qtreeview qcombobox qkeyevent
1个回答
3
投票

树视图和组合框都需要在按键时更新,以便组合框上的项目可见更新。

并且由于似乎需要设置 rootModelIndex,并且初始根在子文件夹中丢失,因此我添加了

QModelIndex
作为成员来存储它。

带有解释性注释的修改后的类:

class TreeComboBox : public QComboBox
{
public:
    //add a new memeber to save the root index 
    QModelIndex rootIndex;
    TreeComboBox(QWidget* parent = 0) : QComboBox(parent)
    {
        QTreeView* tree = new QTreeView(this);
        setView(tree);
    }

    void ShowFileList(QString path, QString filesFilter) 
    {
        this->blockSignals(true); 

        QFileSystemModel *fileModel = new QFileSystemModel(this); 
        fileModel->setReadOnly(true);
        fileModel->setFilter(QDir::AllDirs | QDir::AllEntries |QDir::NoDotAndDotDot);
        fileModel->setOption(QFileSystemModel::DontUseCustomDirectoryIcons); 
        fileModel->setOption(QFileSystemModel::DontWatchForChanges); 
        
        QStringList filter; 
        filter << filesFilter;
        fileModel->setNameFilters(filter);
        fileModel->setRootPath("");

        QTreeView *view = new QTreeView;
        this->setView(view); 

        this->setModel(fileModel);

        QModelIndex index = fileModel->index(path);
        for (int i = 1; i < fileModel->columnCount(); ++i) 
            view->hideColumn(i);
        
        view->setAnimated(true); 
        view->setSortingEnabled(true);
        view->sortByColumn(0, Qt::AscendingOrder); 
        view->expand(index); 
        view->scrollTo(index); 
        view->setRootIndex(index); 

        //save the root index
        rootIndex=index;
        //then set to your comboBox
        setRootModelIndex(index);

        this->blockSignals(false);
    }

    QString GetFile()
    {
        QModelIndex index = view()->currentIndex();

        QString path = model()->data(index, QFileSystemModel::FilePathRole).toString(); 
        QFileInfo info(path); 
        if (info.isFile()) 
            return path; 
        else 
            return QString(); 
    }

private:
    virtual void hidePopup() 
    {
        if (!view()->underMouse()) { 
            QComboBox::hidePopup(); 
            return;
        }

        QModelIndex index = view()->currentIndex();
        if (!model()->hasChildren(index))
            QComboBox::hidePopup();
    }

    virtual void keyPressEvent(QKeyEvent *keyboardEvent)
    {
        if (this->hasFocus())
        {
            //I just used this to avoid having to select an item by clicking on it
            //you can remove it if it's of no use to you
            if(!view()->currentIndex().isValid())
            {
                view()->setCurrentIndex(view()->indexAt(QPoint(0,0)));
            }
            if (keyboardEvent->key() == Qt::Key_Up)
            {
                QModelIndex index = view()->currentIndex();
                int n = index.row() - 1;
                QModelIndex sibling = index.siblingAtRow(n);

                if (sibling.isValid())
                {
                    //update view's current index
                    view()->setCurrentIndex(sibling);
                    //update combobox
                    setRootModelIndex(sibling.parent());
                    setCurrentIndex(sibling.row());
                    //this is where you save the root index from being lost
                    if(rootModelIndex()!=rootIndex)
                        setRootModelIndex(rootIndex);
                }

                keyboardEvent->accept();
            }
            else
                if (keyboardEvent->key() == Qt::Key_Down)
                {
                    QModelIndex index = view()->currentIndex();
                    int n = index.row() + 1;
                    QModelIndex sibling = index.siblingAtRow(n);

                    if(sibling.isValid())
                    {
                        //update view's current index
                        view()->setCurrentIndex(sibling);
                        //update combobox
                        setRootModelIndex(sibling.parent());
                        setCurrentIndex(sibling.row());
                        
                        if(rootModelIndex()!=rootIndex)
                            setRootModelIndex(rootIndex);
                    }
                    
                    keyboardEvent->accept();
                }
        }
    }
};

结果:

了解更多:

© www.soinside.com 2019 - 2024. All rights reserved.