Pull to refresh

PyS60: Сказ о том, как блокнот для Symbian писался

Reading time7 min
Views4.2K

Предисловие


Доброго времени суток, хабраюзеры!
Так уж получилось, что большую часть времени я нахожусь вдали от своего ПК, поэтому большинство его функций возлагаются на девайс, который всегда со мной — на смартфон на базе Symbian 9.4. Наряду с мультимедийными функциями мне очень часто бывает необходимо делать текстовые заметки, наброски статей для блога, а случается, что и приходится работать с (x)html и CSS. И если с мультимедийными функциями Nokia 5530XM справляется на ура, то работа с текстом осложнена отсутствием удобного для меня софта – мой привередливый нрав не признает те программные продукты, которые я имел возможность найти на просторах Интернета и протестировать на своем девайсе. Я нуждался в самом простом блокнотике, в то время как мне встречались текстовые редакторы, обремененные лишними для меня функциями. И когда я вспомнил, что спасение утопающих – дело рук самих утопающих, тогда и понял, что свои потребности удовлетворять придется самому.


Спасательный круг


Я, если честно, к программированию прямого отношения не имею, ибо мое хобби – дизайны для мобильных сайтов, но в то же время программирование для меня не чуждо – во время учебы в университете мне довелось познакомиться (вполне успешно) с классикой – с паскалем, с C/С++ и с Delphi, поэтому миновав этап грустных от безысходности вздохов я стал выбирать язык для разработки.
Особого выбора не было:
  • Qt/C++
  • mShell
  • Python

Для того, чтобы комфортно писать на Qt/C++, необходимо много чего скачать и установить, что мне было лень.
mShell – паскалеподобный нераспространенный язык. После установки интерпретатора необходимо осуществить бесплатную процедуру регистрации через SMS, чего я не желал.
А вот Python меня заинтересовал – я раньше никогда не сталкивался с ним и мне было интересно познакомиться с чем-то новым на ниве программирования.

Готовимся к работе


Итак, выбор пал на Python. Теперь, чтобы начать писать нашу первую программу, нам надо подружить смартфон с этим самым Python’ом, для чего я скачал и установил Python 1.4.5, а в дополнение к нему набор модулей megaPyModulePack 2.0.1 и PythonScriptShell.
Для написания программ требуется текстовый редактор – скрипя зубами пришлось установить на смартфон нелюбимую мною софтинку LightText.
Ну а для перемещения написанных скриптов в папку Питона я выбрал старый добрый файловый менеджер X-plore.
Все скачали, все установили, все работает – Python встречает нас своей консолью:
image

Чего же мы хотим?


Чего же мы хотим от программы? Самую малость – возможность создать и сохранить в UTF-8 введенный нами текст. Отсюда делаем выводы о виде программы – большое поле ввода, менюшка из пары пунктов (Новый документ, Сохранить, Инфо) и кнопка выхода. Все.

Знание — сила


И без этой силы делать нам тут было бы нечего. Я черпал силы из четырех источников:
Книга Pankaj Nathani и Bogdan Galiceanu “Python on Symbian”
— Статьи Альберта Газетдинова на сайте mobi.ru
Учебник Python 2.6 на викибукс
— Форум allnokia.ru

С места в карьер


Не буду писать о том, как я читал справочную информацию и какие у меня этот процесс вызвал эмоции – все же это не роман, поэтому сразу перейду к процессу созидания.
Начнем с того, что поможем программе подружиться с великим и могучим русским языком:

ru=lambda x: x.decode('utf-8')


Теперь для того, чтобы программа не огорчала нас неведомыми символами, будет достаточно использовать функцию ru(), пример которой вы увидите совсем скоро.
Следующий шаг – подключение нужных нам модулей:
  • appuifw (от application user interface framework) – с помощью этого модуля мы будем создавать нехитрый интерфейс нашей программы.
  • os – работа с файлами, папками и их путями.
  • e32 – работа с сервисными функциями Symbian

Итак, подключаем:

import appuifw, os, e32


Теперь вполне логично было бы как-нибудь обозвать нашу будущую программу. Так как мегафункционалом она не отличается, то и название ей дадим соответствующее – P(rimitive)Pad.
Сообщим программе ее название:

