Almost any program ask user for file or folder names. Android does not provide file dialog by default. Let's create our own.
It should be simple enough, but it should allow user to browse throuh the file system and select file. Also it should allow user to refuse selection by clicking Cancel button. Our function will take two arguments: text prompt (title) and initial folder to browse:
See here if you don't know how to define functions in Python.
Both parameters have default values — they will be used if you don't indicate an argument when calling the function.
'.' means "current folder".
You will use it as follows:
So, first of all we should list of files in the given directory (folder). There are several ways to do it in Python. We will use function
glob in module glob. This function returns list of paths matching a pattern given. For instance, if you need to get list of all Python files in the current directory, you can get it using
glob('*.py'). We need a list of the whole contents of the directory, so we will use
'*' pattern, which means "any file or folder name".
The item selected by user (folder or filename) will be kept in a variable
d. In case of filename it will be returned, otherwise we will show the directory's contents to the user. Initially we set it from argument
Then we should get the list of items of the directory
d. In order to do it, we join the file pattern
'*' with the directory name before passing it to the function
glob. We can do it using
os.path.join() function (os.path is extremely useful module, and we're going to use other functions from it). By the way, you can always get help on every module, every class, object or function in python. Just ask help(...):
This show you short help message about the function
Help function is the most useful features of Python and probably the most often used in the console.
So, let's check in the console what we are going to do:
Ok, there are three files in the current directory (obviously, you will see another file names). And we are ready now to show them to the user. But I don't really like these
./ at the beginning of each name showed to user. I want to show local file names without any path. Let's cut off them. There is a function split in the module
os.path, which splits a pathname and returns tuple "(head, tail)" where "tail" is everything after the final slash — this "tail" is what we need! So, assuming
fn contains any pathname, we could get the last portion as
os.path.split(fn) (tuples' and lists' index starts with 0, so
x is the second item in a tuple or list).
Stop! We need to cut off each item in our list which was returned by
glob()! And here is one thing I love in Python very much:
flist receives the list of last parts ("tails") of pathnames returned by
glob(). This line equals to the following code but much shorter and expressive:
See more about list comprehensions here.
Ok, even if you didn't understand how it works,
flist variable already contains the list of files and folders ready to display to user. ;)
Just one little thing:
glob() returns folders/files which is inside the certain directory. How a user can go outside? Let's add a "special item", indicating "upper, parent directory". It's a common practice to indicate this special item as two dots:
... Only root directory (
/) has no parent, so we should check before adding:
If you don't know how
insert() works, type in the console:
Now, I propose to move platform-specific code to the separate function. In this case we could port our code to another system much more easily just replacing this function. Let a function
_dialog() draws dialog box itself and shows it to the user. It should take two arguments: title and filelist to show, and return selected item's index in the list. It should return None in case the user clicked "Cancel". So, our
chooseFile() function should call this function this way:
I'll show this functions later. And now we are going to analize what it has returned. First of all, we must check is it
None, or no, and return
None if yes:
Then, in case of not
None value, we should understand what the user selected. Since
selected is just a number, indicating selected position in the list,
flist[selected] gives us the item (filename) itself. Then we are going to get full, absolute path of the selected item. First of all, we're joining it with the current directory name:
os.path.join(d, flist[selected]), then we're using os.path.abspath() function to convert the value to the absolute path. Since
".." as a parent directory as well, we don't worry about this "special case". Finally, this absolute path should become a new value of
d, and if it is not a directory, it should be returned:
d now contains directory (folder) name, we must go back and repeat the whole process. So, I'm wrapping the whole code to "infinite" loop using while True: (it is not actually infinite since there is an "exit" — return operator).
Here is the whole code of our function:
Well, let's talk about user interface. We agreed to move it to the separate function
_dialog(). It must be placed before
chooseFile since it is called from the last. Here is the function:
The code is really simple and self-explaining. The first line after the function's definition and documentation string imports
androidhelper, the next one creates an object
droid (see my previous post if you don't know what is it), then we construct a dialog box with title, scrollable list of selectable items and a cancel button. There can be up to three buttons in a dialog: "positive" (i.e. "Ok"), "negative" ("Cancel") and "neutral". We need only negative button.
Then we show the dialog to the user (
droid.dialogShow()), get response (
resp = droid.dialogGetResponse()) and finally we must dismiss the dialog (
The last portion of the code analizes the response. If the user seleted one of the items in the list, the item's index returned as
'item' element of
rezult dictionary. Otherwise (the user clicked "Cancel"),
resp.result has no
'item' at all. Condition
True if dictionary
resp.result has a key "
False otherwise. In the first case we return the value of
resp.result['item'] — index of selected item in the list, and in the second case we return
None indicating that the user refused selection.
That's it. I understand that it's a bit hard to understand such long code. Believe me, it's hard to explain it as well! :) That's why I provide links to different explanations in Pythons documentation. If you are really new to Python, please read Python Tutorial first of all.
Finally, you can get the whole code of the file chooser here to use in your own programs. To test it just run — it should ask you for the file name. And the selected file name will be shown in the program's output.
Next time I'm going to make this code portable (so, we could use the same module on different platforms with different GUIs). Stay tuned! :)