one-time blog

less is more. more or less

Постим в Инстаграм с компьютера

| Comments

Gramblr — простенький клиент для Инстаграма, позволяющий выкладывать картинки прямо с компьютера (Windows/Mac OS X), не прибегая к помощи мобильного телефона. Интерфейс у него простенький (если не сказать убогий). После запуска нужно указать ваши логин/пароль на Инстаграме, а потом можно выкладывать картинки.

Gramblr настолько прост, что умеет только загружать готовые картинки, поэтому изображения для загрузки надо сперва подготовить (JPEG, 650 пикселов по обеим сторонам, не более 500 килобайт). После загрузки можно добавить подпись к картинке. И все.

2GIS - карты для BB

| Comments

На днях натолкнулся на замечательное навигационное приложение для BlackBerry 102GIS. По функционалу приложение аналогично Google Maps, но карты устанавливаются на устройство и доступны офлайн, поэтому приложением вполне можно пользоваться не имея подключения к интернету.

При первом запуске приложение предложит установить карты нужных вам городов. В списке, кроме 191 города в России, присутствуют Украина (12 городов), Казахстан (4 города), Чехия, Италия, Кипр. Список поддерживаемых городов, я надеюсь, будет расти.

В 2GIS можно легко найти интересуюший вас объект (фирму, магазин и т.п.) по названию или по адресу. Можно искать по сфере деятельности. Правда, мне показалось, что поиск не отличается особой быстротой, но, главное, работает.

Приложение умеет следить за вашими перемещениями, отображая ваше текущее местоположение на карте в виде стрелки — как, впрочем, любая другая аналогичная программа.

На карте можно просматривать отмеченные на ней объекты — всё вполне интуитивно и удобно.

Еще программа умеет прокладывать маршруты. Прравда, делает она это ещё медленнее, чем ищет. Маршруты для поездок на общественном транспорте, на метро (зачем-то отдельно) и на автомобиле. Если маршрут оказался слишком коротким, вам предложат прогуляться пешком. :)

В оющем, очень достойное и удобное навигационное приложение для городских жителей. И, замечу, совершенно бесплатное!

File Selection Dialog in QPython

| Comments

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:

def chooseFile(title='Choose File', folder='.')

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:

import filechooser
filename = filechooser.chooseFile("Open File", myworkdir)

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 folder:

d = folder

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(...):

>>> help(os.path.join)

This show you short help message about the function os.path.join:

join(a, *p)
    Join two or more pathname components, inserting '/' as needed.
    If any component is an absolute path, all previous path components
    will be discarded.

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:

>>> from glob import glob
>>> import os.path
>>> d = '.'
>>> os.path.join(d, '*')
'./*'
>>> glob(os.path.join(d, '*'))
['./filechooser.py', './utils.py', './file.py']
>>>

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)[1] (tuples' and lists' index starts with 0, so x[1] 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 = [ os.path.split(fn)[1] for fn in glob(os.path.join(d, '*')) ]

Variable 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:

flist = []
for fn in glob(os.path.join(d, '*')):
    tail = os.path.split(fn)[1]
    flist.append(tail)

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 d != '/':
    flist.insert(0, '..')

If you don't know how insert() works, type in the console: help([].insert).

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:

selected = _dialog(title, flist)

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:

if selected is None:
    return None

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 abspath() understands ".." 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 = os.path.abspath(os.path.join(d, flist[selected]))
if not os.path.isdir(d):
    return d

If 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:

def chooseFile(title='Choose File', folder='.'):
    '''Display choose file dialog with title and a list of
    files/folders in specified folder and allow user to
    browse file system and select file.
    Return full name of the file choosen or None
    '''
    import os.path
    from glob import glob
    d = folder
    while True:
        flist = [ os.path.split(fn)[1] for fn in glob(os.path.join(d, '*')) ]
        if d != '/':               # if it is not root

            flist.insert(0, '..')  # add parent 

        selected = _dialog(title, flist)
        if selected is None:       # user cancelled

            return None
        d = os.path.abspath(os.path.join(d, flist[selected]))
        if not os.path.isdir(d):
            return d

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:

def _dialog(title, flist):
    '''display dialog with list of files/folders title
    allowing user to select any item or click Cancel 
    get user input and return selected index or None
    '''
    import androidhelper
    droid = androidhelper.Android()
    droid.dialogCreateAlert(title, '')
    droid.dialogSetItems(flist)
    droid.dialogSetNegativeButtonText('Cancel')
    droid.dialogShow()
    resp = droid.dialogGetResponse()
    droid.dialogDismiss()
    print resp
    if resp.result.has_key('item'):
        print 'RETURN:', resp.result['item']
        return resp.result['item']
    else:
        return None

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 (droid.dialogDismiss()).

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 resp.result.has_key('item') returns True if dictionary resp.result has a key "item", and 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! :)

