• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10/pyobjc-45/2.6/pyobjc/pyobjc-framework-Cocoa/Examples/Twisted/WebServicesTool-CocoaBindings/
1"""
2Instances of WSTConnectionWindowController are the controlling object
3for the document windows for the Web Services Tool application.
4
5Implements a standard toolbar.
6"""
7
8from AppKit import *
9
10from twisted.internet import defer
11from twisted.web.xmlrpc import Proxy
12
13from RPCMethod import *
14
15#from twisted.python import log
16#import sys
17#log.startLogging(sys.stdout)
18
19# cheap dirty way to turn those messages off
20# from twisted.python import log
21# log.logerr = open('/dev/null','w')
22
23# Identifier for 'reload contents' toolbar item.
24kWSTReloadContentsToolbarItemIdentifier = u"WST: Reload Contents Toolbar Identifier"
25
26# Identifier for 'preferences' toolbar item.
27kWSTPreferencesToolbarItemIdentifier = u"WST: Preferences Toolbar Identifier"
28
29# Identifier for URL text field toolbar item.
30kWSTUrlTextFieldToolbarItemIdentifier = u"WST: URL Textfield Toolbar Identifier"
31
32def addToolbarItem(aController, anIdentifier, aLabel, aPaletteLabel,
33                   aToolTip, aTarget, anAction, anItemContent, aMenu):
34    """
35    Adds an freshly created item to the toolbar defined by
36    aController.  Makes a number of assumptions about the
37    implementation of aController.  It should be refactored into a
38    generically useful toolbar management untility.
39    """
40    toolbarItem = NSToolbarItem.alloc().initWithItemIdentifier_(anIdentifier)
41
42    toolbarItem.setLabel_(aLabel)
43    toolbarItem.setPaletteLabel_(aPaletteLabel)
44    toolbarItem.setToolTip_(aToolTip)
45    toolbarItem.setTarget_(aTarget)
46    if anAction:
47        toolbarItem.setAction_(anAction)
48
49    if isinstance(anItemContent, NSImage):
50        toolbarItem.setImage_(anItemContent)
51    else:
52        toolbarItem.setView_(anItemContent)
53        bounds = anItemContent.bounds()
54        minSize = (100, bounds[1][1])
55        maxSize = (1000, bounds[1][1])
56        toolbarItem.setMinSize_( minSize )
57        toolbarItem.setMaxSize_( maxSize )
58
59    if aMenu:
60        menuItem = NSMenuItem.alloc().init()
61        menuItem.setSubmenu_(aMenu)
62        menuItem.setTitle_( aMenu.title() )
63        toolbarItem.setMenuFormRepresentation_(menuItem)
64
65    aController.k_toolbarItems[anIdentifier] = toolbarItem
66
67class WSTConnectionWindowController (NSWindowController):
68    methodDescriptionTextView = objc.IBOutlet()
69    methodsTable = objc.IBOutlet()
70    progressIndicator = objc.IBOutlet()
71    statusTextField = objc.IBOutlet()
72    urlTextField = objc.IBOutlet()
73
74    @classmethod
75    def connectionWindowController(self):
76        """
77        Create and return a default connection window instance.
78        """
79        return WSTConnectionWindowController.alloc().init()
80
81    def init(self):
82        """
83        Designated initializer.
84
85        Returns self (as per ObjC designated initializer definition,
86        unlike Python's __init__() method).
87        """
88        self = self.initWithWindowNibName_(u"WSTConnection")
89
90        self.k_toolbarItems = {}
91        self.k_toolbarDefaultItemIdentifiers = []
92        self.k_toolbarAllowedItemIdentifiers = []
93
94        self.k_methods = {}
95        self.k_methodArray = []
96        return self
97
98    def awakeFromNib(self):
99        """
100        Invoked when the NIB file is loaded.  Initializes the various
101        UI widgets.
102        """
103        self.retain() # balanced by autorelease() in windowWillClose_
104
105        self.statusTextField.setStringValue_(u"No host specified.")
106        self.progressIndicator.setStyle_(NSProgressIndicatorSpinningStyle)
107        self.progressIndicator.setDisplayedWhenStopped_(False)
108
109        self.createToolbar()
110
111    def windowWillClose_(self, aNotification):
112        """
113        Clean up when the document window is closed.
114        """
115        self.autorelease()
116
117    def createToolbar(self):
118        """
119        Creates and configures the toolbar to be used by the window.
120        """
121        toolbar = NSToolbar.alloc().initWithIdentifier_(u"WST Connection Window")
122        toolbar.setDelegate_(self)
123        toolbar.setAllowsUserCustomization_(True)
124        toolbar.setAutosavesConfiguration_(True)
125
126        self.createToolbarItems()
127
128        self.window().setToolbar_(toolbar)
129
130        lastURL = NSUserDefaults.standardUserDefaults().stringForKey_(u"LastURL")
131        if lastURL and len(lastURL):
132            self.urlTextField.setStringValue_(lastURL)
133
134    def createToolbarItems(self):
135        """
136        Creates all of the toolbar items that can be made available in
137        the toolbar.  The actual set of available toolbar items is
138        determined by other mechanisms (user defaults, for example).
139        """
140        addToolbarItem(
141            self, kWSTReloadContentsToolbarItemIdentifier,
142            u"Reload", u"Reload", u"Reload Contents", None,
143            "reloadVisibleData:", NSImage.imageNamed_(u"Reload"), None)
144        addToolbarItem(
145            self, kWSTPreferencesToolbarItemIdentifier,
146            u"Preferences", u"Preferences", u"Show Preferences", None,
147            "orderFrontPreferences:", NSImage.imageNamed_(u"Preferences"), None)
148        addToolbarItem(
149            self, kWSTUrlTextFieldToolbarItemIdentifier,
150            u"URL", u"URL", u"Server URL", None,
151            None, self.urlTextField, None)
152
153        self.k_toolbarDefaultItemIdentifiers = [
154            kWSTReloadContentsToolbarItemIdentifier,
155            kWSTUrlTextFieldToolbarItemIdentifier,
156            NSToolbarSeparatorItemIdentifier,
157            NSToolbarCustomizeToolbarItemIdentifier,
158        ]
159
160        self.k_toolbarAllowedItemIdentifiers = [
161            kWSTReloadContentsToolbarItemIdentifier,
162            kWSTUrlTextFieldToolbarItemIdentifier,
163            NSToolbarSeparatorItemIdentifier,
164            NSToolbarSpaceItemIdentifier,
165            NSToolbarFlexibleSpaceItemIdentifier,
166            NSToolbarPrintItemIdentifier,
167            kWSTPreferencesToolbarItemIdentifier,
168            NSToolbarCustomizeToolbarItemIdentifier,
169        ]
170
171    def toolbarDefaultItemIdentifiers_(self, anIdentifier):
172        """
173        Return an array of toolbar item identifiers that identify the
174        set, in order, of items that should be displayed on the
175        default toolbar.
176        """
177        return self.k_toolbarDefaultItemIdentifiers
178
179    def toolbarAllowedItemIdentifiers_(self, anIdentifier):
180        """
181        Return an array of toolbar items that may be used in the toolbar.
182        """
183        return self.k_toolbarAllowedItemIdentifiers
184
185    def toolbar_itemForItemIdentifier_willBeInsertedIntoToolbar_(self,
186                                                                 toolbar,
187                                                                 itemIdentifier, flag):
188        """
189        Delegate method fired when the toolbar is about to insert an
190        item into the toolbar.  Item is identified by itemIdentifier.
191
192        Effectively makes a copy of the cached reference instance of
193        the toolbar item identified by itemIdentifier.
194        """
195        newItem = NSToolbarItem.alloc().initWithItemIdentifier_(itemIdentifier)
196        item = self.k_toolbarItems[itemIdentifier]
197
198        newItem.setLabel_( item.label() )
199        newItem.setPaletteLabel_( item.paletteLabel() )
200        if item.view():
201            newItem.setView_( item.view() )
202        else:
203            newItem.setImage_( item.image() )
204
205        newItem.setToolTip_( item.toolTip() )
206        newItem.setTarget_( item.target() )
207        newItem.setAction_( item.action() )
208        newItem.setMenuFormRepresentation_( item.menuFormRepresentation() )
209
210        if newItem.view():
211            newItem.setMinSize_( item.minSize() )
212            newItem.setMaxSize_( item.maxSize() )
213
214        return newItem
215
216    def setStatusTextFieldMessage_(self, aMessage):
217        """
218        Sets the contents of the statusTextField to aMessage and
219        forces the fileld's contents to be redisplayed.
220        """
221        if not aMessage:
222            aMessage = u"Displaying information about %d methods." % (len(self.k_methods),)
223        self.statusTextField.setStringValue_(aMessage)
224    setStatusTextFieldMessage_ = objc.accessor(setStatusTextFieldMessage_)
225
226    def startWorking(self):
227        """Signal the UI there's work goin on."""
228        self.progressIndicator.startAnimation_(self)
229
230    def stopWorking(self):
231        """Signal the UI that the work is done."""
232        self.progressIndicator.stopAnimation_(self)
233
234    @objc.IBAction
235    def reloadVisibleData_(self, sender):
236        """
237        Reloads the list of methods and their signatures from the
238        XML-RPC server specified in the urlTextField.  Displays
239        appropriate error messages, if necessary.
240        """
241        url = self.urlTextField.stringValue()
242        self.k_methods = {}
243
244        if not url:
245            self.window().setTitle_(u"Untitled.")
246            self.setStatusTextFieldMessage_(u"No URL specified.")
247            return
248
249        self.window().setTitle_(url)
250        NSUserDefaults.standardUserDefaults().setObject_forKey_(url, u"LastURL")
251
252        self.setStatusTextFieldMessage_(u"Retrieving method list...")
253        self.getMethods(url)
254
255    def getMethods(self, url):
256        _server = self.k_server = Proxy(url.encode('utf8'))
257        self.startWorking()
258        return _server.callRemote('listMethods').addCallback(
259            # call self.receivedMethods(result, _server, "") on success
260            self.receivedMethods, _server, ""
261        ).addErrback(
262            # on error, call this lambda
263            lambda e: _server.callRemote('system.listMethods').addCallback(
264                # call self.receievedMethods(result, _server, "system.")
265                self.receivedMethods, _server, 'system.'
266            )
267        ).addErrback(
268            # log the failure instance, with a method
269            self.receivedMethodsFailure, 'listMethods()'
270        ).addBoth(
271            # stop working nomatter what trap all errors (returns None)
272            lambda n:self.stopWorking()
273        )
274
275    def receivedMethodsFailure(self, why, method):
276        self.k_server = None
277        self.k_methodPrefix = None
278        self.setStatusTextFieldMessage_(
279           (u"Server failed to respond to %s.  "
280            u"See below for more information."       ) % (method,)
281        )
282        #log.err(why)
283        self.methodDescriptionTextView.setString_(why.getTraceback())
284
285    def receivedMethods(self, _methods, _server, _methodPrefix):
286        self.k_server = _server
287        self.k_methods = {}
288        self.k_methodPrefix = _methodPrefix
289        for aMethod in _methods:
290            self.k_methods[aMethod] = RPCMethod.alloc().initWithDocument_name_(self, aMethod)
291        self.setMethodArray_(self.k_methods.values())
292        self.k_methodPrefix = _methodPrefix
293
294        self.setStatusTextFieldMessage_(
295            u"Retrieving information about %d methods." % (len(self.k_methods),)
296        )
297
298        # we could make all the requests at once :)
299        # but the server might not like that so we will chain them
300        d = defer.succeed(None)
301        for index, aMethod in enumerate(self.k_methodArray):
302            d.addCallback(
303                self.fetchMethodSignature, index, aMethod
304            ).addCallbacks(
305                callback = self.processSignatureForMethod,
306                callbackArgs = (index, aMethod),
307                errback = self.couldntProcessSignatureForMethod,
308                errbackArgs = (index, aMethod),
309            )
310        return d.addCallback(
311            lambda ig: self.setStatusTextFieldMessage_(None)
312        )
313
314    def fetchMethodSignature(self, ignore, index, aMethod):
315        self.setStatusTextFieldMessage_(
316            u"Retrieving signature for method %s (%d of %d)."
317            % (aMethod.methodName() , index, len(self.k_methods))
318        )
319        return self.k_server.callRemote(
320            (self.k_methodPrefix + 'methodSignature').encode('utf-8'),
321            aMethod.methodName().encode('utf-8')
322        )
323
324    def processSignatureForMethod(self, methodSignature, index, aMethod):
325        signatures = None
326        if not len(methodSignature):
327            return
328        for aSignature in methodSignature:
329            if isinstance(aSignature, list) and len(aSignature) > 0:
330                signature = u"%s %s(%s)" % (aSignature[0], aMethod.methodName(), u", ".join(aSignature[1:]))
331            else:
332                signature = aSignature
333        if signatures:
334            signatures = signatures + u", " + signature
335        else:
336            signatures = signature
337
338        aMethod.setMethodSignature_(signatures)
339        self.replaceObjectInMethodArrayAtIndex_withObject_(index, aMethod)
340
341    def couldntProcessSignatureForMethod(self, why, index, aMethod):
342        #log.err(why)
343        aMethod.setMethodSignature_(u"<error> %s %s" % (aMethod.methodName(), why.getBriefTraceback()))
344        self.replaceObjectInMethodArrayAtIndex_withObject_(index, aMethod)
345
346    def fetchMethodDescription_(self, aMethod):
347        def cacheDesc(v):
348            aMethod.setMethodDescription_(v or u'No description available.')
349
350        self.setStatusTextFieldMessage_(u"Retrieving documentation for method %s..." % (aMethod.methodName(),))
351        self.startWorking()
352        self.k_server.callRemote((self.k_methodPrefix + u'methodHelp').encode('utf-8'), aMethod.methodName().encode('utf-8')).addCallback(cacheDesc)
353
354    def methodArray(self):
355        return self.k_methodArray
356
357    @objc.accessor
358    def countOfMethodArray(self):
359        if self.k_methodArray is None:
360            return 0
361        return self.k_methodArray
362
363    @objc.accessor
364    def objectInMethodArrayAtIndex_(self, anIndex):
365        return self.k_methodArray[anIndex]
366
367    @objc.accessor
368    def insertObject_inMethodArrayAtIndex_(self, anObject, anIndex):
369        self.k_methodArray.insert(anIndex, anObject)
370
371    @objc.accessor
372    def removeObjectFromMethodArrayAtIndex_(self, anIndex):
373        del self.k_methodArray[anIndex]
374
375    @objc.accessor
376    def replaceObjectInMethodArrayAtIndex_withObject_(self, anIndex, anObject):
377        self.k_methodArray[anIndex] = anObject
378
379    @objc.accessor
380    def setMethodArray_(self, anArray):
381        self.k_methodArray = anArray
382