示例
功能
功能是参照本地资源管理器
- 按键盘字母,自动选中文件名开头对应的字母
- 列表视图和图标视图按住ctrl滚轮切换
- 排序
- 重命名
- shift多选
- ctrl单选反选
功能具体实现
重命名(列表视图)
因为列表视图是横向单行,所以用了QLineEdit控件
创建控件和填充数据均是在delegate中完成
// 显示控件
openPersistentEditor(QModelIndex)
// 隐藏控件
closePersistentEditor(QModelIndex)
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
if (index.column() == 1) {
const auto container = new QWidget(parent);
const auto lineEdit = new RenameLineEdit(container);
return container;
}
return QStyledItemDelegate::createEditor(parent, option, index);
}
void setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (!editor) {
return;
}
QList<RenameLineEdit*> list = editor->findChildren<RenameLineEdit*>();
if (list.isEmpty()) {
return;
}
// 因为qlineedit控件创建的时候只有一个,所以固定取第一个
const auto lineEdit = list[0];
if (!lineEdit) {
return;
}
// 获取数据
const auto data = index.data(Qt::UserRole).value<QString>();
lineEdit->setFocus();
lineEdit->SetText(data);
// 选中文件名前缀部分,文件格式不选中
int selectionIndex = data.length() - 1;
const auto suffix = QFileInfo(data).suffix();
if (!suffix.isEmpty()) {
selectionIndex = data.length() - suffix.length() - 1;
}
lineEdit->setSelection(0, selectionIndex);
}
重命名(图标视图)
因为图标视图的重命名框需要显示多行,内容比较多,所以使用了QTextEdit
// 显示控件
openPersistentEditor(QModelIndex)
// 隐藏控件
closePersistentEditor(QModelIndex)
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
const auto container = new QWidget(parent);
const auto textEdit = new RenameTextEdit(container);
textEdit->setAlignment(Qt::AlignCenter);
return container;
}
void setEditorData(QWidget* editor, const QModelIndex& index) const
{
if (!editor) {
return;
}
const auto textEdit = editor->findChildren<RenameTextEdit*>()[0];
if (!textEdit) {
return;
}
const auto data = index.data(Qt::UserRole).value<QString>();
textEdit->setFocus();
textEdit->SetText(data);
int selectionIndex = data.length() - 1;
const QString suffix = QFileInfo(data).suffix();
if (!suffix.isEmpty()) {
selectionIndex = data.length() - suffix.length() - 1;
}
// QTextEdit是使用游标的方式来操作选中的,这个和QLineEdit完全不一样
auto cursor = textEdit->textCursor();
cursor.setPosition(0, QTextCursor::MoveAnchor);
cursor.setPosition(selectionIndex, QTextCursor::KeepAnchor);
textEdit->setTextCursor(cursor);
}
单选,多选,取消选中
// 单选(通过多选的逻辑实现的单选)
void selectRow(int row)
{
if (row == -1) {
return;
}
QItemSelectionModel* model = selectionModel();
if (!model || !model_) {
return;
}
QItemSelection selection;
selection.merge({ model_->index(row, 0) , model_->index(row, 7) }, QItemSelectionModel::Select);
model->select(selection, QItemSelectionModel::Select);
}
// 多选
void selectMulRows(int startRow, int endRow)
{
const auto model = selectionModel();
if (!model || !model_) {
return;
}
QItemSelection selection;
int begin = startRow;
int end = endRow;
if (startRow > endRow) {
begin = endRow;
end = startRow;
}
QList<int> selectRows;
for(int i = begin; i < end + 1; i++) {
selection.merge({ model_->index(i, 0) , model_->index(i, 7) }, QItemSelectionModel::Select);
selectRows << i;
}
model->select(selection, QItemSelectionModel::Select);
}
// 取消选中
void unSelectRow(int row)
{
if (row == -1) {
return;
}
const auto model = selectionModel();
if (!model || !model_) {
return;
}
model->select({ model_->index(row, 0) , model_->index(row, 7) }, QItemSelectionModel::Deselect);
}
图标视图文字绘制
图标视图绘制图标下方文件名,文件名过长的时候,需要进行换行,这里有个注意点
// 需要使用TextWrapAnywhere
painter->drawText(rect, Qt::AlignHCenter | Qt::TextWrapAnywhere, name);
// 不能使用TextWordWrap,因为这个是遇到换行符才会进行换行
painter->drawText(rect, Qt::AlignHCenter | Qt::TextWordWrap, name);
图标视图显示缩略图
// 加载本地图片
QImage image;
image.load("xxxx");
// 读取svg格式的图片
QIcon icon;
icon.addFile("xxxx");
// 实际情况下,并不是所有的png都能通过QImage读取成功,具体原因不知道,QImage和QImageReader都会直接返回错误,这个时候,使用gdiplus来读取文件
IStream* stream;
SHCreateStreamOnFileEx(path.toStdWString().c_str(), STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
if (stream) {
const auto bitmap(Gdiplus::Bitmap::FromStream(stream));
stream->Release();
HICON icon;
bitmap->GetHICON(&icon);
const auto pixmap = QtWin::fromHICON(icon);
if (pixmap.isNull()) {
return false;
}
return true;
}