1 """This modules implements I{Help-->Check job status} dialog.
2
3 Classes:
4 * CheckJobsStatusDialog: creates I{Help-->Check job status} dialog used to
5 check the status of the nMOLDYN running jobs.
6 """
7
8
9 from glob import glob
10 import re
11 import os
12 import sys
13 if sys.platform == 'win32':
14 import win32api
15 import win32process
16 else:
17 import pwd
18 from tempfile import gettempdir
19
20
21 import Dialog
22 from Tkinter import *
23
24
25 from Scientific.IO.TextFile import TextFile
26
27
28 nmoldyn_package_path = os.path.dirname(os.path.split(__file__)[0])
29
31 """Sets up a dialog used to check the status of the nMOLDYN running jobs.
32 """
33
34 - def __init__(self, parent, title = None):
35 """The constructor.
36
37 @param parent: the parent widget.
38
39 @param title: a string specifying the title of the dialog.
40 @type title: string
41 """
42
43 Toplevel.__init__(self, parent)
44 self.transient(parent)
45
46 if title:
47 self.title(title)
48
49 self.parent = parent
50
51 self.jobs = {}
52
53 body = Frame(self)
54 self.initial_focus = self.body(body)
55 body.grid(row = 0, column = 0, sticky = EW)
56
57 self.buttonbox()
58
59 self.grab_set()
60
61 if not self.initial_focus:
62 self.initial_focus = self
63
64 self.protocol("WM_DELETE_WINDOW", self.cancel)
65
66 self.resizable(width = NO, height = NO)
67
68 self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50))
69
70 self.initial_focus.focus_set()
71
72 self.wait_window(self)
73
74 - def body(self, master):
75 """
76 Create dialog body. Return widget that should have initial focus.
77 """
78
79 jobsFrame = LabelFrame(master, text = 'Jobs status', bd = 2, relief = GROOVE)
80 jobsFrame.grid(row = 0, column = 0, sticky = EW, padx = 3, pady = 3)
81 jobsFrame.grid_columnconfigure(0, weight = 1)
82
83 textScrollbar = Scrollbar(jobsFrame)
84 textScrollbar.grid(row = 0, column = 1, sticky = NS)
85 self.jobsWindow = Text(jobsFrame, height = 20, width = 120)
86 self.jobsWindow.grid(row = 0, column = 0)
87 self.jobsWindow.config(yscrollcommand = textScrollbar)
88 textScrollbar.config(command = self.jobsWindow.yview)
89 b1 = Button(self.jobsWindow,\
90 bd = 0,\
91 bg = 'lightblue',\
92 highlightbackground = 'lightblue',\
93 disabledforeground = 'black',\
94 width = 15,\
95 height = 2,\
96 text = 'Kill job ?',\
97 font = ('Courier','10','bold'),\
98 anchor = CENTER,\
99 relief = FLAT,\
100 state = DISABLED)
101 b2 = Button(self.jobsWindow,\
102 bd = 0,\
103 bg = 'lightblue',\
104 highlightbackground = 'lightblue',\
105 disabledforeground = 'black',\
106 width = 40,\
107 height = 2,\
108 text = 'Name',\
109 font = ('Courier','10','bold'),\
110 anchor = CENTER,\
111 relief = FLAT,\
112 state = DISABLED)
113 b3 = Button(self.jobsWindow,\
114 bd = 0,\
115 bg = 'lightblue',\
116 highlightbackground = 'lightblue',\
117 disabledforeground = 'black',\
118 width = 25,\
119 height = 2,\
120 text = 'Started on',\
121 font = ('Courier','10','bold'),\
122 anchor = CENTER,\
123 relief = FLAT,\
124 state = DISABLED)
125 b4 = Button(self.jobsWindow,\
126 bd = 0,\
127 bg = 'lightblue',\
128 highlightbackground = 'lightblue',\
129 disabledforeground = 'black',\
130 width = 5,\
131 height = 2,\
132 text = 'PID',\
133 font = ('Courier','10','bold'),\
134 anchor = CENTER,\
135 relief = FLAT,\
136 state = DISABLED)
137 b5 = Button(self.jobsWindow,\
138 bd = 0,\
139 bg = 'lightblue',\
140 highlightbackground = 'lightblue',\
141 disabledforeground = 'black',\
142 width = 10,\
143 height = 2,\
144 text = 'Owner',\
145 font = ('Courier','10','bold'),\
146 anchor = CENTER,\
147 relief = FLAT,\
148 state = DISABLED)
149 b6 = Button(self.jobsWindow,\
150 bd = 0,\
151 bg = 'lightblue',\
152 highlightbackground = 'lightblue',\
153 disabledforeground = 'black',\
154 width = 15,\
155 height = 2,\
156 text = 'Progress in %',\
157 font = ('Courier','10','bold'),\
158 anchor = CENTER,\
159 relief = FLAT,\
160 state = DISABLED)
161
162 self.jobsWindow.configure(state = NORMAL)
163 self.jobsWindow.window_create(END, window = b1)
164 self.jobsWindow.window_create(END, window = b2)
165 self.jobsWindow.window_create(END, window = b3)
166 self.jobsWindow.window_create(END, window = b4)
167 self.jobsWindow.window_create(END, window = b5)
168 self.jobsWindow.window_create(END, window = b6)
169 self.jobsWindow.insert(END, '\n')
170 self.jobsWindow.configure(state = DISABLED)
171
172 self.refresh()
173
174 return None
175
193
194
195 - def ok(self, event = None):
196
197 if not self.validate():
198 self.initial_focus.focus_set()
199 return
200
201 self.withdraw()
202 self.update_idletasks()
203
204 self.apply()
205
206 self.cancel()
207
208 - def cancel(self, event=None):
209
210
211 self.parent.focus_set()
212 self.destroy()
213
214
217
218
221
223 """Refreshes the nMOLDYN running jobs list and its associated frame.
224 """
225
226
227 self.activeJobs, self.inactiveJobs = self.findJobs()
228
229 print self.activeJobs, self.inactiveJobs
230
231
232 for jobs in self.inactiveJobs:
233 os.unlink(jobs)
234
235 jobsPID = []
236 jobsStart = []
237 jobsOwner = []
238 jobsName = []
239 jobsProgress = []
240
241
242 for actJob in self.activeJobs:
243
244
245 tempFile = TextFile(actJob,'r')
246 data = tempFile.readlines()
247 tempFile.close()
248
249
250 jobsPID.append(data[0].strip())
251
252 jobsStart.append(data[1].strip())
253
254 jobsOwner.append(data[2].strip())
255
256 jobsName.append(data[3].strip())
257
258 jobsProgress.append(data[-1].strip())
259
260
261 for k in self.jobs.keys():
262 if k not in jobsPID:
263 self.jobs[k]['kill'].config(state = DISABLED)
264 self.jobs[k]['progress'].config(text = '100 %')
265 else:
266 self.jobs[k]['progress'].config(text = jobsProgress[jobsPID.index(k)] + ' %')
267
268 for jobPID in jobsPID:
269
270 if not self.jobs.has_key(jobPID):
271 tempFile = self.activeJobs[jobsPID.index(jobPID)]
272 jobStart = jobsStart[jobsPID.index(jobPID)]
273 jobOwner = jobsOwner[jobsPID.index(jobPID)]
274 jobName = jobsName[jobsPID.index(jobPID)]
275 self.jobs[jobPID] = {'name' : jobName, 'start' : jobStart, 'owner' : jobOwner, 'file' : tempFile}
276
277 self.jobs[jobPID]['kill'] = Button(self.jobsWindow,\
278 bd = 0,\
279 bg = 'red',\
280 width = 110,\
281 height = 22,\
282 bitmap = "@"+os.path.join(nmoldyn_package_path, 'GUI', 'Images', 'xbones.xbm'),\
283 anchor = CENTER,\
284 relief = RAISED,\
285 activebackground = 'red',\
286 command = lambda : self.killJobs(int(jobPID)))
287
288 b2 = Button(self.jobsWindow,\
289 bd = 0,\
290 bg = 'white',\
291 highlightbackground = 'white',\
292 disabledforeground = 'black',\
293 width = 40,\
294 height = 2,\
295 text = self.jobs[jobPID]['name'],\
296 font = ('Courier','10','bold'),\
297 anchor = CENTER,\
298 relief = FLAT,\
299 state = DISABLED)
300
301 b3 = Button(self.jobsWindow,\
302 bd = 0,\
303 bg = 'white',\
304 highlightbackground = 'white',\
305 disabledforeground = 'black',\
306 width = 25,\
307 height = 2,\
308 text = self.jobs[jobPID]['start'],\
309 font = ('Courier','10','bold'),\
310 anchor = CENTER,\
311 relief = FLAT,\
312 state = DISABLED)
313
314 b4 = Button(self.jobsWindow,\
315 bd = 0,\
316 bg = 'white',\
317 highlightbackground = 'white',\
318 disabledforeground = 'black',\
319 width = 5,\
320 height = 2,\
321 text = jobPID,\
322 font = ('Courier','10','bold'),\
323 anchor = CENTER,\
324 relief = FLAT,\
325 state = DISABLED)
326
327 b5 = Button(self.jobsWindow,\
328 bd = 0,\
329 bg = 'white',\
330 highlightbackground = 'white',\
331 disabledforeground = 'black',\
332 width = 10,\
333 height = 2,\
334 text = self.jobs[jobPID]['owner'],\
335 font = ('Courier','10','bold'),\
336 anchor = CENTER,\
337 relief = FLAT,\
338 state = DISABLED)
339
340 self.jobs[jobPID]['progress'] = Button(self.jobsWindow,\
341 bd = 0,\
342 bg = 'white',\
343 highlightbackground = 'white',\
344 disabledforeground = 'black',\
345 width = 15,\
346 height = 2,\
347 text = jobsProgress[jobsPID.index(jobPID)] + ' %',\
348 font = ('Courier','10','bold'),\
349 anchor = CENTER,\
350 relief = FLAT,\
351 state = DISABLED)
352
353 self.jobsWindow.configure(state = NORMAL)
354 self.jobsWindow.window_create(END, window = self.jobs[jobPID]['kill'])
355 self.jobsWindow.window_create(END, window = b2)
356 self.jobsWindow.window_create(END, window = b3)
357 self.jobsWindow.window_create(END, window = b4)
358 self.jobsWindow.window_create(END, window = b5)
359 self.jobsWindow.window_create(END, window = self.jobs[jobPID]['progress'])
360 self.jobsWindow.insert(END, '\n')
361 self.jobsWindow.configure(state = DISABLED)
362
363
364
365 self.afterId = self.jobsWindow.after(5000, self.refresh)
366
368 """
369 This method find the nMOLDYN active and inactive jobs.
370 Output:
371 -a list of the temporary nMOLDYN running job logfiles.
372 """
373
374
375 if sys.platform == 'win32':
376 owner = win32api.GetUserName()
377 else:
378 try:
379 owner = os.environ['USER']
380 except KeyError:
381 owner = pwd.getpwuid(os.getuid())[0]
382
383
384 jobNames = glob(os.path.join(gettempdir(),'*'+owner+'*moldyn'))
385
386
387 jobsDict = {}
388 for job in jobNames:
389 pid = re.findall(owner+'\.(\d*)\.',job)[0]
390 jobsDict[pid] = job
391
392 activeJobs, inactiveJobs = [], []
393
394 if sys.platform == 'win32':
395 for jobPID in jobsDict:
396 try:
397 jobHandle = win32api.OpenProcess(1, 0, int(jobPID))
398 except:
399 inactiveJobs.append(jobsDict[jobPID])
400 else:
401 activeJobs.append(jobsDict[jobPID])
402 win32api.CloseHandle(jobHandle)
403 else:
404
405 for jobPID in jobsDict:
406 try:
407 os.getpgid(int(jobPID))
408 except:
409 inactiveJobs.append(jobsDict[jobPID])
410 else:
411 activeJobs.append(jobsDict[jobPID])
412
413 return activeJobs, inactiveJobs
414
416 """
417 This method is called when the user press the button 'Kill' of the dialog.
418 It loops over all the running jobs and for those which have been selected to be killed
419 asks the user to confirm that (s)he really wants to kill them. If so, kills them and
420 updates the dialog.
421
422 Arguments:
423 - pid: the pid of the job to kill.
424 """
425
426
427 dialog = Dialog.Dialog(None,\
428 title = 'Warning',\
429 text = 'Do you really want to kill the job with pid %s ?.' % pid,\
430 bitmap = 'warning',\
431 default = 0,\
432 strings = ('No', 'Yes'))
433
434 if dialog.num == 0:
435
436 return
437
438 elif dialog.num == 1:
439
440 if sys.platform == 'win32':
441 handle = win32api.OpenProcess(1, 0, pid)
442 win32api.TerminateProcess(handle, 0)
443
444 else:
445 os.kill(pid, 9)
446
447 self.jobs[str(pid)]['progress'].config(text = 'Killed')
448 try:
449 os.unlink(self.jobs[str(pid)]['file'])
450 except:
451 pass
452 del self.jobs[str(pid)]
453
454
455 self.refresh()
456