Coverage for main/threads/utils_threads.py: 100%
33 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 15:51 +0000
1import os
2import shutil
4from PyQt6.QtCore import QThread, pyqtSignal, QCoreApplication
7class CopyDeleteThread(QThread):
8 """
9 Background worker thread for performing **file or directory copy and delete operations**
10 without blocking the PyQt GUI.
12 This class provides an asynchronous way to execute potentially long file system tasks
13 (copying large files, removing folders, etc.) in a background thread, while emitting
14 progress and error signals that can be handled by the main thread safely.
16 ---
18 **Signals:**
20 - `finished (str)`: Emitted when the operation completes successfully with a message.
21 - `error (str)`: Emitted when an error occurs, providing an error message.
23 ---
25 **Parameters:**
27 - `src` (*str*): Source file or folder path.
28 - `dst` (*str | None*): Destination path (required for copy operations).
29 - `is_folder` (*bool*): If `True`, the operation targets a folder instead of a file.
30 - `copy` (*bool*): If `True`, performs a copy operation.
31 - `delete` (*bool*): If `True`, performs a delete operation.
32 """
34 # Signal emitted when the operation finishes successfully.
35 finished = pyqtSignal(str)
36 """**Signal(str):** Emitted when the operation completes successfully.
37 The parameter is a status message describing the result or outcome.
38 """
40 # Signal emitted when an error occurs during execution.
41 error = pyqtSignal(str)
42 """**Signal(str):** Emitted when an error occurs during the operation.
43 The parameter contains a descriptive error message.
44 """
46 def __init__(self, src, dst=None, is_folder=False, copy=False, delete=False):
47 """
48 Initialize the worker thread with operation parameters.
50 Args:
51 src (str): Path to the source file or directory.
52 dst (str | None): Destination path for copy operations.
53 is_folder (bool): Whether the source represents a folder.
54 copy (bool): Whether to perform a copy operation.
55 delete (bool): Whether to perform a delete operation.
56 """
57 super().__init__()
58 self.src = src
59 self.dst = dst
60 self.is_folder = is_folder
61 self.copy = copy
62 self.delete = delete
64 def run(self):
65 """
66 Executes the file system operation.
68 Depending on the configuration flags (`copy`, `delete`, `is_folder`),
69 this method performs one or both operations:
70 - **Copy**: Copies a file or directory from `src` to `dst`.
71 - **Delete**: Removes a file or directory at `src`.
73 Emits:
74 - `finished (str)`: On successful completion.
75 - `error (str)`: On any exception during execution.
76 """
77 try:
78 # --- COPY OPERATION ---
79 if self.copy:
80 if self.src is None or self.dst is None:
81 raise ValueError(QCoreApplication.translate("Threads", "Missing src or dst"))
83 if self.is_folder:
84 # Copy entire directory recursively
85 shutil.copytree(self.src, self.dst, dirs_exist_ok=True)
86 else:
87 # Copy single file
88 shutil.copy(self.src, self.dst)
90 self.finished.emit(
91 QCoreApplication.translate(
92 "Threads",
93 "Successfully copied {0} to {1}"
94 ).format(self.src, self.dst)
95 )
97 # --- DELETE OPERATION ---
98 if self.delete:
99 if self.src is None:
100 raise ValueError(QCoreApplication.translate("Threads", "Missing src"))
102 if self.is_folder:
103 # Remove entire folder tree
104 shutil.rmtree(self.src)
105 else:
106 # Remove single file
107 os.remove(self.src)
109 self.finished.emit(
110 QCoreApplication.translate(
111 "Threads",
112 "Successfully deleted {0}"
113 ).format(self.src)
114 )
116 except Exception as e:
117 # Emit a formatted error message including source, destination, and exception details
118 self.error.emit(
119 QCoreApplication.translate(
120 "Threads",
121 "Error src:{0}, dst:{1}, {2}"
122 ).format(self.src, self.dst, e)
123 )