1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  from twisted.internet import gtk2reactor 
 18  gtk2reactor.install()  
 19   
 20  import sys, time 
 21   
 22  import pygtk  
 23  pygtk.require("2.0") 
 24  import gobject, gtk  
 25  assert(gtk.Window)  
 26   
 27  from twisted.spread import pb 
 28   
 29   
 30  from buildbot.clients.base import TextClient, StatusClient 
 31  from buildbot.util import now 
 32   
 33  from buildbot.status.results import SUCCESS, WARNINGS, FAILURE, EXCEPTION 
 34   
 35  ''' 
 36  class Pane: 
 37      def __init__(self): 
 38          pass 
 39   
 40  class OneRow(Pane): 
 41      """This is a one-row status bar. It has one square per Builder, and that 
 42      square is either red, yellow, or green. """ 
 43   
 44      def __init__(self): 
 45          Pane.__init__(self) 
 46          self.widget = gtk.VBox(gtk.FALSE, 2) 
 47          self.nameBox = gtk.HBox(gtk.TRUE) 
 48          self.statusBox = gtk.HBox(gtk.TRUE) 
 49          self.widget.add(self.nameBox) 
 50          self.widget.add(self.statusBox) 
 51          self.widget.show_all() 
 52          self.builders = [] 
 53           
 54      def getWidget(self): 
 55          return self.widget 
 56      def addBuilder(self, builder): 
 57          print "OneRow.addBuilder" 
 58          # todo: ordering. Should follow the order in which they were added 
 59          # to the original BotMaster 
 60          self.builders.append(builder) 
 61          # add the name to the left column, and a label (with background) to 
 62          # the right 
 63          name = gtk.Label(builder.name) 
 64          status = gtk.Label('??') 
 65          status.set_size_request(64,64) 
 66          box = gtk.EventBox() 
 67          box.add(status) 
 68          name.show() 
 69          box.show_all() 
 70          self.nameBox.add(name) 
 71          self.statusBox.add(box) 
 72          builder.haveSomeWidgets([name, status, box]) 
 73       
 74  class R2Builder(Builder): 
 75      def start(self): 
 76          self.nameSquare.set_text(self.name) 
 77          self.statusSquare.set_text("???") 
 78          self.subscribe() 
 79      def haveSomeWidgets(self, widgets): 
 80          self.nameSquare, self.statusSquare, self.statusBox = widgets 
 81   
 82      def remote_newLastBuildStatus(self, event): 
 83          color = None 
 84          if event: 
 85              text = "\n".join(event.text) 
 86              color = event.color 
 87          else: 
 88              text = "none" 
 89          self.statusSquare.set_text(text) 
 90          if color: 
 91              print "color", color 
 92              self.statusBox.modify_bg(gtk.STATE_NORMAL, 
 93                                       gtk.gdk.color_parse(color)) 
 94   
 95      def remote_currentlyOffline(self): 
 96          self.statusSquare.set_text("offline") 
 97      def remote_currentlyIdle(self): 
 98          self.statusSquare.set_text("idle") 
 99      def remote_currentlyWaiting(self, seconds): 
100          self.statusSquare.set_text("waiting") 
101      def remote_currentlyInterlocked(self): 
102          self.statusSquare.set_text("interlocked") 
103      def remote_currentlyBuilding(self, eta): 
104          self.statusSquare.set_text("building") 
105   
106   
107  class CompactRow(Pane): 
108      def __init__(self): 
109          Pane.__init__(self) 
110          self.widget = gtk.VBox(gtk.FALSE, 3) 
111          self.nameBox = gtk.HBox(gtk.TRUE, 2) 
112          self.lastBuildBox = gtk.HBox(gtk.TRUE, 2) 
113          self.statusBox = gtk.HBox(gtk.TRUE, 2) 
114          self.widget.add(self.nameBox) 
115          self.widget.add(self.lastBuildBox) 
116          self.widget.add(self.statusBox) 
117          self.widget.show_all() 
118          self.builders = [] 
119           
120      def getWidget(self): 
121          return self.widget 
122           
123      def addBuilder(self, builder): 
124          self.builders.append(builder) 
125   
126          name = gtk.Label(builder.name) 
127          name.show() 
128          self.nameBox.add(name) 
129   
130          last = gtk.Label('??') 
131          last.set_size_request(64,64) 
132          lastbox = gtk.EventBox() 
133          lastbox.add(last) 
134          lastbox.show_all() 
135          self.lastBuildBox.add(lastbox) 
136   
137          status = gtk.Label('??') 
138          status.set_size_request(64,64) 
139          statusbox = gtk.EventBox() 
140          statusbox.add(status) 
141          statusbox.show_all() 
142          self.statusBox.add(statusbox) 
143   
144          builder.haveSomeWidgets([name, last, lastbox, status, statusbox]) 
145   
146      def removeBuilder(self, name, builder): 
147          self.nameBox.remove(builder.nameSquare) 
148          self.lastBuildBox.remove(builder.lastBuildBox) 
149          self.statusBox.remove(builder.statusBox) 
150          self.builders.remove(builder) 
151       
152  class CompactBuilder(Builder): 
153      def setup(self): 
154          self.timer = None 
155          self.text = [] 
156          self.eta = None 
157      def start(self): 
158          self.nameSquare.set_text(self.name) 
159          self.statusSquare.set_text("???") 
160          self.subscribe() 
161      def haveSomeWidgets(self, widgets): 
162          (self.nameSquare, 
163           self.lastBuildSquare, self.lastBuildBox, 
164           self.statusSquare, self.statusBox) = widgets 
165           
166      def remote_currentlyOffline(self): 
167          self.eta = None 
168          self.stopTimer() 
169          self.statusSquare.set_text("offline") 
170          self.statusBox.modify_bg(gtk.STATE_NORMAL, 
171                                   gtk.gdk.color_parse("red")) 
172      def remote_currentlyIdle(self): 
173          self.eta = None 
174          self.stopTimer() 
175          self.statusSquare.set_text("idle") 
176      def remote_currentlyWaiting(self, seconds): 
177          self.nextBuild = now() + seconds 
178          self.startTimer(self.updateWaiting) 
179      def remote_currentlyInterlocked(self): 
180          self.stopTimer() 
181          self.statusSquare.set_text("interlocked") 
182      def startTimer(self, func): 
183          # the func must clear self.timer and return gtk.FALSE when the event 
184          # has arrived 
185          self.stopTimer() 
186          self.timer = gtk.timeout_add(1000, func) 
187          func() 
188      def stopTimer(self): 
189          if self.timer: 
190              gtk.timeout_remove(self.timer) 
191              self.timer = None 
192      def updateWaiting(self): 
193          when = self.nextBuild 
194          if now() < when: 
195              next = time.strftime("%H:%M:%S", time.localtime(when)) 
196              secs = "[%d seconds]" % (when - now()) 
197              self.statusSquare.set_text("waiting\n%s\n%s" % (next, secs)) 
198              return gtk.TRUE # restart timer 
199          else: 
200              # done 
201              self.statusSquare.set_text("waiting\n[RSN]") 
202              self.timer = None 
203              return gtk.FALSE 
204   
205      def remote_currentlyBuilding(self, eta): 
206          self.stopTimer() 
207          self.statusSquare.set_text("building") 
208          if eta: 
209              d = eta.callRemote("subscribe", self, 5) 
210   
211      def remote_newLastBuildStatus(self, event): 
212          color = None 
213          if event: 
214              text = "\n".join(event.text) 
215              color = event.color 
216          else: 
217              text = "none" 
218          if not color: color = "gray" 
219          self.lastBuildSquare.set_text(text) 
220          self.lastBuildBox.modify_bg(gtk.STATE_NORMAL, 
221                                      gtk.gdk.color_parse(color)) 
222   
223      def remote_newEvent(self, event): 
224          assert(event.__class__ == GtkUpdatingEvent) 
225          self.current = event 
226          event.builder = self 
227          self.text = event.text 
228          if not self.text: self.text = ["idle"] 
229          self.eta = None 
230          self.stopTimer() 
231          self.updateText() 
232          color = event.color 
233          if not color: color = "gray" 
234          self.statusBox.modify_bg(gtk.STATE_NORMAL, 
235                                   gtk.gdk.color_parse(color)) 
236   
237      def updateCurrent(self): 
238          text = self.current.text 
239          if text: 
240              self.text = text 
241              self.updateText() 
242          color = self.current.color 
243          if color: 
244              self.statusBox.modify_bg(gtk.STATE_NORMAL, 
245                                       gtk.gdk.color_parse(color)) 
246      def updateText(self): 
247          etatext = [] 
248          if self.eta: 
249              etatext = [time.strftime("%H:%M:%S", time.localtime(self.eta))] 
250              if now() > self.eta: 
251                  etatext += ["RSN"] 
252              else: 
253                  seconds = self.eta - now() 
254                  etatext += ["[%d secs]" % seconds] 
255          text = "\n".join(self.text + etatext) 
256          self.statusSquare.set_text(text) 
257      def updateTextTimer(self): 
258          self.updateText() 
259          return gtk.TRUE # restart timer 
260       
261      def remote_progress(self, seconds): 
262          if seconds == None: 
263              self.eta = None 
264          else: 
265              self.eta = now() + seconds 
266          self.startTimer(self.updateTextTimer) 
267          self.updateText() 
268      def remote_finished(self, eta): 
269          self.eta = None 
270          self.stopTimer() 
271          self.updateText() 
272          eta.callRemote("unsubscribe", self) 
273  ''' 
274   
283   
286   
287 -    def setText(self, text): 
 288          self.text = text 
