13

I have QGIS-Project-Files saved to a network drive and was wondering if it is possible to warn users that a project file is already opened by an other user to prevent concurrent editing of the same project file.

I was thinking of showing an alert like this:

enter image description here

Does anyone have a clue how this could be done?

I found some information on stackexchange but I am not sure if these could be implemented in QGIS in some generic way(not a manually added 'openProject' macro):

https://stackoverflow.com/questions/21126108/ensuring-that-my-program-is-not-doing-a-concurrent-file-write/21149744#21149744

https://stackoverflow.com/questions/11114492/check-if-a-file-is-not-open-not-used-by-other-process-in-python

I know that QGIS shows a warning if I try to save a project file another user has changed since I opened it.... But it would be even better if I got the information in the moment when I open the project that someone else has opened the project, too.

The best option would be to implement this in QGIS core (C++) but a pythonic solution would be also interesting.

A generic C++ solution is probably harder as it has to check the exclusive file access on different OS's (windows, linux, mac).

2
+50

Took me a while to find: a long while ago I had a naive lock file implementation in place, using Python Macros (activate and alter in <QGIS>|Project|Properties...|Macros).

I updated the code to use the QGIS 3.x classes and syntax:

import os
import socket
from qgis.core import QgsProject
from PyQt5.QtWidgets import QMessageBox


### utility functions ###

# get the executing machine and process id #
def _getId():
    return socket.getfqdn()+'.'+str(os.getpid())

# read machine and process id holding lock from lock file #  
def _getLockerId(file):
    try:
        with open(file) as _file:
            return _file.readline()
    except:
        return

# create lock file path #
def _getLockFile():
    _path, _file = __getProjectPath()
    return os.path.join(_path,'.'+_file+'.lock')
    
# create current project file path root and name #
def __getProjectPath():
    return os.path.split(QgsProject.instance().absoluteFilePath())  


### QGIS macros ###

def openProject():
    lockfile = _getLockFile()
    
    try:
        with open(lockfile, "x") as _file:
            _file.write(_getId())
    except OSError:
        _path, _file = __getProjectPath()
        copyfile = os.path.join(_path, _getId() + "__" + _file)
        
        QMessageBox.information(
            None,
            "Project locked",
            "Cannot open project with write access: "+
              "the following user (user.PID)\n\n"+_getLockerId(lockfile)+
              "\n\nhas already aquired a lock on the project!"+
              "\nCopying project and switching context to\n\n"+copyfile
        )

        QgsProject.instance().write(copyfile)
        QgsProject.instance().read()
        QgsProject.instance().setDirty()

def saveProject():
    pass

def closeProject():
    lockfile = _getLockFile()
    
    if _getId() == _getLockerId(lockfile):
        os.remove(lockfile)

This will make QGIS check for the existence of a (hidden) lock file (naming schema: .<project_name>.lock) at the project location (needs write access!), and

  • if present, opens a notification window, copies the project to the same location (naming schema: <socket_user.PID>__<project_name>) and reads the copy into QGIS
  • if not present, one is created holding the current machine and process id, and the original project is opened as usual

The lock holder will delete the file after closing the project.


This worked well enough for a small team, when I had to come up with a quick and dirty solution since no proper DB/versioning system had been at hand. I haven't done extensive testing; things that may be an issue:

  • no safety against stale locks due to killed QGIS instances or whatever; deleting the lock file manually is the way to go then
  • no error handling whatsoever; with write access at the path location, I haven't encountered any issue, but I'm sure there are
  • you'll get a note that "The loaded project file on disk was meanwhile changed.", which refers to the original project file as per the date, but overwrites the copy, so you can safely ignore it

An implementation using the lockfile module may be better; I haven't done any idiomatic, cosmetic or performance updates to the code since I've first written it. Feel free to improve and post as answers.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.