PDA

View Full Version : Subclassing QTreeWidgetItem for Drag & Drop?



kerchen
24th April 2008, 20:01
Greetings all,
Regular lurker, first time posting. I want to be able to allow drops only on certain items in a QTreeWidget. Is it possible to subclass QTreeWidgetItem to allow it to be a "drop target" for a drag & drop operation? The code below shows the approach I tried, but it doesn't work correctly: only the first MyTreeWidgetItem I add to the tree control accepts drops! The fact that this only 'sort of' works makes me suspect I'm bumping into a side effect of some of Qt's C++ magic. Is it possible to subclass QTreeWidgetItem to accept drops? Alternatively, if there is another way to achieve the goal of fine-grained control of which items can be "dropped on", I'd be happy to learn about that.

Thanks!
Paul Kerchen

Platform/environment: Qt 4.3.3, MSVC 7.1, Windows XP



class MyTreeWidgetItem : public QWidget, public QTreeWidgetItem
{
Q_OBJECT

public:
MyTreeWidgetItem( QTreeWidget* parent);

signals:
void itemDropped( MyTreeWidgetItem* item, QWidget* source );

protected:
void dragEnterEvent(QDragEnterEvent* event);
void dragMoveEvent(QDragMoveEvent* event);
void dropEvent(QDropEvent* event);
};

MyTreeWidgetItem::MyTreeWidgetItem( TreeWidget* parent )
: QWidget(parent)
, QTreeWidgetItem( parent )
{
setAcceptDrops(true);
}



void MyTreeWidgetItem::
dragEnterEvent(QDragEnterEvent* event)
{
event->setDropAction(Qt::CopyAction);
event->acceptProposedAction();
}


void MyTreeWidgetItem::
dragMoveEvent(QDragMoveEvent* event)
{
event->setDropAction(Qt::CopyAction);
event->acceptProposedAction();
}


void MyTreeWidgetItem::
dropEvent(QDropEvent* event)
{
event->setDropAction(Qt::CopyAction);
event->acceptProposedAction();
emit itemDropped(this, event->source());
}

kerchen
24th April 2008, 20:56
Well, I found another way to do what I need to do without having to subclass QTreeWidgetItem:

Subclass QTreeWidget and reimplement dragEnterEvent() and dragMoveEvent() so that they check to see if the item that the event occurred over is an item that can accept the drop. For example:



void MyTreeWidget::
dragEnterEvent(QDragEnterEvent* event)
{
QTreeWidgetItem* item = itemAt(event->pos());

if ( itemAcceptsEvent(event,item) )
event->setDropAction(Qt::CopyAction);
else
event->setDropAction(Qt::IgnoreAction);
event->accept();
}


A few important points:

Call accept() and not acceptProposedAction() if you're overriding the proposed action.
IgnoreAction must be one of the possible actions specified by the drag source when the drag was initiated. Otherwise, it defaults to CopyAction.


Hopefully this saves someone a little time!

patrik08
26th April 2008, 13:50
If you can read my code this QListWidget can drag drop multile items as icon.. and text
from attribute hotels sauna bar, ecc..




class IconScroller : public QListWidget
{
Q_OBJECT
//
public:
IconScroller( bool enable = false , QWidget* parent = 0 )
: QListWidget( parent )
{
enableout = enable;
setSelectionMode ( QAbstractItemView::ExtendedSelection );
setAcceptDrops(true);
setDropIndicatorShown(false);
if (enable) {
setDragEnabled(true);
}
///////setIconSize ( QSize(80,80));
}

void mousePressEvent(QMouseEvent *event)
{
if (enableout) {
QListWidgetItem *child = itemAt(event->pos());
if (!child) {
return;
}
/* IcoLine = hotel attribut icon mit beschreibung in 6 sprachen und
int kategorie int id + pixmap 30x30 */
IcoLine Io = child->data(Qt::UserRole).value<IcoLine>();

QPixmap dragicon = Io.pix().scaledToWidth(Io.pix().width() * 3); /* zoom it */
/* TabIcone liste von IcoLine */
TabIcone dd;
dd.all_lines.append(Io);

QList<QListWidgetItem *> tutti = selectedItems();

for (int i=0; i<tutti.size(); i++) {
QListWidgetItem *pane = tutti[i];
IcoLine Iter = pane->data(Qt::UserRole).value<IcoLine>();
dd.all_lines.append(Iter);
}

QString stream = SaveTabelicon(dd); /* QDataStream zu toBase64 fuer sql*/
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-atticonhotel",stream.toUtf8());

QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
drag->setPixmap(dragicon);
///////drag->setHotSpot(event->pos() - ????? ); //////
/* make pixmap large and setHotSpot not need */

if (drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction) == Qt::MoveAction) {
qDebug() << "### drag move it " << Io.id();
}

}

QListWidget::mousePressEvent(event);
}
void dropEvent(QDropEvent *event)
{
///////qDebug() << "### dropEvent";
if (event->mimeData()->hasFormat("application/x-atticonhotel")) {
QByteArray itemData = event->mimeData()->data("application/x-atticonhotel");
//////////qDebug() << "### dropEvent entra siiiiiiiiiiii ";
QString daten = QString::fromUtf8(itemData.data());
TabIcone liste = OpenTabelicon(daten);
emit AppendList(liste);
event->accept();

} else {
event->ignore();
}
}



void dragEnterEvent(QDragEnterEvent *event)
{
qDebug() << "### dragEnterEvent ";

if (event->mimeData()->hasFormat("application/x-atticonhotel")) {
qDebug() << "### dragEnterEvent si ";
event->acceptProposedAction();
} else {
event->ignore();
}
}

void dragMoveEvent(QDragMoveEvent *event)
{
qDebug() << "### dragMoveEvent ";

if (event->mimeData()->hasFormat("application/x-atticonhotel")) {
qDebug() << "### dragMoveEvent si ";
event->acceptProposedAction();
} else {
event->ignore();
}
}
bool enableout;
signals:
void AppendList(TabIcone);
public slots:

};

wysota
26th April 2008, 18:42
It is not required to reimplement any events to handle drops. You need to do three things. First make sure your item returns a ItemIsDropEnabled flag among its QListWidgetItem::flags(). Then you will possibly need to reimplement QListWidget::dropMimeData() to handle a drop. You'll probably want to reimplement QListWidget::mimeData() as well, to be able to start drags properly.