diff --git a/src/providers/grass/qgis.r.in.cpp b/src/providers/grass/qgis.r.in.cpp index 4759c44d4a0..f221969c59e 100644 --- a/src/providers/grass/qgis.r.in.cpp +++ b/src/providers/grass/qgis.r.in.cpp @@ -58,6 +58,7 @@ extern "C" #define G_set_raster_value_d Rast_set_d_value #define G_put_raster_row Rast_put_row #define G_raster_size Rast_cell_size +#define G_unopen_cell Rast_unopen #endif int main( int argc, char **argv ) @@ -127,9 +128,15 @@ int main( int argc, char **argv ) void *buf = G_allocate_raster_buf( grass_type ); int expectedSize = cols * QgsRasterBlock::typeSize( qgis_type ); + bool isCanceled; QByteArray byteArray; for ( int row = 0; row < rows; row++ ) { + stdinStream >> isCanceled; + if ( isCanceled ) + { + break; + } stdinStream >> byteArray; if ( byteArray.size() != expectedSize ) { @@ -162,11 +169,18 @@ int main( int argc, char **argv ) G_put_raster_row( cf, buf, grass_type ); } - G_close_cell( cf ); - struct History history; - G_short_history( name, "raster", &history ); - G_command_history( &history ); - G_write_history( name, &history ); + if ( isCanceled ) + { + G_unopen_cell( cf ); + } + else + { + G_close_cell( cf ); + struct History history; + G_short_history( name, "raster", &history ); + G_command_history( &history ); + G_write_history( name, &history ); + } exit( EXIT_SUCCESS ); } diff --git a/src/providers/grass/qgis.v.in.cpp b/src/providers/grass/qgis.v.in.cpp index 61bdceaf955..fb1de1ed31c 100644 --- a/src/providers/grass/qgis.v.in.cpp +++ b/src/providers/grass/qgis.v.in.cpp @@ -66,6 +66,33 @@ void writePolyline( struct Map_info* map, int type, QgsPolyline polyline, struct Vect_write_line( map, type, line, cats ); } +void exitIfCanceled( QDataStream& stdinStream, bool isPolygon, + const QString & tmpName, struct Map_info * tmpMap, + const QString & finalName, struct Map_info * finalMap ) +{ + bool isCanceled; + stdinStream >> isCanceled; + if ( !isCanceled ) + { + return; + } + if ( isPolygon ) + { + Vect_close( tmpMap ); + Vect_delete( tmpName.toUtf8().data() ); + } + Vect_close( finalMap ); + Vect_delete( finalName.toUtf8().data() ); + G_warning( "import canceled -> maps deleted" ); + exit( EXIT_SUCCESS ); +} + +// G_set_percent_routine only works in GRASS >= 7 +//int percent_routine (int) +//{ +// TODO: use it to interrupt cleaning functions +//} + int main( int argc, char **argv ) { struct Option *mapOption; @@ -91,6 +118,7 @@ int main( int argc, char **argv ) QGis::WkbType wkbFlatType = QGis::flatType( wkbType ); bool isPolygon = QGis::singleType( wkbFlatType ) == QGis::WKBPolygon; + QString finalName = QString( mapOption->answer ); struct Map_info finalMap, tmpMap; Vect_open_new( &finalMap, mapOption->answer, 0 ); struct Map_info * map = &finalMap; @@ -148,6 +176,7 @@ int main( int argc, char **argv ) qint32 featureCount = 0; while ( true ) { + exitIfCanceled( stdinStream, isPolygon, tmpName, &tmpMap, finalName, &finalMap ); stdinStream >> feature; if ( !feature.isValid() ) { @@ -279,6 +308,7 @@ int main( int argc, char **argv ) // read once more to assign centroids to polygons while ( true ) { + exitIfCanceled( stdinStream, isPolygon, tmpName, &tmpMap, finalName, &finalMap ); stdinStream >> feature; if ( !feature.isValid() ) { diff --git a/src/providers/grass/qgsgrass.cpp b/src/providers/grass/qgsgrass.cpp index 172e500603b..78061a66b31 100644 --- a/src/providers/grass/qgsgrass.cpp +++ b/src/providers/grass/qgsgrass.cpp @@ -1007,10 +1007,13 @@ QStringList GRASS_LIB_EXPORT QgsGrass::vectorLayers( const QString& gisdbase, co return list; } + // TODO: Handle errors as exceptions. Do not open QMessageBox here! This method is also used in browser + // items which are populated in threads and creating dialog QPixmap is causing crash or even X server freeze. if ( level == 1 ) { QgsDebugMsg( "Cannot open vector on level 2" ); - QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2 on level 2 (topology not available, try to rebuild topology using v.build module)." ).arg( mapName ).arg( mapset ) ); + // Do not open QMessageBox here! + //QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2 on level 2 (topology not available, try to rebuild topology using v.build module)." ).arg( mapName ).arg( mapset ) ); // Vect_close here is correct, it should work, but it seems to cause // crash on win http://trac.osgeo.org/qgis/ticket/2003 // disabled on win test it @@ -1023,7 +1026,8 @@ QStringList GRASS_LIB_EXPORT QgsGrass::vectorLayers( const QString& gisdbase, co else if ( level < 1 ) { QgsDebugMsg( "Cannot open vector" ); - QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2" ).arg( mapName ).arg( mapset ) ); + // Do not open QMessageBox here! + //QMessageBox::warning( 0, QObject::tr( "Warning" ), QObject::tr( "Cannot open vector %1 in mapset %2" ).arg( mapName ).arg( mapset ) ); GRASS_UNLOCK return list; } diff --git a/src/providers/grass/qgsgrassimport.cpp b/src/providers/grass/qgsgrassimport.cpp index 8e5671384d3..a53e0badc3c 100644 --- a/src/providers/grass/qgsgrassimport.cpp +++ b/src/providers/grass/qgsgrassimport.cpp @@ -13,6 +13,8 @@ * (at your option) any later version. * * * ***************************************************************************/ +#include + #include #include @@ -37,6 +39,7 @@ extern "C" QgsGrassImport::QgsGrassImport( QgsGrassObject grassObject ) : QObject() , mGrassObject( grassObject ) + , mCanceled( false ) , mFutureWatcher( 0 ) { } @@ -220,15 +223,27 @@ bool QgsGrassRasterImport::import() char * data = block->bits( row, 0 ); int size = iterCols * block->dataTypeSize(); QByteArray byteArray = QByteArray::fromRawData( data, size ); // does not copy data and does not take ownership + if ( isCanceled() ) + { + outStream << true; // cancel module + break; + } + outStream << false; // not canceled outStream << byteArray; } delete block; + if ( isCanceled() ) + { + outStream << true; // cancel module + break; + } } // TODO: send something back from module and read it here to close map correctly in module process->closeWriteChannel(); - process->waitForFinished( 5000 ); + // TODO: best timeout? + process->waitForFinished( 30000 ); QString stdoutString = process->readAllStandardOutput().data(); QString stderrString = process->readAllStandardError().data(); @@ -387,9 +402,16 @@ bool QgsGrassVectorImport::import() { feature.geometry()->transform( coordinateTransform ); } + if ( isCanceled() ) + { + outStream << true; // cancel module + break; + } + outStream << false; // not canceled outStream << feature; } feature = QgsFeature(); // indicate end by invalid feature + outStream << false; // not canceled outStream << feature; QgsDebugMsg( "features sent" ); } diff --git a/src/providers/grass/qgsgrassimport.h b/src/providers/grass/qgsgrassimport.h index 436acff135f..17be9f7ac60 100644 --- a/src/providers/grass/qgsgrassimport.h +++ b/src/providers/grass/qgsgrassimport.h @@ -39,9 +39,15 @@ class GRASS_LIB_EXPORT QgsGrassImport : public QObject // get error if import failed QString error(); virtual QStringList names() const; - + bool isCanceled() const { return mCanceled; } public slots: void onFinished(); + // TODO: this is not completely kosher, because QgsGrassImport exist on the main thread + // but import is running in another thread, to do it right, we should have an import object + // created on another thread, send cancel signal to that object which regularly processes events + // and thus recieves the signal. + // Most probably however, it will work correctly, even if read/write the bool wasn't atomic + void cancel() { mCanceled = true; } signals: // sent when process finished @@ -52,6 +58,7 @@ class GRASS_LIB_EXPORT QgsGrassImport : public QObject void setError( QString error ); QgsGrassObject mGrassObject; QString mError; + bool mCanceled; QFutureWatcher* mFutureWatcher; }; diff --git a/src/providers/grass/qgsgrassprovidermodule.cpp b/src/providers/grass/qgsgrassprovidermodule.cpp index e888d50eaea..70c0a316339 100644 --- a/src/providers/grass/qgsgrassprovidermodule.cpp +++ b/src/providers/grass/qgsgrassprovidermodule.cpp @@ -99,7 +99,15 @@ QVector QgsGrassMapsetItem::createChildren() QgsGrassObject vectorObject( mGisdbase, mLocation, mName, name, QgsGrassObject::Vector ); QgsGrassVectorItem *map = 0; - if ( layerNames.size() > 1 ) + if ( layerNames.size() == 0 ) + { + // TODO: differentiate if it is layer with no layers or without topo (throw exception from QgsGrass::vectorLayers) + // TODO: refresh (remove) error if topo was build + QgsErrorItem * errorItem = new QgsErrorItem( this, name, mapPath ); + items.append( errorItem ); + continue; + } + else if ( layerNames.size() > 1 ) { //map = new QgsDataCollectionItem( this, name, mapPath ); //map->setCapabilities( QgsDataItem::NoCapabilities ); // disable fertility @@ -612,11 +620,33 @@ QgsGrassRasterItem::QgsGrassRasterItem( QgsDataItem* parent, QgsGrassObject gras QgsGrassImportItem::QgsGrassImportItem( QgsDataItem* parent, const QString& name, const QString& path, QgsGrassImport* import ) : QgsDataItem( QgsDataItem::Layer, parent, name, path ) , QgsGrassObjectItemBase( import->grassObject() ) + , mImport( import ) { setCapabilities( QgsDataItem::NoCapabilities ); // disable fertility setState( Populating ); } +QList QgsGrassImportItem::actions() +{ + QList lst; + + QAction* actionRename = new QAction( tr( "Cancel" ), this ); + connect( actionRename, SIGNAL( triggered() ), this, SLOT( cancel() ) ); + lst.append( actionRename ); + + return lst; +} + +void QgsGrassImportItem::cancel() +{ + QgsDebugMsg( "Entered" ); + if ( !mImport ) // should not happen + { + return; + } + mImport->cancel(); +} + //------------------------------------------------------------------------- QGISEXTERN int dataCapabilities() diff --git a/src/providers/grass/qgsgrassprovidermodule.h b/src/providers/grass/qgsgrassprovidermodule.h index 7e7d74a3a16..0a222c9db50 100644 --- a/src/providers/grass/qgsgrassprovidermodule.h +++ b/src/providers/grass/qgsgrassprovidermodule.h @@ -148,16 +148,17 @@ class QgsGrassImportItem : public QgsDataItem, public QgsGrassObjectItemBase //virtual void setState( State state ) override { // QgsDataItem::setState(state); //} // do nothing to keep Populating - //virtual QList actions() override; + virtual QList actions() override; public slots: virtual void refresh() override {} - //void deleteGrassObject(); + void cancel(); protected: // override refresh to keep Populating state virtual void refresh( QVector children ) override { Q_UNUSED( children )}; //bool mDeleteAction; + QgsGrassImport* mImport; }; #endif // QGSGRASSPROVIDERMODULE_H