使用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
) 是正确的。
我尝试过的:用
//
注释掉这些行。
期望的结果:组合框选择并显示列表中的上一个或下一个项目,对于同一父级(无需向上或向下到另一个父级)。
树视图和组合框都需要在按键时更新,以便组合框上的项目可见更新。
并且由于似乎需要设置 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();
}
}
}
};
结果:
了解更多: