1   
  2  from twisted.internet import gtk2reactor 
  3  gtk2reactor.install() 
  4   
  5  import sys, time 
  6   
  7  import pygtk 
  8  pygtk.require("2.0") 
  9  import gobject, gtk 
 10  assert(gtk.Window)  
 11   
 12  from twisted.spread import pb 
 13   
 14   
 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   
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   
271   
272 -    def setText(self, text): 
 273          self.text = text 
274          self.label.set_text(text) 
 275   
277          if not color: 
278              return 
279          self.box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) 
 280   
287   
292   
294          if self.timer: 
295              gobject.source_remove(self.timer) 
296              self.timer = None 
297          self.label.set_text(self.text) 
 298   
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  
305          else: 
306               
307              self.label.set_text("%s\n[soon]\n[overdue]" % (self.text,)) 
308              self.timer = None 
309              return False 
  310   
311   
312   
323   
326   
328          d = self.ref.callRemote("getLastFinishedBuild") 
329          d.addCallback(self.gotLastBuild) 
 334   
335 -    def gotLastText(self, text): 
 336          print "Got text", text 
337          self.last.setText("\n".join(text)) 
 338   
346   
348          self.ref.callRemote("getState").addCallback(self.gotState) 
 350          state, ETA, builds = res 
351           
352           
353           
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   
366          print "[%s] buildStarted" % (self.name,) 
367          self.current.setColor("yellow") 
 368   
374   
376          print "[%s] buildETAUpdate: %s" % (self.name, eta) 
377          self.current.setETA(eta) 
 378   
379   
 392   
393   
396          self.window = window 
397          self.buildernames = [] 
398          self.builders = {} 
 399   
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   
411          for child in self.table.get_children(): 
412              self.table.remove(child) 
413          self.pane.remove(self.table) 
 414   
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   
435   
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   
448          del self.builders[buildername] 
449          self.buildernames.remove(buildername) 
450          self.rebuildTable() 
 451   
458   
465   
472   
476   
 480   
481   
483      ClientClass = ThreeRowClient 
484   
486          self.master = master 
487   
488          w = gtk.Window() 
489          self.w = w 
490           
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   
 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   
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