PDA

View Full Version : QDirModel and QTreeView cut and paste



Micawber
28th May 2008, 15:18
I'm using the examples of the simple file manager using QDirModel and TreeView mentioned in this (http://www.qtcentre.org/forum/f-qt-programming-2/t-suggestionsideas-about-a-file-browser-page2-1625.html) post.


#include <QtGui>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow( QWidget *parent = 0 ) : QMainWindow( parent)
{
splitter = new QSplitter( this );

model = new QDirModel( this );
model->setReadOnly( false );

tree = new QTreeView;
tree->setModel( model );
tree->setRootIndex( model->index( QString( "/home/randy" ) ) );

model1 = new QDirModel( this );
model1->setReadOnly( false );

righttree = new QTreeView;
righttree->setModel( model1 );
righttree->setRootIndex( model1->index( QString( "/tmp" ) ) );

tree->setDragEnabled( true );
tree->setAcceptDrops( true );
tree->setDropIndicatorShown( true );
tree->setDragDropOverwriteMode( false );

righttree->setDragEnabled( true );
righttree->setAcceptDrops( true );
righttree->setDropIndicatorShown( true );
righttree->setDragDropOverwriteMode( false );

splitter->addWidget( tree );
splitter->addWidget( righttree );

timer = new QTimer( this );


//QObject::connect( model, SIGNAL( layoutChanged( ) ), this, SLOT( refreshleft( ) ) );
//QObject::connect( model1, SIGNAL( layoutChanged( ) ), this, SLOT( refreshright( ) ) );
QObject::connect( timer, SIGNAL( timeout( ) ), this, SLOT( refreshleft( ) ) );
QObject::connect( timer, SIGNAL( timeout( ) ), this, SLOT( refreshright( ) ) );

setCentralWidget( splitter );
timer->start( 1000 );

}

private slots:
void refreshleft ()
{
model->refresh( tree->rootIndex() );
}

void refreshright ()
{
model1->refresh( righttree->rootIndex() );
}


private:
QSplitter *splitter;
QDirModel *model;
QTreeView *tree;
QDirModel *model1;
QTreeView *righttree;
QTimer *timer;
};

int main( int argc, char *argv[] )
{
// a "file manager" at simplest :)
QApplication a( argc, argv );
MainWindow window;
window.show();
return a.exec( );
}

#include "app.moc"


I am running into a problem when I do cut and paste in that when using the shift key modifier to indicate a true cut and paste (instead of a copy and paste), I see the new file has been inserted in the tree that received the drop, however the tree that initiated the drag still shows the file. I checked in the file system and the file has in fact been removed so the cut and paste worked, its just that since QDirModel uses a cache, it needs a refresh.

Any ideas on how to force a QDirModel->refresh() upon successful cut and paste? (I would have thought the thing would have done it automatically.)

I have a workaround by starting a 1 second timer that calls refresh on timeout but thats not exactly what I want.

I tried connecting QDirModel->dataChanged to a slot that calls refresh on the QDirModel but that didn't work because that doesn't get emitted when new data is added.

I then tried connecting QDirModel->layoutChanged to that slot but that caused the app to lock up.

lyuts
28th May 2008, 16:18
What if you call refresh( const QModelIndex & ) for the parents of files that have been "cut&pasted"? and then repaint for QTreeView?

Micawber
28th May 2008, 16:29
What if you call refresh( const QModelIndex & ) for the parents of files that have been "cut&pasted"? and then repaint for QTreeView?

I haven't subclassed QTreeView so I don't have access to the repaint. I'm trying to use the standard widgets if possible.

Micawber
28th May 2008, 19:42
I have a solution using an event filter shown below.

One would think that one would be able to intercept an event of type QEvent;;Drop heading for the TreeViews but I was unable to detect one. Strange...Unless it is the MetaCall event it receives just after the drop. Since I could not make use of the information in the meta call event I can't know if it was the Drop event or not.

At anyrate, the QEvent::ChildRemoved event type proved to work just as well. Perhaps even better since I should only get that when the model/view accepts the drop and I don't have to put in lots of ugly code to send the event and check its return to see if the drop was accepted or not.

Enjoy...

-Mic



#include <QtGui>

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow( QWidget *parent = 0 ) : QMainWindow( parent)
{
splitter = new QSplitter( this );

model = new QDirModel( this );
model->setReadOnly( false );

tree = new QTreeView;
tree->setModel( model );
tree->setRootIndex( model->index( QString( "/home/randy" ) ) );

model1 = new QDirModel( this );
model1->setReadOnly( false );

righttree = new QTreeView;
righttree->setModel( model1 );
righttree->setRootIndex( model1->index( QString( "/tmp" ) ) );

tree->setDragEnabled( true );
tree->setAcceptDrops( true );
tree->setDropIndicatorShown( true );
tree->setDragDropOverwriteMode( false );
tree->installEventFilter( this );

righttree->setDragEnabled( true );
righttree->setAcceptDrops( true );
righttree->setDropIndicatorShown( true );
righttree->setDragDropOverwriteMode( false );
righttree->installEventFilter( this );

splitter->addWidget( tree );
splitter->addWidget( righttree );

setCentralWidget( splitter );
}

bool eventFilter( QObject *target, QEvent *event )
{
if( target == tree && event->type() == QEvent::ChildRemoved ) {
model->refresh( tree->rootIndex() );
}
else if( target == righttree && event->type() == QEvent::ChildRemoved ) {
model1->refresh( righttree->rootIndex() );
}
return false;
}

private:
QSplitter *splitter;
QDirModel *model;
QTreeView *tree;
QDirModel *model1;
QTreeView *righttree;
QTimer *timer;
};

int main( int argc, char *argv[] )
{
// a "file manager" at simplest :)
QApplication a( argc, argv );
MainWindow window;
window.show();
return a.exec( );
}

#include "app.moc"

wysota
28th May 2008, 20:16
I admit I haven't read the whole thread, but based on the parts I did read I can say that you have to make sure you return Qt::MoveAction as the drop action (cut and paste is really another interface to drag&drop). Depending on how you handle the D&D mechanism (meaning if you do it in the model or in the view), there are different ways you can do it. Otherwise you'll have to detect the move action yourself and delete entries from the model as you probably did using the event filter.