Package nMOLDYN :: Package GUI :: Module CheckJobsStatusDialog
[hide private]
[frames] | no frames]

Source Code for Module nMOLDYN.GUI.CheckJobsStatusDialog

  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  # The standard distribution modules 
  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  # The Tcl/Tk modules 
 21  import Dialog 
 22  from Tkinter import * 
 23   
 24  # The ScientificPython modules. 
 25  from Scientific.IO.TextFile import TextFile 
 26       
 27  # Package path 
 28  nmoldyn_package_path = os.path.dirname(os.path.split(__file__)[0]) 
 29   
30 -class CheckJobsStatusDialog(Toplevel):
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
176 - def buttonbox(self):
177 """ 178 Add standard button box. 179 """ 180 181 # The frame that contains the 'Cancel' and 'OK' buttons. 182 box = LabelFrame(self, text = 'Actions', bd = 2, relief = GROOVE) 183 box.grid(row = 1, column = 0, sticky = EW, padx = 3, pady = 3) 184 box.grid_columnconfigure(0, weight = 1) 185 186 w = Button(box, text = "Cancel", width = 10, command = self.cancel) 187 w.grid(row = 0, column = 0, sticky = E) 188 w = Button(box, text = "OK", width=10, command = self.ok, default=ACTIVE) 189 w.grid(row = 0, column = 1, sticky = E) 190 191 self.bind("<Return>", self.ok) 192 self.bind("<Escape>", self.cancel)
193 194 # Standard button semantics.
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 # Put focus back to the parent window 211 self.parent.focus_set() 212 self.destroy()
213 214 # Command hooks
215 - def validate(self):
216 return True
217 218 # Command hooks
219 - def apply(self):
220 pass
221
222 - def refresh(self):
223 """Refreshes the nMOLDYN running jobs list and its associated frame. 224 """ 225 226 # The list of the active and inactive jobs. 227 self.activeJobs, self.inactiveJobs = self.findJobs() 228 229 print self.activeJobs, self.inactiveJobs 230 231 # The temporary file corresponding to the inactive jobs are deleted. 232 for jobs in self.inactiveJobs: 233 os.unlink(jobs) 234 235 jobsPID = [] 236 jobsStart = [] 237 jobsOwner = [] 238 jobsName = [] 239 jobsProgress = [] 240 241 # Loop over all the nMOLDYN active jobs. 242 for actJob in self.activeJobs: 243 244 # The rest is inside. 245 tempFile = TextFile(actJob,'r') 246 data = tempFile.readlines() 247 tempFile.close() 248 249 # The job Id 250 jobsPID.append(data[0].strip()) 251 # The job starting time. 252 jobsStart.append(data[1].strip()) 253 # The owner of the job. 254 jobsOwner.append(data[2].strip()) 255 # The job name. 256 jobsName.append(data[3].strip()) 257 # The last line of the temporary file contains the current step of the running job. 258 jobsProgress.append(data[-1].strip()) 259 260 # Look for jobs that have finished. 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 # Register an alarm callback called every 5s. 364 # Returns the alarm Id. 365 self.afterId = self.jobsWindow.after(5000, self.refresh)
366
367 - def findJobs(self):
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 # Some platforms specific import and setups. 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 # The list of all the nMOLDYN temporary logfile names. 384 jobNames = glob(os.path.join(gettempdir(),'*'+owner+'*moldyn')) 385 386 # |jobsDict| = lookup dictionnary whose keys are the jobs PID and the variables the jobs names. 387 jobsDict = {} 388 for job in jobNames: 389 pid = re.findall(owner+'\.(\d*)\.',job)[0] 390 jobsDict[pid] = job 391 392 activeJobs, inactiveJobs = [], [] 393 # Some platforms specific import and setups. 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 # |activeJobs| = list of currently nMOLDYN active jobs. 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
415 - def killJobs(self, pid):
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 # A Warning dialog for the confirmation of the killing. 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 # The job should not ne killed. 436 return 437 438 elif dialog.num == 1: 439 # WIN32 440 if sys.platform == 'win32': 441 handle = win32api.OpenProcess(1, 0, pid) 442 win32api.TerminateProcess(handle, 0) 443 # POSIX. 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 # Once the job killed, updates the dialog. 455 self.refresh()
456