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