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