43 |
46 |
44 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
47 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False) |
45 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
48 self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True) |
46 |
49 |
47 self.process = None |
50 self.process = None |
|
51 self.copyProcess = None |
48 self.cmdname = cmdname |
52 self.cmdname = cmdname |
49 |
53 |
50 def start(self, args, script): |
54 def start(self, args, parms, ppath, mainscript): |
51 """ |
55 """ |
52 Public slot to start the packager command. |
56 Public slot to start the packager command. |
53 |
57 |
54 @param args commandline arguments for packager program (list of strings) |
58 @param args commandline arguments for packager program (list of strings) |
|
59 @param parms parameters got from the config dialog (dict) |
|
60 @param ppath project path (string) |
55 @param script main script name to be processed by by the packager (string) |
61 @param script main script name to be processed by by the packager (string) |
56 @return flag indicating the successful start of the process |
62 @return flag indicating the successful start of the process |
57 """ |
63 """ |
58 self.errorGroup.hide() |
64 self.errorGroup.hide() |
59 |
65 script = os.path.join(ppath, mainscript) |
60 dname = os.path.dirname(script) |
66 dname = os.path.dirname(script) |
61 script = os.path.basename(script) |
67 script = os.path.basename(script) |
62 |
68 |
|
69 self.ppath = ppath |
|
70 self.additionalFiles = parms.get('additionalFiles', []) |
|
71 self.targetDirectory = os.path.join(parms.get('targetDirectory', 'dist')) |
|
72 |
63 self.contents.clear() |
73 self.contents.clear() |
64 self.errors.clear() |
74 self.errors.clear() |
65 |
75 |
66 args.append(script) |
76 args.append(script) |
67 |
77 |
68 self.process = QProcess() |
78 self.process = QProcess() |
69 self.process.setWorkingDirectory(dname) |
79 self.process.setWorkingDirectory(dname) |
70 |
80 |
71 self.process.readyReadStandardOutput.connect(self.__readStdout) |
81 self.process.readyReadStandardOutput.connect(self.__readStdout) |
72 self.process.readyReadStandardError.connect(self.__readStderr) |
82 self.process.readyReadStandardError.connect(self.__readStderr) |
73 self.process.finished.connect(self.__finish) |
83 self.process.finished.connect(self.__finishedFreeze) |
74 |
84 |
75 self.setWindowTitle(self.trUtf8('{0} - {1}').format(self.cmdname, script)) |
85 self.setWindowTitle(self.trUtf8('{0} - {1}').format(self.cmdname, script)) |
76 self.contents.insertPlainText(' '.join(args) + '\n\n') |
86 self.contents.insertPlainText(' '.join(args) + '\n\n') |
77 self.contents.ensureCursorVisible() |
87 self.contents.ensureCursorVisible() |
78 |
88 |
96 @param button button that was clicked (QAbstractButton) |
106 @param button button that was clicked (QAbstractButton) |
97 """ |
107 """ |
98 if button == self.buttonBox.button(QDialogButtonBox.Close): |
108 if button == self.buttonBox.button(QDialogButtonBox.Close): |
99 self.accept() |
109 self.accept() |
100 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): |
110 elif button == self.buttonBox.button(QDialogButtonBox.Cancel): |
|
111 self.additionalFiles = [] # Skip copying additional files |
101 self.__finish() |
112 self.__finish() |
102 |
113 |
103 def __finish(self): |
114 def __finish(self): |
104 """ |
115 """ |
105 Private slot called when the process finished. |
116 Private slot called when the process finished. |
106 |
117 |
107 It is called when the process finished or |
118 It is called when the process finished or |
108 the user pressed the cancel button. |
119 the user pressed the cancel button. |
109 """ |
120 """ |
110 if self.process is not None and \ |
121 if self.process is not None: |
111 self.process.state() != QProcess.NotRunning: |
122 self.process.disconnect(self.__finishedFreeze) |
112 self.process.terminate() |
123 self.process.terminate() |
113 QTimer.singleShot(2000, self.process.kill) |
124 QTimer.singleShot(2000, self.process.kill) |
114 self.process.waitForFinished(3000) |
125 self.process.waitForFinished(3000) |
115 |
126 self.process = None |
|
127 |
|
128 if self.copyProcess is not None: |
|
129 self.copyProcess.terminate() |
|
130 self.copyProcess = None |
|
131 |
|
132 self.contents.insertPlainText( |
|
133 self.trUtf8('\n{0} aborted.\n').format(self.cmdname)) |
|
134 |
|
135 self.__enableButtons() |
|
136 |
|
137 def __finishedFreeze(self): |
|
138 """ |
|
139 Private slot called when the process finished. |
|
140 |
|
141 It is called when the process finished or |
|
142 the user pressed the cancel button. |
|
143 """ |
|
144 self.process = None |
|
145 |
|
146 self.contents.insertPlainText( |
|
147 self.trUtf8('\n{0} finished.\n').format(self.cmdname)) |
|
148 |
|
149 self.copyProcess = copyAdditionalFiles(self) |
|
150 self.copyProcess.insertPlainText.connect(self.contents.insertPlainText) |
|
151 self.copyProcess.finished.connect(self.__enableButtons) |
|
152 self.copyProcess.start() |
|
153 |
|
154 def __enableButtons(self): |
|
155 """ |
|
156 Private slot called when all processes finished. |
|
157 |
|
158 It is called when the process finished or |
|
159 the user pressed the cancel button. |
|
160 """ |
|
161 self.copyProcess = None |
116 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
162 self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True) |
117 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
163 self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False) |
118 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
164 self.buttonBox.button(QDialogButtonBox.Close).setDefault(True) |
119 |
|
120 self.process = None |
|
121 |
|
122 self.contents.insertPlainText( |
|
123 self.trUtf8('\n{0} finished.\n').format(self.cmdname)) |
|
124 self.contents.ensureCursorVisible() |
165 self.contents.ensureCursorVisible() |
125 |
166 |
126 def __readStdout(self): |
167 def __readStdout(self): |
127 """ |
168 """ |
128 Private slot to handle the readyReadStandardOutput signal. |
169 Private slot to handle the readyReadStandardOutput signal. |
129 |
170 |
130 It reads the output of the process, formats it and inserts it into |
171 It reads the output of the process, formats it and inserts it into |
153 s = str(self.process.readAllStandardError(), |
194 s = str(self.process.readAllStandardError(), |
154 Preferences.getSystem("IOEncoding"), |
195 Preferences.getSystem("IOEncoding"), |
155 'replace') |
196 'replace') |
156 self.errors.insertPlainText(s) |
197 self.errors.insertPlainText(s) |
157 self.errors.ensureCursorVisible() |
198 self.errors.ensureCursorVisible() |
|
199 |
|
200 |
|
201 class copyAdditionalFiles(QThread): |
|
202 """ |
|
203 Thread to copy the distribution dependend files. |
|
204 |
|
205 @signal insertPlainText(text) emitted to inform user about the copy progress |
|
206 """ |
|
207 insertPlainText = pyqtSignal(str) |
|
208 |
|
209 def __init__(self, main): |
|
210 """ |
|
211 Constructor, which stores the needed variables. |
|
212 |
|
213 @param main self-object of the caller |
|
214 """ |
|
215 super(copyAdditionalFiles, self).__init__() |
|
216 |
|
217 self.ppath = main.ppath |
|
218 self.additionalFiles = main.additionalFiles |
|
219 self.targetDirectory = main.targetDirectory |
|
220 |
|
221 def __copytree(self, src, dst): |
|
222 """ |
|
223 Copies a file or folder. Wildcards allowed. Existing files are overwitten. |
|
224 |
|
225 @param src source file or folder to copy. Wildcards allowed. (str) |
|
226 @param dst destination (str) |
|
227 """ |
|
228 def src2dst(srcname, base, dst): |
|
229 """ |
|
230 Combines the relativ path of the source (srcname) with the |
|
231 destination folder. |
|
232 |
|
233 @param srcname actual file or folder to copy |
|
234 @param base basename of the source folder |
|
235 @param dst basename of the destination folder |
|
236 @return destination path |
|
237 """ |
|
238 delta = srcname.split(base)[1] |
|
239 return os.path.join(dst, delta[1:]) |
|
240 |
|
241 base, fileOrFolderName = os.path.split(src) |
|
242 initDone = False |
|
243 for root, dirs, files in os.walk(base): |
|
244 copied = False |
|
245 # remove all none matching directorynames, create all others |
|
246 for dir in dirs[:]: |
|
247 pathname = os.path.join(root, dir) |
|
248 if initDone or fnmatch.fnmatch(pathname, src): |
|
249 newDir = src2dst(pathname, base, dst) |
|
250 # avoid infinit loop |
|
251 if fnmatch.fnmatch(newDir, src): |
|
252 dirs.remove(dir) |
|
253 continue |
|
254 try: |
|
255 copied = True |
|
256 os.makedirs(newDir) |
|
257 except OSError as err: |
|
258 if err.errno != errno.EEXIST: # it's ok if directory already exists |
|
259 raise err |
|
260 else: |
|
261 dirs.remove(dir) |
|
262 |
|
263 for file in files: |
|
264 fn = os.path.join(root, file) |
|
265 if initDone or fnmatch.fnmatch(fn, src): |
|
266 newFile = src2dst(fn, base, dst) |
|
267 # copy files, give errors to caller |
|
268 shutil.copy2(fn, newFile) |
|
269 copied = True |
|
270 |
|
271 # check if file was found and copied |
|
272 if len(files) and not copied: |
|
273 raise IOError(errno.ENOENT, |
|
274 self.trUtf8("No such file or directory: '{0}'").format(src)) |
|
275 |
|
276 initDone = True |
|
277 |
|
278 def run(self): |
|
279 """ |
|
280 QThread entry point to copy the selected additional files and folders. |
|
281 """ |
|
282 self.insertPlainText.emit('----\n') |
|
283 os.chdir(self.ppath) |
|
284 for fn in self.additionalFiles: |
|
285 self.insertPlainText.emit( |
|
286 self.trUtf8('\nCopying {0}: ').format(fn)) |
|
287 |
|
288 # on linux normpath doesn't replace backslashes to slashes. |
|
289 fn = fn.replace('\\', '/') |
|
290 fn = os.path.abspath(os.path.normpath(fn)) |
|
291 dst = os.path.join(self.ppath, self.targetDirectory) |
|
292 if fn.startswith(os.path.normpath(self.ppath)): |
|
293 dirname = fn.split(self.ppath+os.sep)[1] |
|
294 dst = os.path.join(dst, os.path.dirname(dirname)) |
|
295 try: |
|
296 os.makedirs(dst) |
|
297 except OSError as err: |
|
298 if err.errno != errno.EEXIST: # it's ok if directory already exists |
|
299 raise err |
|
300 |
|
301 try: |
|
302 self.__copytree(fn, dst) |
|
303 self.insertPlainText.emit(self.trUtf8('ok')) |
|
304 except IOError as err: |
|
305 self.insertPlainText.emit(self.trUtf8('failed: {0}').format(err.strerror)) |