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.