289          self.label.set_text(text) 
 290   
292          if not color: 
293              return 
294          self.box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color)) 
 295   
302   
307   
313   
315          if now() < self.when: 
316              next = time.strftime("%H:%M:%S", time.localtime(self.when)) 
317              secs = "[%d secs]" % (self.when - now()) 
318              self.label.set_text("%s\n%s\n%s" % (self.text, next, secs)) 
319              return True  
320          else: 
321               
322              self.label.set_text("%s\n[soon]\n[overdue]" % (self.text,)) 
323              self.timer = None 
324              return False 
  325   
326   
327   
338   
341   
343          d = self.ref.callRemote("getLastFinishedBuild") 
344          d.addCallback(self.gotLastBuild) 
 349   
350 -    def gotLastText(self, text): 
 351          print "Got text", text 
352          self.last.setText("\n".join(text)) 
 353   
361   
363          self.ref.callRemote("getState").addCallback(self.gotState) 
 365          state, ETA, builds = res 
366           
367           
368           
369          currentmap = {"offline": "red", 
370                        "idle": "white", 
371                        "waiting": "yellow", 
372                        "interlocked": "yellow", 
373                        "building": "yellow",} 
374          text = state 
375          self.current.setColor(currentmap[state]) 
376          if ETA is not None: 
377              text += "\nETA=%s secs" % ETA 
378          self.current.setText(state) 
 379   
