retrieve all records when listing authority, and filter the user ones
[sface.git] / sface / mainwindow.py
1 import os, os.path
2 import sys
3 import time
4 import traceback
5
6 from PyQt4.QtCore import *
7 from PyQt4.QtGui import *
8
9 import sface.screens
10 from sface.config import config
11 from sface.logwindow import LogWindow
12 from sface.rspecwindow import RSpecWindow
13 from sface.screens.sfascreen import SfaScreen
14
15 # depending on the platform..
16 # could probably use Qt's resource system but looks overkill for just one file...
17 def locate_image_file (filename):
18     for dir in [ '/usr/share', '/Applications/sface.app/Contents/Resources/sface' ] :
19         for suffix in ['png','jpg']:
20             attempt=os.path.join(dir,'images',"%s.%s"%(filename,suffix))
21             if os.path.isfile(attempt) : return attempt
22     return os.path.join('/could/not/locate/image/file',filename)
23
24 def load_screens(dirname):
25     modnames = []
26     for fn in os.listdir(dirname):
27         if not fn.endswith(".py"):
28             continue
29         modname = fn.rsplit(".py",1)[0]
30         if modname == "sfascreen":
31             # ignore this, it's the base class, not a screen
32             continue
33         modnames.append(modname)
34
35     # we want the stock screens to show up in a specific order. plugins can
36     # show up in any order afterward.
37
38     sort_order = ["mainscreen", "userscreen", "configscreen", "helpscreen"]
39     sort_order.reverse()
40     for modname in sort_order:
41         if modname in modnames:
42             modnames.remove(modname)
43             modnames.insert(0,modname)
44
45     # import each module and find whatever class(es) is descendant from
46     # SfaScreen within the module. Might be a better idea to just define a
47     # screens=[] variable in each module
48
49     screens = []
50     for modname in modnames:
51         try:
52             mod = __import__("sface.screens." + modname, fromlist=["modname"])
53         except:
54             print "Exception while importing screen", modname
55             traceback.print_exc()
56             continue
57
58         for object in dir(mod):
59             object = getattr(mod, object)
60             if hasattr(object, "__bases__") and sface.screens.sfascreen.SfaScreen in object.__bases__:
61                 screens.append(object)
62
63     return screens
64
65 class Nav(QWidget):
66     def __init__(self, screens, parent=None):
67         QWidget.__init__(self, parent)
68
69         self.title = QLabel("", self)
70         scene=QGraphicsScene()
71         pixmap = QPixmap(locate_image_file('graphic-sfa64'))
72         logolabel=QLabel("",self)
73         logolabel.setPixmap(pixmap)
74
75         hlayout = QHBoxLayout()
76         hlayout.addWidget(logolabel)
77         hlayout.addWidget(self.title)
78         hlayout.addStretch()
79         gotolabel=QLabel("Go to: ", self)
80         gotolabel.setAlignment(Qt.AlignRight)
81         hlayout.addWidget(gotolabel)
82
83         self.screenLabels = []
84         for screen in screens:
85             label = QLabel(screen.getLinkText(), self)
86             label.setAlignment(Qt.AlignRight)
87             self.screenLabels.append(label)
88             hlayout.addWidget(label)
89
90         self.setLayout(hlayout)
91
92     def setTitle(self, title):
93         self.title.setText(title)
94
95
96 class Status(QLabel):
97     def __init__(self, parent=None):
98         QLabel.__init__(self, "", parent)
99         self.setMaximumWidth(640)
100         self.sliceUpdateDate()
101
102     def set(self, msg, timeout):
103         self.setText(msg)
104         if timeout:
105             QTimer.singleShot(timeout, self.reset)
106
107     def sliceUpdateDate(self):
108         rspec_file = config.getSliceRSpecFile()
109         if not os.path.exists(rspec_file):
110             return
111
112         creation_time = os.stat(rspec_file).st_ctime
113         last_update = time.ctime(creation_time)
114         self.set("Slice data last refreshed on %s" % last_update, timeout=None)
115
116     def reset(self):
117         self.setText("")
118         QTimer.singleShot(1500, self.sliceUpdateDate)
119
120
121 class MainWindow(QWidget):
122     def __init__(self, parent=None):
123         QWidget.__init__(self, parent)
124
125         # These are top-level windows and should be initialized with parent set
126         # to our parent. Otherwise, getting a segfault on exit in Ubuntu.
127         self.logWindow = LogWindow(parent)
128         self.rspecWindow = RSpecWindow(parent)
129
130         self.pix = QLabel(self)
131
132         screenClasses = load_screens(os.path.dirname(sface.screens.__file__))
133         self.screenWidgets = []
134
135         self.screens = QStackedWidget(self)
136         for screen in screenClasses:
137             # use a try/catch block to isolate the screen. Third-party plugins
138             # could be buggy.
139             try:
140                 screenWidget = screen(self)
141                 self.screenWidgets.append(screenWidget)
142                 self.screens.addWidget(screenWidget)
143             except:
144                 print "Exception while creating screen", screen.__name__
145                 traceback.print_exc()
146
147         self.screens.addWidget(self.pix)
148         self.next_screen = None
149
150         self.nav = Nav(self.screenWidgets, self)
151
152         if self.screenWidgets:
153             self.nav.setTitle(self.screenWidgets[0].getTitleText())
154
155         self.status = Status(self)
156         self.log = QLabel("<a href='showlog'>Show Log</a>", self)
157         self.rspec = QLabel("<a href='showlog'>Show RSpec</a>", self)
158
159         hlayout = QHBoxLayout()
160         hlayout.addWidget(self.status)
161         hlayout.addStretch()
162         hlayout.addWidget(self.rspec)
163         hlayout.addWidget(self.log)
164
165         layout = QVBoxLayout()
166         layout.addWidget(self.nav)
167         layout.addWidget(self.screens)
168         layout.addLayout(hlayout)
169         self.setLayout(layout)
170         self.resize(800, 500)
171
172         for link in self.nav.screenLabels:
173             self.connect(link, SIGNAL('linkActivated(QString)'),
174                          self.animateToScreen)
175
176         self.connect(self.log, SIGNAL('linkActivated(QString)'),
177                      self.showLogWindow)
178         self.connect(self.rspec, SIGNAL('linkActivated(QString)'),
179                      self.showRSpecWindow)
180
181     def redirectOutputToLog(self):
182         self.logWindow.redirectOutput()
183
184     def showLogWindow(self, link):
185         self.logWindow.show()
186         self.logWindow.resize(800, 200)
187         self.logWindow.raise_()
188         self.logWindow.activateWindow()
189
190     def showRSpecWindow(self, link):
191         self.rspecWindow.show()
192         self.rspecWindow.resize(500, 640)
193         self.rspecWindow.raise_()
194         self.rspecWindow.activateWindow()
195
196
197     def animatePixmap(self, y):
198         self.pix.move(0, y)
199
200     def animateToScreen(self, link):
201         for screen in self.screenWidgets:
202             if link == screen.name:
203                 self.next_screen = screen
204
205         curr_screen = self.screens.currentWidget()
206
207         if self.next_screen == curr_screen:
208             self.setStatus("Already showing %s" % curr_screen.getTitleText(), timeout=1000)
209             return
210
211         # This is an optimization to have a smoother animation. We
212         # render the widget into a pixmap and animate that instead of
213         # moving the whole widget around.
214         pixmap = QPixmap(self.screens.size())
215         curr_screen.render(pixmap)
216         self.screens.setCurrentWidget(self.pix)
217         self.pix.setPixmap(pixmap)
218
219         timeLine = QTimeLine(500, self)
220         timeLine.setFrameRange(0, self.screens.height());
221         self.connect(timeLine, SIGNAL('frameChanged(int)'), self.animatePixmap)
222         self.connect(timeLine, SIGNAL('finished()'), self.toNextScreen)
223         timeLine.start()
224
225
226     def toNextScreen(self):
227         self.screens.setCurrentWidget(self.next_screen)
228         self.nav.setTitle(self.next_screen.getTitleText())
229
230     def setStatus(self, msg, timeout):
231         self.status.set(msg, timeout)
232
233     def nodeSelectionChanged(self, hostname):
234         if self.rspecWindow.isVisible():
235             self.rspecWindow.showNode(hostname)
236
237     def closeEvent(self, event):
238         # give the screens an opportunity to veto the close
239         for screen in self.screenWidgets:
240             if not screen.canClose():
241                 event.ignore()
242                 return
243
244         # give the screens an opportunity to close gracefully
245         for screen in self.screenWidgets:
246             screen.mainWindowClose()
247
248         QWidget.closeEvent(self, event)
249