• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /macosx-10.10/pyobjc-45/pyobjc/pyobjc-framework-SearchKit-2.5.1/Examples/SearchKitExample1/
1from Cocoa import *
2from SearchKit import *
3
4import os
5
6class AppController (NSObject):
7
8    myWindow = objc.IBOutlet()
9
10    selectDirectoryButton = objc.IBOutlet()
11    directoryTextField = objc.IBOutlet()
12
13    numberOfDocumentsTextField = objc.IBOutlet()
14    numberOfTermsTextField = objc.IBOutlet()
15
16    buildIndexButton = objc.IBOutlet()
17
18    searchField = objc.IBOutlet()
19    searchResultsTextView = objc.IBOutlet()
20
21    directoryToIndex = objc.ivar()
22    myIndex = objc.ivar()
23
24
25    def awakeFromNib(self):
26        # set our default directory to index to be our home directory
27        self.directoryToIndex = NSHomeDirectory()
28        # go ahead and put that value into the UI right on wake-up
29        self.directoryTextField.setStringValue_(self.directoryToIndex)
30
31        # tell the search field cell that we only want to send
32        # search requests (the associated action) on pressing return
33        searchCell = self.searchField.cell()
34        searchCell.setSendsWholeSearchString_(True)
35        # the alternative (False) would be to get a search initiated
36        # with every keystroke - which may be what is desired in a
37        # prefix style search (like iTunes).
38
39
40    @objc.IBAction
41    def chooseDirectory_(self, sender):
42        # we are setting up an NSOpenPanel to select only a directory and then
43        # we will use that directory to choose where to place our index file and
44        # which files we'll read in to make searchable.
45        op = NSOpenPanel.openPanel()
46        op.setCanChooseDirectories_(True)
47        op.setCanChooseFiles_(False)
48        op.setResolvesAliases_(True)
49        op.setAllowsMultipleSelection_(False)
50        result = op.runModalForDirectory_file_types_(None, None, None)
51        if result == NSOKButton:
52            self.directoryToIndex = op.filename()
53            self.directoryTextField.setStringValue_(self.directoryToIndex)
54
55    @objc.IBAction
56    def buildIndex_(self, sender):
57        # we will need an NSFileManager object to do various checking of files.
58        fm = NSFileManager.defaultManager()
59
60        # you don't have to provide the index with a name, but we will...
61        indexName = "My Arbitrary Index Name"
62        # when you build an index on disk, you need to give it a file:# URL
63        # So we pick the directory that's been chosen and append "/myindex"
64        # onto it
65        indexFile = os.path.join(self.directoryToIndex, "myindex")
66        # and build an NSURL from that
67        fileUrlToIndex = NSURL.fileURLWithPath_(indexFile)
68
69        # When indexing documents, there's a number of index specific
70        # considerations that can be applied with search kit. To set any of
71        # them manually, we first need to create a Dictionary in which to hold
72        # them.
73        analysisDict = {}
74        # Then we can set things like the language to expect
75        # note that the constants associated with these attributes are
76        # CFStringRef's so we cast them to NSString to make it easy to insert
77        # into the dictionary.
78        # We could also do this all in procedural C with the native
79        # CoreFoundation components, but I find this is significantly easier
80        # - both to write and  to understand.
81        analysisDict[kSKLanguageTypes] = u"en"
82        # another example of setting an attribute, in this case the minimum term
83        # length
84        analysisDict[kSKMinTermLength] = 2
85        # when we hand this dictionary into the function to create the index, we
86        # simply cast it back to a CFDictionaryRef and everything just nicely
87        # moves with it. This toll-free bridging concept is so handy!
88
89        if fm.fileExistsAtPath_(indexFile):
90            # our index file already exists... if we try to create one anyone,
91            # the function will silently fail. So let's just be sure and
92            # delete it. in a proper function, we would recognize it already
93            # exists and attmept to open it for editing rather than recreate
94            # the whole thing.
95            fm.removeFileAtPath_handler_(indexFile, None)
96
97        # the function to create the on-disk index
98        self.myIndex = SKIndexCreateWithURL(
99                                        # the file:# URL of where to place the
100                                        # index file
101                                        fileUrlToIndex,
102                                        # a name for the index (this may be None)
103                                        indexName,
104                                        # the type of index
105                                        kSKIndexInverted,
106                                        # and our index attributes dictionary
107                                        analysisDict)
108        # note that the above function call will silently fail if you give it a
109        # directory and not a file... or if the index file already exists
110
111        if self.myIndex is None:
112            # we shouldn't get here...
113            NSLog("TROUBLE: index is None")
114            return
115
116        # display information about our newly created and completely blank index
117        self.displayIndexInformation()
118
119        # before we get into the meat of reading in these files, we need to tell
120        # the Search Kit to load the default extractor plugins so that the
121        # SearchKit can find the bits it needs to from the files. In
122        # MacOS 10.3, the only plugins supported are the default plugins,
123        # which include processing for plaintext, PDF, HTML, RTF, and
124        # Microsoft Word documents.
125        SKLoadDefaultExtractorPlugIns()
126
127        # we're just going to get a "short list". You could alternately use the
128        # NSDirectoryEnumerator class to recursively descend through all the
129        # files under a directory, and you would probably want to do it in
130        # some other thread, updating a progress bar or such to indicate
131        # something was happening. It can take a while (for example) to
132        # process every file in your home directory and beneath.
133
134        listOfFiles = fm.directoryContentsAtPath_(self.directoryToIndex)
135        for aFile in listOfFiles:
136            # the particular function we'll use like to get an NSURL
137            # object, so we'll create one and pass it over.
138
139            # create the URL with the filename
140            fileURL = NSURL.fileURLWithPath_(
141                    os.path.join(self.directoryToIndex, aFile))
142
143            # invoke a helper method to add this file into our index
144            self.addDocumentToIndex_(fileURL)
145
146        # once all the files have been added to the index, it's very important
147        # to flush the data down to disk. Without this step, the data resides
148        # in memory but isn't useful for searching and won't respond with the
149        # correct information about the index (number of documents, number of
150        # terms, etc).
151        if not SKIndexFlush(self.myIndex):
152            NSLog("A problem was encountered flushing the index to disk.")
153
154        NSLog("flushed index to disk")
155
156        # update the index information
157        self.displayIndexInformation()
158
159    @objc.IBAction
160    def search_(self, sender):
161        # do some sanity checking to make sure we're not going to run into some
162        # memory exception because we never initialized our SearchKit index
163        # properly
164        if self.myIndex is None:
165            msg = "No index has been created to against which to search."
166            NSLog(msg)
167            self.searchResultsTextView.setString_(msg)
168            return
169
170        # Since this is just an example, I am going to be lazy and just report
171        # the results of our search into a text field. I'm creating an
172        # NSMutableString to build up what will appear there.
173        textOfResults = ''
174
175        searchQuery = sender.stringValue()
176        textOfResults += "Searching for:"
177        textOfResults += searchQuery
178        textOfResults += "\n"
179
180        # to do a search, we need a SearchGroup
181        # so we start by creating an array of 1 item (our search index)
182        searchArray = [self.myIndex]
183
184        # and then we immediately turn around and use that to create
185        # a Search Kit Search Group reference.
186        searchgroup = SKSearchGroupCreate(searchArray)
187
188        # now that we have a searchgroup, we can request a result set
189        # from it with our search terms.
190        searchResults = SKSearchCreate(self.myIndex, searchQuery, kSKSearchRanked)
191
192        if searchResults is None:
193            msg = "Search function failed"
194            NSLog(msg)
195            self.searchResultsTextView.setString_(msg)
196            return
197
198        # now to go through the results, we can create an array for each
199        # SearchKit document and another for the scores, and then populate
200        # them from the SearchResults
201        busy, outDocumentIDsArray, scoresArray, resultCount=  SKSearchFindMatches(searchResults, # the search result set
202                                          10,
203                                          None,      # an array of SKDocumentID
204                                          None,      # an array of scores
205                                          1.0,       # max 1 sec
206                                          None       # outFoundcount
207                                          )
208        if busy:
209            textOfResults += "%d Results Found (still busy)\n"%(resultCount,)
210        else:
211            textOfResults += "%d Results Found\n"%(resultCount,)
212
213        assert resultCount == len(scoresArray)
214
215        # iterate over the results and tell the NSTextView what we found
216        for score, hitID in zip(scoresArray, outDocumentIDsArray):
217            hit = SKIndexCopyDocumentForDocumentID(self.myIndex, hitID)
218            if hit is None:
219                continue
220
221            documentName = SKDocumentGetName(hit)
222
223            textOfResults += "Score: %f ==> %s\n"%(score, documentName)
224
225        self.searchResultsTextView.setString_(textOfResults)
226
227    def addDocumentToIndex_(self, fileURL):
228        # do some sanity checking to make sure we're not going to run into some
229        # memory exception because we never initialized our SearchKit index
230        # properly
231        if self.myIndex is None:
232            NSLog("myIndex is None - not processing %@",
233                    fileURL)
234            return
235
236        NSLog("Processing %@", fileURL.absoluteString())
237        # create the Search Kit document object
238        # note that this method only accepts file:# style URL's
239        aDocument = SKDocumentCreateWithURL(fileURL)
240
241        # if you wanted to watch them process in, just uncomment the following
242        # 2 lines.
243        #NSLog("Name: %@", SKDocumentGetName(aDocument))
244        #NSLog("Scheme: %@", SKDocumentGetSchemeName(aDocument))
245
246        # add the document to the index
247        if not SKIndexAddDocument(self.myIndex, # a reference ot the index added to
248                                 aDocument, # the document we want to add
249                                 None, # this could be a mime type hint in the form
250                                      # of a CFStringRef
251                                  1):   # a boolean value indicating the document
252                                      # can be overwritten
253            NSLog("There was a problem adding %@", fileURL)
254
255
256    def displayIndexInformation(self):
257        if self.myIndex is not None:
258            # get the number of documents in the index
259            numberOfDocuments = SKIndexGetDocumentCount(self.myIndex)
260            # place it into the text field
261            self.numberOfDocumentsTextField.setIntValue_(numberOfDocuments)
262
263            # get the number of terms in the index
264            maxTerm = SKIndexGetMaximumTermID(self.myIndex)
265            # place it into the text field
266            self.numberOfTermsTextField.setIntValue_(maxTerm)
267
268        else:
269            self.numberOfDocumentsTextField.setStringValue_('N/A')
270            self.numberOfTermsTextField.setStringValue_('N/A')
271