import os, os.path
import sys
import time
import traceback
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import sface.screens
from sface.config import config
from sface.logwindow import LogWindow
from sface.rspecwindow import RSpecWindow
from sface.screens.sfascreen import SfaScreen
# 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", "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
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.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 updated 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)
# 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.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.log = QLabel("Show Log", self)
self.rspec = QLabel("Show RSpec", self)
hlayout = QHBoxLayout()
hlayout.addWidget(self.status)
hlayout.addStretch()
hlayout.addWidget(self.rspec)
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.log, SIGNAL('linkActivated(QString)'),
self.showLogWindow)
self.connect(self.rspec, SIGNAL('linkActivated(QString)'),
self.showRSpecWindow)
def redirectOutputToLog(self):
self.logWindow.redirectOutput()
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 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)