Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 62 additions & 33 deletions PPOCRLabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,49 @@
logger = logging.getLogger("PPOCRLabel")


def moveFileToTrash(filePath):
if platform.system() == "Windows":
import ctypes
from ctypes import wintypes

class SHFILEOPSTRUCTW(ctypes.Structure):
_fields_ = [
("hwnd", wintypes.HWND),
("wFunc", wintypes.UINT),
("pFrom", wintypes.LPCWSTR),
("pTo", wintypes.LPCWSTR),
("fFlags", ctypes.c_ushort),
("fAnyOperationsAborted", wintypes.BOOL),
("hNameMappings", ctypes.c_void_p),
("lpszProgressTitle", wintypes.LPCWSTR),
]

operation = SHFILEOPSTRUCTW()
operation.wFunc = 3 # FO_DELETE
fromPath = os.path.abspath(filePath) + "\0\0"
fromBuffer = ctypes.create_unicode_buffer(fromPath)
operation.pFrom = ctypes.cast(fromBuffer, wintypes.LPCWSTR)
operation.fFlags = 0x0040 | 0x0010 | 0x0004 | 0x0400
result = ctypes.windll.shell32.SHFileOperationW(ctypes.byref(operation))
return result == 0 and not operation.fAnyOperationsAborted

if platform.system() == "Linux":
return subprocess.call(["trash", filePath]) == 0

if platform.system() == "Darwin":
absPath = os.path.abspath(filePath).replace("\\", "\\\\").replace('"', '\\"')
cmd = [
"osascript",
"-e",
'tell app "Finder" to move {the POSIX file "' + absPath + '"} to trash',
]
logger.debug("Executing command: %s", " ".join(cmd))
with open(os.devnull, "w") as devnull:
return subprocess.call(cmd, stdout=devnull) == 0

return False


__appname__ = "PPOCRLabel"

LABEL_COLORMAP = label_colormap()
Expand Down Expand Up @@ -2831,41 +2874,27 @@ def deleteImg(self):
if deletePath is not None:
deleteInfo = self.deleteImgDialog()
if deleteInfo == QMessageBox.Yes:
if platform.system() == "Windows":
# from win32com import shell, shellcon
# shell.SHFileOperation((0, shellcon.FO_DELETE, deletePath, None,
# shellcon.FOF_SILENT | shellcon.FOF_ALLOWUNDO | shellcon.FOF_NOCONFIRMATION,
# None, None))
os.remove(deletePath)
# linux
elif platform.system() == "Linux":
cmd = "trash " + deletePath
os.system(cmd)
# macOS
elif platform.system() == "Darwin":
import subprocess

absPath = (
os.path.abspath(deletePath)
.replace("\\", "\\\\")
.replace('"', '\\"')
imgidx = self.getImglabelidx(deletePath)
try:
deleteSucceeded = moveFileToTrash(deletePath)
except Exception as error:
logger.exception("Failed to move image to trash: %s", error)
deleteSucceeded = False

if not deleteSucceeded:
QMessageBox.warning(
self,
"Attention",
"The image could not be moved to the recycle bin.",
)
cmd = [
"osascript",
"-e",
'tell app "Finder" to move {the POSIX file "'
+ absPath
+ '"} to trash',
]
logger.debug("Executing command: %s", " ".join(cmd))
subprocess.call(cmd, stdout=open(os.devnull, "w"))

if self.filePath in self.fileStatedict.keys():
self.fileStatedict.pop(self.filePath)
imgidx = self.getImglabelidx(self.filePath)
if imgidx in self.PPlabel.keys():
self.PPlabel.pop(imgidx)
return

self.fileStatedict.pop(imgidx, None)
self.PPlabel.pop(imgidx, None)
self.Cachelabel.pop(imgidx, None)
self.saveFilestate()
self.savePPlabel(mode="Auto")
self.saveCacheLabel()
self.importDirImages(self.lastOpenDir, isDelete=True)

def deleteImgDialog(self):
Expand Down
45 changes: 45 additions & 0 deletions tests/test_windows_trash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import ast
import os
import platform
import subprocess
import tempfile
import unittest
from pathlib import Path


sourcePath = Path(__file__).parents[1] / "PPOCRLabel.py"
tree = ast.parse(sourcePath.read_text(encoding="utf-8"))
moveFunction = next(
node
for node in tree.body
if isinstance(node, ast.FunctionDef) and node.name == "moveFileToTrash"
)
namespace = {
"os": os,
"platform": platform,
"subprocess": subprocess,
"logger": type("Logger", (), {"debug": staticmethod(lambda *_args: None)})(),
}
exec(
compile(ast.Module([moveFunction], type_ignores=[]), str(sourcePath), "exec"),
namespace,
)


class WindowsTrashTest(unittest.TestCase):
@unittest.skipUnless(platform.system() == "Windows", "Windows-only check")
def test_explicit_path_buffer(self):
with tempfile.NamedTemporaryFile(
prefix="ppocrlabel_trash_", delete=False
) as file:
tempPath = Path(file.name)

try:
self.assertTrue(namespace["moveFileToTrash"](str(tempPath)))
self.assertFalse(tempPath.exists())
finally:
tempPath.unlink(missing_ok=True)


if __name__ == "__main__":
unittest.main()
Loading