appuifw.app.title = u"PPad"


С этого момента программа обрела свое имя. Оно отображается в верхней части окна вместо надписи «Python». Но мы его не увидим и сейчас объясню почему: я уже говорил, что в данном блокнотике я хочу видеть большое поле ввода. Модуль appuifw позволяет нам выбирать размер тела программы из трех возможных:
  • normal — тело программы будет занимать место между нижними кнопками Функции/Выход и верхней частью экрана, в которой отображаются название Программы и индикаторы сигнала и уровня заряда батареи.
  • large — на экране будут лишь тело программы и кнопки Функции/Выход.
  • full – тело программы будет занимать весь экран.

Я посчитал, что самым подходящим вариантом для нас является размер large:

appuifw.app.screen = "large"


Когда мы определились с размерами экрана, предлагаю сделать еще один шаг в сторону создания блокнота – определить тело документа как объект Text, чтобы мы могли вводить/выводить/редактировать текст:

appuifw.app.body = t = appuifw.Text()


Через переменную t мы сможем обращаться к этому объекту за введенным текстом и некоторыми другими просьбами, например, с просьбой о смене цвета текста. По умолчанию цвет текста зеленый, что меня не удовлетворяет – при такой расцветке мне не очень комфортно работать с текстом, поэтому я предпочту зеленому классический черный цвет:

t.color = 0x000000


При запуске программы нас будет встречать пустое текстовое поле. Давайте сделаем так, чтобы там было небольшое приветствие:

t.add(ru("Вас приветствует PPad - простейший текстовый редактор для быстрого создания и сохранения Ваших заметок."))


На этом примере вы можете увидеть, для чего была создана функция ru() в самой первой строке программы и как она используется.

Теперь, пожалуй, осталось довести до ума меню программы. На скриншоте вы можете видеть две клавиши: «Функции» (appuifw.app.menu) и «Выход» (appuifw.app.exit_key_handler). При нажатии на «Выход» программа мгновенно закроется, что не совсем правильно, ибо при случайном нажатии мы можем потерять все введенные данные, чего допустить нельзя. Значит начнем доводку именно с нее – назначим на нее функцию (назовем ее quit), которая бы спрашивала у юзера разрешение:

appuifw.app.exit_key_handler = quit


Ну и напишем саму функцию:

def quit():
		if appuifw.query(ru("Хотите выйти?"), "query") == 1: appuifw.app.set_exit()


appuifw.query(“Вопрос юзеру”, “тип”) – окошко для ввода данных. Бывает 7 типов – text (для ввода текста), code (для ввода пароля), number (для ввода числа), float (для ввода вещественного числа), date (для ввода даты), time (для ввода времени), и query (для вывода вопроса с вариантами ответа «Ок» или «Отмена»). В нашем случае мы спрашиваем хочет ли выйти пользователь и если пользователь действительно хочет, то нажимает «Ok», на что функция возвращает 1 и мы убедительно просим программу оставить нас.

После того, как мы решили вопрос с выходом из программы, пришла пора «причесать» клавишу «Функции», на которую мы назначим меню. Менюшка наша будет состоять из трех пунктов – Новый/Очистить, Сохранить в UTF-8 и Инфо. Задается каждый пункт в виде кортежа (“Название пункта”, выполняемая функция). Создадим наше меню:

appuifw.app.menu = [(ru("Новый/Очистить"), ClearDoc), (ru("Сохранить в UTF-8"), SaveDoc), (ru("Инфо"), AboutSoft)]


Теперь нам предстоит написать три функции – ClearDoc, SaveDoc и AboutSoft. Пойдем от простой к сложной, а значит начнем с функции AboutSoft:

def AboutSoft():
		appuifw.note(ru("Огромная благодарность:\nEvil_Penguin за поддержку\nJOIN_ME за вывод из тупика"), "info")


Функция appuifw.note выводит на экран окошко с текстом на несколько секунд, что мы и хотели увидеть в этой функции.

Функция создания нового документа тоже проста до безобразия – от нее требуется лишь очистить экран. В этом нам поможет та самая переменная t:

def ClearDoc():
		if appuifw.query(ru("Создать новый документ?"), "query") == 1:
			t.clear()
			t.set_pos(0)


С функцией appuifw.query мы уже встречались – с ее помощью мы снова спрашиваем ползователя, есть ли его воля на создание нового документа, и если таковая имеется, то очищаем поле (t.clear()) и для верности устанавливаем курсор в исходную позицию (t.set_pos(0)).

Осталась последняя функция – функция сохранения документа. Функция не сложная, но мы приправим ее несколькими условиями, чтобы было интереснее.
Итак, каков алгоритм функции?
1. Запрашиваем у пользователя название для сохраняемого файла
2. Проверяем, есть ли в памяти смартфона данный файл
3. Если такого файла не существует, то смело записываем все в файл
4. Если же файл существует, то спрашиваем пользователя, заменить файл или нет. Если юзер предпочтет заменить файл, то, соответственно, записываем все в файл, в противном случае уведомляем о том, что файл не сохранен.
5. Если при запросе (п.1) пользователь не ввел название, а нажал «Отмена», то уведомляем пользователя о том, что файл не сохранен.

Вот как функция выглядит вживую:
def SaveDoc():
	filename = appuifw.query(ru("Введите имя файла"), "text")
	if filename:
		fullpath = str(filename) + ".txt"
		fullpath = "c:\\" + fullpath
		if os.path.exists(fullpath) == 0:
			filewrite = open(fullpath, 'w')
			content = t.get().replace(u"\u2029", u"\r\n")
			encontent = content.encode("UTF-8")
			filewrite.writelines(encontent)
			filewrite.close()
			appuifw.note(ru("Файл " + fullpath + " сохранен!"), "conf")
		elif os.path.exists(fullpath) == 1:
			rewritefile = appuifw.query(ru("Файл уже существует! Заменить?"), "query")
			if rewritefile == 1:
				filewrite = open(fullpath, 'w')
				content = t.get().replace(u"\u2029", u"\r\n")
				encontent = content.encode("UTF-8")
				filewrite.writelines(encontent)
				filewrite.close()
				appuifw.note(ru("Файл " + fullpath + " сохранен!"), "conf")
			else:
					appuifw.note(ru("Файл НЕ сохранен!"), "error")
	else:
		appuifw.note(ru("Файл НЕ сохранен!"), "error")


При написании функции сохранения текста в файл у меня возникла одна проблема – содержимое записывалось в одну строчку, а на тех местах, где должен был быть перенос строки, красовался квадратик. Я все свои надежды в решении данной проблемы связывал с заменой в тексте \n на \r\n перед записью, однако надежды не оправдались. И не знаю, что бы я делал, если бы хороший человек с форума dimonvideo.ru с ником JOIN_ME не ниспослал бы мне решение траблы, которое заключалось в замене u"\u2029" на u"\r\n".

Остался последний штрих:

lock = e32.Ao_lock()
os.abort = lock.signal
lock.wait()


Данная конструкция поможет нам сделать так, чтобы приложение не закрывалось сразу же после запуска.
Ну и, пожалуй, все на данный момент.

Подводя итоги


Как же выглядит наша программа в конечном счете?
Вот так

А как выглядит наша программа на смартфоне?
Вот так:
image

Цели


Что хотел я: написать простенький блокнот прямо со смартфона.
Какие цели преследует данная статья: помочь таким же новичкам, как и я, познать некоторые моменты на практике.

Напоследок


Что же дальше? Казалось бы все, миссия выполнена. Ан нет! «Аппетит приходит во время еды», как все мы знаем. Чувство того, что ты создал нечто действительно тебе нужное, непередаваемо. Даже если это что-то ничтожно. Аппетит разожжен, идеи есть – значит быть и продолжению этой статьи, ведь скромному PPad.py предстоит стать PPad.sis со свистелками и перделками c преферансом и блудницами. И я надеюсь, что мой оптимизм из сегодняшних радужных ожиданий завтра превратится в факт.
Спасибо за внимание.
Tags:
Hubs:
+16
Comments21

Articles