BlackBerry OS 10.2.1 — первые впечатления

| Comments

Итак, BlackBerry OS обновилась до версии 10.2.1. Я тут не буду пересказывать официальные пресс-релизы. Расскажу только о том, что понравилось лично мне.

Улучшенное меню быстрых настроек

То, которое «вытягивается» с верха экрана. Оно стало настраиваемым, многие полезные вещи можно туда понапихать: состояние системы (заряд аккумулятора в процентах показывается прямо в меню — весьма удобно), включение/выключение блютуса, вайфая, NFC и т.п. Очень порадовало появление фонарика.

FM радио

Оказывается, в Q10, Z10 и др. аппаратах есть встроенный FM-радиоприемник — а мужики-то не знали! Только он до выхода 10.2.1 был заблокирован! Теперь можно еще и радио слушать. Через приложение Музыка.:)

Улучшенная поддержка приложений Андроида

Если раньше установка Андроид-приложений происходила при помощи танцев с бубном (надо было раздобыть APK-файл, сконвертировать его на компе в BAR-файл, потом заливать его с помощью каких-то левых утилит), то теперь я могу ставить приложения прямо из Google Play. Единственное, что надо было сделать — это установить приложение Snap и создать для него пароль в Гугле (у меня включена двухфакторная аутентификация). Вот тут перечислены другие способы установки. Личные ощущения: Андроид-приложения стали работать надежнее, глюков (например, со вводом с клавиатуры) стало заметно меньше.

В общем, после обновления BlackBerry нравится мне еще больше. Когда меня спросили как мне этот аппарат, я ответил в таком духе, что этому аппарату еще бы полноценный Андроид на борт — цены б ему не было! Не в том, конечно, смысле, что BlackBerry OS 10 плоха, а в том, что под Андроидом приложения разнообразней и функциональней (по крайней мере, среди тех, что я использую). Так вот, с апдейтом до 10.2.1 это мое пожелание отчасти выполнено. :)

Instagram for BlackBerry 10

| Comments

Ну вот, наконец-то появилось нормальное, полноценное, нативное (и бесплатное!) приложение для Инстаграма на BlackBerry 10! iGrann позволяет смотреть ленту, лайкать, оставлять комментарии, искать и, конечно же, постить свои фоточки в Инстаграм.

В отличие от портированной с Андроида версии самого Инстаграма, не глючит. Отсутствуют инстаграмовские родные фильтры (ну и фиг с ними — я ими все равно почти никогда не пользуюсь). Не поддерживается загрузка видео и Instagram Direct. В остальном очень приятное приложение, умеющиее, кроме всего прочего, уведомлять штатным для Блэкберри способом и меняющее картинки в Active Frame (период обновления, как и тему, и некоторые другие параметры можно настроить).


iGrann is the first full native and free client for Instagram™ on BlackBerry 10. It is not just a viewer, it is a fully featured app. You can browse feed, like and comment photos and videos, receive notifications, follow/unfollow people, search users and hashtags and, of course, upload photos!

Instagram Direct, upload videos, apply IG filters are not available in the current version but will be implemented soon. And it's free! :)

Фоторедакторы для BlackBerry 10

| Comments

Кажется, я наконец-то нашел пару толковых приложений для обработки фото на BlackBerry 10. Сначала я натолкнулся на Effetica — весьма мощный редактор, умеющий не только эффекты накладывать (которых у него, кстати, куча!) и рамки делать, но и позволяющий работать с уровнями, цветовым балансом, каналами и прочим, что обычно присутствует во «взрослых» фоторедакторах, но чего не хватает в мобильных приложениях.

