source: trunk/src/testing/ui/SAGESession.py @ 4

Revision 4, 14.4 KB checked in by ajaworski, 13 years ago (diff)

Added modified SAGE sources

Line 
1############################################################################
2#
3# SAGE UI - A Graphical User Interface for SAGE
4# Copyright (C) 2005 Electronic Visualization Laboratory,
5# University of Illinois at Chicago
6#
7# All rights reserved.
8#
9# Redistribution and use in source and binary forms, with or without
10# modification, are permitted provided that the following conditions are met:
11#
12#  * Redistributions of source code must retain the above copyright
13#    notice, this list of conditions and the following disclaimer.
14#  * Redistributions in binary form must reproduce the above
15#    copyright notice, this list of conditions and the following disclaimer
16#    in the documentation and/or other materials provided with the distribution.
17#  * Neither the name of the University of Illinois at Chicago nor
18#    the names of its contributors may be used to endorse or promote
19#    products derived from this software without specific prior written permission.
20#
21# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32#
33# Direct questions, comments etc about SAGE UI to www.evl.uic.edu/cavern/forum
34#
35# Author: Ratko Jagodic
36#       
37############################################################################
38
39
40import os.path
41import os
42import time
43from math import ceil
44import string
45import wx
46from Mywx import MyBitmapButton
47from threading import Timer
48from globals import *
49
50# some global vars
51hashRecordingWords = {}  #RJ 2005-04-07
52hashRecordingWords[1001] = "exec"
53hashRecordingWords[1002] = "kill"
54hashRecordingWords[1003] = "move"
55hashRecordingWords[1004] = "resize"
56hashRecordingWords[1007] = "bg"
57hashRecordingWords[1008] = "depth"
58hashRecordingWords[1100] = "shutdown"
59
60
61
62############################################################################
63#
64#  CLASS: SessionRecorder
65#
66#  DESCRIPTION: A class for recording user actions to a file in a directory.
67#               To use just create an instance of this class and call
68#               RecordAction(code, data) to write a line to a file. "Code" is
69#               the message code sent to SAGE and it gets mapped to a text string
70#               according to a hash defined here. "Data" is whatever you want to
71#               record in a file and it's code dependent. The last (optional)
72#               parameter to RecordAction is a boolean signifying if you want
73#               to insert a pause after an action. By default pauses are inserted
74#               in the file with the value of the elapsed time since the last
75#               recorded message. When done recording, just call Close() and
76#               destroy the object.
77#
78#  DATE:        April, 2005
79#
80############################################################################
81
82class SessionRecorder:
83
84    def __init__(self, filename):
85        #recDir = ConvertPath("./sessions/")  # a dir to write session files to
86        print "Session recording started. Session will be saved in ", filename
87        self.file = open(filename, "w")
88       
89        self.prevTime = 0
90       
91
92
93    def RecordAction(self, code, data, insertPause = True):
94        # only record if the action is supported
95        if hashRecordingWords.has_key( int(code) ):
96            #record a pause, a session always starts with a 2 second pause!
97            if self.prevTime == 0:
98                    pauseTime = 2
99            else:
100                    pauseTime = int( ceil( time.time() - self.prevTime ) )
101            self.prevTime = time.time()
102
103            # finally, do the writing
104            if insertPause:
105                self.file.write("pause " + str(pauseTime) + "\n")
106            self.file.write( hashRecordingWords[int(code)] +" "+ data+"\n" )
107
108
109    def Close(self):
110        print "Session recording stopped"
111        self.file.close()
112       
113
114       
115
116
117############################################################################
118#
119#  CLASS: SessionReader
120#
121#  DESCRIPTION: A class for reading user actions from a specified file.
122#               To use, create an instance of this class and pass in a
123#               filename to read from. Once created, keep calling ReadAction()
124#               in order to read new lines from a file. It returns data in this
125#               format: (code, data). It is your job to use this data then.
126#               This class also has options to pause, stop, speed up and slow
127#               down the playback of the session file. It does all that by
128#               playing with the length of the pause (speed), repeatedly
129#               returning pause (pausing) or returning a fake EOF (stopping
130#               prematurely).
131#
132#
133#  DATE:        April, 2005
134#
135############################################################################
136
137class SessionReader:
138
139    def __init__(self, sessionFile):           
140        if os.path.isfile(sessionFile):
141            self.file = open(sessionFile, "r")
142        else:
143            print "ERROR: Sessions file \"", sessionFile, "\" doesn't exist."
144
145        # control variables
146        self.speed = 1.0
147        self.stop = False
148        self.paused = False
149
150
151    def SetPause(self, doPause):
152        self.paused = doPause
153
154       
155
156    # reads the file one line at a time
157    # it returns a tuple in this format: (code, data)
158    # code is: -1 (paused), 0 (EOF) or **** (message code)
159    # data depends on the message
160    def ReadAction(self):
161
162        # this is how we stop session playback
163        if self.stop:
164            return (0, "EOF")
165
166        # this is how we pause playback (pretend that we encountered a
167        # pause in the file. Instead we are pausing the playback and
168        # not reading any new lines from the file
169        if self.paused:
170            return (-1, "0.5")
171       
172        if not hasattr(self, "file"):
173            return
174
175        line = self.file.readline()
176        line = string.strip(line)  # remove the newline char
177
178        # EOF case
179        if line == "":
180            self.file.close()
181            return (0, "EOF")
182       
183        # split the line into action and data
184        action, data = string.split(line, " ", 1)
185
186        # send a pause separately
187        if action == "pause":
188            data = float(data) * self.speed
189            return (-1, str(data))
190
191        # this is the actual message to be sent to SAGE
192        code = -2 # any non-existent code
193        for k, v in hashRecordingWords.iteritems():
194            if v == action:
195                code = int(k)   #extract the code           
196        return (code, data)
197
198
199
200    def GoSlower(self):
201        self.speed = self.speed * 2
202        if self.speed > 8:
203            self.speed = 8
204
205        if self.speed > 1:
206            return "1/" + str(int(self.speed))
207        elif self.speed == 1:
208            return "1"
209        elif self.speed < 1:
210            return str(int((0.125 / self.speed) * 8))
211
212
213    def GoFaster(self):
214        self.speed = float(self.speed) / 2
215       
216        if self.speed < 0.125:
217            self.speed = 0.125
218
219        if self.speed > 1:
220            return "1/" + str(int(self.speed))
221        elif self.speed == 1:
222            return "1"
223        elif self.speed < 1:
224            return str(int((0.125 / self.speed) * 8))
225       
226
227    def Stop(self):
228        self.stop = True
229       
230    def Close(self):
231        if hasattr(self, "file"):
232           self.file.close()
233       
234           
235
236
237
238############################################################################
239#
240#  CLASS: PlaybackDialog
241#
242#  DESCRIPTION: This class describes the dialog box that controls the session
243#               playback. It's a modal dialog so the UI can't be interacted
244#               with during the playback. However, you can speed up or slow
245#               down the playback, pause it or quit it prematurely.
246#
247#  NOTE:        This dialog is not neccessary for the operation of playback
248#               but it gives user more control over it.
249#
250#  DATE:        April, 2005
251#
252############################################################################
253
254class PlaybackDialog(wx.Dialog):
255
256    def __init__(self, parent, sessionReader):
257        wx.Dialog.__init__(self, parent, -1, "Session Playback")#, style = wx.STATIC_BORDER)
258        self.sessionReader = sessionReader
259        self.SetSize((250, 120))
260        self.SetBackgroundColour(wx.Colour(0, 51, 52))  #003334
261        self.SetForegroundColour(wx.Colour(255, 255, 255))
262        self.CenterOnParent(wx.BOTH)
263        self.SetFont(StandardFont())
264        wx.StaticText(self, -1, "Playback in progress...", (50, 20))
265        #self.CaptureMouse()
266        self.pause = False
267
268        # create the controls for the dialog
269        self.pauseBtn = MyBitmapButton( self, (30, 50), (30, 30), "images/pause_up.jpg", "images/pause_down.jpg", "images/pause_over.jpg")
270        self.stopBtn = MyBitmapButton( self, (70, 50), (30, 30), "images/stop_up.jpg", "images/stop_down.jpg", "images/stop_over.jpg")
271        self.slowerBtn = MyBitmapButton( self, (135, 50), (30, 30), "images/slower_up.jpg", "images/slower_down.jpg", "images/slower_over.jpg")
272        self.speedText = wx.StaticText( self, -1, "x 1", (172, 60), style = wx.ALIGN_CENTRE)
273        self.fasterBtn = MyBitmapButton( self, (208, 50), (30, 30), "images/faster_up.jpg", "images/faster_down.jpg", "images/faster_over.jpg")
274
275        # bind the events and event handlers for the buttons
276        self.pauseBtn.Bind( wx.EVT_LEFT_UP, self.OnPause)
277        self.stopBtn.Bind( wx.EVT_LEFT_UP, self.OnClose)
278        self.fasterBtn.Bind( wx.EVT_LEFT_UP, self.OnFaster)
279        self.slowerBtn.Bind( wx.EVT_LEFT_UP, self.OnSlower)
280        self.Bind( wx.EVT_CLOSE, self.OnClose)
281       
282        self.Show()
283
284   
285    def OnPause(self, evt):
286        if self.pause:
287            self.pauseBtn.SetUpBitmap("images/pause_up.jpg")
288            self.pauseBtn.SetDownBitmap("images/pause_down.jpg")
289            self.pauseBtn.SetOverBitmap("images/pause_over.jpg")
290            self.pause = False
291            self.sessionReader.SetPause(False)
292        else:
293            self.pauseBtn.SetUpBitmap("images/play_up.jpg")
294            self.pauseBtn.SetDownBitmap("images/play_down.jpg")
295            self.pauseBtn.SetOverBitmap("images/play_over.jpg")
296            self.pause = True
297            self.sessionReader.SetPause(True)
298
299        MyBitmapButton.OnLeftUp(evt.GetEventObject(), evt)
300       
301
302    # this just hides the window since sageGate will close it when it's done
303    # it also tells the sessionReader to stop the playback
304    def OnClose(self, evt):
305        self.sessionReader.Stop()
306        #self.Destroy()
307        self.Show(False) # SAGEGate is the one that destroys the window so just hide it for now
308       
309
310    # sageGate calls this function when EOF has been reached
311    def Close(self):
312        #self.ReleaseMouse()
313        self.Destroy()
314       
315
316    # this calls SessionReader and sets the speed of playback (basically it
317    # changes the multiplier of the pauses)
318    def OnFaster(self, evt):
319        MyBitmapButton.OnLeftUp(evt.GetEventObject(), evt)
320        newSpeed = self.sessionReader.GoFaster()
321        self.speedText.SetLabel("x " + newSpeed)
322
323
324    # this calls SessionReader and sets the speed of playback (basically it
325    # changes the multiplier of the pauses)
326    def OnSlower(self, evt):
327        MyBitmapButton.OnLeftUp(evt.GetEventObject(), evt)
328        newSpeed = self.sessionReader.GoSlower()
329        self.speedText.SetLabel("x " + newSpeed)
330
331
332
333def ShowLoadSessionDialog():
334    if not os.path.isdir("sessions"):  #make the directory if it doesnt exist
335        try:   #in case the file and directory permissions are not right
336            os.mkdir("sessions")   
337        except:
338            dlg = wx.FileDialog(None, "Session to load:", os.path.abspath("."), "", "*.ses", style=wx.OPEN )
339        else:
340            dlg = wx.FileDialog(None, "Session to load:", os.path.abspath("sessions"), "", "*.ses", style=wx.OPEN )
341    else:
342        dlg = wx.FileDialog(None, "Session to load:", os.path.abspath("sessions"), "", "*.ses", style=wx.OPEN )
343
344
345    if dlg.ShowModal() == wx.ID_OK:
346        if "__WXMSW__" in wx.PlatformInfo:
347            chosenFile = ConvertPath(dlg.GetPath())
348        elif not ConvertPath(dlg.GetPath())[0] == "/":
349            chosenFile = "/"+ConvertPath(dlg.GetPath())
350        else:
351            chosenFile = ConvertPath(dlg.GetPath())
352
353        #make sure that the extension is ".ses"
354        (path, ext) = os.path.splitext(chosenFile)
355        if not ext == ".ses":
356            chosenFile = chosenFile+".ses"
357
358    else:
359        chosenFile = None
360    dlg.Destroy()
361    return chosenFile
362
363
364def ShowSaveSessionDialog():
365    if not os.path.isdir("sessions"):  #make the directory if it doesnt exist
366        try:   #in case the file and directory permissions are not right
367            os.mkdir("sessions")   
368        except:
369            dlg = wx.FileDialog(None, "Save session as:", os.path.abspath("."), "", "*.ses", style=wx.SAVE | wx.OVERWRITE_PROMPT )
370        else:
371            dlg = wx.FileDialog(None, "Save session as:", os.path.abspath("sessions"), "", "*.ses", style=wx.SAVE | wx.OVERWRITE_PROMPT )
372    else:
373        dlg = wx.FileDialog(None, "Save session as:", os.path.abspath("sessions"), "", "*.ses", style=wx.SAVE | wx.OVERWRITE_PROMPT )
374
375   
376    if dlg.ShowModal() == wx.ID_OK:
377        if "__WXMSW__" in wx.PlatformInfo:
378            chosenFile = ConvertPath(dlg.GetPath())
379        elif not ConvertPath(dlg.GetPath())[0] == "/":
380            chosenFile = "/"+ConvertPath(dlg.GetPath())
381        else:
382            chosenFile = ConvertPath(dlg.GetPath())
383
384        #make sure that the extension is ".ses"
385        (path, ext) = os.path.splitext(chosenFile)
386        if not ext == ".ses":
387            chosenFile = chosenFile+".ses"
388
389        # check if we have a permission to write to this folder
390        (head, tail) = os.path.split(chosenFile)
391        if not os.access(head, os.W_OK):
392            ShowWriteFailedDialog(tail)
393            chosenFile = None
394    else:
395        chosenFile = None
396    dlg.Destroy()
397    return chosenFile
398
399
400
401
Note: See TracBrowser for help on using the repository browser.