我正在构建一个带有树视图的应用程序,并创建了一个派生自 QAbstractItemModel 的模型。我可以通过拖放文件(将文件从应用程序外部拖到应用程序中)和在树中拖放项目来操作树。
问题是文件放到应用程序中并不总是有效,因为我重新实现了该功能mimeData(const QModelIndexList &indexes)
。有时且仅当树中有一个选定的项目(它为函数提供有效的项目索引mimeData
)时,文件删除不起作用,因为函数mimeData
被调用但函数dropMimeData
从未被调用。
我必须重新实现该函数mimeData
才能创建自己的 mimeData,该 mimeData 在树内拖放期间使用。这个 mimeData 用在函数dropMimeData
中,效果很好。
看起来mimeData
在文件删除期间不应调用该函数,因为应用程序已经知道 mimeData 格式:text/uri-list
。
我的函数dropMimeData
处理我自己的 mimeData 格式和text/uri-list
格式。
有没有人有同样的问题或对此有任何想法?再一次,它不像它根本不工作,它只是在某些时候失败。
任何帮助或想法都会很棒。
干杯。
以下可能完全不相关,这是我在尝试调试时遇到的。看起来可能与窗口的状态有关:QEvent::WindowActivate
或QEvent::WindowDeactivate
。在文件放置期间,窗口变为非活动状态并再次活动,但当窗口无法再次变为活动状态时似乎会出现问题。?
我添加了源代码来重现问题。将文件拖放到应用程序中。选择树中的项目。删除更多文件。有时滴不工作???
// main.cpp
#include "dragdropmainwindow.h"
#include <QtGui/QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
DragDropMainWindow w;
w.show();
return a.exec();
}
// dragdropmainwindow.h
#ifndef DRAGDROPMAINWINDOW_H
#define DRAGDROPMAINWINDOW_H
#include <QtGui/QMainWindow>
#include "ui_dragdropmainwindow.h"
#include "ControlTreeView.h"
class DragDropMainWindow : public QMainWindow
{
Q_OBJECT
public:
DragDropMainWindow(QWidget *parent = 0, Qt::WFlags flags = 0);
~DragDropMainWindow();
private:
Ui::DragDropMainWindowClass m_ui;
ControlTreeView *m_controlTree;
};
#endif // DRAGDROPMAINWINDOW_H
// dragdropmainwindow.cpp
#include "dragdropmainwindow.h"
DragDropMainWindow::DragDropMainWindow(QWidget *parent, Qt::WFlags flags)
: QMainWindow(parent, flags)
{
m_ui.setupUi(this);
// Create a model to control the view
m_controlTree = new ControlTreeView(NULL);
m_ui.m_treeView->setModel(m_controlTree);
}
DragDropMainWindow::~DragDropMainWindow()
{
}
// ControlTreeView.h
#pragma once
#include <QAbstractItemModel>
#include <QModelIndex>
#include <QStringList>
#include <QVector>
#include <QList>
class TreeItem
{
public:
TreeItem(const QVector<QVariant> &data, TreeItem *parent = 0);
~TreeItem();
TreeItem *child(int number);
int childCount() const;
int columnCount() const;
QVariant data(int column) const;
bool insertChildren(int position, int count, int columns);
bool insertColumns(int position, int columns);
TreeItem *parent();
bool removeChildren(int position, int count);
bool removeColumns(int position, int columns);
int childNumber() const;
bool setData(int column, const QVariant &value);
private:
QList<TreeItem*> childItems;
QVector<QVariant> itemData;
TreeItem *parentItem;
};
class ControlTreeView : public QAbstractItemModel
{
Q_OBJECT
public:
enum TREE_COLUMNS
{
eCOLUMN_FILENAME
};
ControlTreeView(QObject *parent);
~ControlTreeView(void);
QModelIndex index (int row, int column, const QModelIndex & parent = QModelIndex()) const;
QModelIndex parent (const QModelIndex & index) const;
int rowCount (const QModelIndex & parent = QModelIndex()) const;
int columnCount (const QModelIndex & parent = QModelIndex()) const;
QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags (const QModelIndex & index) const;
bool setData (const QModelIndex & index, const QVariant & value, int role = Qt::EditRole);
QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
bool insertRows (int row, int count, const QModelIndex & parent = QModelIndex());
//bool insertColumns (int column, int count, const QModelIndex & parent = QModelIndex());
bool removeRows (int row, int count, const QModelIndex & parent = QModelIndex());
//bool removeColumns (int column, int count, const QModelIndex & parent = QModelIndex());
bool dropMimeData (const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
Qt::DropActions supportedDropActions() const;
QStringList mimeTypes () const;
QMimeData *mimeData(const QModelIndexList &indexes) const;
bool setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role);
private:
TreeItem *getItem(const QModelIndex &jobIndex) const;
TreeItem *m_rootItem; // Root of the view tree
};
// ControlTreeView.cpp
#include "ControlTreeView.h"
#include <QtGui>
#include <QtGui/QGraphicsView>
class TreeColumn
{
public:
ControlTreeView::TREE_COLUMNS m_enumColumn;
QString m_header;
int m_index;
};
static const int NUMBER_COLUMNS = 1;
static TreeColumn s_columns[NUMBER_COLUMNS];
TreeItem::TreeItem(const QVector<QVariant> &data, TreeItem *parent)
{
parentItem = parent;
itemData = data;
}
TreeItem::~TreeItem()
{
qDeleteAll(childItems);
}
TreeItem *TreeItem::parent()
{
return parentItem;
}
TreeItem *TreeItem::child(int number)
{
return childItems.value(number);
}
int TreeItem::childCount() const
{
return childItems.count();
}
int TreeItem::childNumber() const
{
if (parentItem)
return parentItem->childItems.indexOf(const_cast<TreeItem*>(this));
return 0;
}
int TreeItem::columnCount() const
{
return itemData.count();
}
QVariant TreeItem::data(int column) const
{
return itemData.value(column);
}
bool TreeItem::setData(int column, const QVariant &value)
{
if (column < 0 || column >= itemData.size())
return false;
itemData[column] = value;
return true;
}
bool TreeItem::insertChildren(int position, int count, int columns)
{
if (position < 0 || position > childItems.size())
return false;
for (int row = 0; row < count; ++row)
{
QVector<QVariant> data(columns);
TreeItem *item = new TreeItem(data, this);
childItems.insert(position, item);
}
return true;
}
bool TreeItem::removeChildren(int position, int count)
{
if (position < 0 || position + count > childItems.size())
return false;
for (int row = 0; row < count; ++row)
delete childItems.takeAt(position);
return true;
}
bool TreeItem::insertColumns(int position, int columns)
{
if (position < 0 || position > itemData.size())
return false;
for (int column = 0; column < columns; ++column)
itemData.insert(position, QVariant());
foreach (TreeItem *child, childItems)
child->insertColumns(position, columns);
return true;
}
ControlTreeView::ControlTreeView(QObject *parent) :
QAbstractItemModel(parent)
{
QStringList headers;
s_columns[ControlTreeView::eCOLUMN_FILENAME].m_header = QObject::tr("File");
s_columns[ControlTreeView::eCOLUMN_FILENAME].m_index = 0;
for (unsigned int index = 0; index < NUMBER_COLUMNS; index++)
headers.append(s_columns[index].m_header);
QVector<QVariant> rootData;
foreach (QString header, headers)
rootData << header;
m_rootItem = new TreeItem(rootData);
}
ControlTreeView::~ControlTreeView()
{
delete m_rootItem;
}
int ControlTreeView::columnCount(const QModelIndex & parent) const
{
return NUMBER_COLUMNS;
};
TreeItem *ControlTreeView::getItem(const QModelIndex &index) const
{
if (index.isValid())
{
TreeItem *item = static_cast<TreeItem*>(index.internalPointer());
if (item) return item;
}
return m_rootItem;
}
int ControlTreeView::rowCount(const QModelIndex &parent) const
{
TreeItem *parentItem = getItem(parent);
return parentItem->childCount();
}
Qt::ItemFlags ControlTreeView::flags(const QModelIndex &index) const
{
//if (!index.isValid())
// return 0;
//return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled | Qt::ItemIsDragEnabled;
Qt::ItemFlags defaultFlags = Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if (index.isValid())
return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
else
return Qt::ItemIsDropEnabled | defaultFlags;
}
QModelIndex ControlTreeView::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid() && parent.column() != 0)
return QModelIndex();
TreeItem *parentItem = getItem(parent);
TreeItem *childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex ControlTreeView::parent(const QModelIndex &index) const
{
if (!index.isValid())
return QModelIndex();
TreeItem *childItem = getItem(index);
TreeItem *parentItem = childItem->parent();
if (parentItem == m_rootItem)
return QModelIndex();
return createIndex(parentItem->childNumber(), 0, parentItem);
}
QVariant ControlTreeView::data(const QModelIndex & index, int role) const
{
if (!index.isValid())
return QVariant();
switch (role)
{
case Qt::DisplayRole:
case Qt::EditRole:
{
TreeItem *item = getItem(index);
return item->data(index.column());
}
break;
case Qt::DecorationRole:
{
if (index.column() == 0)
{
if (!index.parent().isValid()) // Only decorate for
{
//QString file = m_jobList->GetJobSrcFileName(index.row());
//if (!file.isEmpty())
// return IconCache::Get().Load(file);
}
}
}
break;
}
return QVariant();
}
bool ControlTreeView::setHeaderData(int section, Qt::Orientation orientation,
const QVariant &value, int role)
{
if (role != Qt::EditRole || orientation != Qt::Horizontal)
return false;
bool result = m_rootItem->setData(section, value);
if (result)
emit headerDataChanged(orientation, section, section);
return result;
}
QVariant ControlTreeView::headerData(int section, Qt::Orientation orientation, int role) const
{
if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
return m_rootItem->data(section);
return QVariant();
}
QStringList ControlTreeView::mimeTypes () const
{
QStringList mimeTypes;
mimeTypes += "text/uri-list";
mimeTypes += "text/conversion-job-tree";
return mimeTypes;
}
QMimeData *ControlTreeView::mimeData(const QModelIndexList &indexes) const
{
qDebug() << __FUNCTION__;
QMimeData *mimeData = 0;
// If the window is out off focus a file is dragged from outside of the app
// This is not normal behaviour as this function should not be call if
// the drag comes from outside of the app (??).
mimeData = new QMimeData();
QByteArray encodedData;
QDataStream stream(&encodedData, QIODevice::WriteOnly);
foreach (QModelIndex index, indexes) {
if (index.isValid() && !index.parent().isValid())
{
stream << index.row();
}
else
if (index.parent().isValid())
stream << -1;
}
mimeData->setData("text/conversion-job-tree", encodedData);
return mimeData;
}
Qt::DropActions ControlTreeView::supportedDropActions() const
{
return Qt::CopyAction;
}
bool ControlTreeView::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
{
qDebug() << __FUNCTION__;
if (action == Qt::IgnoreAction)
return true;
bool success = false;
if (action == Qt::CopyAction)
{
if (data->hasFormat("text/uri-list"))
{
QList<QUrl> urls = data->urls();
for (int i = 0; i < urls.size(); ++i)
{
QString file = urls[i].toLocalFile();
if (file != "")
{
// Check if the file is already in the table
int insertRow = -1;
if (parent.isValid())
insertRow = parent.row();
if (insertRow == -1)
if (row == -1)
insertRow = m_rootItem->childCount();
else
insertRow = row;
insertRows(insertRow, 1, QModelIndex());
setData(index(insertRow, s_columns[ControlTreeView::eCOLUMN_FILENAME].m_index), file, Qt::EditRole);
success = true;
}
}
}
else
{
// Different MineData used for drop tree items
if (data->hasFormat("text/conversion-job-tree"))
{
// Only drop on a job
if (parent.isValid() && parent.parent().isValid())
return false;
int insertRow = -1;
if (parent.isValid())
insertRow = parent.row();
if (insertRow == -1)
if (row == -1)
insertRow = m_rootItem->childCount();
else
insertRow = row;
QByteArray encodedData = data->data("text/conversion-job-tree");
QDataStream stream(&encodedData, QIODevice::ReadOnly);
int srcRowIndex = -1;
while (!stream.atEnd()) {
stream >> srcRowIndex;
}
// Exit if the source is not valid
if (srcRowIndex == -1)
return false;
if (srcRowIndex < insertRow)
{
insertRow--;
if (insertRow < 0)
insertRow = 0;
}
if (srcRowIndex == insertRow)
return false;
// Remove the original raw
removeRows(srcRowIndex,1);
// Insert a new raw
insertRows(insertRow, 1, QModelIndex());
//setData(index(insertRow, s_columns[ConversionJobTreeViewCtrl::eCOLUMN_FILENAME].m_index), m_jobList->At(insertRow)->GetBaseSrcFileName(), Qt::EditRole);
return success;
}
}
}
else
success = false;
return success;
}
bool ControlTreeView::insertRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success;
beginInsertRows(parent, position, position + rows - 1);
success = parentItem->insertChildren(position, rows, m_rootItem->columnCount());
endInsertRows();
return success;
}
bool ControlTreeView::removeRows(int position, int rows, const QModelIndex &parent)
{
TreeItem *parentItem = getItem(parent);
bool success = true;
beginRemoveRows(parent, position, position + rows - 1);
success = parentItem->removeChildren(position, rows);
endRemoveRows();
return success;
}
bool ControlTreeView::setData(const QModelIndex &index, const QVariant &value, int role)
{
if (role != Qt::EditRole)
return false;
TreeItem *item = getItem(index);
bool result = item->setData(index.column(), value);
if (result)
emit dataChanged(index, index);
return result;
}