-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
for SQlite vectors wait rendering end to avoid file lock: fixes #15498 #5305
for SQlite vectors wait rendering end to avoid file lock: fixes #15498 #5305
Conversation
src/app/qgisapp.cpp
Outdated
// Wait only if the current layer is in the rendering pool | ||
if ( mMapCanvas->mapSettings().layers().contains( vlayer->id() ) ) | ||
{ | ||
if ( vlayer->dataProvider()->storageType().count( "SQLite" ) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either the above comment or this test are incorrect. The GPKG driver will identify itself as GPKG and not SQLite. Actually perhaps the issue doesn't occur with GPKG since we turn WAL on, and normally in WAL mode, readers and writers shouldn't conflict.
And shouldn't that also affect the QGIS own spatialite provider ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for my late, I was sick... now checking the correct storageType returned by:
- plain SQlite
- spatialite (I remeber is "SQLite with spatialite extension")
- gpkg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- plain SQLite => "SQlite"
- external splite => "SQlite"
- empty splite created in qgis => "SQLite database with SpatiaLite extension"
- demo gpkgs => "GPKG"
=> adapting PR
my only concerns about the solution I proposed is that the message sent to the user is done using QgsMessageBar. MessageBar, changing the extent of the drawing canvas, trigger a refresh appearing and disappearing. |
@luipir wrote:
Test with the following in Python console, with layers loaded:
|
src/app/qgisapp.cpp
Outdated
QgsMessageBarItem *item = new QgsMessageBarItem( tr( "Save edit paused" ), | ||
tr( "Waiting for end of rendering" ), | ||
QgsMessageBar::WARNING, 0 ); | ||
messageBar()->pushItem( item ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably just use messageBar()->pushWarning(...)
instead of creating a new object, since the widget doesn't have any custom child widgets.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
or messageBar()->pushMessage(...)
if you want no duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind. Sorry, should have reviewed all the code first. I see you reference QgsMessageBarItem
object below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
exactly, I need the item reference to remove it directly after save commit is done
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I get this right, you are basically adding a sleep in the main thread (that will block the GUI, even if it's for a short amount of time), have you considered adding a singleshot timer that re-triggers the same slot in case the canvas is drawing? This would have the same effect (delaying the commit) but without blocking the GUI.
For master and C++11, a lambda would probably be the best way to implement it (see snippet below), but there might be ways to do that in 2.x too.
QTimer::singleShot(100, this, [ = ] {
saveEdits( layer, leaveEditable, triggerRepaint );
});
Also, instead of hardcoding the storage type, I would suggest to add a capability flag to the data provider class that indicates if there support for file lock (or the lack of it).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another idea: what about connecting to connect(mMapCanvas, &QgsMapCanvas::renderComplete
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I though about this kind of solution, but the main problem to introduce async management is that it would change/brake to "save editing" feature execution flow. In my implementation I just introduce a kind of not blocking "sleep" instead of restructuring the set of method calls.
Another reason is to be conservative in bug fixing mode to avoid to introduce modification in the sequence of calls that can introduce more side effects (or regressions). Side effect not checked by unit or integrations tests because lack of them in the 2.x branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About adding a capability, I also though about this, but I was not conformtable to enter a kind a capability that depend make sense only in some rendering cases. A Capability should be an objective property of a provider, so how to call it? isLockedDuringAccess() isLockedConcurrentwriteReadAccess() ? Because wasn't clear how to define this capability I preferred to avoid to introduce a new capability for only a bug fix.
I'm open to find a general capability semanttic that can fit this provider case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A delay/sleep in the main thread is blocking by definition and it's not very robust, because you'll never know if the delay is long enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to use the main event loop I can single shooting QgisApp::saveEdits slot instead of creating a new event loop.
The main idea that I want to maintain is to avoid to change the current code flow. Tha is exactly the solutuion proposed by Alessandro.
src/app/qgisapp.cpp
Outdated
// wait | ||
while ( mMapCanvas->isDrawing() ) | ||
{ | ||
QgsApplication::instance()->processEvents( QEventLoop::AllEvents, 100 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this just to sleep for 100 ms, or do the app's events specifically need processed?
This also worked for me and also did not block the GUI:
QEventLoop loop;
QTimer t;
connect( &t, SIGNAL( timeout() ), &loop, SLOT( quit() ) );
t.start( 100 );
loop.exec();
@elpaso, was your concern the usage of QgsApplication::instance()->processEvents()
? If so, does this handle it more appropriately, and if not, what do you suggest?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is for sleep without blocking, events have to be processed without blocking user interaction with the interface.
The proposed solution use the common and unique event loop.
I can't say if there are drawback on using QgsApplication::instance()->processEvents.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BTW I understand it's better an isolated event loop for an isolated feature than interact with common used event loop possibly interfering with other parts of the code.
PyQgsComposerPicture failed test, in my experience is a random failing test. is it possibile to retrigger build? |
@luipir, latest changes work smoothly here on macOS 10.11.6! |
now PyQgsMapLayerRegistry is failing and not PyQgsComposerPicture :(. But it locally passes :/ |
Description
In case of SQLite based vectors as Spatialite or GPKG, before to saveEdits, wait for end of rendering to avoid error of "File lock". The user is notified of the deferred save via messageBar. the message is automatically removed after rendering is terminated.
Checklist
fixes #11111
in the commit message next to the description[FEATURE]
in the commit message[needs-docs]
in the commit message and containt sufficient information in the commit message to be documentedscripts/prepare-commit.sh
script before each commit