Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/buildbot/buildbot/clients/gtkPanes.py
diff options
context:
space:
mode:
Diffstat (limited to 'buildbot/buildbot/clients/gtkPanes.py')
-rw-r--r--buildbot/buildbot/clients/gtkPanes.py532
1 files changed, 532 insertions, 0 deletions
diff --git a/buildbot/buildbot/clients/gtkPanes.py b/buildbot/buildbot/clients/gtkPanes.py
new file mode 100644
index 0000000..8acba1b
--- /dev/null
+++ b/buildbot/buildbot/clients/gtkPanes.py
@@ -0,0 +1,532 @@
+
+from twisted.internet import gtk2reactor
+gtk2reactor.install()
+
+import sys, time
+
+import pygtk
+pygtk.require("2.0")
+import gobject, gtk
+assert(gtk.Window) # in gtk1 it's gtk.GtkWindow
+
+from twisted.spread import pb
+
+#from buildbot.clients.base import Builder, Client
+from buildbot.clients.base import TextClient
+from buildbot.util import now
+
+from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
+
+'''
+class Pane:
+ def __init__(self):
+ pass
+
+class OneRow(Pane):
+ """This is a one-row status bar. It has one square per Builder, and that
+ square is either red, yellow, or green. """
+
+ def __init__(self):
+ Pane.__init__(self)
+ self.widget = gtk.VBox(gtk.FALSE, 2)
+ self.nameBox = gtk.HBox(gtk.TRUE)
+ self.statusBox = gtk.HBox(gtk.TRUE)
+ self.widget.add(self.nameBox)
+ self.widget.add(self.statusBox)
+ self.widget.show_all()
+ self.builders = []
+
+ def getWidget(self):
+ return self.widget
+ def addBuilder(self, builder):
+ print "OneRow.addBuilder"
+ # todo: ordering. Should follow the order in which they were added
+ # to the original BotMaster
+ self.builders.append(builder)
+ # add the name to the left column, and a label (with background) to
+ # the right
+ name = gtk.Label(builder.name)
+ status = gtk.Label('??')
+ status.set_size_request(64,64)
+ box = gtk.EventBox()
+ box.add(status)
+ name.show()
+ box.show_all()
+ self.nameBox.add(name)
+ self.statusBox.add(box)
+ builder.haveSomeWidgets([name, status, box])
+
+class R2Builder(Builder):
+ def start(self):
+ self.nameSquare.set_text(self.name)
+ self.statusSquare.set_text("???")
+ self.subscribe()
+ def haveSomeWidgets(self, widgets):
+ self.nameSquare, self.statusSquare, self.statusBox = widgets
+
+ def remote_newLastBuildStatus(self, event):
+ color = None
+ if event:
+ text = "\n".join(event.text)
+ color = event.color
+ else:
+ text = "none"
+ self.statusSquare.set_text(text)
+ if color:
+ print "color", color
+ self.statusBox.modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.color_parse(color))
+
+ def remote_currentlyOffline(self):
+ self.statusSquare.set_text("offline")
+ def remote_currentlyIdle(self):
+ self.statusSquare.set_text("idle")
+ def remote_currentlyWaiting(self, seconds):
+ self.statusSquare.set_text("waiting")
+ def remote_currentlyInterlocked(self):
+ self.statusSquare.set_text("interlocked")
+ def remote_currentlyBuilding(self, eta):
+ self.statusSquare.set_text("building")
+
+
+class CompactRow(Pane):
+ def __init__(self):
+ Pane.__init__(self)
+ self.widget = gtk.VBox(gtk.FALSE, 3)
+ self.nameBox = gtk.HBox(gtk.TRUE, 2)
+ self.lastBuildBox = gtk.HBox(gtk.TRUE, 2)
+ self.statusBox = gtk.HBox(gtk.TRUE, 2)
+ self.widget.add(self.nameBox)
+ self.widget.add(self.lastBuildBox)
+ self.widget.add(self.statusBox)
+ self.widget.show_all()
+ self.builders = []
+
+ def getWidget(self):
+ return self.widget
+
+ def addBuilder(self, builder):
+ self.builders.append(builder)
+
+ name = gtk.Label(builder.name)
+ name.show()
+ self.nameBox.add(name)
+
+ last = gtk.Label('??')
+ last.set_size_request(64,64)
+ lastbox = gtk.EventBox()
+ lastbox.add(last)
+ lastbox.show_all()
+ self.lastBuildBox.add(lastbox)
+
+ status = gtk.Label('??')
+ status.set_size_request(64,64)
+ statusbox = gtk.EventBox()
+ statusbox.add(status)
+ statusbox.show_all()
+ self.statusBox.add(statusbox)
+
+ builder.haveSomeWidgets([name, last, lastbox, status, statusbox])
+
+ def removeBuilder(self, name, builder):
+ self.nameBox.remove(builder.nameSquare)
+ self.lastBuildBox.remove(builder.lastBuildBox)
+ self.statusBox.remove(builder.statusBox)
+ self.builders.remove(builder)
+
+class CompactBuilder(Builder):
+ def setup(self):
+ self.timer = None
+ self.text = []
+ self.eta = None
+ def start(self):
+ self.nameSquare.set_text(self.name)
+ self.statusSquare.set_text("???")
+ self.subscribe()
+ def haveSomeWidgets(self, widgets):
+ (self.nameSquare,
+ self.lastBuildSquare, self.lastBuildBox,
+ self.statusSquare, self.statusBox) = widgets
+
+ def remote_currentlyOffline(self):
+ self.eta = None
+ self.stopTimer()
+ self.statusSquare.set_text("offline")
+ self.statusBox.modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.color_parse("red"))
+ def remote_currentlyIdle(self):
+ self.eta = None
+ self.stopTimer()
+ self.statusSquare.set_text("idle")
+ def remote_currentlyWaiting(self, seconds):
+ self.nextBuild = now() + seconds
+ self.startTimer(self.updateWaiting)
+ def remote_currentlyInterlocked(self):
+ self.stopTimer()
+ self.statusSquare.set_text("interlocked")
+ def startTimer(self, func):
+ # the func must clear self.timer and return gtk.FALSE when the event
+ # has arrived
+ self.stopTimer()
+ self.timer = gtk.timeout_add(1000, func)
+ func()
+ def stopTimer(self):
+ if self.timer:
+ gtk.timeout_remove(self.timer)
+ self.timer = None
+ def updateWaiting(self):
+ when = self.nextBuild
+ if now() < when:
+ next = time.strftime("%H:%M:%S", time.localtime(when))
+ secs = "[%d seconds]" % (when - now())
+ self.statusSquare.set_text("waiting\n%s\n%s" % (next, secs))
+ return gtk.TRUE # restart timer
+ else:
+ # done
+ self.statusSquare.set_text("waiting\n[RSN]")
+ self.timer = None
+ return gtk.FALSE
+
+ def remote_currentlyBuilding(self, eta):
+ self.stopTimer()
+ self.statusSquare.set_text("building")
+ if eta:
+ d = eta.callRemote("subscribe", self, 5)
+
+ def remote_newLastBuildStatus(self, event):
+ color = None
+ if event:
+ text = "\n".join(event.text)
+ color = event.color
+ else:
+ text = "none"
+ if not color: color = "gray"
+ self.lastBuildSquare.set_text(text)
+ self.lastBuildBox.modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.color_parse(color))
+
+ def remote_newEvent(self, event):
+ assert(event.__class__ == GtkUpdatingEvent)
+ self.current = event
+ event.builder = self
+ self.text = event.text
+ if not self.text: self.text = ["idle"]
+ self.eta = None
+ self.stopTimer()
+ self.updateText()
+ color = event.color
+ if not color: color = "gray"
+ self.statusBox.modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.color_parse(color))
+
+ def updateCurrent(self):
+ text = self.current.text
+ if text:
+ self.text = text
+ self.updateText()
+ color = self.current.color
+ if color:
+ self.statusBox.modify_bg(gtk.STATE_NORMAL,
+ gtk.gdk.color_parse(color))
+ def updateText(self):
+ etatext = []
+ if self.eta:
+ etatext = [time.strftime("%H:%M:%S", time.localtime(self.eta))]
+ if now() > self.eta:
+ etatext += ["RSN"]
+ else:
+ seconds = self.eta - now()
+ etatext += ["[%d secs]" % seconds]
+ text = "\n".join(self.text + etatext)
+ self.statusSquare.set_text(text)
+ def updateTextTimer(self):
+ self.updateText()
+ return gtk.TRUE # restart timer
+
+ def remote_progress(self, seconds):
+ if seconds == None:
+ self.eta = None
+ else:
+ self.eta = now() + seconds
+ self.startTimer(self.updateTextTimer)
+ self.updateText()
+ def remote_finished(self, eta):
+ self.eta = None
+ self.stopTimer()
+ self.updateText()
+ eta.callRemote("unsubscribe", self)
+'''
+
+class Box:
+ def __init__(self, text="?"):
+ self.text = text
+ self.box = gtk.EventBox()
+ self.label = gtk.Label(text)
+ self.box.add(self.label)
+ self.box.set_size_request(64,64)
+ self.timer = None
+
+ def getBox(self):
+ return self.box
+
+ def setText(self, text):
+ self.text = text
+ self.label.set_text(text)
+
+ def setColor(self, color):
+ if not color:
+ return
+ self.box.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse(color))
+
+ def setETA(self, eta):
+ if eta:
+ self.when = now() + eta
+ self.startTimer()
+ else:
+ self.stopTimer()
+
+ def startTimer(self):
+ self.stopTimer()
+ self.timer = gobject.timeout_add(1000, self.update)
+ self.update()
+
+ def stopTimer(self):
+ if self.timer:
+ gobject.source_remove(self.timer)
+ self.timer = None
+ self.label.set_text(self.text)
+
+ def update(self):
+ if now() < self.when:
+ next = time.strftime("%H:%M:%S", time.localtime(self.when))
+ secs = "[%d secs]" % (self.when - now())
+ self.label.set_text("%s\n%s\n%s" % (self.text, next, secs))
+ return True # restart timer
+ else:
+ # done
+ self.label.set_text("%s\n[soon]\n[overdue]" % (self.text,))
+ self.timer = None
+ return False
+
+
+
+class ThreeRowBuilder:
+ def __init__(self, name, ref):
+ self.name = name
+
+ self.last = Box()
+ self.current = Box()
+ self.step = Box("idle")
+ self.step.setColor("white")
+
+ self.ref = ref
+
+ def getBoxes(self):
+ return self.last.getBox(), self.current.getBox(), self.step.getBox()
+
+ def getLastBuild(self):
+ d = self.ref.callRemote("getLastFinishedBuild")
+ d.addCallback(self.gotLastBuild)
+ def gotLastBuild(self, build):
+ if build:
+ build.callRemote("getText").addCallback(self.gotLastText)
+ build.callRemote("getResults").addCallback(self.gotLastResult)
+
+ def gotLastText(self, text):
+ print "Got text", text
+ self.last.setText("\n".join(text))
+
+ def gotLastResult(self, result):
+ colormap = {SUCCESS: 'green',
+ FAILURE: 'red',
+ WARNINGS: 'orange',
+ EXCEPTION: 'purple',
+ }
+ self.last.setColor(colormap[result])
+
+ def getState(self):
+ self.ref.callRemote("getState").addCallback(self.gotState)
+ def gotState(self, res):
+ state, ETA, builds = res
+ # state is one of: offline, idle, waiting, interlocked, building
+ # TODO: ETA is going away, you have to look inside the builds to get
+ # that value
+ currentmap = {"offline": "red",
+ "idle": "white",
+ "waiting": "yellow",
+ "interlocked": "yellow",
+ "building": "yellow",}
+ text = state
+ self.current.setColor(currentmap[state])
+ if ETA is not None:
+ text += "\nETA=%s secs" % ETA
+ self.current.setText(state)
+
+ def buildStarted(self, build):
+ print "[%s] buildStarted" % (self.name,)
+ self.current.setColor("yellow")
+
+ def buildFinished(self, build, results):
+ print "[%s] buildFinished: %s" % (self.name, results)
+ self.gotLastBuild(build)
+ self.current.setColor("white")
+ self.current.stopTimer()
+
+ def buildETAUpdate(self, eta):
+ print "[%s] buildETAUpdate: %s" % (self.name, eta)
+ self.current.setETA(eta)
+
+
+ def stepStarted(self, stepname, step):
+ print "[%s] stepStarted: %s" % (self.name, stepname)
+ self.step.setText(stepname)
+ self.step.setColor("yellow")
+ def stepFinished(self, stepname, step, results):
+ print "[%s] stepFinished: %s %s" % (self.name, stepname, results)
+ self.step.setText("idle")
+ self.step.setColor("white")
+ self.step.stopTimer()
+ def stepETAUpdate(self, stepname, eta):
+ print "[%s] stepETAUpdate: %s %s" % (self.name, stepname, eta)
+ self.step.setETA(eta)
+
+
+class ThreeRowClient(pb.Referenceable):
+ def __init__(self, window):
+ self.window = window
+ self.buildernames = []
+ self.builders = {}
+
+ def connected(self, ref):
+ print "connected"
+ self.ref = ref
+ self.pane = gtk.VBox(False, 2)
+ self.table = gtk.Table(1+3, 1)
+ self.pane.add(self.table)
+ self.window.vb.add(self.pane)
+ self.pane.show_all()
+ ref.callRemote("subscribe", "logs", 5, self)
+
+ def removeTable(self):
+ for child in self.table.get_children():
+ self.table.remove(child)
+ self.pane.remove(self.table)
+
+ def makeTable(self):
+ columns = len(self.builders)
+ self.table = gtk.Table(2, columns)
+ self.pane.add(self.table)
+ for i in range(len(self.buildernames)):
+ name = self.buildernames[i]
+ b = self.builders[name]
+ last,current,step = b.getBoxes()
+ self.table.attach(gtk.Label(name), i, i+1, 0, 1)
+ self.table.attach(last, i, i+1, 1, 2,
+ xpadding=1, ypadding=1)
+ self.table.attach(current, i, i+1, 2, 3,
+ xpadding=1, ypadding=1)
+ self.table.attach(step, i, i+1, 3, 4,
+ xpadding=1, ypadding=1)
+ self.table.show_all()
+
+ def rebuildTable(self):
+ self.removeTable()
+ self.makeTable()
+
+ def remote_builderAdded(self, buildername, builder):
+ print "builderAdded", buildername
+ assert buildername not in self.buildernames
+ self.buildernames.append(buildername)
+
+ b = ThreeRowBuilder(buildername, builder)
+ self.builders[buildername] = b
+ self.rebuildTable()
+ b.getLastBuild()
+ b.getState()
+
+ def remote_builderRemoved(self, buildername):
+ del self.builders[buildername]
+ self.buildernames.remove(buildername)
+ self.rebuildTable()
+
+ def remote_builderChangedState(self, name, state, eta):
+ self.builders[name].gotState((state, eta, None))
+ def remote_buildStarted(self, name, build):
+ self.builders[name].buildStarted(build)
+ def remote_buildFinished(self, name, build, results):
+ self.builders[name].buildFinished(build, results)
+
+ def remote_buildETAUpdate(self, name, build, eta):
+ self.builders[name].buildETAUpdate(eta)
+ def remote_stepStarted(self, name, build, stepname, step):
+ self.builders[name].stepStarted(stepname, step)
+ def remote_stepFinished(self, name, build, stepname, step, results):
+ self.builders[name].stepFinished(stepname, step, results)
+
+ def remote_stepETAUpdate(self, name, build, stepname, step,
+ eta, expectations):
+ # expectations is a list of (metricname, current_value,
+ # expected_value) tuples, so that we could show individual progress
+ # meters for each metric
+ self.builders[name].stepETAUpdate(stepname, eta)
+
+ def remote_logStarted(self, buildername, build, stepname, step,
+ logname, log):
+ pass
+
+ def remote_logFinished(self, buildername, build, stepname, step,
+ logname, log):
+ pass
+
+
+class GtkClient(TextClient):
+ ClientClass = ThreeRowClient
+
+ def __init__(self, master):
+ self.master = master
+
+ w = gtk.Window()
+ self.w = w
+ #w.set_size_request(64,64)
+ w.connect('destroy', lambda win: gtk.main_quit())
+ self.vb = gtk.VBox(False, 2)
+ self.status = gtk.Label("unconnected")
+ self.vb.add(self.status)
+ self.listener = self.ClientClass(self)
+ w.add(self.vb)
+ w.show_all()
+
+ def connected(self, ref):
+ self.status.set_text("connected")
+ TextClient.connected(self, ref)
+
+"""
+ def addBuilder(self, name, builder):
+ Client.addBuilder(self, name, builder)
+ self.pane.addBuilder(builder)
+ def removeBuilder(self, name):
+ self.pane.removeBuilder(name, self.builders[name])
+ Client.removeBuilder(self, name)
+
+ def startConnecting(self, master):
+ self.master = master
+ Client.startConnecting(self, master)
+ self.status.set_text("connecting to %s.." % master)
+ def connected(self, remote):
+ Client.connected(self, remote)
+ self.status.set_text(self.master)
+ remote.notifyOnDisconnect(self.disconnected)
+ def disconnected(self, remote):
+ self.status.set_text("disconnected, will retry")
+"""
+
+def main():
+ master = "localhost:8007"
+ if len(sys.argv) > 1:
+ master = sys.argv[1]
+ c = GtkClient(master)
+ c.run()
+
+if __name__ == '__main__':
+ main()
+