В общем, весьма и весьма продвинутый редактор. Что мне понравилось: функция выравнивания горизонта — Straighten, кроме кропа есть еще *Pad — добавление полей для того, чтобы изображение вписалось в заданный формат (удобно чтобы, например, прямоугольное фото, не обрезая, привести к квадратному формату Инстаграма), Gamma correction позволяет быстро и аккуратно притемнить или осветлить картинку, просмотр EXIF изображения — Image info, куча разнообразных эффектов и настраиваемых рамок, возможность произвольного ресайза картинок.

Что не понравилось, так это работа Auto-levels — на мой взгляд, картинка становится «цианистой», голубоватой, чего я не люблю. Добывать солнечную золотистость приходится руками. Другой момент, который мне не очень понравился — приложение не запоминает пользовательствие настройки в инструментах, поэтому цвет рамки, например, всегда приходится заново менять с белого на черный.

Впрочем, это уже скорее мелочи. По большому счету Effetica — едва ли не самый продвинутый фоторедактор для BlackBerry. Стоит это приложение не мало — без малого 4 бакса. Но оно того стоит!

Второе приложение — Mono Art — я нашел по следам первого (оба они сделаны I&N MobiSoft.com). На самом деле, это то, что я давно хотел даже больше самого продвинутого в мире фоторедактора. Основная функция приложения — перевод фотографий в черно-белый вариант. В приложении около 30 пресетов, имитирующих разные виды черно-белой пленки. Кроме того, картинку можно тонировать, обрезать и выполнять еще несколько базовых функций по коррекции. Собственно, Mono Art выглядит (и является) усеченной и специализированной версией Effetica.

Приложение тоже не бесплатно, но за хороший Ilford Delta 100 и Kodak Tri-X два бакса не жалко. :)

Hello, QPython!

| Comments

Well, after you became a bit more familiar with QPython, let's create our first program in QPython. Obviously, it will be helloworld.py. ;)

Start QPython, open editor and enter the following code:

hello0.py
import androidhelper
droid = androidhelper.Android()
droid.makeToast('Hello, Username!')

No wonder, it's just similar to any other hello-world program. When executed, it just shows pop-up message on the screen (see screenshot on the top). Anyway, it's a good example of QPython program.

It begins with import androidhelper — the most useful module in QPython, which encapsulates almost all interface with Android, available in Python. Any script developed in QPython starts with this statement (at least if it claims to communicate with user). Read more about Python library here and import statement here.

By the way, if you're going to make your script compatible with SL4A, you should replace the first line with the following code (and use android instead androidhelper further in the program):

try:
    import androidhelper as android
except ImportError:
    import android

Ok, next we're creating an object droid (actually a class), it is necessary to call RPC functions in order to communicate with Android.

And the last line of our code calls such function, droid.makeToast(), which shows a small pop-up message (a "toast") on the screen.

Well, let's add some more functionality. Let it ask the user name and greet them.

We can display a simple dialog box with the title, prompt, edit field and buttons Ok and Cancel using dialogGetInput call. Replace the last line of your code and save it as hello1.py:

hello1.py
import androidhelper
droid = androidhelper.Android()
response = droid.dialogGetInput("Hello", "What is your name?")

Well, I think it should return any response, any user reaction. That's why I wrote response = .... But what does the call actually return? Let's check. Just add print statement after the last line:

hello1.py
import androidhelper
droid = androidhelper.Android()
response = droid.dialogGetInput("Hello", "What is your name?")
print response

Then save and run it...

Oops! Nothing printed? Don't worry. Just pull notification bar and you will see "QPython Program Output: hello1.py" — tap it!

Voilà!

As you can see, droid.dialogGetInput() returns a JSON object with three fields. We need only one — result which contains an actual input from user.

Let's add script's reaction:

hello1.py
import androidhelper
droid = androidhelper.Android()
response = droid.dialogGetInput("Hello", "What is your name?")
print response
message = 'Hello, %s!' % response.result
droid.makeToast(message)

Last two lines (1) format the message and (2) show the message to the user in the toast. See Python docs if you still don't know what % means.

Wow! It works! ;)

Now I'm going to add a bit of logic there. Think: what happen if the user clicks Cancel button, or clicks Ok leaving the input field blank?

You can play with the program checking what contains response variable in every case.

First of all, I want to put text entered by user to a separate variable: name = response.result. Then I'm going to check it, and if it contains any real text, it will be considered as a name and will be used in greeting. Otherwise another message will be shown. Replace fifth line message = 'Hello, %s!' % response.result with the following code:

