import os, os.path import sys import time import traceback from PyQt4.QtCore import * from PyQt4.QtGui import * from distutils.version import LooseVersion from sfa.util.version import version_core import sface.screens from sface.config import config from sface.logwindow import LogWindow from sface.rspecwindow import RSpecWindow, ResourcesWindow from sface.screens.sfascreen import SfaScreen from sface.xmlrpcwindow import get_tracker, init_tracker MINIMUM_SFA_VERSION = "1.0-37" # depending on the platform.. # could probably use Qt's resource system but looks overkill for just one file... def locate_image_file (filename): for dir in [ '/usr/share', '/Applications/sface.app/Contents/Resources/sface' ] : for suffix in ['png','jpg']: attempt=os.path.join(dir,'images',"%s.%s"%(filename,suffix)) if os.path.isfile(attempt) : return attempt return os.path.join('/could/not/locate/image/file',filename) def load_screens(dirname): modnames = [] for fn in os.listdir(dirname): if not fn.endswith(".py"): continue modname = fn.rsplit(".py",1)[0] if modname == "sfascreen": # ignore this, it's the base class, not a screen continue modnames.append(modname) # we want the stock screens to show up in a specific order. plugins can # show up in any order afterward. sort_order = ["mainscreen", "userscreen", "configscreen", "helpscreen"] sort_order.reverse() for modname in sort_order: if modname in modnames: modnames.remove(modname) modnames.insert(0,modname) # import each module and find whatever class(es) is descendant from # SfaScreen within the module. Might be a better idea to just define a # screens=[] variable in each module screens = [] for modname in modnames: try: mod = __import__("sface.screens." + modname, fromlist=["modname"]) except: print "Exception while importing screen", modname traceback.print_exc() continue for object in dir(mod): object = getattr(mod, object) if hasattr(object, "__bases__") and sface.screens.sfascreen.SfaScreen in object.__bases__: screens.append(object) return screens def check_version(): sfa_version = version_core()['code_tag'] if (LooseVersion(sfa_version) < LooseVersion(MINIMUM_SFA_VERSION)): QMessageBox.warning(None, "Old SFA Version", "sfa version %s is required. " "Your installed version is %s. " "Please upgrade your sfa and sfa-client packages." % (MINIMUM_SFA_VERSION, sfa_version) ) sys.exit(-1) class Nav(QWidget): def __init__(self, screens, parent=None): QWidget.__init__(self, parent) self.title = QLabel("", self) scene=QGraphicsScene() pixmap = QPixmap(locate_image_file('graphic-sfa64')) logolabel=QLabel("",self) logolabel.setPixmap(pixmap) hlayout = QHBoxLayout() hlayout.addWidget(logolabel) hlayout.addWidget(self.title) hlayout.addStretch() gotolabel=QLabel("Go to: ", self) gotolabel.setAlignment(Qt.AlignRight) hlayout.addWidget(gotolabel) self.screenLabels = [] for screen in screens: label = QLabel(screen.getLinkText(), self) label.setAlignment(Qt.AlignRight) self.screenLabels.append(label) hlayout.addWidget(label) self.setLayout(hlayout) def setTitle(self, title): self.title.setText(title) class Status(QLabel): def __init__(self, parent=None): QLabel.__init__(self, "", parent) self.setMaximumWidth(640) self.sliceUpdateDate() def set(self, msg, timeout): self.setText(msg) if timeout: QTimer.singleShot(timeout, self.reset) def sliceUpdateDate(self): rspec_file = config.getSliceRSpecFile() if not os.path.exists(rspec_file): return creation_time = os.stat(rspec_file).st_ctime last_update = time.ctime(creation_time) self.set("Slice data last refreshed on %s" % last_update, timeout=None) def reset(self): self.setText("") QTimer.singleShot(1500, self.sliceUpdateDate) class MainWindow(QWidget): def __init__(self, parent=None): QWidget.__init__(self, parent) check_version() # These are top-level windows and should be initialized with parent set # to our parent. Otherwise, getting a segfault on exit in Ubuntu. self.logWindow = LogWindow(parent) self.rspecWindow = RSpecWindow(parent) self.resourcesWindow = ResourcesWindow(parent) self.trackerWindow = init_tracker(parent) self.pix = QLabel(self) screenClasses = load_screens(os.path.dirname(sface.screens.__file__)) self.screenWidgets = [] self.screens = QStackedWidget(self) for screen in screenClasses: # use a try/catch block to isolate the screen. Third-party plugins # could be buggy. try: screenWidget = screen(self) self.screenWidgets.append(screenWidget) self.screens.addWidget(screenWidget) except: print "Exception while creating screen", screen.__name__ traceback.print_exc() self.screens.addWidget(self.pix) self.next_screen = None self.nav = Nav(self.screenWidgets, self) if self.screenWidgets: self.nav.setTitle(self.screenWidgets[0].getTitleText()) self.status = Status(self) self.tracker = QLabel("Xmlrpc", self) self.log = QLabel("Log", self) self.rspec = QLabel("RSpec", self) self.resources = QLabel("Resources", self) hlayout = QHBoxLayout() hlayout.addWidget(self.status) hlayout.addStretch() hlayout.addWidget(self.tracker) hlayout.addWidget(self.rspec) hlayout.addWidget(self.resources) hlayout.addWidget(self.log) layout = QVBoxLayout() layout.addWidget(self.nav) layout.addWidget(self.screens) layout.addLayout(hlayout) self.setLayout(layout) self.resize(800, 500) for link in self.nav.screenLabels: self.connect(link, SIGNAL('linkActivated(QString)'), self.animateToScreen) self.connect(self.tracker, SIGNAL('linkActivated(QString)'), self.showTrackerWindow) self.connect(self.log, SIGNAL('linkActivated(QString)'), self.showLogWindow) self.connect(self.rspec, SIGNAL('linkActivated(QString)'), self.showRSpecWindow) self.connect(self.resources, SIGNAL('linkActivated(QString)'), self.showResourcesWindow) def redirectOutputToLog(self): self.logWindow.redirectOutput() def showTrackerWindow(self): tracker = get_tracker() tracker.show() tracker.resize(500, 640) tracker.raise_() tracker.activateWindow() def showLogWindow(self, link): self.logWindow.show() self.logWindow.resize(800, 200) self.logWindow.raise_() self.logWindow.activateWindow() def showRSpecWindow(self, link): self.rspecWindow.show() self.rspecWindow.resize(500, 640) self.rspecWindow.raise_() self.rspecWindow.activateWindow() def showResourcesWindow(self, link): self.resourcesWindow.show() self.resourcesWindow.resize(500, 640) self.resourcesWindow.raise_() self.resourcesWindow.activateWindow() def animatePixmap(self, y): self.pix.move(0, y) def animateToScreen(self, link): for screen in self.screenWidgets: if link == screen.name: self.next_screen = screen curr_screen = self.screens.currentWidget() if self.next_screen == curr_screen: self.setStatus("Already showing %s" % curr_screen.getTitleText(), timeout=1000) return # This is an optimization to have a smoother animation. We # render the widget into a pixmap and animate that instead of # moving the whole widget around. pixmap = QPixmap(self.screens.size()) curr_screen.render(pixmap) self.screens.setCurrentWidget(self.pix) self.pix.setPixmap(pixmap) timeLine = QTimeLine(500, self) timeLine.setFrameRange(0, self.screens.height()); self.connect(timeLine, SIGNAL('frameChanged(int)'), self.animatePixmap) self.connect(timeLine, SIGNAL('finished()'), self.toNextScreen) timeLine.start() def toNextScreen(self): self.screens.setCurrentWidget(self.next_screen) self.nav.setTitle(self.next_screen.getTitleText()) def setStatus(self, msg, timeout): self.status.set(msg, timeout) def nodeSelectionChanged(self, hostname): if self.rspecWindow.isVisible(): self.rspecWindow.showNode(hostname) def closeEvent(self, event): # give the screens an opportunity to veto the close for screen in self.screenWidgets: if not screen.canClose(): event.ignore() return # give the screens an opportunity to close gracefully for screen in self.screenWidgets: screen.mainWindowClose() QWidget.closeEvent(self, event)