3

I am currently working on a script that searches for a currently loaded layer in PyQGIS and does some calculations. Then it removes the layer and deletes the whole shapefile in its directory.

Replicable example:

source_layer = QgsProject.instance().mapLayersByName("input1")[0] #the pointlayer input1 is currently loaded in the QGIS Project
#make something..... e.g.
QgsProject.instance().removeMapLayer(source_layer)
source_layer = None #according to the web to delete dependencies
del source_layer    #according to the web to delete dependencies
QgsVectorFileWriter.deleteShapeFile("XXXX/input1.shp")

But it does not delete the .shp and .dbf in the shapefile folder. I think there must be a permission problem. But I can't figure out how to fix it. Does anyone have any idea?

Taras
  • 32,823
  • 4
  • 66
  • 137
eagleadmiral
  • 447
  • 2
  • 10
  • 1
    Does it work if you leave off the ".shp"? Do you have any files opened in other applications? – bugmenot123 Oct 30 '23 at 22:30
  • Hi, I have only my QGIS project open and run the code via a self-made plugin with 1 PushButton. If I leave off the .shp, it does not work. – eagleadmiral Oct 31 '23 at 07:35
  • you are welcomed to reproduce the problem yourself. @bugmenot123 – eagleadmiral Oct 31 '23 at 08:38
  • 1
    Works fine for me with QGIS 3.33.0 on Linux. – bugmenot123 Oct 31 '23 at 09:11
  • 1
    is it possible that Windows thinks the file is still open when you try to delete it? – Ian Turton Oct 31 '23 at 11:40
  • @bugmenot123 yeah, I think Linux users dont have this problem. I think it is kind of a "Windows"-User Problem. – eagleadmiral Oct 31 '23 at 15:11
  • @IanTurton I think Windows thinks that the file is still open. But how can i "convince" Windows, that the file is already closed? It would help me alot! – eagleadmiral Oct 31 '23 at 15:12
  • 1
    Can't help with windows I'm afraid – Ian Turton Oct 31 '23 at 16:53
  • @IanTurton No problem. Thank you anyways! I need to rethink and do it in a "dirty" way. Maybe just leave the file and delete it in the next run of the plugin. Or another person could help? – eagleadmiral Oct 31 '23 at 17:05
  • 1
    I can confirm the same issue on Windows. A workaround, if the user actually uses a saved project, would be to save the project, close it, delete the shapefile, open the project again. Not elegant, but should work. – BritishSteel Oct 31 '23 at 17:38
  • 1
    Just noticed that when you remain in the same project and execute a script that removes the shapefile, and then execute a separate script that deletes the shapefile, it works fine. So the problem seems to be that Python or PyQGIS locks the shapefile (versus the project’s registry of layers). Maybe you can try to write a function that deletes the shapefile and store it in a separate script. Then import the function and see if it works. I am almost sure it doesn’t work, but it could be worth a try. – BritishSteel Oct 31 '23 at 17:52
  • 1
    Also just tried it with os.remove(), instead of using the PyQGIS method. Same result. It’s bugging me a lot and I tried fixing it sporadically over the last years but never found an actual solution. Thought a new try would help, but it didn’t. Curious to see if someone finds a way to do this. – BritishSteel Oct 31 '23 at 18:02
  • I think there has to be a way. Perhapt there is a way to bruteforce it? I am not experienced with such technical questions, but I will try to solve this problem. Thanks @BritishSteel – eagleadmiral Nov 01 '23 at 08:36

1 Answers1

7

Even if you keep in mind several things regarding your code:

And after improvements, your code might look as follows:

# imports
from os import listdir, remove
from os.path import isfile, realpath, join, split, basename, splitext
from qgis.core import QgsProject, QgsVectorFileWriter

referring to the original Vector layer

source_layer = QgsProject.instance().mapLayersByName("points2")[0]

getting the real path where the Vector layer is stored

path_to_shp = realpath(source_layer.source())

removing the Vector layer from the registry by its ID

QgsProject.instance().removeMapLayer(source_layer.id())

deleting source_layer variable

del source_layer

deleting the shapefile

QgsVectorFileWriter.deleteShapeFile(path_to_shp) # False

getting file location dir and the shapefile name

shp_dir, shp_file = split(path_to_shp)

getting shapefile pure name and pure extension

shp_file_name, shp_file_extension = splitext(shp_file)

obtaining all files associated with the shapefile in the working dir

files_to_delete = [join(shp_dir, file) for file in listdir(shp_dir) if isfile(join(shp_dir, file)) and shp_file_name in basename(file)]

removing all files associated with the shapefile

for file in files_to_delete: remove(file)

You may still encounter several issues:

  • shapefile dependencies with some extensions .shp, .qmd, and .dbf can remain in the folder

  • PermissionError can yield:

    Traceback (most recent call last):
       File "C:\OSGeo4W\apps\Python39\lib\code.py", line 90, in runcode
         exec(code, self.locals)
       File "<input>", line 1, in <module>
       File "<string>", line 26, in <module>
    PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'D:\\qgis_test\\points2.dbf'
    

However, there is a solution (a combination of QGIS- and OS-related things), that should erase everything associated with a shapefile. The trick is to delete all features in the Vector layer, afterwards, the metadata file .dbf can be also erased.

Let's assume there is a shapefile called 'points2' and stored under D:/test/, see the image below:

input

To delete everything associated with a shapefile, try the following code:

# imports
from os import listdir, remove
from os.path import isfile, realpath, join, split, basename, splitext
from qgis.core import QgsProject, QgsVectorFileWriter

referring to the original Vector layer

source_layer = QgsProject.instance().mapLayersByName("points2")[0]

accessing Vector layer provider

provider = source_layer.dataProvider()

getting the real path where the Vector layer is stored

path_to_shp = realpath(provider.dataSourceUri())

deleting all features in the Vector layer

provider.truncate()

removing the Vector layer from the registry by its ID

QgsProject.instance().removeMapLayer(source_layer.id())

deleting the shapefile

QgsVectorFileWriter.deleteShapeFile(path_to_shp) # True

getting file location dir and the shapefile name

shp_dir, shp_file = split(path_to_shp)

getting shapefile pure name and pure extension

shp_file_name, shp_file_extension = splitext(shp_file)

obtaining all files associated with the shapefile in the folder

files_to_delete = [join(shp_dir, file) for file in listdir(shp_dir) if isfile(join(shp_dir, file)) and shp_file_name in basename(file)]

removing all files associated with the shapefile

for file in files_to_delete: remove(file)

After the dir must be empty:

result


References:

Taras
  • 32,823
  • 4
  • 66
  • 137