name = response.result
if name:
    message = 'Hello, %s!' % name
else:
    message = "Hey! And you're not very polite, %Username%!"

Use < and > buttons on the toolbar to indent/unindent lines in if-statement (or just use space/backspace keys). You can read more about indentation in Python here; if-statement described here.

First of all, we put user input to the variable name. Then we check if name contains anything? In case the user left the line blank and clicked Ok, the return value is empty string ''. In case of Cancel button pressed, the return value is None. Both are treated as false in if-statement. So, only if name contans anything meaninful, then-statement is executed and greeting "Hello, ...!" shown. In case of empty input the user will see "Hey! And you're not very polite, %Username%!" message.

Ok, here is the whole program:

hello2.py
import androidhelper
droid = androidhelper.Android()
response = droid.dialogGetInput("Hello", "What is your name?")
print response
name = response.result
if name:
    message = 'Hello, %s!' % name
else:
    message = "Hey! And you're not very polite, %Username%!"
droid.makeToast(message)

Next time we will have closer look to androidhelper package. Stay tuned! :)

QPython - How To Start

| Comments

You wanna stop programming and start living start programming on Android but hate Java have no Java skills? It's easy!

Just go to Google Play Market and install QPython on your device. QPython is Python implementation for Android. It is based on Scripting Layer for Android (SL4A), but as for me, it is a bit more comfortable.

If you hate Python as well, you can install SL4A itself along with Perl, JRuby, Lua, BeanShell, JavaScript, Tcl, or shell instead. Go to that page and follow instructions.

After you installed QPython, start it in the usual way by tapping its icon in the menu. Screenshot on the top of this post shows what you should see when QPython just started.

By tapping the big button with Python logo in the center of the screen you can (1) launch any local script or project, (2) get script from QR code (funny brand new way to share and distribute your code).

If you swipe to the left instead of tapping, you will see another (second) main screen of QPython. As for me, it is much more useful and comfortable for developer.

Tools available here:

  • Console — yes, it's regular Python console, feel free to comunicate with interpreter directly
  • Editor — QPython has a nice text editor integrated with the rest, you can write code and run it without leaving the application
  • My QPython — here you can find your scripts and projects
  • System — maintain libraries and components: install and uninstall them
  • Package Index opens the page QPyPI in browser allowing to install packages listed there
  • Community leads to QPython Questions page. Signup and feel free to ask and answer questions in the community.

Next, let's see the console and the editor.

As I said before, there is an ordinary Python console. Many people usually use it to explore objects' properties, consult about syntax and test their ideas. You can type your commands directly and Python interpreter will execute them. You can open additional consoles by tapping the plus button (1) and use drop-down list on the upper left corner to switch between consoles (2). To close the console just tap the close button (3).

Please note, there will be notification in the notification bar unless you explicitly close the console and you always can reach the open console by tapping the notification.

The editor allows you obviously (hello Cap!) enter and modify text. Here you can develop your scripts, save them and execute. The editor supports Python syntax highlighting and shows line numbers (there is no ability to go to the line by number though).

When typing, you can easily control indentation level (which is critical for Python code) using two buttons on the toolbar (1). Next buttons on the toolbar are Save and Save As (2), then goes Run (3), Undo, Search, Recent Files and Settings buttons. Also there are two buttons on the top: Open and New (5).

When saving, don't forget to add .py estension to the file name since the editor don't do it for you.

That's it for now. Next time we'll try to write a short "helloworld" program and test it using QPython.

Python для BlackBerry 10

| Comments

Обновился Python для BlackBerry 10. Новая версия, кажется, стала более юзабельна и пригодна для использования.

Приложение является имплементацией Python 3.2.2 и позволяет разрабатывать и выполнять скрипты прямо на устройстве. При практическом отсутствии приложений этого класса (интерпретаторы языков программирования) в BlackBerry World, эта аппликуха является практически единственной в своем роде. Стоит оно два бакса, или не стоит — каждый решает для себя сам. Ну, и стоит ли переходить с 2.* на Python 3 каждый тоже решает сам. ;)

Quine in Lua

| Comments

Here is a simple quine in Lua:

s = [[s = %c%c%s%c%c
print(string.format(s,91,91,s,93,93))]]
print(string.format(s,91,91,s,93,93))