PK ]Es1—<;jrjrSharedImageViewer.pyUT q%A)AUx""" A shared image viewer application for the Access Grid. This application allows the user to view and annotate image data in a collaborative environment provided by the Access Grid Toolkit. This is a major revision of the original AGBasicImage application. The modifications made bring the application into conformance with the AG2.3 environment. This new version of the application is modeled after the sharedbrowser.py application distributed with the AG2.3 software. AG Version - 2.3 Dave Semeraro NCSA - UIUC 2004 """ # Import the usual suspects import os import sys import logging import getopt import string try: from tempfile import mkdtemp except: from tempfile import mktemp from shutil import copyfile from locale import atoi # Import the wxPython stuff import wx from wx.lib.imagebrowser import * # Import the AG stuff from AccessGrid.SharedAppClient import SharedAppClient from AccessGrid.Platform.Config import UserConfig from AccessGrid.ClientProfile import ClientProfile from AccessGrid import icons from AccessGrid.Toolkit import WXGUIApplication from AccessGrid.Toolkit import CmdlineApplication from AccessGrid.DataStoreClient import GetVenueDataStore from AccessGrid import Platform # # grab some event ID's # ID_OPEN = wx.NewId() ID_OPEN_VENUE = wx.NewId() ID_CLEAR = wx.NewId() ID_RELOAD = wx.NewId() ID_SAVE = wx.NewId() ID_EXIT = wx.NewId() ID_ERASEVENUEMARKUP = wx.NewId() ID_ERASEMARKUP = wx.NewId() # wildcard = "JPEG Files (*.JPG)|*.jpg|"\ "Gif Files (*.GIF)|*.gif|"\ "All Files (*.*)|*.*" if wx.MAJOR_VERSION == 2 and wx.MINOR_VERSION < 5: SIMG_PRE25 = True else: SIMG_PRE25 = False def _ConvertMarkupToString(markup): """ Convert the markup data to a string for storage in the venue application. The markup exists locally as a list of tuples. The contents of this list is converted to a string of space separated entries. """ tmpstrlist = [] tmpstrlist.append(str(len(markup))) for marks in markup: tmpstrlist.append(marks[0]) tmpstrlist.append(str(marks[1])) coordstrings = _CoordListToStringList(marks[2]) tmpstrlist.append(str(len(coordstrings))) for item in coordstrings: tmpstrlist.append(item) marksstring = string.join(tmpstrlist,' ') #print marksstring return marksstring def _ConvertStringToMarkup(mstring): markslist = mstring.split() mark = 0 i = 1 markuplist = [] while mark < atoi(markslist[0]): color = markslist[i] thickness = atoi(markslist[i+1]) numcoords = atoi(markslist[i+2]) i = i + 3 tmpcoordstringlist = [] for j in xrange(numcoords): tmpcoordstringlist.append(markslist[i+j]) markuplist.append((color,thickness,_StringListToCoordList(tmpcoordstringlist))) i = i + numcoords mark = mark + 1 #print markuplist return markuplist def _CoordListToStringList(coordlist): """ change a list of coordinates to a string. The input list contains tuples of coordinate pairs that constitute the end points of line segments. This list of tuples of tuples is changed to a space separated string of characters representing the coordinate values. for example [((624, 478),(625,477)), ((336,389),(367,389))] is mapped to ['624','478','625','477','336','389','367','389'] """ tmplist = [] for segment in coordlist: for point in segment: tmplist.append(str(point[0])) tmplist.append(str(point[1])) return tmplist def _StringListToCoordList(stringlist): """ reverse the conversion from the coords to string """ tmp = [] for i in xrange(0,len(stringlist),4): x1 = atoi(stringlist[i]) y1 = atoi(stringlist[i+1]) x2 = atoi(stringlist[i+2]) y2 = atoi(stringlist[i+3]) tmp.append(((x1,y1),(x2,y2))) return tmp class BIFileDropTarget(wx.FileDropTarget): def __init__(self,viewer): wx.FileDropTarget.__init__(self) self.viewer = viewer def OnDropFiles(self,x,y,filenames): self.viewer.log.info("loading image files %s " %filenames) for localImageFile in filenames: self.viewer.window.LoadImageFromFilename(localImageFile) venueImageFile =self.viewer.UploadFile(localImageFile) file = os.path.split(localImageFile)[-1] self.viewer.sharedAppClient.SetData(self.viewer.imagedataname,file) self.viewer.sharedAppClient.SendEvent("NewImage",(self.viewer.PublicId,file)) self.viewer.frame.SetTitle(file) self.viewer.frame.Refresh(True) class ViewerFrame(wx.Frame): menuColours = { 200 : 'Black', 201 : 'Yellow', 202 : 'Red', 203 : 'Green', 204 : 'Blue', 205 : 'Purple', 206 : 'Brown' } # this doesn't do much but set up the menu. The events are mapped to another # class def __init__(self,parent,ID): wx.Frame.__init__(self,parent,ID,"SharedImageViewer: no image loaded",size=(800,600),style=wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE) menu = wx.Menu() menu.Append(ID_OPEN,"&Open...","Open an image file") menu.Append(ID_OPEN_VENUE,"&Open from venue...","Open a venue image file") menu.Append(ID_CLEAR,"&Clear Image","Erase screen image and clear global image data") menu.Append(ID_RELOAD,"&Reload Image","Get the current image and markup from the venue") menu.Append(ID_SAVE,"&Save Image","Save the current image to the local file system") menu.AppendSeparator() menu.Append(ID_EXIT,"&Exit","Terminate with extreme prejudice") colormenu = wx.Menu() keys = self.menuColours.keys() keys.sort() for k in keys: text = self.menuColours[k] colormenu.AppendRadioItem(k,text) menubar = wx.MenuBar() menubar.Append(menu,"&File") menubar.Append(colormenu,"&Colors") settingmenu = wx.Menu() settingmenu.AppendCheckItem(ID_ERASEMARKUP,"Erase Local Markup","Erase local markup on loading image") settingmenu.AppendCheckItem(ID_ERASEVENUEMARKUP,"Erase Venue Markup","Erase global markup on loading image") menubar.Append(settingmenu,"&Settings") self.SetMenuBar(menubar) # # Define the viewerwindow class here # class ViewerWindow(wx.Window): def __init__(self,parent,ID): wx.Window.__init__(self,parent,ID,style=wx.NO_FULL_REPAINT_ON_RESIZE) self.imagefile = None self.parentframe = parent self.image = None self.lines = [] self.thickness = 1 self.SetColour("Black") self.SetBackgroundColour("WHITE") self.InitBuffer() # bind some events some window events are bound in another place if SIMG_PRE25: wx.EVT_IDLE(self, self.OnIdle) wx.EVT_SIZE(self, self.OnSize) wx.EVT_PAINT(self, self.OnPaint) wx.EVT_LEFT_DOWN(self, self.OnLeftDown) wx.EVT_MOTION(self, self.OnMotion) else: self.Bind(wx.EVT_IDLE, self.OnIdle) self.Bind(wx.EVT_SIZE, self.OnSize) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_MOTION, self.OnMotion) def ClearWindow(self,event): self.lines = [] self.image = None self.reInitBuffer = True self.Refresh(True) def InitBuffer(self): size = self.GetClientSize() if self.image == None: self.buffer = wx.EmptyBitmap(size.width,size.height) dc = wx.BufferedDC(None,self.buffer) dc.SetBackground(wx.Brush(self.GetBackgroundColour())) dc.Clear() else: #self.Refresh(True) self.buffer = self.image.ConvertToBitmap() dc = wx.BufferedDC(None, self.buffer) dc.SetBackground(wx.Brush(self.GetBackgroundColour())) self.DrawLines(dc) self.reInitBuffer = False self.Refresh(True) def LoadImageFromFilename(self,imagefilename): self.imagefile = imagefilename self.image = wx.Image(self.imagefile) self.reInitBuffer = True self.Refresh(True) def SetColour(self,colour): self.colour = colour self.pen = wx.Pen(wx.NamedColour(self.colour),self.thickness,wx.SOLID) def SetThickness(self,num): self.thickness = num self.pen = wx.Pen(wx.NamedColour(self.colour),self.thickness,wx.SOLID) # def LoadImage(self,animage): # imgdat = base64.decodestring(animage.data) # self.image = wx.EmptyImage(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(True) def OnSize(self,event): self.reInitBuffer = True def OnPaint(self,event): dc = wx.BufferedPaintDC(self,self.buffer) def OnLeftDown(self,event): self.curLine = [] self.x, self.y = event.GetPositionTuple() self.CaptureMouse() def OnLeftUp(self): if self.HasCapture(): self.lines.append((self.colour, self.thickness, self.curLine)) self.ReleaseMouse() #print len(self.lines),self.lines[0:] def GetCurrentMarks(self): return (self.colour, self.thickness, self.curLine) def DrawLines(self,dc): dc.BeginDrawing() for colour, thickness, line in self.lines: pen = wx.Pen(wx.NamedColour(colour),thickness,wx.SOLID) dc.SetPen(pen) for coords in line: if SIMG_PRE25: p1 = coords[0] p2 = coords[1] x1,y1 = p1 x2,y2 = p2 dc.DrawLine(x1, y1, x2, y2) else: apply(dc.DrawLine, coords) dc.EndDrawing() def OnMotion(self,event): if event.Dragging() and event.LeftIsDown(): dc = wx.BufferedDC(wx.ClientDC(self), self.buffer) dc.BeginDrawing() dc.SetPen(self.pen) pos = event.GetPositionTuple() coords = ((self.x,self.y), pos) self.curLine.append(coords) if SIMG_PRE25: xpos,ypos = pos dc.DrawLine(self.x, self.y, xpos, ypos) else: dc.DrawLine((self.x,self.y),pos) self.x,self.y = pos dc.EndDrawing() # # Define the shared image viewer class here # class SharedImageViewer( wx.App ): def OnInit(self): return 1 def OnExit(self,event): self.sharedAppClient.Shutdown() self.frame.Close(True) # Delete temporary storage directory & contents dfiles = os.listdir(self.tmpStorageArea) for f in dfiles: self.log.info("Removing: %s" % f) os.remove(os.path.join(self.tmpStorageArea, f)) os.removedirs(self.tmpStorageArea) os._exit(1) def __init__(self, appUrl, venueUrl, name): wx.App.__init__(self, False) self.sharedAppClient = SharedAppClient(name) self.log = self.sharedAppClient.InitLogging() self.markupdataname = "MarkupData" self.imagedataname = "ImageFile" self.EraseLocalMarkupOnLoad = False self.EraseVenueMarkupOnLoad = False try: self.tmpStorageArea = mkdtemp(prefix=name) except: self.log.exception("failed to create temporary storage area") self.tmpStorageArea = os.tempnam() os.mkdir(self.tmpStorageArea) else: os.chdir(self.tmpStorageArea) # Get a Client Profile try: clientProfileFile = os.path.join(UserConfig.instance().GetConfigDir(), "profile") clientProfile = ClientProfile(clientProfileFile) except: self.log.info("SharedAppClient.Connect: Could not load client profile, set clientProfile = None") clientProfile = None self.sharedAppClient.Join(appUrl,clientProfile) self.PublicId = self.sharedAppClient.GetPublicId() try: self.dataStoreClient = GetVenueDataStore(venueUrl) print "GOT self.dataStoreClient OK" except: print "Exception:", sys.exc_type, sys.exc_value import traceback traceback.print_stack() self.sharedAppClient.RegisterEventCallback("NewImage",self.HandleNewImage) self.sharedAppClient.RegisterEventCallback("NewMarkup",self.HandleNewMarkup) self.frame = ViewerFrame(None,-1) self.window = ViewerWindow(self.frame, -1) if SIMG_PRE25: wx.EVT_LEFT_UP(self.window, self.ShareMarkup) wx.EVT_MENU(self.frame, ID_OPEN, self.On_Open) wx.EVT_MENU(self.frame, ID_OPEN_VENUE, self.On_OpenVenue) wx.EVT_MENU(self.frame, ID_EXIT, self.OnExit) wx.EVT_MENU(self.frame, ID_CLEAR, self.window.ClearWindow) wx.EVT_MENU(self.frame, ID_RELOAD, self.Reload) wx.EVT_MENU(self.frame, ID_SAVE, self.On_Save) wx.EVT_MENU(self.frame, ID_ERASEMARKUP, self.SetEraseLocalMarkup) #wx.EVT_MENU(self.frame, ID_ERASEVENUEMARKUP, self.SetEraseVenueMarkup) else: self.window.Bind(wx.EVT_LEFT_UP,self.ShareMarkup) self.frame.Bind(wx.EVT_MENU,self.On_Open,id=ID_OPEN) self.frame.Bind(wx.EVT_MENU,self.On_OpenVenue,id=ID_OPEN_VENUE) self.frame.Bind(wx.EVT_MENU,self.OnExit,id=ID_EXIT) self.frame.Bind(wx.EVT_MENU,self.window.ClearWindow,id=ID_CLEAR) self.frame.Bind(wx.EVT_MENU,self.Reload,id=ID_RELOAD) self.frame.Bind(wx.EVT_MENU,self.On_Save,id=ID_SAVE) self.frame.Bind(wx.EVT_MENU,self.SetEraseLocalMarkup,id=ID_ERASEMARKUP) #self.frame.Bind(wx.EVT_MENU,self.SetEraseVenueMarkup,id=ID_ERASEVENUEMARKUP) keys = self.frame.menuColours.keys() for k in keys: if SIMG_PRE25: wx.EVT_MENU(self.frame, k, self.OnMenuSetColour) else: self.frame.Bind(wx.EVT_MENU,self.OnMenuSetColour,id=k) dt = BIFileDropTarget(self) self.window.SetDropTarget(dt) # see if there is an image in the venue and load it up animage = self.sharedAppClient.GetData(self.imagedataname) if animage == "": self.log.info("SharedAppViewer: no image in venue") else: self.log.info("SharedAppViewer: initial image found") localImageFile = self.DownloadFile(animage) self.window.LoadImageFromFilename(localImageFile) title = os.path.split(localImageFile)[-1] self.frame.SetTitle(title) # see if there is markup in the venue and load it up scribble = self.sharedAppClient.GetData(self.markupdataname) if scribble == "": self.log.info("SharedAppViewer: no markup in venue") else: self.window.lines = _ConvertStringToMarkup(scribble) self.frame.Show(1) self.SetTopWindow(self.frame) def SetEraseLocalMarkup(self,event): if event.IsChecked(): self.EraseLocalMarkupOnLoad = True else: self.EraseLocalMarkupOnLoad = False def SetEraseVenueMarkup(self,event): if event.IsChecked(): self.EraseVenueMarkupOnLoad = True else: self.EraseVenueMarkupOnLoad = False def Reload(self,event): self.sharedAppClient.UpdateDataCache() animage = self.sharedAppClient.GetData(self.imagedataname) if animage == "": self.log.info("SharedAppViewer: no image in venue") else: self.log.info("SharedAppViewer: reloading image %s" %animage) localImageFile = self.DownloadFile(animage) self.window.LoadImageFromFilename(localImageFile) title = os.path.split(localImageFile)[-1] self.frame.SetTitle(title) # see if there is markup in the venue and load it up scribble = self.sharedAppClient.GetData(self.markupdataname) if scribble == "": self.log.info("SharedAppViewer: no markup in venue") else: self.window.lines = _ConvertStringToMarkup(scribble) self.window.Refresh(True) def On_Save(self,event): try: dir = os.path.expanduser("~") except: dir = os.path.expanduser("") currFile = self.frame.GetTitle() dlg = wx.FileDialog(parent = self.window, message = "Choose a file name to save the image as", defaultDir = dir, defaultFile = currFile, wildcard = "*", style = wx.SAVE) if dlg.ShowModal() == wx.ID_OK: copyfile(currFile, dlg.GetPath()) dlg.Destroy() def On_Open(self,event): dir = os.getcwd() try: dlg = ImageDialog(self.frame, dir) except: dlg = wx.FileDialog(self.window, message = "Select Image", defaultDir = dir, defaultFile = "", wildcard = wildcard, style = wx.OPEN) dlg.Centre() if dlg.ShowModal() == wx.ID_OK: if self.EraseLocalMarkupOnLoad: self.window.lines = [] try: # used ImageDialog localImageFile = dlg.GetFile() except: # used FileDialog localImageFile = dlg.GetPath() # To take advantage of ImageFialog for future loads, # either save this file here (in tmpStorageArea), # or cd to directory where localImageFile lives. headpart,tailpart = os.path.split(localImageFile) tmpLocalImageFile = os.path.join(self.tmpStorageArea, tailpart) copyfile(localImageFile, tmpLocalImageFile) self.window.LoadImageFromFilename(localImageFile) venueImageFile =self.UploadFile(localImageFile) file = os.path.split(localImageFile)[-1] self.sharedAppClient.SetData(self.imagedataname,file) self.sharedAppClient.SendEvent("NewImage",(self.PublicId,file)) self.frame.SetTitle(file) self.frame.Refresh(True) def On_OpenVenue(self,event): self.dataStoreClient.LoadData() dlg = wx.SingleChoiceDialog(self.frame,"Select an image file to load", "Load Venue Image Dialog", self.dataStoreClient.dataIndex.keys()) if dlg.ShowModal() == wx.ID_OK: if self.EraseLocalMarkupOnLoad: self.window.lines = [] venueImageFile = dlg.GetStringSelection() localImageFile = self.DownloadFile(venueImageFile) self.sharedAppClient.SetData(self.imagedataname, venueImageFile) file = os.path.split(localImageFile)[-1] self.sharedAppClient.SendEvent("NewImage",(self.PublicId,file)) self.window.LoadImageFromFilename(localImageFile) title = os.path.split(localImageFile)[-1] self.frame.SetTitle(title) def UploadFile(self,localFile): # first see if a file of the same name is already there filename = "" filename = self.dataStoreClient.QueryMatchingFiles(localFile) if len(filename) == 0: try: self.dataStoreClient.Upload(localFile) except: self.log.exception("failed to upload %s" %localFile) file = os.path.split(localFile) [-1] venueDataUrl = os.path.join(self.dataStoreClient.uploadURL,file) return venueDataUrl else: self.log.info("file %s is already in the venue" %localFile) def DownloadFile(self,venueDataUrl): title = os.path.split(venueDataUrl)[-1] file = os.path.join(self.tmpStorageArea, title) self.dataStoreClient.LoadData() self.dataStoreClient.Download(venueDataUrl,file) self.frame.SetTitle(title) return file def HandleNewImage(self,eventdata): (senderId,venueImageFile) = eventdata.data if senderId != self.sharedAppClient.GetPublicId(): try: localImageFile = self.DownloadFile(venueImageFile) self.window.LoadImageFromFilename(localImageFile) except: self.log.exception("SharedImageViewer: could not load image file from venue") def ShareMarkup(self,eventdata): """ Share the markup with the other instances. This is done in two ways. First the total markup store in the venue is updated and then the markup is sent in the event data for all to enjoy. The venue based markup is only accessed when an application first joins an ongoing session. """ self.window.OnLeftUp() # refresh the data cache self.sharedAppClient.UpdateDataCache(self.markupdataname) # retrieve the markup that is stored in the venue scribble = self.sharedAppClient.GetData(self.markupdataname) if scribble == "": self.log.info("SharedImageViewer: no markup in venue") markup = [] else: markup = _ConvertStringToMarkup(scribble) markup.append(self.window.GetCurrentMarks()) self.sharedAppClient.SetData(self.markupdataname,_ConvertMarkupToString(markup)) self.sharedAppClient.SendEvent("NewMarkup",(self.PublicId,self.window.GetCurrentMarks())) def OnMenuSetColour(self,eventdata): self.window.colour = self.frame.menuColours[eventdata.GetId()] self.window.pen = wx.Pen(wx.NamedColour(self.window.colour),self.window.thickness,wx.SOLID) def HandleNewMarkup(self,eventdata): (senderId,markslist) = eventdata.data if senderId != self.sharedAppClient.GetPublicId(): try: self.window.lines.append((markslist[0],markslist[1],markslist[2])) self.window.reInitBuffer = True self.window.Refresh(True) except: self.log.exception("SharedImageViewer: whacked markup import") class ArgumentManager: """ stolen from the shared browser app and modified slightly for our use """ def __init__(self): self.arguments = {} self.arguments['applicationUrl'] = None self.arguments['venueUrl'] = None self.arguments['debug'] = 0 def GetArguments(self): return self.arguments def Usage(self): print "%s:" % sys.argv[0] print " -a|--applicationURL : " print " -v|--venueURL : " print " -d|--debug : " print " -h|--help : " def ProcessArgs(self): try: opts,args = getopt.getopt(sys.argv[1:], "a:v:d:h", ["applicationURL=","venueURL=","debug","help"]) except getopt.GetoptError: self.Usage() sys.exit(2) for o, a in opts: if o in ("-a", "--applicationURL"): self.arguments["applicationUrl"] = a elif o in ("-v", "--venueURL"): self.arguments["venueUrl"] = a elif o in ("-d", "--debug"): self.arguments["debug"] = 1 elif o in ("-h", "--help"): self.Usage() self.exit(0) if __name__ == "__main__": app = WXGUIApplication() #app = CmdlineApplication.instance() name = "SharedImageViewer" am = ArgumentManager() am.ProcessArgs() aDict = am.GetArguments() appUrl = aDict['applicationUrl'] venueUrl = aDict['venueUrl'] debugMode = aDict['debug'] init_args = [] if "--debug" in sys.argv or "-d" in sys.argv: init_args.append("--debug") #app.Initialize(name) app.Initialize(name,args=init_args) if not appUrl: am.Usage() elif not venueUrl: am.Usage() else: #appUrl = "https://voyager.ncsa.uiuc.edu:9000/Venues/default/apps/000000ff1ca76bc4008d008e0040000b437" #venueUrl = "https://voyager.ncsa.uiuc.edu:9000/Venues/default" wx.InitAllImageHandlers() iv = SharedImageViewer(appUrl,venueUrl,name) iv.MainLoop() PK HDZ1:SharedImageViewer.appUT g}A%AUxd[application] name = Shared Image Viewer mimetype = application/x-ag-shared-image-viewer extension = sharedimageviewer files = SharedImageViewer.py [commands] Open = %(python)s SharedImageViewer.py -a %(appUrl)s -v %(venueUrl)s PK ]Es1—<;jrjr SharedImageViewer.pyUTq%AUxPK HDZ1: rSharedImageViewer.appUTg}AUxPKs