381          print "[%s] buildStarted" % (self.name,) 
382          self.current.setColor("yellow") 
 383   
389   
391          print "[%s] buildETAUpdate: %s" % (self.name, eta) 
392          self.current.setETA(eta) 
 393   
394   
 407   
408   
411          self.window = window 
412          self.buildernames = [] 
413          self.builders = {} 
 414   
416          print "connected" 
417          self.ref = ref 
418          self.pane = gtk.VBox(False, 2) 
419          self.table = gtk.Table(1+3, 1) 
420          self.pane.add(self.table) 
421          self.window.vb.add(self.pane) 
422          self.pane.show_all() 
423          ref.callRemote("subscribe", "logs", 5, self) 
 424   
426          for child in self.table.get_children(): 
427              self.table.remove(child) 
428          self.pane.remove(self.table) 
 429   
431          columns = len(self.builders) 
432          self.table = gtk.Table(2, columns) 
433          self.pane.add(self.table) 
434          for i in range(len(self.buildernames)): 
435              name = self.buildernames[i] 
436              b = self.builders[name] 
437              last,current,step = b.getBoxes() 
438              self.table.attach(gtk.Label(name), i, i+1, 0, 1) 
439              self.table.attach(last, i, i+1, 1, 2, 
440                                xpadding=1, ypadding=1) 
441              self.table.attach(current, i, i+1, 2, 3, 
442                                xpadding=1, ypadding=1) 
443              self.table.attach(step, i, i+1, 3, 4, 
444                                xpadding=1, ypadding=1) 
445          self.table.show_all() 
 446   
450   
461   
463          del self.builders[buildername] 
464          self.buildernames.remove(buildername) 
465          self.rebuildTable() 
 466   
473   
480   
487   
491   
 495   
496   
498      ClientClass = ThreeRowClient 
499   
500 -    def __init__(self, master, events="steps", username="statusClient", passwd="clientpw"): 
 501          self.master = master 
502          self.username = username 
503          self.passwd = passwd 
504          self.listener = StatusClient(events) 
505   
506          w = gtk.Window() 
507          self.w = w 
508           
509          w.connect('destroy', lambda win: gtk.main_quit()) 
510          self.vb = gtk.VBox(False, 2) 
511          self.status = gtk.Label("unconnected") 
512          self.vb.add(self.status) 
513          self.listener = self.ClientClass(self) 
514          w.add(self.vb) 
515          w.show_all() 
 516   
 520   
521  """ 
522      def addBuilder(self, name, builder): 
523          Client.addBuilder(self, name, builder) 
524          self.pane.addBuilder(builder) 
525      def removeBuilder(self, name): 
526          self.pane.removeBuilder(name, self.builders[name]) 
527          Client.removeBuilder(self, name) 
528           
529      def startConnecting(self, master): 
530          self.master = master 
531          Client.startConnecting(self, master) 
532          self.status.set_text("connecting to %s.." % master) 
533      def connected(self, remote): 
534          Client.connected(self, remote) 
535          self.status.set_text(self.master) 
536          remote.notifyOnDisconnect(self.disconnected) 
537      def disconnected(self, remote): 
538          self.status.set_text("disconnected, will retry") 
539  """ 
540   
542      master = "localhost:8007" 
543      if len(sys.argv) > 1: 
544          master = sys.argv[1] 
545      c = GtkClient(master) 
546      c.run() 
 547   
548  if __name__ == '__main__': 
549      main() 
550