Shared Image Viewer, Version 1
Justin Binns
binns at mcs.anl.gov
Thu Apr 10 14:13:03 CDT 2003
First pass at a shared image viewer. The model is very simlar to the
shared browser. It is capable of viewing venue-stored images as well, all
we need to do is figure out how to get the URL into it (it uses the same
mechanisms as the 'open...' feature in the VC for getting data)
Justin
-------------- next part --------------
import os
import sys
import logging
from wxPython.wx import *
from AccessGrid.hosting.pyGlobus import Client
from AccessGrid import Events
from AccessGrid.ClientProfile import ClientProfile
from AccessGrid import EventClient
from AccessGrid.hosting.pyGlobus.Utilities import GetDefaultIdentityDN
from AccessGrid import DataStore
from AccessGrid.Platform import GetTempDir
log = logging.getLogger("SharedImageViewer")
def init_logging(appName):
logger = logging.getLogger("AG")
logger.setLevel(logging.DEBUG)
hdlr = logging.StreamHandler()
fmt = logging.Formatter("%(name)-17s %(asctime)s %(levelname)-5s %(message)s")
hdlr.setFormatter(fmt)
logger.addHandler(hdlr)
appLogger = logging.getLogger(appName)
appLogger.setLevel(logging.DEBUG)
appLogger.addHandler(hdlr)
class Viewer(wxPanel):
"""
Basic image viewer status panel
"""
def __init__(self, parent, id, displayContact, displaySize, frame = None):
wxPanel.__init__(self, parent, id)
self.displayContact=displayContact
self.displaySize=displaySize
self.current = None
self.populate()
self.pid_set=0
self.view_callbacks = []
EVT_CLOSE(self,self.OnExit)
self.frame = frame
if frame is not None:
self.title_base = frame.GetTitle()
self.just_received_view = 0
def OnExit(self):
if self.pid_set:
import posix
posix.kill(self.pid_set,9)
self.Close()
def add_view_callback(self, listener):
self.view_callbacks.append(listener)
def remove_view_callback(self, listener):
self.view_callbacks.remove(listener)
def add_button(self, name, func, sizer):
b = wxButton(self, -1, name)
EVT_BUTTON(self, b.GetId(), func)
sizer.Add(b, 1, wxEXPAND)
def populate(self):
sizer = wxBoxSizer(wxVERTICAL)
#
# Create the button bar
#
bsizer = wxBoxSizer(wxHORIZONTAL)
t = wxStaticText(self, -1, "Location: ")
bsizer.Add(t, 0, wxEXPAND)
self.location = wxComboBox(self, wxNewId(), "",
style=wxCB_DROPDOWN|wxPROCESS_ENTER)
EVT_COMBOBOX(self, self.location.GetId(), self.OnView)
bsizer.Add(self.location, 1, wxEXPAND)
sizer.Add(bsizer, 0, wxEXPAND)
self.add_button("View",self.OnView,sizer);
self.SetSizer(sizer)
self.SetAutoLayout(1)
self.Layout()
def OnView(self,event):
viewURL=self.location.GetValue()
if self.just_received_view:
self.just_received_view=0;
else:
map(lambda a: a(viewURL), self.view_callbacks);
self.display(viewURL)
def display(self, viewURL):
print "Displaying %s..."%(viewURL)
self.location.SetValue(viewURL)
name = viewURL.split('/')[-1]
tfilepath = os.path.join(GetTempDir(), name)
wxLogDebug("Get ident and download %s -> %s"%(viewURL,tfilepath))
try:
if viewURL.startswith("https"):
wxLogDebug("url=%s, local path =%s"%(viewURL, tfilepath))
DataStore.GSIHTTPDownloadFile(viewURL, tfilepath, None, None)
wxLogDebug("finished GSIHTTPDownload")
else:
wxLogDebug("url does not start with https")
my_identity = GetDefaultIdentityDN()
DataStore.HTTPDownloadFile(my_identity, viewURL, tfilepath, None, None)
except DataStore.DownloadFailed, e:
wxCallAfter(wxLogError, "Got exception on download")
if self.displayContact.startswith("Win32"):
os.system("start %s"%(tfilepath))
else:
import posix
if self.pid_set:
posix.kill(self.pid_set,9)
self.pid_set=0
self.pid_set=posix.fork()
if self.pid_set:
return
else:
posix.system("display -display %s %s &"%("localhost:"+self.displayContact[6:].split(":")[-1],tfilepath))
posix._exit(0)
class SharedImageViewer( wxApp ):
"""
SharedImageViewer is an image viewer application that uses the SharedApplication
framework from AGTk 2.0, Node Display Services, and does 'the right thing'.
"""
def OnInit(self):
return 1
def OnExit(self):
self.eventClient.Stop()
self.ExitMainLoop()
os._exit(1)
def __init__( self, venueUrl, profile, nodeServiceURL="https://localhost:11000/NodeService" ):
wxApp.__init__(self, false)
self.venueUrl = venueUrl
self.profile = profile
self.nodeServiceURL=nodeServiceURL;
self.venueProxy = Client.Handle(venueUrl).GetProxy()
self.nodeServiceProxy = Client.Handle(nodeServiceURL).GetProxy();
# Join the application
self.privateId = self.venueProxy.Join(profile)
#
# Retrieve the channel id
#
(self.channelId, eventServiceLocation ) = self.venueProxy.GetDataChannel(self.privateId)
#
# Retrieve list of node services
#
serviceList=self.nodeServiceProxy.GetServices()
#
# Parse service list, remove all non-display services
#
self.displayServices=[]
for service in serviceList:
for capability in service.capabilities:
if capability.role=="consumer" and capability.type=="display":
service.capabilities=Client.Handle(service.uri).GetProxy().GetCapabilities();
self.displayServices.append(service);
if len(self.displayServices)<=0:
print "ERROR: No display services found in NodeServiceManager %s!\n"%(self.nodeServiceURL)
return
#
# Subscribe to the event channel
#
self.eventClient = EventClient.EventClient(eventServiceLocation, self.channelId)
self.eventClient.start()
self.eventClient.Send(Events.ConnectEvent(self.channelId))
#
# Register the 'view' event callback
#
# The callback function is invoked with one argument, the data from the call.
self.eventClient.RegisterCallback("view", self.ViewCallback )
#
# Create View status panel
#
# Some fancy WX stuff here...
self.frame=wxFrame(None, -1, "Shared Image Viewer",size=(400,75));
if len(self.displayServices) > 1:
displayServiceNames=[];
for service in self.displayServices:
displayServiceNames.append(service.name+"|"+service.uri)
self.targetSurface=""
while self.targetSurface=="":
dlg=wxSingleChoiceDialog(self.frame,"Display Services","Choose target display service:",displayServiceNames,wxOK)
if dlg.ShowModal() == wxID_OK:
for service in self.displayServices:
if dlg.GetStringSelection()==(service.name+"|"+service.uri):
for capability in service.capabilities:
if capability.role=="consumer" and capability.type=="display":
self.targetSurface=capability.parms["DisplayContact"]
self.displaySize=capability.parms["PixelSize"].split('x')
self.displaySize[0]=eval(self.displaySize[0])
self.displaySize[1]=eval(self.displaySize[1])
else:
for capability in self.displayServices[0].capabilities:
if capability.role=="consumer" and capability.type=="display":
self.targetSurface=capability.parms["DisplayContact"]
self.displaySize=capability.parms["PixelSize"].split('x')
self.displaySize[0]=eval(self.displaySize[0])
self.displaySize[1]=eval(self.displaySize[1])
print "Display Contact: %s (%dx%d)"%(self.targetSurface,self.displaySize[0],self.displaySize[1]);
self.viewer=Viewer(self.frame,-1,self.targetSurface,self.displaySize);
EVT_CLOSE(self,self.OnExit)
# Add callback for local image selection
self.viewer.add_view_callback( self.newImage )
# Browse to the current url, if exists
currentImage = self.venueProxy.GetData(self.privateId, "image")
if len(currentImage) > 0:
self.viewer.display(currentImage)
self.frame.Show(1)
self.SetTopWindow(self.frame)
def newImage(self,data):
print "About to send %s..."%(data)
#
# Send out the event, including our public ID in the message.
#
self.eventClient.Send(Events.Event("view", self.channelId, ( self.profile.publicId, data ) ))
# Store the URL in the app object in the venue
self.venueProxy.SetData(self.privateId, "image", data)
def ViewCallback(self, data):
"""
Callback invoked when incoming view events arrive. Events
can include this component's view events, so these need to be
filtered out.
"""
# Determine if the sender of the event is this component or not.
(senderId, image) = data
if senderId == self.profile.publicId:
print "Ignoring %s from myself" % (image)
else:
print "Displaying ", image
self.viewer.display(image)
if __name__ == "__main__":
init_logging("watcher")
if len(sys.argv) < 3:
print "Usage: %s <appObjectUrl> <profileFile>" % sys.argv[0]
sys.exit(1)
venueUrl = sys.argv[1]
profileFile = sys.argv[2]
profile = ClientProfile(profileFile)
sb = SharedImageViewer( venueUrl, profile )
sb.MainLoop()
More information about the ag-dev
mailing list