• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.9.5/pyobjc-42/2.5/pyobjc/pyobjc-framework-Quartz/Examples/ImageKit/ImageBrowser/
1from Cocoa import *
2from Quartz import *
3from LaunchServices import *
4
5import os
6
7#	openFiles
8#
9#	A simple C function that opens NSOpenPanel and returns an array of file paths.
10#	It uses uniform type identifiers (UTIs) for proper filtering of image files.
11# -------------------------------------------------------------------------
12def openFiles():
13    # Get a list of extensions to filter in our NSOpenPanel.
14    panel = NSOpenPanel.openPanel()
15
16    # The user can choose a folder; images in the folder are added recursively.
17    panel.setCanChooseDirectories_(True)
18    panel.setCanChooseFiles_(True)
19    panel.setAllowsMultipleSelection_(True)
20
21    if panel.runModalForTypes_(NSImage.imageUnfilteredTypes()) == NSOKButton:
22        return panel.filenames()
23
24    return []
25
26#==============================================================================
27# This is the data source object.
28class myImageObject (NSObject):
29    _path = objc.ivar()
30
31    # -------------------------------------------------------------------------
32    #	setPath:path
33    #
34    #	The data source object is just a file path representation
35    # -------------------------------------------------------------------------
36    def setPath_(self, inPath):
37        self._path = inPath
38
39    # The required methods of the IKImageBrowserItem protocol.
40
41    # -------------------------------------------------------------------------
42    #	imageRepresentationType:
43    #
44    #	Set up the image browser to use a path representation.
45    # -------------------------------------------------------------------------
46    def imageRepresentationType(self):
47        return IKImageBrowserPathRepresentationType
48
49    # -------------------------------------------------------------------------
50    #	imageRepresentation:
51    #
52    #	Give the path representation to the image browser.
53    # -------------------------------------------------------------------------
54    def imageRepresentation(self):
55        return self._path
56
57    # -------------------------------------------------------------------------
58    #	imageUID:
59    #
60    #	Use the absolute file path as the identifier.
61    # -------------------------------------------------------------------------
62    def imageUID(self):
63        return self._path
64
65class ImageBrowserController (NSWindowController):
66    imageBrowser = objc.IBOutlet()
67
68    images = objc.ivar()
69    importedImages = objc.ivar()
70
71
72    # -------------------------------------------------------------------------
73    #	awakeFromNib:
74    # -------------------------------------------------------------------------
75    def awakeFromNib(self):
76        # Create two arrays : The first is for the data source representation.
77        # The second one contains temporary imported images  for thread safeness.
78        self.images = NSMutableArray.alloc().init()
79        self.importedImages = NSMutableArray.alloc().init()
80
81        # Allow reordering, animations and set the dragging destination
82        # delegate.
83        self.imageBrowser.setAllowsReordering_(True)
84        self.imageBrowser.setAnimates_(True)
85        self.imageBrowser.setDraggingDestinationDelegate_(self)
86
87    # -------------------------------------------------------------------------
88    #	updateDatasource:
89    #
90    #	This is the entry point for reloading image browser data and
91    #   triggering setNeedsDisplay.
92    # -------------------------------------------------------------------------
93    def updateDatasource(self):
94        # Update the datasource, add recently imported items.
95        self.images.extend(self.importedImages)
96
97        # Empty the temporary array.
98        del self.importedImages[:]
99
100        # Reload the image browser, which triggers setNeedsDisplay.
101        self.imageBrowser.reloadData()
102
103
104    # -------------------------------------------------------------------------
105    #	isImageFile:filePath
106    #
107    #	This utility method indicates if the file located at 'filePath' is
108    #	an image file based on the UTI. It relies on the ImageIO framework for
109    #   the supported type identifiers.
110    #
111    # -------------------------------------------------------------------------
112    def isImageFile_(self, filePath):
113        isImageFile = False
114        uti = None
115
116        url = CFURLCreateWithFileSystemPath(None, filePath, kCFURLPOSIXPathStyle, False)
117
118        res, info =  LSCopyItemInfoForURL(url, kLSRequestExtension | kLSRequestTypeCreator, None)
119        if res == 0:
120            # Obtain the UTI using the file information.
121
122            # If there is a file extension, get the UTI.
123            if info[3] != None:
124                    uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, info[3], kUTTypeData)
125
126            # No UTI yet
127            if uti is None:
128                # If there is an OSType, get the UTI.
129                typeString = UTCreateStringForOSType(info.filetype)
130                if typeString != None:
131                    uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassOSType, typeString, kUTTypeData)
132
133            # Verify that this is a file that the ImageIO framework supports.
134            if uti is not None:
135                supportedTypes = CGImageSourceCopyTypeIdentifiers()
136
137                for item in supportedTypes:
138                    if UTTypeConformsTo(uti, item):
139                        isImageFile = True
140                        break
141
142        return isImageFile
143
144    # -------------------------------------------------------------------------
145    #	addAnImageWithPath:path
146    # -------------------------------------------------------------------------
147    def addAnImageWithPath_(self, path):
148        addObject = False
149
150        fileAttribs = NSFileManager.defaultManager().fileAttributesAtPath_traverseLink_(path, True)
151        if fileAttribs is not None:
152            # Check for packages.
153            if NSFileTypeDirectory == fileAttribs[NSFileType]:
154                if not NSWorkspace.sharedWorkspace().isFilePackageAtPath_(path):
155                    addObject = True	# If it is a file, it's OK to add.
156
157            else:
158                    addObject = True	# It is a file, so it's OK to add.
159
160        if addObject and self.isImageFile_(path):
161            # Add a path to the temporary images array.
162            p = myImageObject.alloc().init()
163            p.setPath_(path)
164            self.importedImages.append(p)
165
166    # -------------------------------------------------------------------------
167    #	addImagesWithPath:path:recursive
168    # -------------------------------------------------------------------------
169    def addImagesWithPath_recursive_(self, path, recursive):
170        dir = os.path.isdir(path)
171        if dir:
172            content = os.listdir(path)
173            # Parse the directory content.
174            for fn in content:
175                if recursive:
176                    self.addImagesWithPath_recursive_(
177                            os.path.join(path, fn), True)
178                else:
179                    self.addAnImageWithPath_(os.path.join(path, fn))
180
181        else:
182            self.addAnImageWithPath_(path)
183
184    # -------------------------------------------------------------------------
185    #	addImagesWithPaths:paths
186    #
187    #	Performed in an independent thread, parse all paths in "paths" and
188    #	add these paths in the temporary images array.
189    # -------------------------------------------------------------------------
190    def addImagesWithPaths_(self, paths):
191        pool = NSAutoreleasePool.alloc().init()
192
193        for path in paths:
194            isdir = os.path.isdir(path)
195            self.addImagesWithPath_recursive_(path, isdir)
196
197        # Update the data source in the main thread.
198        self.performSelectorOnMainThread_withObject_waitUntilDone_(
199                'updateDatasource', None, True)
200
201        del pool
202
203
204    #pragma mark -
205    #pragma mark actions
206
207    # -------------------------------------------------------------------------
208    #	addImageButtonClicked:sender
209    #
210    #	The user clicked the Add button.d
211    # -------------------------------------------------------------------------
212    @objc.IBAction
213    def addImageButtonClicked_(self, sender):
214        path = openFiles()
215        if path:
216            # launch import in an independent thread
217            NSThread.detachNewThreadSelector_toTarget_withObject_(
218                    'addImagesWithPaths:', self, path)
219
220    # -------------------------------------------------------------------------
221    #	addImageButtonClicked:sender
222    #
223    #	Action called when the zoom slider changes.
224    # -------------------------------------------------------------------------
225    @objc.IBAction
226    def zoomSliderDidChange_(self, sender):
227        # update the zoom value to scale images
228        self.imageBrowser.setZoomValue_(sender.floatValue())
229
230        # redisplay
231        self.imageBrowser.setNeedsDisplay_(True)
232
233    # Implement the image browser  data source protocol .
234    # The data source representation is a simple mutable array.
235
236    # -------------------------------------------------------------------------
237    #	numberOfItemsInImageBrowser:view
238    # -------------------------------------------------------------------------
239    def numberOfItemsInImageBrowser_(self, view):
240        # The item count to display is the datadsource item count.
241        return len(self.images)
242
243    # -------------------------------------------------------------------------
244    #	imageBrowser:view:index:
245    # -------------------------------------------------------------------------
246    def imageBrowser_itemAtIndex_(self, view, index):
247        return self.images[index]
248
249
250    # Implement some optional methods of the image browser  datasource protocol to allow for removing and reodering items.
251
252    # -------------------------------------------------------------------------
253    #	removeItemsAtIndexes:
254    #
255    #	The user wants to delete images, so remove these entries from the data source.
256    # -------------------------------------------------------------------------
257    def imageBrowser_removeItemsAtIndexes_(self, view, indexes):
258        self.images.removeObjectsAtIndexes_(indexes)
259
260    # -------------------------------------------------------------------------
261    #	moveItemsAtIndexes:
262    #
263    #	The user wants to reorder images, update the datadsource and the browser
264    #	will reflect our changes.
265    # -------------------------------------------------------------------------
266    def imageBrowser_moveItemsAtIndexes_toIndex_(self, browser, indexes, destinationIndex):
267        temporaryArray = []
268
269        # First remove items from the data source and keep them in a
270        # temporary array.
271        for index in reversed(sorted(list(indexes))):
272            if index < destinationIndex:
273                destinationIndex -= 1
274
275            obj = self.images[index]
276            temporaryArray.append(obj)
277            del images[index]
278
279        # Then insert the removed items at the appropriate location.
280        for item in temporaryArray:
281            self.images.insertObject_atIndex_(item, destinationIndex)
282
283        return True
284
285
286    # -------------------------------------------------------------------------
287    #	draggingEntered:sender
288    # -------------------------------------------------------------------------
289    def draggingEntered_(self, sender):
290        return NSDragOperationCopy
291
292    # -------------------------------------------------------------------------
293    #	draggingUpdated:sender
294    # -------------------------------------------------------------------------
295    def draggingUpdated_(self, sender):
296        return NSDragOperationCopy
297
298    # -------------------------------------------------------------------------
299    #	performDragOperation:sender
300    # -------------------------------------------------------------------------
301    def performDragOperation_(self, sender):
302        pasteboard = sender.draggingPasteboard()
303
304        # Look for paths on the pasteboard.
305        data = None
306        if NSFilenamesPboardType in pasteboard.types():
307            data = pasteboard.dataForType_(NSFilenamesPboardType)
308
309        if data is not None:
310            # Retrieve  paths.
311            filenames, format, errorDescription = NSPropertyListSerialization.propertyListFromData_mutabilityOption_format_errorDescription_(
312                    data , kCFPropertyListImmutable, None, None)
313
314            # Add paths to the data source.
315            for fn in filenames:
316                self.addAnImageWithPath_(fn)
317
318            # Make the image browser reload the data source.
319            self.updateDatasource()
320
321        # Accept the drag operation.
322        return True
323