""" This is the Access Grid Enabled version of the Basic Image viewer. It does the same thing as the Basic Image ( load an image into a window ) but will share the image over the Access Grid. Again there is no way to pan or scroll the window. This is just an attempt to get the BasicImage viewer to work over the AG. The scheme is that the script is executed and given an application URL... like this AGBasicImage appurl. There is no error handling in this version. Dave Semeraro NCSA-UIUC 2003 """ """ Modifications: - Change state from image data stored in the appobject to the name of a venue-resident image file - If a local file is opened, it is transferred to the venue - add drag and drop - add markup """ # generic and gui related imports import os import sys from wxPython.wx import * import base64 import cPickle import string # AG related imports import logging from AccessGrid.hosting.pyGlobus import Client from AccessGrid import Events from AccessGrid import EventClient from AccessGrid import Platform from AccessGrid.DataStoreClient import GetVenueDataStore ID_OPEN = wxNewId() ID_OPEN_VENUE = wxNewId() ID_EXIT = wxNewId() wildcard = "JPEG Files (*.JPG)|*.jpg|"\ "Gif Files (*.GIF)|*.gif|"\ "All Files (*.*)|*.*" class BIFileDropTarget(wxFileDropTarget): def __init__(self,window): wxFileDropTarget.__init__(self) self.window = window def OnDropFiles(self,x,y,filenames): for file in filenames: self.window.wind.LoadImageFromFilename(file) # load up the dropped file into venue self.window.AG.UploadFile(file) self.window.AG.PutData(self.window.imagedataname,os.path.split(file)[1]) # fire event telling all there is new data self.window.AG.SendEvent("NewImage",os.path.split(file)[1]) # this is an image holder object.. guess what it does. class ImageHolder: def __init__(self,image,name): self.imagename = name self.width = str(image.GetWidth()) self.height = str(image.GetHeight()) self.data = base64.encodestring(image.GetData()) # A helper class to hide all the AG ugly class AGSharedObject: def __init__(self,url,venueUrl): self.logname = "AGBasicImagelog" self.appUrl = None self.appName = "Basic Image" self.appDescription = "Access Grid Shared Image" self.appMimetype = "application/x-ag-basic-image" self.init_logging(self.logname) self.log.debug("AGSharedObject: initialize with url = %s ", url) self.appUrl = url self.log.debug("AGSharedObject: Getting application proxy") self.appProxy = Client.Handle(self.appUrl).GetProxy() self.log.debug("AGSharedObject: join application") (self.puid,self.prid) = self.appProxy.Join() self.log.debug("AGSharedObject: get data channel") (self.chanid,self.esl) = self.appProxy.GetDataChannel(self.prid) self.eventClient = EventClient.EventClient(self.prid,self.esl,self.chanid) self.eventClient.Start() self.eventClient.Send(Events.ConnectEvent(self.chanid,self.prid)) self.log.debug("AGSharedObject: connected and event channel started") self.dataStoreClient = GetVenueDataStore(venueUrl) def init_logging(self,logname): logFormat = "%(name)-17s %(asctime)s %(levelname)-5s %(message)s" self.log = logging.getLogger(logname) self.log.setLevel(logging.DEBUG) self.log.addHandler(logging.StreamHandler()) def GetData(self,dataname): self.log.debug("looking for data: %s", dataname) tim = self.appProxy.GetData(self.prid,dataname) if len(tim) > 0: return tim else: return None def PutData(self,dataname,data): self.log.debug("loading data named: %s into server",dataname) self.appProxy.SetData(self.prid,dataname,data) def RegisterEvent(self,eventname,callback): self.log.debug("Registering event %s :",eventname) self.eventClient.RegisterCallback(eventname,callback) def SendEvent(self,eventname,eventdata): self.eventClient.Send(Events.Event(eventname,self.chanid,(self.puid,eventdata))) def UploadFile(self,localFile): # Upload the file self.dataStoreClient.Upload(localFile) # Construct the venue data url file = os.path.split(localFile)[-1] venueDataUrl = os.path.join(self.dataStoreClient.uploadURL, file) return venueDataUrl def DownloadFile(self,venueDataUrl): # Construct the local filename file = os.path.split(venueDataUrl)[-1] path = os.path.join(Platform.GetTempDir(),file) # Update the local store of venue data self.dataStoreClient.LoadData() # Download the file self.dataStoreClient.Download(venueDataUrl,file) return file class ImageWindow(wxWindow): def __init__(self,parent,ID): wxWindow.__init__(self,parent,ID,style=wxNO_FULL_REPAINT_ON_RESIZE) self.imagefile = None self.parentframe = parent self.image = None self.lines = [] self.thickness = 1 self.SetColour("Red") self.SetBackgroundColour("WHITE") self.InitBuffer() EVT_IDLE(self,self.OnIdle) EVT_SIZE(self,self.OnSize) EVT_PAINT(self,self.OnPaint) # mouse event hooks EVT_LEFT_DOWN(self, self.OnLeftDown) EVT_LEFT_UP(self, self.OnLeftUp) EVT_MOTION(self, self.OnMotion) def InitBuffer(self): size = self.GetClientSize() if self.image == None: self.buffer = wxEmptyBitmap(size.width,size.height) dc = wxBufferedDC(None,self.buffer) dc.SetBackground(wxBrush(self.GetBackgroundColour())) dc.Clear() else: self.Clear() self.buffer = self.image.ConvertToBitmap() dc = wxBufferedDC(None,self.buffer) dc.SetBackground(wxBrush(self.GetBackgroundColour())) self.DrawLines(dc) self.reInitBuffer = false def LoadImageFromFilename(self,imagefilename): self.lines = [] self.ClearVenueMarkup() self.imagefile = imagefilename self.image = wxImage(self.imagefile) self.reInitBuffer = true self.Refresh(true) def SetColour(self,colour): self.colour = colour self.pen = wxPen(wxNamedColour(self.colour),self.thickness,wxSOLID) def SetThickness(self,num): self.thickness = num self.pen = wxPen(wxNamedColour(self.colour),self.thickness,wxSOLID) def LoadImage(self,animage): imgdat = base64.decodestring(animage.data) self.image = wxEmptyImage(string.atoi(animage.width),string.atoi(animage.height)) self.image.SetData(imgdat) self.reInitBuffer = true def OnIdle(self,event): if self.reInitBuffer: self.InitBuffer() self.Refresh(FALSE) def OnSize(self,event): self.reInitBuffer = true def OnPaint(self,event): dc = wxBufferedPaintDC(self,self.buffer) def OnLeftDown(self,event): self.curLine = [] self.x, self.y = event.GetPositionTuple() self.CaptureMouse() def OnLeftUp(self,event): if self.HasCapture(): self.parentframe.AG.SendEvent("NewMarks",(self.colour,self.thickness,self.curLine)) self.lines.append((self.colour, self.thickness, self.curLine)) self.UploadMarks((self.colour,self.thickness,self.curLine)) self.ReleaseMouse() def UploadMarks(self,marks): scribble = self.parentframe.AG.GetData(self.parentframe.markupdataname) if scribble == None: markup = [] else: markup = cPickle.loads(scribble) markup.append(marks) self.parentframe.AG.PutData(self.parentframe.markupdataname,cPickle.dumps(markup,0)) def ClearVenueMarkup(self): markup = [] self.parentframe.AG.PutData(self.parentframe.markupdataname,cPickle.dumps(markup,0)) def LoadMarkup(self,data): self.lines = cPickle.loads(data) def OnMotion(self,event): if event.Dragging() and event.LeftIsDown(): dc = wxBufferedDC(wxClientDC(self), self.buffer) dc.BeginDrawing() dc.SetPen(self.pen) pos = event.GetPositionTuple() coords = (self.x,self.y) + pos self.curLine.append(coords) dc.DrawLine(self.x,self.y,pos[0],pos[1]) self.x, self.y = pos dc.EndDrawing() def GetLinesData(self): return self.lines[:] def SetLinesData(self,lines): self.lines = lines[:] self.InitBuffer() self.Refresh() def DrawLines(self,dc): dc.BeginDrawing() for colour, thickness, line in self.lines: pen = wxPen(wxNamedColour(colour),thickness,wxSOLID) dc.SetPen(pen) for coords in line: apply(dc.DrawLine, coords) dc.EndDrawing() class ImageFrame(wxFrame): def __init__(self,parent,ID): wxFrame.__init__(self,parent,ID,"AGImage: no image loaded",size=(800,600), style=wxDEFAULT_FRAME_STYLE | wxNO_FULL_REPAINT_ON_RESIZE) menu = wxMenu() menu.Append(ID_OPEN,"&Open...","Open an image file") menu.Append(ID_OPEN_VENUE,"&Open from venue...","Open a venue image file") menu.AppendSeparator() menu.Append(ID_EXIT,"&Exit","Terminate with extreme prejudice") menubar = wxMenuBar() menubar.Append(menu,"&File") self.SetMenuBar(menubar) EVT_MENU(self,ID_OPEN, self.On_Open) EVT_MENU(self,ID_OPEN_VENUE, self.On_OpenVenue) EVT_MENU(self,ID_EXIT, self.On_Exit) # start the ag stuff and see if there is an image already there self.imagedataname = "ImageFile" self.markupdataname = "MarkupData" dt=BIFileDropTarget(self) self.SetDropTarget(dt) self.AG = AGSharedObject(sys.argv[1], sys.argv[2]) self.AG.RegisterEvent("NewImage",self.HandleNewImage) self.AG.RegisterEvent("NewMarks",self.HandleNewMarks) self.wind = ImageWindow(self,-1) animage = self.AG.GetData(self.imagedataname) scribble = self.AG.GetData(self.markupdataname) if animage == None: print "No image found in server" else: print "Loading image: ", animage localImageFile = self.AG.DownloadFile(animage) self.LoadImageFromFilename(localImageFile) if scribble == None: print "No markup found in server" else: print "Loading markup" self.wind.LoadMarkup(scribble) def On_Open(self,event): dlg = wxFileDialog(self,"Select An Image", os.getcwd(), "",wildcard,wxOPEN) if dlg.ShowModal() == wxID_OK: # Load the image locally localImageFile = dlg.GetPath() # Push the data to the venue server # - copy the image to the venue server venueImageFile = self.AG.UploadFile(localImageFile) file = os.path.split(localImageFile)[-1] # - store the image url in the app object self.AG.PutData(self.imagedataname, file) # Load the image locally and erase the current markup self.LoadImageFromFilename(localImageFile) # Now fire the event that tells everyone else the image is changed self.AG.SendEvent("NewImage",file) def On_OpenVenue(self,event): self.AG.dataStoreClient.LoadData() # Let user select a venue file dlg = wxSingleChoiceDialog( self, "Select an image file to load", "Load Venue Image Dialog", self.AG.dataStoreClient.dataIndex.keys() ) if dlg.ShowModal() == wxID_OK: venueImageFile = dlg.GetStringSelection() # Download the file locally localImageFile = self.AG.DownloadFile(venueImageFile) # Store the image url in the app object self.AG.PutData(self.imagedataname, venueImageFile) # Now fire the event that tells everyone else the image is changed file = os.path.split(localImageFile)[-1] self.AG.SendEvent("NewImage",file) # Load it self.LoadImageFromFilename(localImageFile) def HandleNewImage(self,eventdata): # received notification that there is a new image on the server (senderId,venueImageFile) = eventdata.data if senderId != self.AG.puid: try: # Download the file and erase current markup localImageFile = self.AG.DownloadFile(venueImageFile) # Load the image self.LoadImageFromFilename(localImageFile) except: print "EXCEPTION : ", sys.exc_type, sys.exc_value import traceback traceback.print_stack() def HandleNewMarks(self,eventdata): (senderId,marklist) = eventdata.data if senderId != self.AG.puid: try: # append the marklist to the line list #self.wind.SetColour(marklist[0]) #self.wind.thickness = marklist[1] self.wind.lines.append((marklist[0],marklist[1],marklist[2])) self.wind.reInitBuffer = true self.wind.Refresh(true) except: print "EXCEPTION : ", sys.exc_type, sys.exc_value import traceback traceback.print_stack() def LoadImageFromFilename(self,filename): # Load the image self.wind.LoadImageFromFilename(filename) # Set the window title title = os.path.split(filename)[-1] self.SetTitle(title) def On_Exit(self,event): self.Close(true) class MyApp(wxApp): def OnInit(self): wxInitAllImageHandlers() frame = ImageFrame(None,-1) frame.Show(true) self.SetTopWindow(frame) return true app = MyApp(0) app.MainLoop()