1 | #!/usr/bin/env python |
---|
2 | |
---|
3 | ############################################################################ |
---|
4 | # |
---|
5 | # SAGE UI - A Graphical User Interface for SAGE |
---|
6 | # Copyright (C) 2005 Electronic Visualization Laboratory, |
---|
7 | # University of Illinois at Chicago |
---|
8 | # |
---|
9 | # All rights reserved. |
---|
10 | # |
---|
11 | # Redistribution and use in source and binary forms, with or without |
---|
12 | # modification, are permitted provided that the following conditions are met: |
---|
13 | # |
---|
14 | # * Redistributions of source code must retain the above copyright |
---|
15 | # notice, this list of conditions and the following disclaimer. |
---|
16 | # * Redistributions in binary form must reproduce the above |
---|
17 | # copyright notice, this list of conditions and the following disclaimer |
---|
18 | # in the documentation and/or other materials provided with the distribution. |
---|
19 | # * Neither the name of the University of Illinois at Chicago nor |
---|
20 | # the names of its contributors may be used to endorse or promote |
---|
21 | # products derived from this software without specific prior written permission. |
---|
22 | # |
---|
23 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
---|
24 | # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
---|
25 | # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
---|
26 | # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
---|
27 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
---|
28 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
---|
29 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
---|
30 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
---|
31 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
---|
32 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
---|
33 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
---|
34 | # |
---|
35 | # Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum |
---|
36 | # |
---|
37 | # Author: Ratko Jagodic |
---|
38 | # Allan Spale |
---|
39 | # |
---|
40 | ############################################################################ |
---|
41 | |
---|
42 | |
---|
43 | |
---|
44 | |
---|
45 | ############################################################################ |
---|
46 | # |
---|
47 | # IMPORTS |
---|
48 | # |
---|
49 | ############################################################################ |
---|
50 | |
---|
51 | # python and wx stuff |
---|
52 | import string, sys, os, copy, optparse, time |
---|
53 | from threading import Timer |
---|
54 | import traceback as tb |
---|
55 | |
---|
56 | # hyperlink module is not (yet) included with wxPython as a standard module |
---|
57 | # so not all distributions have it |
---|
58 | global use_hyperlink |
---|
59 | use_hyperlink = True |
---|
60 | try: |
---|
61 | import wx.lib.hyperlink as hyperlink #for the about box |
---|
62 | except: |
---|
63 | use_hyperlink = False |
---|
64 | |
---|
65 | # if this UI should autosave |
---|
66 | global autosave |
---|
67 | |
---|
68 | # my stuff |
---|
69 | import Graph |
---|
70 | from sageGate import * |
---|
71 | from sageData import * |
---|
72 | from canvases import * |
---|
73 | from globals import * |
---|
74 | from users import * |
---|
75 | import preferences as prefs |
---|
76 | import launcherAdmin as lAdmin |
---|
77 | from sagePath import getUserPath |
---|
78 | |
---|
79 | |
---|
80 | ############################################################################ |
---|
81 | # |
---|
82 | # GLOBAL CONSTANTS |
---|
83 | # |
---|
84 | ############################################################################ |
---|
85 | |
---|
86 | # current version of the UI |
---|
87 | VERSION = "3.0a" |
---|
88 | setUIVersion(VERSION) |
---|
89 | |
---|
90 | |
---|
91 | ### IDS used for the menu items: |
---|
92 | SAVE_STATE = 10 |
---|
93 | LOAD_STATE = 1 |
---|
94 | ASPECT_RATIO = 2 |
---|
95 | SAGE_COLOR = 3 |
---|
96 | PERF_DATA = 4 |
---|
97 | LOG_PERF_DATA = 5 |
---|
98 | RENDER_BW = 6 |
---|
99 | DISPLAY_BW = 7 |
---|
100 | RECORD_SESSION = 8 |
---|
101 | READ_SESSION = 9 |
---|
102 | SAGE_SHUTDOWN = 11 |
---|
103 | TILE_WINDOWS = 12 |
---|
104 | |
---|
105 | |
---|
106 | |
---|
107 | ############################################################################ |
---|
108 | |
---|
109 | class Log: |
---|
110 | def WriteText(self, text): |
---|
111 | if text[-1:] == '\n': |
---|
112 | text = text[:-1] |
---|
113 | wx.LogMessage(text) |
---|
114 | write = WriteText |
---|
115 | |
---|
116 | |
---|
117 | assertMode = wx.PYAPP_ASSERT_DIALOG |
---|
118 | |
---|
119 | |
---|
120 | |
---|
121 | ############################################################################ |
---|
122 | # |
---|
123 | # CLASS: DisplayNotebook |
---|
124 | # |
---|
125 | # DESCRIPTION: This is the main container (notebook) for the UI. It holds |
---|
126 | # one DisplayPage for every connection to SAGE. It also handles |
---|
127 | # page changes and menu changes when switching pages. |
---|
128 | # |
---|
129 | # DATE: June, 2005 |
---|
130 | # |
---|
131 | ############################################################################ |
---|
132 | |
---|
133 | class DisplayNotebook(wx.Notebook): |
---|
134 | |
---|
135 | def __init__(self, parent): |
---|
136 | wx.Notebook.__init__(self, parent, -1, style=wx.NO_BORDER) |
---|
137 | self.Bind(wx.EVT_NOTEBOOK_PAGE_CHANGED, self.OnPageChange) |
---|
138 | |
---|
139 | # open the file for recording totals for all sites |
---|
140 | try: |
---|
141 | stDateTime = time.strftime("%Y%m%d-%H%M%S", time.localtime()) |
---|
142 | stFilename = "ALL_SITES_TOTALS-" + stDateTime |
---|
143 | stPath = opj(DATA_DIR, stFilename + ".txt") |
---|
144 | self._totalsFile = open(stPath, "w") |
---|
145 | self._totalsFile.write( time.asctime() + "\n" ) |
---|
146 | self._totalsFile.write( '-' * 40 + "\n" ) |
---|
147 | tempString = (' Timestamp(s) Disp BW(Gbps) Rend BW(Gbps) Num Sites\n') |
---|
148 | self._totalsFile.write(tempString) |
---|
149 | self._totalsFile.write( '-' * len(tempString) + "\n" ) |
---|
150 | self._totalsFile.flush() |
---|
151 | except: |
---|
152 | print "\n\nERROR: Unable to record performance totals in a file:\n" |
---|
153 | print "".join(tb.format_exception(sys.exc_info()[0], sys.exc_info()[1], sys.exc_info()[2])) |
---|
154 | self._totalsFile = None |
---|
155 | |
---|
156 | # start the timer to periodically record totals |
---|
157 | self.Bind(wx.EVT_TIMER, self.onPerfTimer) |
---|
158 | self.perfTimer = wx.Timer(self) |
---|
159 | self.perfTimer.Start(1000) |
---|
160 | |
---|
161 | |
---|
162 | ### this timer is called once per second to write the performance |
---|
163 | ### data totals to a file (for each sage connection) |
---|
164 | def onPerfTimer(self, event): |
---|
165 | totalR,totalD = 0,0 # totals for ALL sites... render and display |
---|
166 | recordTotals = False |
---|
167 | |
---|
168 | for pageIndex in range(0, self.GetPageCount()): |
---|
169 | p = self.GetPage(pageIndex) |
---|
170 | p.SaveSiteTotals() |
---|
171 | totalR += p.GetRenderTotal() |
---|
172 | totalD += p.GetDisplayTotal() |
---|
173 | |
---|
174 | # if no sites are logging data, dont record totals either |
---|
175 | if p.sageData.isLoggingEnabled(): |
---|
176 | recordTotals = True |
---|
177 | |
---|
178 | |
---|
179 | # now save the total of all sites |
---|
180 | if recordTotals and self._totalsFile: |
---|
181 | temp = "%12d %12.4f %12.4f %d\n" % (getTimeStamp(), |
---|
182 | totalD, |
---|
183 | totalR, |
---|
184 | self.GetPageCount()) |
---|
185 | self._totalsFile.write(temp) |
---|
186 | self._totalsFile.flush() |
---|
187 | |
---|
188 | |
---|
189 | ### test whether an application window was dropped on a notebook tab |
---|
190 | ### if it was, send a message to SAGE about the request |
---|
191 | ### used to request streaming of an application to multiple displays |
---|
192 | def DropTest(self, windowId, pt): |
---|
193 | tab_tuple = self.HitTest((pt[0], pt[1]*(-1))) # *(-1) since tabs are above the canvas (negative y coordinate with respect to it) |
---|
194 | newTab = tab_tuple[0] |
---|
195 | currentTab = self.GetSelection() |
---|
196 | |
---|
197 | if newTab == wx.NOT_FOUND or newTab == currentTab: |
---|
198 | return False |
---|
199 | else: |
---|
200 | currentPage = self.GetPage(currentTab) |
---|
201 | newPageMachine = self.GetPage(newTab).GetMachine() |
---|
202 | fsmIP = newPageMachine.GetSystemIP() |
---|
203 | fsmPort = newPageMachine.GetSystemPort() |
---|
204 | currentPage.sageGate.streamApp(windowId, fsmIP, fsmPort) #send a message |
---|
205 | self.SetSelection( newTab ) #open the new tab |
---|
206 | return True |
---|
207 | |
---|
208 | |
---|
209 | |
---|
210 | def GetFrame(self): |
---|
211 | return self.GetParent() |
---|
212 | |
---|
213 | |
---|
214 | ### this takes care of setting the proper menu items in the frame when a page changes |
---|
215 | ### it basically pulls the state of all the menu check items from the newly selected |
---|
216 | ### page and copies that hash into the hash of menu check items of the frame |
---|
217 | def OnPageChange(self, event): |
---|
218 | currentPageIndex = event.GetSelection() #must use event.GetSelection() instead of self.GetSelection() because WXMSW and WXMAC report the old selection otherwise |
---|
219 | if not currentPageIndex == -1: #windows reports -1 for some reason during creation |
---|
220 | newPage = self.GetPage(currentPageIndex) |
---|
221 | self.GetFrame().UpdateMenuItems(newPage.GetCurrentMenuItems()) #refresh the menu now so that it reflects the menu Items from the different page |
---|
222 | event.Skip() |
---|
223 | |
---|
224 | |
---|
225 | def SetDefaultMenuCheckItems(self, hashItems): |
---|
226 | self.defaultMenuCheckItems = hashItems.copy() |
---|
227 | |
---|
228 | |
---|
229 | def GetDefaultMenuCheckItems(self): |
---|
230 | return self.defaultMenuCheckItems.copy() |
---|
231 | |
---|
232 | |
---|
233 | def CloseCurrentPage(self, closeUI=True): |
---|
234 | if self.GetPageCount() < 2 and closeUI: |
---|
235 | self.GetFrame().Close(True) |
---|
236 | elif self.GetPageCount() > 1: |
---|
237 | currentPageIndex = self.GetSelection() |
---|
238 | if not currentPageIndex == -1: #windows reports -1 for some reason during creation |
---|
239 | page = self.GetPage(currentPageIndex) |
---|
240 | page.OnClose() |
---|
241 | self.DeletePage(currentPageIndex) |
---|
242 | |
---|
243 | #refresh the menu now so that it reflects the menu Items from the different page |
---|
244 | newPageIndex = self.GetSelection() |
---|
245 | newPage = self.GetPage(newPageIndex) |
---|
246 | self.GetFrame().UpdateMenuItems(newPage.GetCurrentMenuItems()) |
---|
247 | else: # this is the case if the user reconnected to SAGE when there was only one tab open previously |
---|
248 | currentPageIndex = self.GetSelection() |
---|
249 | if not currentPageIndex == -1: #windows reports -1 for some reason during creation |
---|
250 | page = self.GetPage(currentPageIndex) |
---|
251 | page.OnClose() |
---|
252 | self.DeletePage(currentPageIndex) |
---|
253 | |
---|
254 | |
---|
255 | def OnClose(self, event): |
---|
256 | for pageIndex in range(0, self.GetPageCount()): |
---|
257 | self.GetPage(pageIndex).OnClose(event) |
---|
258 | |
---|
259 | if self._totalsFile: self._totalsFile.close() |
---|
260 | self.perfTimer.Stop() |
---|
261 | |
---|
262 | |
---|
263 | def OnKeyEvent(self, event): |
---|
264 | if event.GetKeyCode() == wx.WXK_F9: |
---|
265 | currentPage = self.GetPage(self.GetSelection()) |
---|
266 | currentPage.GetDisplayCanvas().TileShapes() |
---|
267 | |
---|
268 | |
---|
269 | def OnMenuEvent(self, event): |
---|
270 | eventId = event.GetId() |
---|
271 | menuItem = self.GetFrame().GetMenuBar().FindItemById(eventId) |
---|
272 | currentPage = self.GetPage(self.GetSelection()) |
---|
273 | |
---|
274 | # this ONLY changes the specific page's state of the selected menu item |
---|
275 | if menuItem.IsCheckable(): |
---|
276 | if menuItem.IsChecked(): |
---|
277 | currentPage.CheckMenuItem(eventId) |
---|
278 | else: |
---|
279 | currentPage.UncheckMenuItem(eventId) |
---|
280 | |
---|
281 | # call the correct menu event handler |
---|
282 | if eventId == SAVE_STATE: |
---|
283 | currentPage.OnSaveState() |
---|
284 | elif eventId == LOAD_STATE: |
---|
285 | currentPage.OnLoadState() |
---|
286 | elif eventId == ASPECT_RATIO: |
---|
287 | currentPage.OnMaintainAspectRatio(menuItem) |
---|
288 | elif eventId == SAGE_COLOR: |
---|
289 | currentPage.OnSAGEColorChange() |
---|
290 | elif eventId == PERF_DATA: |
---|
291 | currentPage.OnReceivePerfData(menuItem) |
---|
292 | elif eventId == LOG_PERF_DATA: |
---|
293 | currentPage.OnLogPerfData(menuItem) |
---|
294 | elif eventId == RENDER_BW: |
---|
295 | currentPage.OnShowRenderBW(menuItem) |
---|
296 | elif eventId == DISPLAY_BW: |
---|
297 | currentPage.OnShowDisplayBW(menuItem) |
---|
298 | elif eventId == RECORD_SESSION: |
---|
299 | currentPage.OnRecordSession(menuItem) |
---|
300 | elif eventId == READ_SESSION: |
---|
301 | currentPage.OnReadSession() |
---|
302 | elif eventId == SAGE_SHUTDOWN: |
---|
303 | currentPage.OnSAGEShutdown() |
---|
304 | else: |
---|
305 | pass #nothing... we dont know how to handle anything else |
---|
306 | |
---|
307 | |
---|
308 | |
---|
309 | |
---|
310 | |
---|
311 | ############################################################################ |
---|
312 | # |
---|
313 | # CLASS: DisplayPage |
---|
314 | # |
---|
315 | # DESCRIPTION: DisplayPage is a panel that holds all the controls associated |
---|
316 | # with one SAGE connection (DisplayCanvas and AppInfoPanel). There |
---|
317 | # is one for every connection and they are all placed in a notebook |
---|
318 | # (DisplayNotebook). |
---|
319 | # |
---|
320 | # DATE: June, 2005 |
---|
321 | # |
---|
322 | ############################################################################ |
---|
323 | |
---|
324 | class DisplayPage(wx.Panel): |
---|
325 | def __init__(self, parent, sageGate, usersClient, machineId, title): |
---|
326 | wx.Panel.__init__(self, parent, -1, style=wx.NO_BORDER) |
---|
327 | |
---|
328 | self.parent = parent |
---|
329 | # create main data storage and conectivity objects |
---|
330 | self.machineId = machineId |
---|
331 | self.usersClient = usersClient |
---|
332 | self.usersData = getUsersData() |
---|
333 | self.sageGate = sageGate |
---|
334 | self.sageData = SageData(sageGate, autosave, title.split('@')[1]) |
---|
335 | self.RegisterCallbacks() |
---|
336 | self.menuCheckItems = self.GetParent().GetDefaultMenuCheckItems() |
---|
337 | self.GetParent().GetFrame().UpdateMenuItems(self.GetCurrentMenuItems()) #update the menu items |
---|
338 | |
---|
339 | self.Bind(wx.EVT_SIZE, self.OnSize) |
---|
340 | |
---|
341 | # get the machine name |
---|
342 | self.siteName = machineId |
---|
343 | m = self.usersData.GetMachine(machineId) |
---|
344 | if m: |
---|
345 | self.siteName = m.GetName() |
---|
346 | |
---|
347 | # (AKS 2005-01-27) Performance graph object |
---|
348 | self.gmGraphManager = Graph.GraphManager( self, self.sageData ) |
---|
349 | # (AKS 2005-05-07) Create constants to identify totals graphs |
---|
350 | self.I_RENDER_BW_GRAPH = 1 |
---|
351 | self.I_DISPLAY_BW_GRAPH = 2 |
---|
352 | |
---|
353 | self.displayCanvas = DisplayCanvas(Log(), self, self.sageGate, self.sageData, self.gmGraphManager, title) |
---|
354 | self.appPanel = AppInfoPanel(self, self.sageGate, self, -1, (0,0), (self.displayCanvas.GetCanvasWidth(), 200), wx.NO_BORDER | wx.NO_FULL_REPAINT_ON_RESIZE) |
---|
355 | self.sageGate.getAppList() # this will get the list and fill the appPanel with it |
---|
356 | #self.Show() < -- this is done in DisplayCanvas.OnStatusMessage() (canvases.py) |
---|
357 | |
---|
358 | |
---|
359 | def GetFrame(self): |
---|
360 | return self.GetParent().GetParent() |
---|
361 | |
---|
362 | def GetNotebook(self): |
---|
363 | return self.GetParent() |
---|
364 | |
---|
365 | def GetNumPages(self): |
---|
366 | return self.GetNotebook().GetPageCount() |
---|
367 | |
---|
368 | def GetCurrentMenuItems(self): |
---|
369 | return self.menuCheckItems.copy() #return the current state of menu items specific to this display |
---|
370 | |
---|
371 | def CheckMenuItem(self, menuItemId): |
---|
372 | self.menuCheckItems[menuItemId] = True |
---|
373 | |
---|
374 | def UncheckMenuItem(self, menuItemId): |
---|
375 | self.menuCheckItems[menuItemId] = False |
---|
376 | |
---|
377 | def GetMachineId(self): |
---|
378 | return self.machineId |
---|
379 | |
---|
380 | def GetMachine(self): |
---|
381 | return self.usersData.GetMachine(self.machineId) |
---|
382 | |
---|
383 | |
---|
384 | #---------------------------------------------------------------------- |
---|
385 | |
---|
386 | def RegisterCallbacks(self): |
---|
387 | # (AKS 2005-01-13) |
---|
388 | # Register callbacks from SageData with SageGate |
---|
389 | # Whenever SageGate receives an update message from SAGE, |
---|
390 | # the client-side "app database" stored in SageData |
---|
391 | # will be updated and then issue its own callback function |
---|
392 | # to tell the registering app to retrieve and process |
---|
393 | # the updated data |
---|
394 | |
---|
395 | self.sageGate.registerCallbackFunction( 40000, self.sageData.setSageStatus ) |
---|
396 | self.sageGate.registerCallbackFunction( 40001, self.sageData.setSageAppInfo ) |
---|
397 | self.sageGate.registerCallbackFunction( 40002, self.sageData.setSagePerfInfo ) |
---|
398 | self.sageGate.registerCallbackFunction( 40003, self.sageData.sageAppShutDown ) |
---|
399 | self.sageGate.registerCallbackFunction( 40004, self.sageData.setDisplayInfo ) |
---|
400 | self.sageGate.registerCallbackFunction( 40005, self.sageData.setSageZValue ) |
---|
401 | self.sageGate.registerCallbackFunction( 40006, self.sageData.setSageAppExecConfig ) |
---|
402 | self.sageGate.registerCallbackFunction( 40007, self.sageData.setDisplayConnections ) |
---|
403 | |
---|
404 | return self.sageData |
---|
405 | |
---|
406 | |
---|
407 | #---------------------------------------------------------------------- |
---|
408 | |
---|
409 | |
---|
410 | def GetDisplayCanvas(self): |
---|
411 | return self.displayCanvas |
---|
412 | |
---|
413 | def GetAppInfoPanel(self): |
---|
414 | return self.appPanel |
---|
415 | |
---|
416 | def GetAppPanel(self): |
---|
417 | return self.appPanel.GetAppPanel() |
---|
418 | |
---|
419 | def GetInfoPanel(self): |
---|
420 | return self.appPanel.GetInfoPanel() |
---|
421 | |
---|
422 | def HideAppInfoPanel(self): |
---|
423 | self.oldHeight = self.GetClientSize().height |
---|
424 | newHeight = self.oldHeight - self.GetAppInfoPanel().GetSize().height + 60 |
---|
425 | self.GetFrame().SetClientSize( (self.GetFrame().GetClientSize().width, newHeight) ) |
---|
426 | self.GetAppInfoPanel().OnSize() |
---|
427 | |
---|
428 | def ShowAppInfoPanel(self): |
---|
429 | self.GetFrame().SetClientSize( (self.GetFrame().GetClientSize().width, self.oldHeight) ) |
---|
430 | self.GetAppInfoPanel().OnSize() |
---|
431 | |
---|
432 | |
---|
433 | def SaveSiteTotals(self): |
---|
434 | self.sageData.saveSiteTotals(self.siteName) |
---|
435 | |
---|
436 | def GetRenderTotal(self): |
---|
437 | """ returns total display bandwidth in gbps """ |
---|
438 | return self.sageData.getRenderBWTotal() |
---|
439 | |
---|
440 | def GetDisplayTotal(self): |
---|
441 | """ returns total rendering bandwidth in gbps """ |
---|
442 | return self.sageData.getDisplayBWTotal() |
---|
443 | |
---|
444 | |
---|
445 | #---------------------------------------------------------------------- |
---|
446 | # EVENT HANDLERS |
---|
447 | #---------------------------------------------------------------------- |
---|
448 | |
---|
449 | def OnDisplayInfo(self): |
---|
450 | pass |
---|
451 | |
---|
452 | |
---|
453 | def OnSize(self, event=None): |
---|
454 | self.SetSize(self.GetParent().GetClientSize())#event.GetSize()) |
---|
455 | if "__WXMSW__" in wx.PlatformInfo: #to bypass MS Windows' deferred resizing crap |
---|
456 | def doResize(): |
---|
457 | self.GetDisplayCanvas().OnSize(event) |
---|
458 | self.GetAppInfoPanel().OnSize(event) |
---|
459 | wx.CallAfter(doResize) |
---|
460 | else: |
---|
461 | self.GetDisplayCanvas().OnSize(event) |
---|
462 | self.GetAppInfoPanel().OnSize(event) |
---|
463 | |
---|
464 | |
---|
465 | def OnClose(self, evt=None): |
---|
466 | self.GetDisplayCanvas().Close() |
---|
467 | self.gmGraphManager.shutdown() # close all the graphs that might be open |
---|
468 | |
---|
469 | # close the "Totals" graphs (if open) |
---|
470 | #if self.menuCheckItems[RENDER_BW]: |
---|
471 | # self.removeGraph(self.I_RENDER_BW_GRAPH) |
---|
472 | #if self.menuCheckItems[DISPLAY_BW]: |
---|
473 | # self.removeGraph(self.I_DISPLAY_BW_GRAPH) |
---|
474 | |
---|
475 | # stop all the running features |
---|
476 | self.usersClient.Unregister(self.machineId) #tell the server that we are not registered in this room anymore |
---|
477 | self.sageData.stopLogging() |
---|
478 | #self.sageGate.StopRecording() |
---|
479 | self.sageGate.disconnectFromSage() #break the connection with SAGE |
---|
480 | |
---|
481 | |
---|
482 | # Menu -> Options -> Change SAGE Background Color |
---|
483 | def OnSAGEColorChange(self): |
---|
484 | (r,g,b) = self.sageData.getSAGEColor() |
---|
485 | colorData = wx.ColourData() |
---|
486 | colorData.SetColour(wx.Colour(r,g,b)) |
---|
487 | dlg = wx.ColourDialog(None, colorData) |
---|
488 | dlg.GetColourData().SetChooseFull(True) |
---|
489 | if dlg.ShowModal() == wx.ID_OK: |
---|
490 | data = dlg.GetColourData() |
---|
491 | self.sageData.setSAGEColor(data.GetColour().Get()) |
---|
492 | self.sageGate.setBgColor(self.sageData.getSAGEColor()) |
---|
493 | self.GetDisplayCanvas().ChangeBackgroundColor(self.sageData.getSAGEColor()) |
---|
494 | dlg.Destroy() |
---|
495 | |
---|
496 | |
---|
497 | # Menu -> File -> Shutdown SAGE |
---|
498 | def OnSAGEShutdown(self): |
---|
499 | dlg = wx.MessageDialog(self, "You are about to shutdown SAGE. All the currently connected "+ |
---|
500 | "users will be disconnected and all applications closed. \nAre "+ |
---|
501 | "you sure you want to continue?", "SAGE Shutdown", wx.YES_NO | wx.ICON_EXCLAMATION) |
---|
502 | if dlg.ShowModal() == wx.ID_YES: |
---|
503 | self.sageGate.shutdownSAGE() |
---|
504 | |
---|
505 | |
---|
506 | # Menu -> File -> Save State |
---|
507 | def OnSaveState(self): |
---|
508 | def OnStateSelected(evt): |
---|
509 | if nameText.GetValue() in stateHash: |
---|
510 | descriptionText.SetValue( stateHash[nameText.GetValue()] ) |
---|
511 | |
---|
512 | def OnDeleteState(evt): |
---|
513 | stateName = nameText.GetValue() |
---|
514 | if stateName in stateHash: |
---|
515 | if self.sageData.deleteState(stateName): |
---|
516 | descriptionText.Clear() |
---|
517 | nameText.Delete(nameText.FindString(stateName)) |
---|
518 | nameText.SetValue("") |
---|
519 | else: |
---|
520 | Message("Error deleting the saved state. Do you have the required permissions?", "Error Deleting") |
---|
521 | |
---|
522 | |
---|
523 | stateHash = self.sageData.getStateList() |
---|
524 | |
---|
525 | dlg = wx.Dialog(self, -1, "Save Display Session") |
---|
526 | okBtn = wx.Button(dlg, wx.ID_OK, "Save") |
---|
527 | delBtn = wx.Button(dlg, wx.ID_OK, " Delete ", style=wx.BU_EXACTFIT) |
---|
528 | delBtn.Bind(wx.EVT_BUTTON, OnDeleteState) |
---|
529 | |
---|
530 | states = stateHash.keys() |
---|
531 | states.sort() |
---|
532 | nameLabel = wx.StaticText(dlg, -1, "Session name:") |
---|
533 | nameText = wx.ComboBox(dlg, -1, "", choices=states, style=wx.CB_DROPDOWN) |
---|
534 | nameText.Bind(wx.EVT_COMBOBOX, OnStateSelected) |
---|
535 | nameText.SetFocus() |
---|
536 | descriptionLabel = wx.StaticText(dlg, -1, "Description:") |
---|
537 | descriptionText = wx.TextCtrl(dlg, -1, "", style=wx.TE_MULTILINE) |
---|
538 | |
---|
539 | nameSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
540 | nameSizer.Add(nameLabel, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5) |
---|
541 | nameSizer.Add(nameText, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL) |
---|
542 | nameSizer.Add(delBtn, 0, wx.ALIGN_RIGHT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=5) |
---|
543 | |
---|
544 | sizer = wx.BoxSizer(wx.VERTICAL) |
---|
545 | sizer.Add(nameSizer, 0, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, border=10) |
---|
546 | sizer.Add(descriptionLabel, 0, wx.ALIGN_LEFT | wx.LEFT | wx.RIGHT | wx.TOP, border=10) |
---|
547 | sizer.Add(descriptionText, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.RIGHT | wx.LEFT, border=10) |
---|
548 | sizer.AddSpacer((15,15)) |
---|
549 | sizer.Add(okBtn, 0, wx.ALIGN_CENTER) |
---|
550 | sizer.AddSpacer((5,5)) |
---|
551 | |
---|
552 | dlg.SetSizer(sizer) |
---|
553 | dlg.SetSize((350, 300)) |
---|
554 | |
---|
555 | stateName = "" |
---|
556 | description = "" |
---|
557 | while dlg.ShowModal() == wx.ID_OK: |
---|
558 | stateName = nameText.GetValue() |
---|
559 | description = descriptionText.GetValue() |
---|
560 | if stateName == "": |
---|
561 | Message("Please name the session you are saving", "Session name required") |
---|
562 | continue |
---|
563 | elif stateName in stateHash: |
---|
564 | if wx.MessageBox("Session with that name already exists. Overwrite?", |
---|
565 | "Overwrite?", style=wx.YES_NO) == wx.YES: |
---|
566 | dlg.Destroy() |
---|
567 | else: |
---|
568 | continue |
---|
569 | self.sageData.saveState(stateName, description) |
---|
570 | break |
---|
571 | |
---|
572 | return |
---|
573 | |
---|
574 | |
---|
575 | |
---|
576 | # Menu -> File -> Load State |
---|
577 | def OnLoadState(self): |
---|
578 | def OnStateSelected(evt): |
---|
579 | descriptionText.SetLabel( stateHash[stateList.GetStringSelection()] ) |
---|
580 | |
---|
581 | def OnDeleteState(evt): |
---|
582 | stateName = stateList.GetStringSelection() |
---|
583 | if stateName in stateHash: |
---|
584 | if self.sageData.deleteState(stateName): |
---|
585 | stateList.Delete(stateList.FindString(stateName)) |
---|
586 | if not stateList.IsEmpty(): |
---|
587 | stateList.Select(0) |
---|
588 | descriptionText.SetLabel(stateHash[stateList.GetString(0)]) |
---|
589 | else: |
---|
590 | descriptionText.SetLabel("") |
---|
591 | else: |
---|
592 | Message("Error deleting the saved state. Do you have the required file permissions?", "Error Deleting") |
---|
593 | |
---|
594 | |
---|
595 | stateHash = self.sageData.getStateList() |
---|
596 | if len(stateHash) == 0: |
---|
597 | Message("No saved sessions exist", "") |
---|
598 | return |
---|
599 | |
---|
600 | dlg = wx.Dialog(self, -1, "Load Saved Session") |
---|
601 | okBtn = wx.Button(dlg, wx.ID_OK, "Load") |
---|
602 | okBtn.Disable() |
---|
603 | delBtn = wx.Button(dlg, wx.ID_OK, " Delete ", style=wx.BU_EXACTFIT) |
---|
604 | delBtn.Bind(wx.EVT_BUTTON, OnDeleteState) |
---|
605 | |
---|
606 | states = stateHash.keys() |
---|
607 | states.sort() |
---|
608 | states = stateHash.keys() |
---|
609 | states.sort() |
---|
610 | stateLabel = wx.StaticText(dlg, -1, "Saved Sessions:") |
---|
611 | stateList = wx.Choice(dlg, -1, choices=states) |
---|
612 | stateList.Bind(wx.EVT_CHOICE, OnStateSelected) |
---|
613 | descriptionBox = wx.StaticBox(dlg, -1, "Description:") |
---|
614 | descriptionText = wx.StaticText(dlg, -1, "", style=wx.ST_NO_AUTORESIZE) |
---|
615 | |
---|
616 | dbSizer = wx.StaticBoxSizer(descriptionBox, wx.VERTICAL) |
---|
617 | dbSizer.Add(descriptionText, 1, wx.EXPAND | wx.ALL, border=5) |
---|
618 | |
---|
619 | if len(stateHash) > 0: |
---|
620 | stateList.SetSelection(0) |
---|
621 | descriptionText.SetLabel( stateHash[stateList.GetString(0)] ) |
---|
622 | okBtn.Enable() |
---|
623 | |
---|
624 | stateSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
625 | stateSizer.Add(stateLabel, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.RIGHT, border=5) |
---|
626 | stateSizer.Add(stateList, 1, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL) |
---|
627 | stateSizer.Add(delBtn, 0, wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=5) |
---|
628 | |
---|
629 | sizer = wx.BoxSizer(wx.VERTICAL) |
---|
630 | sizer.Add(stateSizer, 0, wx.EXPAND | wx.ALIGN_LEFT | wx.ALL, border=10) |
---|
631 | sizer.Add(dbSizer, 1, wx.EXPAND | wx.ALIGN_LEFT | wx.RIGHT | wx.LEFT, border=10) |
---|
632 | sizer.AddSpacer((15,15)) |
---|
633 | sizer.Add(okBtn, 0, wx.ALIGN_CENTER) |
---|
634 | sizer.AddSpacer((5,5)) |
---|
635 | |
---|
636 | dlg.SetSizer(sizer) |
---|
637 | dlg.SetSize((350, 300)) |
---|
638 | |
---|
639 | if dlg.ShowModal() == wx.ID_OK: |
---|
640 | stateName = stateList.GetStringSelection() |
---|
641 | dlg.Destroy() |
---|
642 | if stateName == "": |
---|
643 | Message("No saved sessions exist", "") |
---|
644 | return |
---|
645 | self.sageData.loadState(stateName) |
---|
646 | |
---|
647 | return |
---|
648 | |
---|
649 | |
---|
650 | |
---|
651 | # Menu -> Options -> RecordSession |
---|
652 | def OnRecordSession(self, menuItem): |
---|
653 | if menuItem.IsChecked(): |
---|
654 | self.sageGate.StartRecording(menuItem) |
---|
655 | else: |
---|
656 | self.sageGate.StopRecording() |
---|
657 | |
---|
658 | |
---|
659 | # Menu -> Options -> ReadSession |
---|
660 | def OnReadSession(self): |
---|
661 | self.sageGate.PlaySession(self) |
---|
662 | |
---|
663 | |
---|
664 | # Menu -> Options -> MaintainAspectRatio |
---|
665 | def OnMaintainAspectRatio(self, menuItem): |
---|
666 | diagram = self.GetDisplayCanvas().GetDiagram() |
---|
667 | shapeList = diagram.GetShapeList() |
---|
668 | |
---|
669 | # store the flag for future apps |
---|
670 | self.GetDisplayCanvas().SetMaintainAspectRatio( menuItem.IsChecked() ) |
---|
671 | |
---|
672 | # change all the current apps' flags |
---|
673 | for s in shapeList: |
---|
674 | if ( s.__class__.__name__ == MyRectangleShape.__name__ ): |
---|
675 | s.SetMaintainAspectRatio( self.displayCanvas.GetMaintainAspectRatio() ) |
---|
676 | |
---|
677 | |
---|
678 | # Menu -> Performance -> Logging |
---|
679 | def OnLogPerfData(self, menuItem): |
---|
680 | self.sageData.setLoggingFlag( menuItem.IsChecked() ) |
---|
681 | |
---|
682 | |
---|
683 | # Menu -> Performance -> Receive Performance Data |
---|
684 | def OnReceivePerfData(self, menuItem): |
---|
685 | self.GetDisplayCanvas().ReceivePerformanceData( menuItem.IsChecked() ) |
---|
686 | |
---|
687 | |
---|
688 | # (AKS 2005-05-07) |
---|
689 | def OnShowRenderBW( self, menuItem ): |
---|
690 | if ( menuItem.IsChecked() == True ): |
---|
691 | self.__sgRenderBWGraph = Graph.SingleGraph( |
---|
692 | self, self.sageData.getRenderBandwidthGraph(), "Totals: Render Bandwidth", |
---|
693 | "Totals: Render Bandwidth", "Bandwidth (Mbps)", self.I_RENDER_BW_GRAPH ) |
---|
694 | self.__sgRenderBWGraph.registerShutdownCallback( self.removeGraph ) |
---|
695 | self.gmGraphManager.AddToUpdateList(self.__sgRenderBWGraph) |
---|
696 | else: |
---|
697 | self.gmGraphManager.RemoveFromUpdateList(self.__sgRenderBWGraph) |
---|
698 | self.__sgRenderBWGraph.manualShutdown() |
---|
699 | |
---|
700 | # end if |
---|
701 | |
---|
702 | |
---|
703 | # (AKS 2005-05-07) |
---|
704 | def OnShowDisplayBW( self, menuItem ): |
---|
705 | if ( menuItem.IsChecked() == True ): |
---|
706 | self.__sgDisplayBWGraph = Graph.SingleGraph( |
---|
707 | self, self.sageData.getDisplayBandwidthGraph(), "Totals: Display Bandwidth", |
---|
708 | "Totals: Display Bandwidth", "Bandwidth (Mbps)", self.I_DISPLAY_BW_GRAPH ) |
---|
709 | self.__sgDisplayBWGraph.registerShutdownCallback( self.removeGraph ) |
---|
710 | self.gmGraphManager.AddToUpdateList(self.__sgDisplayBWGraph) |
---|
711 | else: |
---|
712 | self.gmGraphManager.RemoveFromUpdateList(self.__sgDisplayBWGraph) |
---|
713 | self.__sgDisplayBWGraph.manualShutdown() |
---|
714 | |
---|
715 | # end if |
---|
716 | |
---|
717 | |
---|
718 | # (AKS 2005-05-07) |
---|
719 | def removeGraph( self, iGraphID ): |
---|
720 | if ( iGraphID == self.I_RENDER_BW_GRAPH ): |
---|
721 | self.gmGraphManager.RemoveFromUpdateList(self.__sgRenderBWGraph) |
---|
722 | self.__sgRenderBWGraph.manualShutdown() |
---|
723 | self.UncheckMenuItem(RENDER_BW) |
---|
724 | self.GetFrame().UpdateMenuItems(self.GetCurrentMenuItems()) #we must manually update the frame |
---|
725 | |
---|
726 | elif ( iGraphID == self.I_DISPLAY_BW_GRAPH ): |
---|
727 | self.gmGraphManager.RemoveFromUpdateList(self.__sgDisplayBWGraph) |
---|
728 | self.__sgDisplayBWGraph.manualShutdown() |
---|
729 | self.UncheckMenuItem(DISPLAY_BW) |
---|
730 | self.GetFrame().UpdateMenuItems(self.GetCurrentMenuItems()) #we must manually update the frame |
---|
731 | |
---|
732 | # end if; else, do nothing |
---|
733 | |
---|
734 | |
---|
735 | |
---|
736 | |
---|
737 | |
---|
738 | |
---|
739 | |
---|
740 | ############################################################################ |
---|
741 | # |
---|
742 | # CLASS: SAGEuiFrame |
---|
743 | # |
---|
744 | # DESCRIPTION: It describes the main frame that holds all the other controls. |
---|
745 | # It creates the two main canvases for the UI: |
---|
746 | # - the tiled display representation (upper canvas) |
---|
747 | # - the area for displaying app info (lower canvas) |
---|
748 | # |
---|
749 | # DATE: February 28, 2005 |
---|
750 | # |
---|
751 | ############################################################################ |
---|
752 | |
---|
753 | class SAGEuiFrame(wx.Frame): |
---|
754 | |
---|
755 | def __init__(self, sageGate, usersClient, firstMachineId, loadState, title=""): |
---|
756 | self.loadState = loadState # for automatically loading a saved display state upon startup |
---|
757 | |
---|
758 | if prefs.visual.GetFramePos() != None: |
---|
759 | position = prefs.visual.GetFramePos() |
---|
760 | else: |
---|
761 | position = (50,0) |
---|
762 | if "__WXMAC__" in wx.PlatformInfo: |
---|
763 | position = (50,50) |
---|
764 | |
---|
765 | wx.Frame.__init__(self, None, -1, "SAGE UI", |
---|
766 | pos=position, |
---|
767 | size=(500,300), |
---|
768 | style=wx.DEFAULT_FRAME_STYLE )#| wx.FULL_REPAINT_ON_RESIZE) |
---|
769 | |
---|
770 | # this is the only common thing between the pages |
---|
771 | self.usersClient = usersClient |
---|
772 | self.usersData = getUsersData() |
---|
773 | |
---|
774 | # so that we can kill SAGE from the UI |
---|
775 | self.sageGate = sageGate |
---|
776 | |
---|
777 | # make the menu |
---|
778 | self.SetMenuBar( self.CreateMenu() ) |
---|
779 | |
---|
780 | # bind any events we need |
---|
781 | self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) |
---|
782 | self.Bind(wx.EVT_SIZE, self.OnSize) |
---|
783 | self.Bind(wx.EVT_MOVE, self.OnMove) |
---|
784 | self.Bind(wx.EVT_ACTIVATE, self.OnActivated) |
---|
785 | |
---|
786 | # This creates some pens and brushes that the OGL library uses. |
---|
787 | # It should be called after the app object has been created, but |
---|
788 | # before OGL is used. |
---|
789 | ogl.OGLInitialize() |
---|
790 | |
---|
791 | self.notebook = DisplayNotebook(self) |
---|
792 | tempHash = {} #extract the values of all the checkable menu items and send them to the notebook as default values |
---|
793 | for key, mi in self.menuCheckItems.iteritems(): |
---|
794 | tempHash[key] = mi.IsChecked() |
---|
795 | self.notebook.SetDefaultMenuCheckItems(tempHash) |
---|
796 | self.notebook.AddPage(DisplayPage(self.notebook, sageGate, usersClient, firstMachineId, title), title.split('@')[1], True) |
---|
797 | self.usersPanel = UsersPanel(self, usersClient) |
---|
798 | #self.Show() < -- this is done in DisplayCanvas.OnSAGEDisplaysConnection() (canvases.py) |
---|
799 | |
---|
800 | |
---|
801 | #---------------------------------------------------------------------- |
---|
802 | |
---|
803 | |
---|
804 | def CreateMenu(self): |
---|
805 | self.menuBar = wx.MenuBar() |
---|
806 | |
---|
807 | # File |
---|
808 | menuFile = wx.Menu() |
---|
809 | |
---|
810 | miFileNew = menuFile.Append(-1, "&New Connection\tCtrl-N", "Connect to another display") |
---|
811 | self.Bind(wx.EVT_MENU, self.OnNewConnection, miFileNew) |
---|
812 | |
---|
813 | miFileClose = menuFile.Append(-1, "Close Connection\tCtrl-W", "Close connection with this display") |
---|
814 | self.Bind(wx.EVT_MENU, self.OnCloseConnection, miFileClose) |
---|
815 | |
---|
816 | menuFile.AppendSeparator() |
---|
817 | |
---|
818 | miFileSave = menuFile.Append(SAVE_STATE, "&Save Session\tCtrl-S", "Save current session") |
---|
819 | self.Bind(wx.EVT_MENU, self.OnMenuItem, miFileSave) |
---|
820 | |
---|
821 | miFileLoad = menuFile.Append(LOAD_STATE, "&Load Session\tCtrl-O", "Load previously saved session") |
---|
822 | self.Bind(wx.EVT_MENU, self.OnMenuItem, miFileLoad) |
---|
823 | |
---|
824 | menuFile.AppendSeparator() |
---|
825 | |
---|
826 | miFileSAGEShutdown = menuFile.Append(SAGE_SHUTDOWN, "Shutdown SAGE", "Shutdown SAGE") |
---|
827 | self.Bind(wx.EVT_MENU, self.OnMenuItem, miFileSAGEShutdown) |
---|
828 | |
---|
829 | menuFile.AppendSeparator() |
---|
830 | |
---|
831 | miFileExit = menuFile.Append(-1, "E&xit\tAlt-X", "Exit demo") |
---|
832 | self.Bind(wx.EVT_MENU, self.OnExitButton, miFileExit) |
---|
833 | |
---|
834 | self.menuBar.Append(menuFile, "&File") |
---|
835 | |
---|
836 | |
---|
837 | # Options |
---|
838 | menuOptions = wx.Menu() |
---|
839 | |
---|
840 | self.miOptionsLibrary = menuOptions.Append(-1, "&File Library\tF2" , "Show File Library") |
---|
841 | self.Bind(wx.EVT_MENU, self.OnShowLibrary, self.miOptionsLibrary) |
---|
842 | |
---|
843 | self.miOptionsAspectRatio = menuOptions.Append(ASPECT_RATIO, "Preserve Aspect Ratio" , "Maintain Aspect Ratio?", wx.ITEM_CHECK) |
---|
844 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miOptionsAspectRatio) |
---|
845 | self.miOptionsAspectRatio.Check(prefs.visual.GetKeepAspectRatio()) |
---|
846 | |
---|
847 | self.miOptionsSAGEColor = menuOptions.Append(SAGE_COLOR, "Change SAGE Background Color" , "Change SAGE Background Color?") |
---|
848 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miOptionsSAGEColor) |
---|
849 | |
---|
850 | self.miOptionsShowChat = menuOptions.Append(-1, "Show Chat Window", "", wx.ITEM_CHECK) |
---|
851 | if self.usersClient.connected: |
---|
852 | if prefs.visual.IsChatShown() != None: |
---|
853 | self.miOptionsShowChat.Check(prefs.visual.IsChatShown()) |
---|
854 | self.Bind(wx.EVT_MENU, self.OnChatShow, self.miOptionsShowChat) |
---|
855 | |
---|
856 | self.miOptionsOutput = menuOptions.Append(-1, "Show Console Output", "", wx.ITEM_CHECK) |
---|
857 | self.Bind(wx.EVT_MENU, self.OnShowConsoleOutput, self.miOptionsOutput) |
---|
858 | self.miOptionsOutput.Check(False) |
---|
859 | |
---|
860 | self.miOptionsLauncherAdmin = menuOptions.Append(-1, "AppLauncher Admin" , "AppLauncher Admin Tool") |
---|
861 | self.Bind(wx.EVT_MENU, self.OnLauncherAdmin, self.miOptionsLauncherAdmin) |
---|
862 | |
---|
863 | self.menuBar.Append(menuOptions, "&Options") |
---|
864 | |
---|
865 | |
---|
866 | # Performance |
---|
867 | menuPerformance = wx.Menu() |
---|
868 | |
---|
869 | self.miPerformancePerformance = menuPerformance.Append(PERF_DATA, "Receive Performance Data", "", wx.ITEM_CHECK) |
---|
870 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miPerformancePerformance) |
---|
871 | self.miPerformancePerformance.Check(prefs.visual.GetReceivePerformanceData()) |
---|
872 | |
---|
873 | self.miPerformanceLogging = menuPerformance.Append(LOG_PERF_DATA, "Log Performance Data" , "Permit performance data logging to a file.", wx.ITEM_CHECK) |
---|
874 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miPerformanceLogging) |
---|
875 | self.miPerformanceLogging.Check(True) |
---|
876 | |
---|
877 | self.miPerformanceRenderBWGraph = menuPerformance.Append(RENDER_BW, "Show Total Rendering Bandwidth" , "Show the graph for Total Rendering Bandwidth.", wx.ITEM_CHECK) |
---|
878 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miPerformanceRenderBWGraph) |
---|
879 | self.miPerformanceRenderBWGraph.Check(False) |
---|
880 | |
---|
881 | self.miPerformanceDisplayBWGraph = menuPerformance.Append(DISPLAY_BW, "Show Total Display Bandwidth" , "Show the graph for Total Display Bandwidth.", wx.ITEM_CHECK) |
---|
882 | self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miPerformanceDisplayBWGraph) |
---|
883 | self.miPerformanceDisplayBWGraph.Check(False) |
---|
884 | |
---|
885 | self.menuBar.Append(menuPerformance, "&Performance") |
---|
886 | |
---|
887 | |
---|
888 | # Session |
---|
889 | ## menuSession = wx.Menu() |
---|
890 | ## self.miSessionRecord = menuSession.Append(RECORD_SESSION, "Record Session" , "", wx.ITEM_CHECK) |
---|
891 | ## self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miSessionRecord) |
---|
892 | ## self.miSessionRead = menuSession.Append(READ_SESSION, "Read Session" , "") |
---|
893 | ## self.Bind(wx.EVT_MENU, self.OnMenuItem, self.miSessionRead) |
---|
894 | ## self.menuBar.Append(menuSession, "&Session") |
---|
895 | |
---|
896 | |
---|
897 | # Help |
---|
898 | menuHelp = wx.Menu() |
---|
899 | self.miAbout = menuHelp.Append(-1, "About SAGE UI\tF1", "") |
---|
900 | self.Bind(wx.EVT_MENU, self.OnAbout, self.miAbout) |
---|
901 | self.menuBar.Append(menuHelp, "&Help") |
---|
902 | |
---|
903 | |
---|
904 | # store all the menu check items that are display dependent |
---|
905 | self.menuCheckItems = {} |
---|
906 | self.menuCheckItems[ASPECT_RATIO] = self.miOptionsAspectRatio#.IsChecked() |
---|
907 | self.menuCheckItems[PERF_DATA] = self.miPerformancePerformance#.IsChecked() |
---|
908 | self.menuCheckItems[LOG_PERF_DATA] = self.miPerformanceLogging#.IsChecked() |
---|
909 | self.menuCheckItems[RENDER_BW] = self.miPerformanceRenderBWGraph#.IsChecked() |
---|
910 | self.menuCheckItems[DISPLAY_BW] = self.miPerformanceDisplayBWGraph#.IsChecked() |
---|
911 | #self.menuCheckItems[RECORD_SESSION] = self.miSessionRecord#.IsChecked() |
---|
912 | |
---|
913 | |
---|
914 | # bind keystrokes to the frame |
---|
915 | self.Bind(wx.EVT_KEY_DOWN, self.OnKeyEvent) |
---|
916 | |
---|
917 | return self.menuBar |
---|
918 | |
---|
919 | |
---|
920 | #---------------------------------------------------------------------- |
---|
921 | |
---|
922 | def UpdateMenuItems(self, newMenuItems): |
---|
923 | # update the menu with the specific menu items from the current page |
---|
924 | for k,mi in self.menuCheckItems.iteritems(): |
---|
925 | mi.Check(newMenuItems[k]) |
---|
926 | self.menuBar.Refresh() |
---|
927 | |
---|
928 | |
---|
929 | def GetUsersPanel(self): |
---|
930 | return self.usersPanel |
---|
931 | |
---|
932 | |
---|
933 | |
---|
934 | ## def Reconnect(self): |
---|
935 | ## dlg = wx.MessageDialog(None, "Connection to SAGE has closed.\nYou will have to reconnect when SAGE becomes available again.", "SAGE Connection Closed", style=wx.OK) |
---|
936 | ## #btn = wx.Button(dlg, 999, "Reconnect") |
---|
937 | ## if dlg.ShowModal() == wx.ID_OK: |
---|
938 | ## self.notebook.CloseCurrentPage(closeUI=False) |
---|
939 | ## sageGate = SageGate() |
---|
940 | ## cd = ConnectionDialog(sageGate, self.usersClient, firstConnection=False) |
---|
941 | ## title = self.usersData.GetMyUsername() + " @ " + cd.hostname |
---|
942 | ## self.notebook.AddPage(DisplayPage(self.notebook, sageGate, self.usersData, self.usersClient, cd.host, title), title, True) |
---|
943 | |
---|
944 | ## dlg.Destroy() |
---|
945 | |
---|
946 | #---------------------------------------------------------------------- |
---|
947 | # EVENT HANDLERS |
---|
948 | #---------------------------------------------------------------------- |
---|
949 | |
---|
950 | |
---|
951 | def OnActivated(self, evt): |
---|
952 | """ happens only when the frame is shown because we unbind the event below """ |
---|
953 | self.Disconnect(-1,-1,wx.wxEVT_ACTIVATE) |
---|
954 | |
---|
955 | # load the saved state of the display if requested |
---|
956 | if self.loadState: |
---|
957 | sageData = self.notebook.GetPage(self.notebook.GetSelection()).sageData |
---|
958 | t = Timer(4, sageData.loadState, [self.loadState]) |
---|
959 | t.start() |
---|
960 | |
---|
961 | |
---|
962 | def OnSize(self, event=None): |
---|
963 | if "__WXMSW__" in wx.PlatformInfo: #to bypass windows' deferred resizing crap |
---|
964 | def doResize(): |
---|
965 | #self.notebook.OnSize(event) |
---|
966 | if self.usersClient.connected: |
---|
967 | self.usersPanel.OnFrameMove() |
---|
968 | wx.CallAfter(doResize) |
---|
969 | else: |
---|
970 | #self.notebook.OnSize(event) |
---|
971 | if self.usersClient.connected: |
---|
972 | self.usersPanel.OnFrameMove() |
---|
973 | event.Skip() #resize the notebook |
---|
974 | |
---|
975 | |
---|
976 | def OnMove(self, event): |
---|
977 | if self.usersClient.connected: |
---|
978 | self.usersPanel.OnFrameMove() |
---|
979 | |
---|
980 | |
---|
981 | def OnKeyEvent(self, event): |
---|
982 | #self.notebook.OnKeyEvent(event) |
---|
983 | event.Skip() |
---|
984 | |
---|
985 | |
---|
986 | def OnCloseFrame(self, evt): |
---|
987 | # save the visual preferences (frame size, position, chat panel) |
---|
988 | prefs.visual.SetAll(self.GetClientSize(), self.GetPosition(), self.usersPanel.IsShown()) |
---|
989 | |
---|
990 | # close all connections to SAGEs |
---|
991 | self.notebook.OnClose(evt) |
---|
992 | |
---|
993 | #if self.usersClient.connected: |
---|
994 | self.usersPanel.Disconnect() |
---|
995 | |
---|
996 | |
---|
997 | # (AKS 2005-05-06) Delete all temp files of size 0 created when ignoring logging function |
---|
998 | # Instead of allowing for the possibility that new apps start and their respective log |
---|
999 | # files are not made causing an error, allow the new app's file to be created but do |
---|
1000 | # not write to it. Later, delete the 0-size file. |
---|
1001 | try: #in case the file and directory permissions are not right |
---|
1002 | listFilenames = os.listdir( DATA_DIR ) |
---|
1003 | |
---|
1004 | # remove files that were never written or minimally written (i.e. <= 1K) |
---|
1005 | for stFilename in listFilenames: |
---|
1006 | stFilename = opj(DATA_DIR, stFilename) |
---|
1007 | stCompleteFilename = os.path.normcase( stFilename ) |
---|
1008 | structStats = os.stat( stCompleteFilename ) |
---|
1009 | |
---|
1010 | if ( structStats.st_size <= 1024 ): |
---|
1011 | os.remove( stCompleteFilename ) |
---|
1012 | print "Deleting", stCompleteFilename |
---|
1013 | # end if |
---|
1014 | # end loop |
---|
1015 | except: |
---|
1016 | pass |
---|
1017 | |
---|
1018 | self.DestroyChildren() |
---|
1019 | self.Destroy() |
---|
1020 | wx.GetApp().ExitMainLoop() |
---|
1021 | |
---|
1022 | |
---|
1023 | #---------------------------------------------------------------------- |
---|
1024 | # MENU HANDLERS |
---|
1025 | #---------------------------------------------------------------------- |
---|
1026 | |
---|
1027 | |
---|
1028 | ### this catches all the menu events that are display specific |
---|
1029 | def OnMenuItem(self, event): |
---|
1030 | self.notebook.OnMenuEvent(event) |
---|
1031 | |
---|
1032 | # Menu -> File -> Exit |
---|
1033 | def OnExitButton(self, evt): |
---|
1034 | self.Close(True) |
---|
1035 | |
---|
1036 | # Menu -> Help -> About |
---|
1037 | def OnAbout(self, evt): |
---|
1038 | AboutDialog(self) |
---|
1039 | |
---|
1040 | # Menu -> Chat -> Show |
---|
1041 | def OnChatShow(self, evt): |
---|
1042 | if self.miOptionsShowChat.IsChecked(): |
---|
1043 | self.usersPanel.Show() |
---|
1044 | else: |
---|
1045 | self.usersPanel.Hide() |
---|
1046 | |
---|
1047 | # Menu -> Options -> AppLauncher Admin |
---|
1048 | def OnLauncherAdmin(self, evt): |
---|
1049 | lAdmin.MainFrame(getSAGEServer()) |
---|
1050 | |
---|
1051 | # Menu -> Options -> Show File Library |
---|
1052 | def OnShowLibrary(self, evt): |
---|
1053 | self.notebook.GetPage(self.notebook.GetSelection()).displayCanvas.ShowLibrary() |
---|
1054 | |
---|
1055 | # Menu -> Options -> Show Console Output |
---|
1056 | def OnShowConsoleOutput(self, evt): |
---|
1057 | if self.miOptionsOutput.IsChecked(): |
---|
1058 | wx.GetApp().RestoreStdio() |
---|
1059 | else: |
---|
1060 | try: |
---|
1061 | wx.GetApp().RedirectStdio( LOG_FILE ) |
---|
1062 | except IOError: |
---|
1063 | message = "\nCould not redirect output to a log file. Probably because of file permissions." |
---|
1064 | message = message+"\nInstead, output will be printed in the console" |
---|
1065 | dlg = wx.MessageDialog(self, message, "Error redirecting output", style=wx.OK) |
---|
1066 | dlg.ShowModal() |
---|
1067 | dlg.Destroy() |
---|
1068 | wx.GetApp().RestoreStdio() |
---|
1069 | self.miOptionsOutput.Check(True) |
---|
1070 | |
---|
1071 | # Menu -> File -> New |
---|
1072 | def OnNewConnection(self, event): |
---|
1073 | sageGate = SageGate() |
---|
1074 | |
---|
1075 | # initialize the dialog and show it. If it returns False, the user pressed quit |
---|
1076 | # otherwise the connection to SAGE was successful and the username was accepted |
---|
1077 | # so create the tab, show it and continue with execution |
---|
1078 | cd = ConnectionDialog(sageGate, self.usersClient, firstConnection=False) |
---|
1079 | if cd.ShowDialog(): |
---|
1080 | title = self.usersData.GetMyUsername() + " @ " + cd.GetMachine().GetName() |
---|
1081 | self.notebook.AddPage(DisplayPage(self.notebook, sageGate, self.usersClient, cd.GetMachine().GetId(), title), cd.GetMachine().GetName(), True) |
---|
1082 | |
---|
1083 | return True |
---|
1084 | |
---|
1085 | # Menu -> File -> Close |
---|
1086 | def OnCloseConnection(self, event): |
---|
1087 | self.notebook.CloseCurrentPage() |
---|
1088 | |
---|
1089 | |
---|
1090 | ############################################################################ |
---|
1091 | # |
---|
1092 | # CLASS: AboutDialog |
---|
1093 | # |
---|
1094 | # DESCRIPTION: It just displays the about box. |
---|
1095 | # |
---|
1096 | # DATE: June 2005 |
---|
1097 | # |
---|
1098 | ############################################################################ |
---|
1099 | |
---|
1100 | class AboutDialog(wx.Dialog): |
---|
1101 | def __init__(self, parent): |
---|
1102 | titleText = "SAGE Graphical User Interface" |
---|
1103 | contentText = "version " + str(VERSION) + "\n\n\n" + "For more information go to:" |
---|
1104 | linkUrl = "www.evl.uic.edu/cavern/sage" |
---|
1105 | link2Text = "Direct questions or comments to:" |
---|
1106 | link2Url = "www.evl.uic.edu/cavern/forum" |
---|
1107 | |
---|
1108 | wx.Dialog.__init__(self, parent, -1, "About SAGE UI", style=wx.CLOSE_BOX) |
---|
1109 | okBtn = wx.Button(self, wx.ID_OK, "OK") |
---|
1110 | title = wx.StaticText(self, -1, titleText, style=wx.ALIGN_CENTER) |
---|
1111 | title.SetFont(BoldFont(title)) |
---|
1112 | |
---|
1113 | if use_hyperlink: |
---|
1114 | link = hyperlink.HyperLinkCtrl(self, wx.ID_ANY, linkUrl, URL="http://"+linkUrl) |
---|
1115 | link.AutoBrowse(True) |
---|
1116 | link2 = hyperlink.HyperLinkCtrl(self, wx.ID_ANY, link2Url, URL="http://"+link2Url) |
---|
1117 | link2.AutoBrowse(True) |
---|
1118 | else: |
---|
1119 | link = wx.StaticText(self, -1, linkUrl, style=wx.ALIGN_CENTER) |
---|
1120 | link2 = wx.StaticText(self, -1, link2Url, style=wx.ALIGN_CENTER) |
---|
1121 | |
---|
1122 | text = wx.StaticText(self, -1, contentText, style=wx.ALIGN_CENTER) |
---|
1123 | text2 = wx.StaticText(self, -1, link2Text, style=wx.ALIGN_CENTER) |
---|
1124 | |
---|
1125 | |
---|
1126 | sizer = wx.BoxSizer(wx.VERTICAL) |
---|
1127 | sizer.AddSpacer((20, 20)) |
---|
1128 | sizer.Add(title, 0, wx.ALIGN_CENTER | wx.RIGHT | wx.LEFT, border=15) |
---|
1129 | sizer.Add(text, 0, wx.ALIGN_CENTER) |
---|
1130 | sizer.Add(link, 0, wx.ALIGN_CENTER) |
---|
1131 | sizer.AddSpacer((15,15)) |
---|
1132 | sizer.Add(text2, 0, wx.ALIGN_CENTER) |
---|
1133 | sizer.Add(link2, 0, wx.ALIGN_CENTER) |
---|
1134 | sizer.AddSpacer((15,15)) |
---|
1135 | sizer.Add(okBtn, 0, wx.ALIGN_CENTER) |
---|
1136 | sizer.AddSpacer((5,5)) |
---|
1137 | self.SetSizer(sizer) |
---|
1138 | self.Fit() |
---|
1139 | self.ShowModal() |
---|
1140 | self.Destroy() |
---|
1141 | |
---|
1142 | |
---|
1143 | |
---|
1144 | |
---|
1145 | |
---|
1146 | ############################################################################ |
---|
1147 | # |
---|
1148 | # CLASS: SAGEui |
---|
1149 | # |
---|
1150 | # DESCRIPTION: This is a starting point for wx. It extends wx.App and |
---|
1151 | # creates the SAGEuiFrame to place the canvases in. It also displays |
---|
1152 | # the initial dialogs for the User profile and SAGE connection. |
---|
1153 | # |
---|
1154 | # DATE: October 2004 |
---|
1155 | # |
---|
1156 | ############################################################################ |
---|
1157 | |
---|
1158 | class SAGEui(wx.App): |
---|
1159 | def __init__(self, usersServerIP, usersServerPort, verbose, autologinMachine, loadState): |
---|
1160 | self.usersServerIP = usersServerIP |
---|
1161 | self.usersServerPort = usersServerPort |
---|
1162 | self.autologinMachine = autologinMachine |
---|
1163 | self.loadState = loadState |
---|
1164 | |
---|
1165 | if verbose: |
---|
1166 | wx.App.__init__(self, redirect=False) |
---|
1167 | else: |
---|
1168 | try: |
---|
1169 | wx.App.__init__(self, redirect=True, filename=LOG_FILE) |
---|
1170 | except IOError: |
---|
1171 | print "\nError: Could not redirect output to a log file. Possibly because of file permissions." |
---|
1172 | print "Instead, output will be printed in the console" |
---|
1173 | wx.App.__init__(self, redirect=False) |
---|
1174 | |
---|
1175 | def Skip( self, evt ): |
---|
1176 | pass |
---|
1177 | |
---|
1178 | def OnInit(self): |
---|
1179 | wx.Log_SetActiveTarget(wx.LogStderr()) |
---|
1180 | self.SetAssertMode(assertMode) |
---|
1181 | socket.setdefaulttimeout(2) |
---|
1182 | |
---|
1183 | # initialize network communication with: |
---|
1184 | # sageGate... the connection to SAGE |
---|
1185 | # usersClient...the connection to UsersServer |
---|
1186 | # usersData... the datastructure used in conjunction with usersClient |
---|
1187 | usersData = UsersDatastructure() |
---|
1188 | setUsersData(usersData) #store it in a global scope |
---|
1189 | sageGate = SageGate() |
---|
1190 | usersClient = UsersClient() |
---|
1191 | |
---|
1192 | # initialize the dialog and show it. If it returns False, the user pressed quit |
---|
1193 | # otherwise the connection to SAGE was successful and the username was accepted |
---|
1194 | # so show the frame and continue with execution |
---|
1195 | cd = ConnectionDialog(sageGate, usersClient, self.usersServerIP, usersServerPort = self.usersServerPort, autologinMachine=self.autologinMachine) |
---|
1196 | if cd.ShowDialog(): |
---|
1197 | title = usersData.GetMyUsername() + " @ " + cd.GetMachine().GetName() |
---|
1198 | self.frame = SAGEuiFrame(sageGate, usersClient, cd.GetMachine().GetId(), self.loadState, title) |
---|
1199 | self.SetTopWindow(self.frame) |
---|
1200 | else: |
---|
1201 | usersClient.Disconnect() |
---|
1202 | sys.exit(0) |
---|
1203 | |
---|
1204 | return True |
---|
1205 | |
---|
1206 | |
---|
1207 | |
---|
1208 | |
---|
1209 | ############################################################################ |
---|
1210 | # |
---|
1211 | # Main entry point for the application |
---|
1212 | # |
---|
1213 | ############################################################################ |
---|
1214 | |
---|
1215 | ### sets up the parser for the command line options |
---|
1216 | def get_commandline_options(): |
---|
1217 | parser = optparse.OptionParser() |
---|
1218 | |
---|
1219 | h = "if set, prints output to console, otherwise to ~/.sageConfig/sageui/output_log.txt" |
---|
1220 | parser.add_option("-v", "--verbose", action="store_true", help=h, dest="verbose", default=False) |
---|
1221 | |
---|
1222 | h = "which sage server / connection manager to use (default is sage.sl.startap.net)" |
---|
1223 | parser.add_option("-s", "--server", dest="server", help=h, default="sage.sl.startap.net") |
---|
1224 | |
---|
1225 | h = "change the port number of the sage server (default is 15558)" |
---|
1226 | parser.add_option("-p", "--port", help=h, type="int", dest="port", default=15558) |
---|
1227 | |
---|
1228 | h = "override which application launcher to use (by default it looks for one on the same machine as sage master. Specify as machine:port)" |
---|
1229 | parser.add_option("-l", "--launcher", dest="launcher", help=h, default="") |
---|
1230 | |
---|
1231 | h = "try autologin to this sage name (what fsManager reports to connection manager from fsManager.conf )" |
---|
1232 | parser.add_option("-a", "--autologin", dest="autologin", help=h, default=None) |
---|
1233 | |
---|
1234 | h = "upon startup load this saved state (write saved state name from saved-states directory)" |
---|
1235 | parser.add_option("-o", "--load_state", dest="load_state", help=h, default=None) |
---|
1236 | |
---|
1237 | h = "perform autosave?" |
---|
1238 | parser.add_option("-t", "--autosave", action="store_true", help=h, dest="autosave", default=False) |
---|
1239 | |
---|
1240 | return parser.parse_args() |
---|
1241 | |
---|
1242 | |
---|
1243 | |
---|
1244 | def main(argv): |
---|
1245 | os.chdir(sys.path[0]) # change to the folder where script is running |
---|
1246 | global autosave |
---|
1247 | verbose = False |
---|
1248 | usersServerIP = "74.114.99.36" |
---|
1249 | usersServerPort = 15558 |
---|
1250 | |
---|
1251 | # parse the command line params |
---|
1252 | (options, args) = get_commandline_options() |
---|
1253 | verbose = options.verbose |
---|
1254 | usersServerPort = options.port |
---|
1255 | usersServerIP = options.server |
---|
1256 | appLauncher = options.launcher |
---|
1257 | autologinMachine = options.autologin |
---|
1258 | loadState = options.load_state |
---|
1259 | autosave = options.autosave |
---|
1260 | |
---|
1261 | # set the overridden app launcher |
---|
1262 | if appLauncher != "": |
---|
1263 | setAppLauncher(appLauncher) |
---|
1264 | |
---|
1265 | # set the global variable for the SAGE SERVER |
---|
1266 | setSAGEServer(usersServerIP) |
---|
1267 | |
---|
1268 | # print some information about the system currently running |
---|
1269 | print "\nCurrently running:\n--------------------------" |
---|
1270 | print "SAGE UI version: ", VERSION |
---|
1271 | print "Python version: ", string.split(sys.version, "(", 1)[0] |
---|
1272 | print "wxPython version: "+str(wx.VERSION[0])+"."+str(wx.VERSION[1])+"."+str(wx.VERSION[2])+"."+str(wx.VERSION[3])+"\n" |
---|
1273 | |
---|
1274 | # read all the preferences |
---|
1275 | import preferences as prefs |
---|
1276 | prefs.readAllPreferences() |
---|
1277 | |
---|
1278 | app = SAGEui(usersServerIP, usersServerPort, verbose, autologinMachine, loadState) |
---|
1279 | app.MainLoop() |
---|
1280 | |
---|
1281 | |
---|
1282 | |
---|
1283 | |
---|
1284 | if __name__ == '__main__': |
---|
1285 | import sys, os |
---|
1286 | main(['', os.path.basename(sys.argv[0])] + sys.argv[1:]) |
---|
1287 | |
---|
1288 | |
---|
1289 | |
---|
1290 | |
---|
1291 | |
---|
1292 | |
---|