首页 > 代码库 > QTreeView + QFileSystemModel rename的实现

QTreeView + QFileSystemModel rename的实现

以前在写Markdown编辑器的时候, 写了一个基于QTreeView+QFileSystemModel的简单资源浏览器, 现在想给其增加一个rename的功能.

其实实现rename还是比较简单的, 首先在QTreeView中设置setEditTriggers(QAbstractItemView::EditKeyPressed);这样就可以按键盘上的F2来来进行rename. 但是如果想做成有一个右键菜单上有rename, 如下图:


那该怎么做呢. 这里需要加一个menu,当然并不是重点, 重点在于如何才会触发rename这个操作, 在前面我们设置好setEditTriggers之后按键盘F2是可以触发rename的.但是我们并不知道在按下F2之后发生了什么.当然我们可以完全不比理会按下F2之后发生了什么, 既然按下F2可以进行rename, 那么我们就产生一个按下F2的事件也是可以实现目的的代码片段如下:

connect(menu_, &ProjectExplorerMenu::signalRename, [this](){
        QString path = model_->filePath(currentIndex());
        if(path.isNull())
        {
            qDebug() << "file path is null";
            return;
        }

        //真逗比但有效...
        QKeyEvent event(QEvent::KeyPress, Qt::Key_F2, Qt::NoModifier);
        keyPressEvent(&event);
});
这里面当menu选中rename这一项的时候发出signalRename的信号, 在对应的槽中实例化了一个QKeyEvent对象, 并将其key设置为Qt::key_F2, 即产按下F2的这个事件, 然后把这个事件传给处理键盘按下的事件过滤器keyPressEvent. 就此目标实现. 但是这样做并不是太干净,想做得更干净一点可以查QTreeView的源码. 在QTreeView的keyPressEvent中:

void QTreeView::keyPressEvent(QKeyEvent *event)
{
    Q_D(QTreeView);
    QModelIndex current = currentIndex();
    //this is the management of the expansion
    if (d->isIndexValid(current) && d->model && d->itemsExpandable) {
        switch (event->key()) {
        case Qt::Key_Asterisk: {
            QStack<QModelIndex> parents;
            parents.push(current);
                while (!parents.isEmpty()) {
                    QModelIndex parent = parents.pop();
                    for (int row = 0; row < d->model->rowCount(parent); ++row) {
                        QModelIndex child = d->model->index(row, 0, parent);
                        if (!d->isIndexValid(child))
                            break;
                        parents.push(child);
                        expand(child);
                    }
                }
                expand(current);
            break; }
        case Qt::Key_Plus:
            expand(current);
            break;
        case Qt::Key_Minus:
            collapse(current);
            break;
        }
    }

    QAbstractItemView::keyPressEvent(event);
}
代码很简单没几行, 处理了按下键盘上+,-,*三个按键. +号展开, -号收缩, *号递归展开其他的键盘事件扔给其基类去处理(不看代码我都不知道原来已经有这样的处理........), 再来看看QAbstractItemView中对F2做了什么特别的处理这里代码就比较多了有几百行, 代码虽长但是F2处理的部分还是很简单的:

void QAbstractItemView::keyPressEvent(QKeyEvent *event)
{
    ...
    switch (event->key()) {
        case Qt::Key_F2:
        if (!edit(currentIndex(), EditKeyPressed, event))
            event->ignore();
        break;
        ...
    }
    ...
}
原来按下F2的时候是使用了edit这个函数来进行处理的, 由此修改我们之前直接生成一个QKeyEvent实例并传递给keyPressEvent的代码可以更改为:

connect(menu_, &ProjectExplorerMenu::signalRename, [this](){
        QString path = model_->filePath(currentIndex());
        if(path.isNull())
        {
            qDebug() << "file path is null";
            return;
        }

        QKeyEvent event(QEvent::KeyPress, Qt::Key_F2, Qt::NoModifier);
        edit(currentIndex(), EditKeyPressed, &event);
});

呵呵很多时候文档里面没查到的东西, 看看代码就知道了, 源码之下无秘密!

QTreeView + QFileSystemModel rename的实现