Package buildbot :: Package clients :: Module gtkPanes
[frames] | no frames]

Source Code for Module buildbot.clients.gtkPanes

  1   
  2  from twisted.internet import gtk2reactor 
  3  gtk2reactor.install() #@UndefinedVariable 
  4   
  5  import sys, time 
  6   
  7  import pygtk #@UnresolvedImport 
  8  pygtk.require("2.0") 
  9  import gobject, gtk #@UnresolvedImport 
 10  assert(gtk.Window) # in gtk1 it's gtk.GtkWindow 
 11   
 12  from twisted.spread import pb 
 13   
 14  #from buildbot.clients.base import Builder, Client 
 15  from buildbot.clients.base import TextClient 
 16  from buildbot.util import now 
 17   
 18  from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION 
 19   
 20  ''' 
 21  class Pane: 
 22      def __init__(self): 
 23          pass 
 24   
 25  class OneRow(Pane): 
 26      """This is a one-row status bar. It has one square per Builder, and that 
 27      square is either red, yellow, or green. """ 
 28   
 29      def __init__(self): 
 30          Pane.__init__(self) 
 31          self.widget = gtk.VBox(gtk.FALSE, 2) 
 32          self.nameBox = gtk.HBox(gtk.TRUE) 
 33          self.statusBox = gtk.HBox(gtk.TRUE) 
 34          self.widget.add(self.nameBox) 
 35          self.widget.add(self.statusBox) 
 36          self.widget.show_all() 
 37          self.builders = [] 
 38           
 39      def getWidget(self): 
 40          return self.widget 
 41      def addBuilder(self, builder): 
 42          print "OneRow.addBuilder" 
 43          # todo: ordering. Should follow the order in which they were added 
 44          # to the original BotMaster 
 45          self.builders.append(builder) 
 46          # add the name to the left column, and a label (with background) to 
 47          # the right 
 48          name = gtk.Label(builder.name) 
 49          status = gtk.Label('??') 
 50          status.set_size_request(64,64) 
 51          box = gtk.EventBox() 
 52          box.add(status) 
 53          name.show() 
 54          box.show_all() 
 55          self.nameBox.add(name) 
 56          self.statusBox.add(box) 
 57          builder.haveSomeWidgets([name, status, box]) 
 58       
 59  class R2Builder(Builder): 
 60      def start(self): 
 61          self.nameSquare.set_text(self.name) 
 62          self.statusSquare.set_text("???") 
 63          self.subscribe() 
 64      def haveSomeWidgets(self, widgets): 
 65          self.nameSquare, self.statusSquare, self.statusBox = widgets 
 66   
 67      def remote_newLastBuildStatus(self, event): 
 68          color = None 
 69          if event: 
 70              text = "\n".join(event.text) 
 71              color = event.color 
 72          else: 
 73              text = "none" 
 74          self.statusSquare.set_text(text) 
 75          if color: 
 76              print "color", color 
 77              self.statusBox.modify_bg(gtk.STATE_NORMAL, 
 78                                       gtk.gdk.color_parse(color)) 
 79   
 80      def remote_currentlyOffline(self): 
 81          self.statusSquare.set_text("offline") 
 82      def remote_currentlyIdle(self): 
 83          self.statusSquare.set_text("idle") 
 84      def remote_currentlyWaiting(self, seconds): 
 85          self.statusSquare.set_text("waiting") 
 86      def remote_currentlyInterlocked(self): 
 87          self.statusSquare.set_text("interlocked") 
 88      def remote_currentlyBuilding(self, eta): 
 89          self.statusSquare.set_text("building") 
 90   
 91   
 92  class CompactRow(Pane): 
 93      def __init__(self): 
 94          Pane.__init__(self) 
 95          self.widget = gtk.VBox(gtk.FALSE, 3) 
 96          self.nameBox = gtk.HBox(gtk.TRUE, 2) 
 97          self.lastBuildBox = gtk.HBox(gtk.TRUE, 2) 
 98          self.statusBox = gtk.HBox(gtk.TRUE, 2) 
 99          self.widget.add(self.nameBox) 
100          self.widget.add(self.lastBuildBox) 
101          self.widget.add(self.statusBox) 
102          self.widget.show_all() 
103          self.builders = [] 
104           
105      def getWidget(self): 
106          return self.widget 
107           
108      def addBuilder(self, builder): 
109          self.builders.append(builder) 
110   
111          name = gtk.Label(builder.name) 
112          name.show() 
113          self.nameBox.add(name) 
114   
115          last = gtk.Label('??') 
116          last.set_size_request(64,64) 
117          lastbox = gtk.EventBox() 
118          lastbox.add(last) 
119          lastbox.show_all() 
120          self.lastBuildBox.add(lastbox) 
121   
122          status = gtk.Label('??') 
123          status.set_size_request(64,64) 
124          statusbox = gtk.EventBox() 
125          statusbox.add(status) 
126          statusbox.show_all() 
127          self.statusBox.add(statusbox) 
128   
129          builder.haveSomeWidgets([name, last, lastbox, status, statusbox]) 
130   
131      def removeBuilder(self, name, builder): 
132          self.nameBox.remove(builder.nameSquare) 
133          self.lastBuildBox.remove(builder.lastBuildBox) 
134          self.statusBox.remove(builder.statusBox) 
135          self.builders.remove(builder) 
136       
137  class CompactBuilder(Builder): 
138      def setup(self): 
139          self.timer = None 
140          self.text = [] 
141          self.eta = None 
142      def start(self): 
143          self.nameSquare.set_text(self.name) 
144          self.statusSquare.set_text("???") 
145          self.subscribe() 
146      def haveSomeWidgets(self, widgets): 
147          (self.nameSquare, 
148           self.lastBuildSquare, self.lastBuildBox, 
149           self.statusSquare, self.statusBox) = widgets 
150           
151      def remote_currentlyOffline(self): 
152          self.eta = None 
153          self.stopTimer() 
154          self.statusSquare.set_text("offline") 
155          self.statusBox.modify_bg(gtk.STATE_NORMAL, 
156                                   gtk.gdk.color_parse("red")) 
157      def remote_currentlyIdle(self): 
158          self.eta = None 
159          self.stopTimer() 
160          self.statusSquare.set_text("idle") 
161      def remote_currentlyWaiting(self, seconds): 
162          self.nextBuild = now() + seconds 
163          self.startTimer(self.updateWaiting) 
164      def remote_currentlyInterlocked(self): 
165          self.stopTimer() 
166          self.statusSquare.set_text("interlocked") 
167      def startTimer(self, func): 
168          # the func must clear self.timer and return gtk.FALSE when the event 
169          # has arrived 
170          self.stopTimer() 
171          self.timer = gtk.timeout_add(1000, func) 
172          func() 
173      def stopTimer(self): 
174          if self.timer: 
175              gtk.timeout_remove(self.timer) 
176              self.timer = None 
177      def updateWaiting(self): 
178          when = self.nextBuild 
179          if now() < when: 
180              next = time.strftime("%H:%M:%S", time.localtime(when)) 
181              secs = "[%d seconds]" % (when - now()) 
182              self.statusSquare.set_text("waiting\n%s\n%s" % (next, secs)) 
183              return gtk.TRUE # restart timer 
184          else: 
185              # done 
186              self.statusSquare.set_text("waiting\n[RSN]") 
187              self.timer = None 
188              return gtk.FALSE 
189   
190      def remote_currentlyBuilding(self, eta): 
191          self.stopTimer() 
192          self.statusSquare.set_text("building") 
193          if eta: 
194              d = eta.callRemote("subscribe", self, 5) 
195   
196      def remote_newLastBuildStatus(self, event): 
197          color = None 
198          if event: 
199              text = "\n".join(event.text) 
200              color = event.color 
201          else: 
202              text = "none" 
203          if not color: color = "gray" 
204          self.lastBuildSquare.set_text(text) 
205          self.lastBuildBox.modify_bg(gtk.STATE_NORMAL, 
206                                      gtk.gdk.color_parse(color)) 
207   
208      def remote_newEvent(self, event): 
209          assert(event.__class__ == GtkUpdatingEvent) 
210          self.current = event 
211          event.builder = self 
212          self.text = event.text 
213          if not self.text: self.text = ["idle"] 
214          self.eta = None 
215          self.stopTimer() 
216          self.updateText() 
217          color = event.color 
218          if not color: color = "gray" 
219          self.statusBox.modify_bg(gtk.STATE_NORMAL, 
220                                   gtk.gdk.color_parse(color)) 
221   
222      def updateCurrent(self): 
223          text = self.current.text 
224          if text: 
225              self.text = text 
226              self.updateText() 
227          color = self.current.color 
228          if color: 
229              self.statusBox.modify_bg(gtk.STATE_NORMAL, 
230                                       gtk.gdk.color_parse(color)) 
231      def updateText(self): 
232          etatext = [] 
233          if self.eta: 
234              etatext = [time.strftime("%H:%M:%S", time.localtime(self.eta))] 
235              if now() > self.eta: 
236                  etatext += ["RSN"] 
237              else: 
238                  seconds = self.eta - now() 
239                  etatext += ["[%d secs]" % seconds] 
240          text = "\n".join(self.text + etatext) 
241          self.statusSquare.set_text(text) 
242      def updateTextTimer(self): 
243          self.updateText() 
244          return gtk.TRUE # restart timer 
245       
246      def remote_progress(self, seconds): 
247          if seconds == None: 
248              self.eta = None 
249          else: 
250              self.eta = now() + seconds 
251          self.startTimer(self.updateTextTimer) 
252          self.updateText() 
253      def remote_finished(self, eta): 
254          self.eta = None 
255          self.stopTimer() 
256          self.updateText() 
257          eta.callRemote("unsubscribe", self) 
258  ''' 
259   
260 -class Box:
261 - def __init__(self, text="?"):
262 self.text = text 263 self.box = gtk.EventBox() 264 self.label = gtk.Label(text) 265 self.box.add(self.label) 266 self.box.set_size_request(64,64) 267 self.timer = None
268
269 - def getBox(self):
270 return self.box
271
272 - def setText(self, text):
273 self.text = text 274 self.label.set_text(text)
275
276 - def setColor(self, color):
277 if not color: 278 return 279 self.box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
280
281 - def setETA(self, eta):
282 if eta: 283 self.when = now() + eta 284 self.startTimer() 285 else: 286 self.stopTimer()
287
288 - def startTimer(self):
289 self.stopTimer() 290 self.timer = gobject.timeout_add(1000, self.update) 291 self.update()
292
293 - def stopTimer(self):
294 if self.timer: 295 gobject.source_remove(self.timer) 296 self.timer = None 297 self.label.set_text(self.text)
298
299 - def update(self):
300 if now() < self.when: 301 next = time.strftime("%H:%M:%S", time.localtime(self.when)) 302 secs = "[%d secs]" % (self.when - now()) 303 self.label.set_text("%s\n%s\n%s" % (self.text, next, secs)) 304 return True # restart timer 305 else: 306 # done 307 self.label.set_text("%s\n[soon]\n[overdue]" % (self.text,)) 308 self.timer = None 309 return False
310 311 312
313 -class ThreeRowBuilder:
314 - def __init__(self, name, ref):
315 self.name = name 316 317 self.last = Box() 318 self.current = Box() 319 self.step = Box("idle") 320 self.step.setColor("white") 321 322 self.ref = ref
323
324 - def getBoxes(self):
325 return self.last.getBox(), self.current.getBox(), self.step.getBox()
326
327 - def getLastBuild(self):
328 d = self.ref.callRemote("getLastFinishedBuild") 329 d.addCallback(self.gotLastBuild)
330 - def gotLastBuild(self, build):
331 if build: 332 build.callRemote("getText").addCallback(self.gotLastText) 333 build.callRemote("getResults").addCallback(self.gotLastResult)
334
335 - def gotLastText(self, text):
336 print "Got text", text 337 self.last.setText("\n".join(text))
338
339 - def gotLastResult(self, result):
340 colormap = {SUCCESS: 'green', 341 FAILURE: 'red', 342 WARNINGS: 'orange', 343 EXCEPTION: 'purple', 344 } 345 self.last.setColor(colormap[result])
346
347 - def getState(self):
348 self.ref.callRemote("getState").addCallback(self.gotState)
349 - def gotState(self, res):
350 state, ETA, builds = res 351 # state is one of: offline, idle, waiting, interlocked, building 352 # TODO: ETA is going away, you have to look inside the builds to get 353 # that value 354 currentmap = {"offline": "red", 355 "idle": "white", 356 "waiting": "yellow", 357 "interlocked": "yellow", 358 "building": "yellow",} 359 text = state 360 self.current.setColor(currentmap[state]) 361 if ETA is not None: 362 text += "\nETA=%s secs" % ETA 363 self.current.setText(state)
364
365 - def buildStarted(self, build):
366 print "[%s] buildStarted" % (self.name,) 367 self.current.setColor("yellow")
368
369 - def buildFinished(self, build, results):
370 print "[%s] buildFinished: %s" % (self.name, results) 371 self.gotLastBuild(build) 372 self.current.setColor("white") 373 self.current.stopTimer()
374
375 - def buildETAUpdate(self, eta):
376 print "[%s] buildETAUpdate: %s" % (self.name, eta) 377 self.current.setETA(eta)
378 379
380 - def stepStarted(self, stepname, step):
381 print "[%s] stepStarted: %s" % (self.name, stepname) 382 self.step.setText(stepname) 383 self.step.setColor("yellow")
384 - def stepFinished(self, stepname, step, results):
385 print "[%s] stepFinished: %s %s" % (self.name, stepname, results) 386 self.step.setText("idle") 387 self.step.setColor("white") 388 self.step.stopTimer()
389 - def stepETAUpdate(self, stepname, eta):
390 print "[%s] stepETAUpdate: %s %s" % (self.name, stepname, eta) 391 self.step.setETA(eta)
392 393
394 -class ThreeRowClient(pb.Referenceable):
395 - def __init__(self, window):
396 self.window = window 397 self.buildernames = [] 398 self.builders = {}
399
400 - def connected(self, ref):
401 print "connected" 402 self.ref = ref 403 self.pane = gtk.VBox(False, 2) 404 self.table = gtk.Table(1+3, 1) 405 self.pane.add(self.table) 406 self.window.vb.add(self.pane) 407 self.pane.show_all() 408 ref.callRemote("subscribe", "logs", 5, self)
409
410 - def removeTable(self):
411 for child in self.table.get_children(): 412 self.table.remove(child) 413 self.pane.remove(self.table)
414
415 - def makeTable(self):
416 columns = len(self.builders) 417 self.table = gtk.Table(2, columns) 418 self.pane.add(self.table) 419 for i in range(len(self.buildernames)): 420 name = self.buildernames[i] 421 b = self.builders[name] 422 last,current,step = b.getBoxes() 423 self.table.attach(gtk.Label(name), i, i+1, 0, 1) 424 self.table.attach(last, i, i+1, 1, 2, 425 xpadding=1, ypadding=1) 426 self.table.attach(current, i, i+1, 2, 3, 427 xpadding=1, ypadding=1) 428 self.table.attach(step, i, i+1, 3, 4, 429 xpadding=1, ypadding=1) 430 self.table.show_all()
431
432 - def rebuildTable(self):
433 self.removeTable() 434 self.makeTable()
435
436 - def remote_builderAdded(self, buildername, builder):
437 print "builderAdded", buildername 438 assert buildername not in self.buildernames 439 self.buildernames.append(buildername) 440 441 b = ThreeRowBuilder(buildername, builder) 442 self.builders[buildername] = b 443 self.rebuildTable() 444 b.getLastBuild() 445 b.getState()
446
447 - def remote_builderRemoved(self, buildername):
448 del self.builders[buildername] 449 self.buildernames.remove(buildername) 450 self.rebuildTable()
451
452 - def remote_builderChangedState(self, name, state, eta):
453 self.builders[name].gotState((state, eta, None))
454 - def remote_buildStarted(self, name, build):
455 self.builders[name].buildStarted(build)
456 - def remote_buildFinished(self, name, build, results):
457 self.builders[name].buildFinished(build, results)
458
459 - def remote_buildETAUpdate(self, name, build, eta):
460 self.builders[name].buildETAUpdate(eta)
461 - def remote_stepStarted(self, name, build, stepname, step):
462 self.builders[name].stepStarted(stepname, step)
463 - def remote_stepFinished(self, name, build, stepname, step, results):
464 self.builders[name].stepFinished(stepname, step, results)
465
466 - def remote_stepETAUpdate(self, name, build, stepname, step, 467 eta, expectations):
468 # expectations is a list of (metricname, current_value, 469 # expected_value) tuples, so that we could show individual progress 470 # meters for each metric 471 self.builders[name].stepETAUpdate(stepname, eta)
472
473 - def remote_logStarted(self, buildername, build, stepname, step, 474 logname, log):
475 pass
476
477 - def remote_logFinished(self, buildername, build, stepname, step, 478 logname, log):
479 pass
480 481
482 -class GtkClient(TextClient):
483 ClientClass = ThreeRowClient 484
485 - def __init__(self, master):
486 self.master = master 487 488 w = gtk.Window() 489 self.w = w 490 #w.set_size_request(64,64) 491 w.connect('destroy', lambda win: gtk.main_quit()) 492 self.vb = gtk.VBox(False, 2) 493 self.status = gtk.Label("unconnected") 494 self.vb.add(self.status) 495 self.listener = self.ClientClass(self) 496 w.add(self.vb) 497 w.show_all()
498
499 - def connected(self, ref):
500 self.status.set_text("connected") 501 TextClient.connected(self, ref)
502 503 """ 504 def addBuilder(self, name, builder): 505 Client.addBuilder(self, name, builder) 506 self.pane.addBuilder(builder) 507 def removeBuilder(self, name): 508 self.pane.removeBuilder(name, self.builders[name]) 509 Client.removeBuilder(self, name) 510 511 def startConnecting(self, master): 512 self.master = master 513 Client.startConnecting(self, master) 514 self.status.set_text("connecting to %s.." % master) 515 def connected(self, remote): 516 Client.connected(self, remote) 517 self.status.set_text(self.master) 518 remote.notifyOnDisconnect(self.disconnected) 519 def disconnected(self, remote): 520 self.status.set_text("disconnected, will retry") 521 """ 522
523 -def main():
524 master = "localhost:8007" 525 if len(sys.argv) > 1: 526 master = sys.argv[1] 527 c = GtkClient(master) 528 c.run()
529 530 if __name__ == '__main__': 531 main() 532