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