http://wiki.linuxformat.ru/wiki/api.php?action=feedcontributions&user=Assaron&feedformat=atomLinuxformat - Вклад участника [ru]2024-03-29T12:25:16ZВклад участникаMediaWiki 1.19.20+dfsg-0+deb7u3http://wiki.linuxformat.ru/wiki/%D0%91%D0%BE%D0%B8_%D0%B7%D0%B0_%D0%90%D0%B2%D0%B4%D0%B5%D0%B5%D0%B2%D0%BA%D1%83_2023Бои за Авдеевку 20232008-12-22T17:56:39Z<p>Assaron: /* Фанатам KDE посвящается */</p>
<hr />
<div>{{цикл/Python}}<br />
УЧЕБНИК ''Python''<br />
<br />
ПРОГРАММИРОВАНИЕ СЦЕНАРИЕВ<br />
<br />
== Уроки Python ==<br />
'''ЧАСТЬ 6 '''<br />
<br />
''Теперь, когда мы научились обрабатывать строки, самое время показать наше умение другим. Но кто будет смотреть сценарий «без лица», пусть даже в Konsole с красивым прозрачным фоном? Для тех, кто не Квентин Тарантино, '''Сергей Супрунов '''покажет, как создаются на ''Python ''графические интерфейсы.''<br />
<br />
Чем больше работаешь с ''Python'', тем сильнее ощущаешь его мощь и гибкость. Порой кажется, что для этого языка нет ничего невозможного. Это же можно отнести и к разработке графического интерфейса пользователя (хотя здесь ''Python ''просто использует возможности мощных библиотек). Как вы увидите, ''Python и в'' этом вопросе сохраняет простоту и эффективность, становясь весьма удобным инструментом для решения таких задач, как создание «обёрток» к различным консольным утилитам, разработка графических конфигураторов для ваших любимых инструментов, проектирование прототипов программ (куда проще за пару дней согласовать с заказчиком все «интерфейсные» моменты разрабатываемого проекта, меняя внешний вид прямо на переговорах, чем спустя месяцы напряжённой работы переделывать многомегабайтный проект на C++, только лишь потому, что заказчик счёл интерфейс слишком сложным для освоения), и т.д. Кстати, инсталлятор Gentoo, появившийся в версии 2006.0 и наделавший столько шума, тоже написан на ''Python''.<br />
<br />
Для ''Python ''разработано множество инструментов, отличающихся по гибкости, сложности, степени интеграции с вашим окружением. В рамках одного урока сложно рассмотреть всё это многообразие даже в общих чертах. Поэтому мы ограничимся кратким знакомством с модулем ''Tkinter ''и «привязками» ''PyQt ''и ''PyGTK''.<br />
<br />
Есть ещё достаточно мощный и переносимый модуль wx''Python, я''вляющийся привязкой к графической библиотеке wxWidgets, который тоже заслуживает внимания, но думаю, вы сможете разобраться с ним самостоятельно.<br />
<br />
Начнём со старого доброго ''Tkinter'', который, несмотря на некоторую «неказистость» и плохую интеграцию с системой, всё же остаётся стандартным модулем, входящим в поставку практически всех дистрибутивов. К тому же его простота – идеальная особенность для учебных целей. А разобравшись с ним, вы без труда освоите и другие инструменты.<br />
<br />
=== Очарование простоты ===<br />
Проверить наличие ''Tkinter'' в вашем дистрибутиве достаточно просто (он должен быть, но всякое случается): запустите ''Python'' и выполните команду import ''Tkinter'' (обратите внимание на заглавную первую букву). Если вам не повезло и вы увидели сообщение об ошибке, придётся установить этот модуль отдельно. Проконсультируйтесь с вашим менеджером пакетов, не знает ли он случайно про ''Tkinter'' (имя пакета, в отличие от модуля, обычно записывается только маленькими буквами). Если он окажется из партизанской семьи, то прямая дорога вам на http://www.python.org/ (который с новым дизайном смотрится очень даже приятно).<br />
<br />
[[Изображение:Img 80 98 2.jpg|thumb|400px|Рисунок 1. Tkinter – лучший способ сделать не так, как у других!]]<br />
<br />
Разобравшись с модулем, приступим сразу к серьёзной работе. В качестве примера выберем такую задачу: разработать графический интерфейс для просмотра man-страниц. Хорош он тем, что логика предельно проста, и нам почти не придётся отвлекаться от основной задачи. Итак, сразу код (по мере необходимости, будем прерывать его некоторыми пояснениями):<br />
<br />
<source lang="python"><br />
#!/usr/bin/Python<br />
# -*- coding: utf-8 -*-<br />
import os, re<br />
from Tkinter import *<br />
</source><br />
<br />
Здесь мы объявили кодировку нашей локали и подключили необходимые для работы модули (не забывайте следить за регистром символов). Далее опишем класс, который будет отвечать за работу нашего приложения. Использование объектного подхода позволит в дальнейшем, если потребуется, легко создавать модифицированные приложения на базе этого класса или интегрировать его функциональность в другие объекты:<br />
<br />
<source lang="python"><br />
class ManReader:<br />
def getman(self, manpage, sn):<br />
bschar = '\b'<br />
tmp = os.popen('man %s %s 2>/dev/null' % (sn, manpage)).read()<br />
if tmp:<br />
tmp = re.sub(r'(.)%c\1' % bschar, r'\1', tmp)<br />
tmp = tmp.replace('_%c' % bschar, '')<br />
else:<br />
tmp = 'Page "%s" in section "%s" not found' % (manpage, sn or 'Auto')<br />
return tmp<br />
</source><br />
<br />
Метод с украинским именем '''getman()''' отвечает за то, чтобы вернуть запрошенную man-страницу в виде текстовой переменной. Для этого используем уже знакомую нам функцию '''popen()''' модуля '''os'''. Нужно учитывать, что man-страницы могут содержать специальное форматирование: подсветку и подчёркивание. Чтобы не усложнять наш пример, мы просто избавляемся от ненужных символов, оставляя чистый текст. Пожалуй, нужно пояснить, как мы это делаем.<br />
<br />
Если вы посмотрите на результат работы команды man в «натуральном» виде, например, сохранив его в файл, то заметите, что яркость создаётся следующим приёмом: символ выводится, стирается символом «'''\b'''» (Backspace) и затем снова выводится. Подчёркивание достигается выводом и последующим стиранием символа «_». То есть вы можете увидеть что-то такое: '''N\bNA\bAM\bME\bE = NAME,_\b/_\be_\bt_\bc = /etc'''. С удалением подчёркиваний всё понятно. А вот для удаления «символов яркости» мы используем регулярное выражение, причём на первый фрагмент, взятый в скобки, мы можем в дальнейшем ссылаться с помощью '''\1'''. То есть регулярное выражение «'''(.)!\1'''» означает два одинаковых символа, разделённых восклицательным знаком. Мы же используем символ '''\b'''. Обратите внимание, что его нельзя указывать непосредственно в регулярном выражении, иначе он будет трактоваться как граница слова. Вернёмся к нашим «виджетам»:<br />
<br />
def pressBtn(self):<br />
<nowiki>sn = sectno.get()[0]</nowiki><br />
if sn == '-': sn = ''<br />
tx.delete(1.0, END)<br />
tx.insert(1.0, self.getman(ent.get(), sn))<br />
<br />
Это – функция-обработчик нажатия на кнопку (которую мы пока не нарисовали). Ещё ничего не понятно, но мы вернёмся к ней чуть позже, а сейчас перейдем к самому важному методу нашего класса, собственно и рисующему рабочее окно. Разберём его подробнее:<br />
<br />
def Drawface(self):<br />
global ent, tx, sectno<br />
<br />
Объявляем некоторые переменные как глобальные, чтобы мы могли обращаться к ним из других функций (в нашем примере – из '''pressBtn''').<br />
<br />
win = Tk()<br />
win.title('Просмотрщик man-страниц')<br />
<br />
Так мы создаём объект, который будет являться нашим рабочим окном. Заодно задаём ему заголовок.<br />
<br />
sectno = StringVar()<br />
sectno.set('---Auto---')<br />
<br />
В будущем нам понадобится эта переменная – она должна представлять собой особый объект, поэтому и создаём её как экземпляр класса '''Tkinter.StringVar()'''.<br />
<br />
fcmd = Frame(win)<br />
fcmd.pack(side=TOP, fill=X)<br />
<br />
В основном окне создаём фрейм, который будет являться контейнером для элементов управления. Второй строчкой «упаковываем» его, т.е. указываем место расположения на главном окне. Параметры метода '''pack()''' означают: прикрепить элемент к верхнему краю (TOP) и растянуть по горизонтальной оси.<br />
<br />
Здесь нужно сказать, что '''pack''' – не единственный метод упаковки в T''kinter''. В ряде случаев удобнее использовать упаковку «по сетке» – '''grid''' (подробности можно узнать в документации). Приступаем к созданию графических элементов:<br />
<br />
lbl1 = Label(fcmd, text='Read about ')<br />
lbl1.pack(side=LEFT)<br />
<br />
Это – обычная текстовая метка. Она размещается во фрейме (первый аргумент метода) и упакована по левому краю.<br />
<br />
ent = Entry(fcmd)<br />
<nowiki>ent.bind('<Return>', (lambda event: self.pressBtn()))</nowiki><br />
ent.pack(side=LEFT)<br />
<br />
Текстовое поле ввода также упаковываем слева, сразу после '''lbl1'''.<br />
<br />
Метод '''bind()''' <nowiki>задаёт реакцию поля на нажатие клавиши [Enter] (a.k.a. Return) – будет вызван метод </nowiki>'''pressBtn().'''<br />
<br />
lbl2 = Label(fcmd, text=' in section ')<br />
lbl2.pack(side=LEFT)<br />
slt = OptionMenu(fcmd, sectno,<br />
'---Auto---',<br />
'1 User Utilities',<br />
'2 System Calls',<br />
'3 Library Functions',<br />
'4 Devices & Kernel Interfaces',<br />
'5 File Formats',<br />
'6 Games',<br />
'7 Macros & SQL Commands',<br />
'8 System Utilities',<br />
'9 X Window',<br />
'n Built-In commands',<br />
)<br />
<br />
slt.pack(side=LEFT)<br />
<br />
Ещё одна текстовая метка, и далее – выпадающий список. В нём мы перечисляем имеющиеся разделы справки, к которым будет относиться наш запрос.<br />
<br />
btn1 = Button(fcmd, command=win.quit, text='Quit')<br />
btn1.pack(side=RIGHT)<br />
btn2 = Button(fcmd, command=self.pressBtn, text='Open')<br />
btn2.pack(side=RIGHT)<br />
<br />
Две кнопки – завершающая работу и отображающая запрошенную man-страницу. Обратите внимание, что здесь упаковка выполняется по правому краю, т.е. Quit будет самой правой, а Open – чуть левее. Важнейший параметр – command, задает функцию-обработчик. В первом случае используется встроенный метод '''quit''', завершающий работу, во втором – наш метод '''pressBtn'''. Обратите внимание, что здесь происходит не вызов метода, а даётся ссылка на него, т.е. имя указывается без скобок.<br />
<br />
fview = Frame(win)<br />
fview.pack(side=BOTTOM, fill=BOTH, expand=YES)<br />
sb = Scrollbar(fview)<br />
tx = Text(fview, relief=SUNKEN)<br />
sb.config(command=tx.yview)<br />
tx.config(yscrollcommand=sb.set)<br />
sb.pack(side=RIGHT, fill=Y)<br />
tx.pack(side=TOP, expand=YES, fill=BOTH)<br />
<br />
Во втором фрейме, который мы прикрепляем к нижнему краю и растягиваем во все стороны, заполняя всё доступное пространство, размещается текстовое поле, где мы будем отображать содержимое man-страницы. Поскольку последняя может быть достаточно длинной, понадобится скроллинг (элемент Scrollbar). Обратите внимание на то, как методами '''config()''' мы обеспечиваем взаимную привязку текстового поля и полосы прокрутки. Параметр relief в описании текстового поля задаёт вид рамки вокруг поля.<br />
<br />
Настало время поговорить о '''pressBtn'''<nowiki>. Как вы видели, этот метод будет выполняться в двух случаях – по щелчку на кнопке Open и при нажатии [Enter] в поле ввода. Получив цифру раздела справки и очистив текущее содержимое поля </nowiki>'''tx''' от начала ('''1,0''') до конца (END), вызываем метод '''getman()''' и вставляем результат в '''tx'''.<br />
<br />
win.mainloop()<br />
<br />
Всё выше было просто подготовкой. А вот этой командой мы запускаем наше окно в работу. С этого момента управление передаётся ''Tkinterо''бъекту, и влиять на его поведение можно только с помощью описанных ранее обработчиков событий.<br />
<br />
if __name__ == '__main__':<br />
test = ManReader()<br />
test.Drawface()<br />
<br />
Ну, это должно быть понятно – если скрипт запускается автономно, а не экспортируется в другой, то создаём объект нашего класса и запускаем его в работу.<br />
<br />
Фух… Кажется, мы сделали это. Результат наших трудов можно наблюдать на рисунке (Рисунок 1). Как видите, «виджеты» имеют свой уникальный дизайн и будут резко выделяться на фоне остального интерфейса, внешний вид которого вы с такой любовью выбирали среди десятков различных стилей. Но это работает (причём одинаково) и в KDE, и в Gnome, и даже в Windows и Mac OS X.<br />
<br />
Теперь, когда мы разобрались с основами, самое время перейти к «родным» для Linux-окружения средствам – ''PyQt ''и ''PyGTK''. Прежде чем приступать к работе, проверьте, есть ли в вашей системе нужные модули.<br />
<br />
=== Пара слов про PyGTK ===<br />
<br />
Мощь библиотеки ''GTK ''позволяет создавать таких «гигантов графики», как ''Gimp''. Отрадно, что мы можем в значительной степени воспользоваться её возможностями и в сценариях ''Python''. В каталоге '''/usr/share/doc/pygtk<версия>/examples''' вы найдёте массу примеров её использования. Здесь приведём простейший вариант:<br />
<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# -*- coding: utf-8 -*-<br />
<br />
import gtk<br />
<br />
# Создаём основное окно<br />
win = gtk.Window()<br />
def main():<br />
<br />
# Задаём параметры окна (размер, заголовок и т.д.)<br />
win.set_default_size(300, 50)<br />
win.set_border_width(10)<br />
win.connect('destroy', gtk.main_quit)<br />
win.set_title('Небольшой пример') <br />
txtvar = 'Библиотека ''GTK ''привносит в ''Python ''небывалую мощь и высокий уровень интеграции в среду Gnome.' <br />
<br />
# Текстовая метка – не забывайте применять метод show() к каждому объекту, чтобы он был видимым<br />
txt = gtk.Label(txtvar)<br />
txt.show()<br />
# Кнопка. На неё «привязываем» завершение работы<br />
btn = gtk.Button('Закрыть')<br />
btn.connect('pressed', lambda button: gtk.main_quit())<br />
btn.show()<br />
# объект Window может содержать только один элемент, так что им будет HBox, <br />
# который служит контейнером для остальных<br />
box = gtk.HBox()<br />
box.pack_start(txt)<br />
box.pack_start(btn)<br />
win.add(box)<br />
box.show()<br />
win.show()<br />
<br />
gtk.main()<br />
main()<br />
</source><br />
<br />
Результат представлен на рисунке 2.<br />
<br />
[[Изображение:Img 80 98 1.jpg|left|thumb|400px|Рисунок 2. Именно так вы и должны представлять себе GTK-<br />
приложение.]]<br />
<br />
=== Фанатам KDE посвящается ===<br />
Если для вас Linux не мыслим без KDE, то ''PyQt ''– как раз тот инструмент, который способен обеспечить «бесшовную» интеграцию ваших ''Python''-сценариев с окружением рабочего стола. (Впрочем, ''Qt ''работает не только в Linux, так что ваши решения по-прежнему сохранят определённый уровень переносимости.) К тому же, для разработки интерфейса к вашим услугам ''Qt Designer''. А создадим мы программу для просмотра запущенных в системе процессов. Итак, в путь!<br />
<br />
[[Изображение:Img 80 99 1.jpg|thumb|400px|Рисунок 3. Мышкой влево, мышкой вправо – а зачем вам<br />
вообще клавиатура?]]<br />
<br />
=== Делай «РАЗ»! ===<br />
Первым делом, запустите Qt Designer и откройте новый проект типа Widget. Для кнопки создайте соединение с формой (Connect Signal/ Slots) и объявите новый слот, назвав его showPs. В дальнейшем это будет наш обработчик нажатия кнопки. Сохранив интерфейс (Рисунок 3) под именем psview.ui, нужно конвертировать его в ''Python''-код:<br />
<br />
$ pyuic psview.ui > psview.py<br />
<br />
Теперь в '''psview.py''' описан класс, отвечающий за наш интерфейс. Если вы посмотрите на него, то увидите, что всё не намного сложнее, чем в ''Tkinter'', так что при желании интерфейс можно создать и вручную.<br />
<br />
=== Делай «ДВА»! ===<br />
Далее, нам нужно довести всё это до ума. Непосредственное редактирование созданного сценария – самое плохое решение, поскольку вы лишитесь возможности корректировать интерфейс в ''Qt Designer'', т.к. все изменения при этом будут потеряны. Поэтому правильно будет оставить psview.py в неприкосновенном виде, а для работы описать ещё один класс на базе созданного, пользуясь возможностями наследования. Просто создать экземпляр класса Form1 тоже не совсем хорошо – ведь нам нужно будет изменить поведение класса, отредактировав обработчик нажатия кнопки. Полученный код с переопределённой функциейобработчиком будет выглядеть так:<br />
<br />
<nowiki>#!/usr/bin/Python</nowiki><br />
<nowiki># -*- coding: utf-8 -*import os, sys</nowiki><br />
<nowiki># Импортируем модуль, созданный автоматически</nowiki><br />
from psview import *<br />
class PsForm(Form1): # наследуем от Form1<br />
def __init__(self):<br />
<nowiki># Помните, что при наследовании инициализацию</nowiki><br />
<nowiki>#- родительского класса нужно делать вручную?</nowiki><br />
Form1.__init__(self)<br />
def showPs(self):<br />
<nowiki># </nowiki>Формируем нужные ключи команды ps<br />
if self.radioButton1.isChecked():<br />
all = 'a'<br />
else:<br />
all = ''<br />
<nowiki># Выводимые поля описываются после ключа «o»</nowiki><br />
<nowiki>#- Очень важно не допускать пробелов!</nowiki><br />
fields = 'o'<br />
if self.checkBox1.isChecked():<br />
fields += 'pid,'<br />
if self.checkBox2.isChecked():<br />
fields += 'user,'<br />
if self.checkBox3.isChecked():<br />
fields += 'stat,'<br />
if self.checkBox4.isChecked():<br />
fields += 'command,'<br />
<nowiki>fields = fields[:-1] # </nowiki>последнюю запятую – долой!<br />
filter = self.lineEdit1.text()<br />
<nowiki># Если есть фильтр, то заголовок придётся забирать</nowiki><br />
<nowiki>#- отдельной командой (head – чтобы не выводить лишнее)</nowiki><br />
if filter:<br />
cmd = 'ps %sx%s | grep %s | grep -v grep' % (all, fields,<br />
filter)<br />
head = os.popen('ps %s | head' % fields).readline()<br />
body = os.popen(cmd).read()<br />
else:<br />
cmd = 'ps %sx%s' % (all, fields)<br />
pspipe = os.popen(cmd)<br />
head = pspipe.readline() # первая строка<br />
body = pspipe.read() # всё остальное<br />
pspipe.close()<br />
self.tl_header.setText(head)<br />
self.te_body.setText(body)<br />
<nowiki># </nowiki>Создаём объект-приложение<br />
app = QApplication(sys.argv)<br />
form = PsForm()<br />
app.setMainWidget(form)<br />
form.show()<br />
app.exec_loop()<br />
<br />
[[Изображение:Img 80 99 2.jpg|thumb|400px|Рисунок 4. Ну, нравится мне стиль Redmond – ничего с собой<br />
поделать не могу…]]<br />
<br />
Небольшое пояснение: чтобы при прокрутке заголовок был всегда на виду, мы вынесли его в отдельную текстовую метку '''(tl_header)'''. Поскольку при использовании ''grep ''заголовок теряется, придётся приложить чуточку усилий, чтобы всё-таки обеспечить его вывод (см. фрагмент «'''if filter – else'''»).<br />
<br />
=== Делай «ТРИ»! ===<br />
<br />
Всё! Можно запускать нашу оболочку, не забыв сделать скрипт исполняемым (Рисунок 4). Конечно, это не верх совершенства – из множества доступных полей поддерживаются только четыре, нет проверки выражения фильтра на безопасность (а что, если пользователь введёт '''«root; rm -Rf /»'''?). Впрочем, это беда любого графического интерфейса – неизбежная потеря функциональности и гибкости в угоду сомнительным удобствам. Тем не менее, наша задача была всё-таки в другом – показать пример разработки ГИП, с чем мы успешно справились.<br />
<br />
Как видите, изложенное сегодня – всего лишь основа. Но, надеюсь, этого будет достаточно, чтобы приступить к быстрой и эффективной разработке графических интерфейсов. На этом мы завершаем «Уроки P''ython''», но не прощаемся – со следующего номера начинается серия «''Python ''для профессионалов», где мы поговорим о многопоточных приложениях, обработке сетевых протоколов, взаимодействии с СУБД и библиотеке ''Python Image Library''.</div>Assaronhttp://wiki.linuxformat.ru/wiki/%D0%91%D0%BE%D0%B8_%D0%B7%D0%B0_%D0%90%D0%B2%D0%B4%D0%B5%D0%B5%D0%B2%D0%BA%D1%83_2023Бои за Авдеевку 20232008-12-22T17:33:35Z<p>Assaron: поправил листинги</p>
<hr />
<div>{{цикл/Python}}<br />
УЧЕБНИК ''Python''<br />
<br />
ПРОГРАММИРОВАНИЕ СЦЕНАРИЕВ<br />
<br />
== Уроки Python ==<br />
'''ЧАСТЬ 6 '''<br />
<br />
''Теперь, когда мы научились обрабатывать строки, самое время показать наше умение другим. Но кто будет смотреть сценарий «без лица», пусть даже в Konsole с красивым прозрачным фоном? Для тех, кто не Квентин Тарантино, '''Сергей Супрунов '''покажет, как создаются на ''Python ''графические интерфейсы.''<br />
<br />
Чем больше работаешь с ''Python'', тем сильнее ощущаешь его мощь и гибкость. Порой кажется, что для этого языка нет ничего невозможного. Это же можно отнести и к разработке графического интерфейса пользователя (хотя здесь ''Python ''просто использует возможности мощных библиотек). Как вы увидите, ''Python и в'' этом вопросе сохраняет простоту и эффективность, становясь весьма удобным инструментом для решения таких задач, как создание «обёрток» к различным консольным утилитам, разработка графических конфигураторов для ваших любимых инструментов, проектирование прототипов программ (куда проще за пару дней согласовать с заказчиком все «интерфейсные» моменты разрабатываемого проекта, меняя внешний вид прямо на переговорах, чем спустя месяцы напряжённой работы переделывать многомегабайтный проект на C++, только лишь потому, что заказчик счёл интерфейс слишком сложным для освоения), и т.д. Кстати, инсталлятор Gentoo, появившийся в версии 2006.0 и наделавший столько шума, тоже написан на ''Python''.<br />
<br />
Для ''Python ''разработано множество инструментов, отличающихся по гибкости, сложности, степени интеграции с вашим окружением. В рамках одного урока сложно рассмотреть всё это многообразие даже в общих чертах. Поэтому мы ограничимся кратким знакомством с модулем ''Tkinter ''и «привязками» ''PyQt ''и ''PyGTK''.<br />
<br />
Есть ещё достаточно мощный и переносимый модуль wx''Python, я''вляющийся привязкой к графической библиотеке wxWidgets, который тоже заслуживает внимания, но думаю, вы сможете разобраться с ним самостоятельно.<br />
<br />
Начнём со старого доброго ''Tkinter'', который, несмотря на некоторую «неказистость» и плохую интеграцию с системой, всё же остаётся стандартным модулем, входящим в поставку практически всех дистрибутивов. К тому же его простота – идеальная особенность для учебных целей. А разобравшись с ним, вы без труда освоите и другие инструменты.<br />
<br />
=== Очарование простоты ===<br />
Проверить наличие ''Tkinter'' в вашем дистрибутиве достаточно просто (он должен быть, но всякое случается): запустите ''Python'' и выполните команду import ''Tkinter'' (обратите внимание на заглавную первую букву). Если вам не повезло и вы увидели сообщение об ошибке, придётся установить этот модуль отдельно. Проконсультируйтесь с вашим менеджером пакетов, не знает ли он случайно про ''Tkinter'' (имя пакета, в отличие от модуля, обычно записывается только маленькими буквами). Если он окажется из партизанской семьи, то прямая дорога вам на http://www.python.org/ (который с новым дизайном смотрится очень даже приятно).<br />
<br />
[[Изображение:Img 80 98 2.jpg|thumb|400px|Рисунок 1. Tkinter – лучший способ сделать не так, как у других!]]<br />
<br />
Разобравшись с модулем, приступим сразу к серьёзной работе. В качестве примера выберем такую задачу: разработать графический интерфейс для просмотра man-страниц. Хорош он тем, что логика предельно проста, и нам почти не придётся отвлекаться от основной задачи. Итак, сразу код (по мере необходимости, будем прерывать его некоторыми пояснениями):<br />
<br />
<source lang="python"><br />
#!/usr/bin/Python<br />
# -*- coding: utf-8 -*-<br />
import os, re<br />
from Tkinter import *<br />
</source><br />
<br />
Здесь мы объявили кодировку нашей локали и подключили необходимые для работы модули (не забывайте следить за регистром символов). Далее опишем класс, который будет отвечать за работу нашего приложения. Использование объектного подхода позволит в дальнейшем, если потребуется, легко создавать модифицированные приложения на базе этого класса или интегрировать его функциональность в другие объекты:<br />
<br />
<source lang="python"><br />
class ManReader:<br />
def getman(self, manpage, sn):<br />
bschar = '\b'<br />
tmp = os.popen('man %s %s 2>/dev/null' % (sn, manpage)).read()<br />
if tmp:<br />
tmp = re.sub(r'(.)%c\1' % bschar, r'\1', tmp)<br />
tmp = tmp.replace('_%c' % bschar, '')<br />
else:<br />
tmp = 'Page "%s" in section "%s" not found' % (manpage, sn or 'Auto')<br />
return tmp<br />
</source><br />
<br />
Метод с украинским именем '''getman()''' отвечает за то, чтобы вернуть запрошенную man-страницу в виде текстовой переменной. Для этого используем уже знакомую нам функцию '''popen()''' модуля '''os'''. Нужно учитывать, что man-страницы могут содержать специальное форматирование: подсветку и подчёркивание. Чтобы не усложнять наш пример, мы просто избавляемся от ненужных символов, оставляя чистый текст. Пожалуй, нужно пояснить, как мы это делаем.<br />
<br />
Если вы посмотрите на результат работы команды man в «натуральном» виде, например, сохранив его в файл, то заметите, что яркость создаётся следующим приёмом: символ выводится, стирается символом «'''\b'''» (Backspace) и затем снова выводится. Подчёркивание достигается выводом и последующим стиранием символа «_». То есть вы можете увидеть что-то такое: '''N\bNA\bAM\bME\bE = NAME,_\b/_\be_\bt_\bc = /etc'''. С удалением подчёркиваний всё понятно. А вот для удаления «символов яркости» мы используем регулярное выражение, причём на первый фрагмент, взятый в скобки, мы можем в дальнейшем ссылаться с помощью '''\1'''. То есть регулярное выражение «'''(.)!\1'''» означает два одинаковых символа, разделённых восклицательным знаком. Мы же используем символ '''\b'''. Обратите внимание, что его нельзя указывать непосредственно в регулярном выражении, иначе он будет трактоваться как граница слова. Вернёмся к нашим «виджетам»:<br />
<br />
def pressBtn(self):<br />
<nowiki>sn = sectno.get()[0]</nowiki><br />
if sn == '-': sn = ''<br />
tx.delete(1.0, END)<br />
tx.insert(1.0, self.getman(ent.get(), sn))<br />
<br />
Это – функция-обработчик нажатия на кнопку (которую мы пока не нарисовали). Ещё ничего не понятно, но мы вернёмся к ней чуть позже, а сейчас перейдем к самому важному методу нашего класса, собственно и рисующему рабочее окно. Разберём его подробнее:<br />
<br />
def Drawface(self):<br />
global ent, tx, sectno<br />
<br />
Объявляем некоторые переменные как глобальные, чтобы мы могли обращаться к ним из других функций (в нашем примере – из '''pressBtn''').<br />
<br />
win = Tk()<br />
win.title('Просмотрщик man-страниц')<br />
<br />
Так мы создаём объект, который будет являться нашим рабочим окном. Заодно задаём ему заголовок.<br />
<br />
sectno = StringVar()<br />
sectno.set('---Auto---')<br />
<br />
В будущем нам понадобится эта переменная – она должна представлять собой особый объект, поэтому и создаём её как экземпляр класса '''Tkinter.StringVar()'''.<br />
<br />
fcmd = Frame(win)<br />
fcmd.pack(side=TOP, fill=X)<br />
<br />
В основном окне создаём фрейм, который будет являться контейнером для элементов управления. Второй строчкой «упаковываем» его, т.е. указываем место расположения на главном окне. Параметры метода '''pack()''' означают: прикрепить элемент к верхнему краю (TOP) и растянуть по горизонтальной оси.<br />
<br />
Здесь нужно сказать, что '''pack''' – не единственный метод упаковки в T''kinter''. В ряде случаев удобнее использовать упаковку «по сетке» – '''grid''' (подробности можно узнать в документации). Приступаем к созданию графических элементов:<br />
<br />
lbl1 = Label(fcmd, text='Read about ')<br />
lbl1.pack(side=LEFT)<br />
<br />
Это – обычная текстовая метка. Она размещается во фрейме (первый аргумент метода) и упакована по левому краю.<br />
<br />
ent = Entry(fcmd)<br />
<nowiki>ent.bind('<Return>', (lambda event: self.pressBtn()))</nowiki><br />
ent.pack(side=LEFT)<br />
<br />
Текстовое поле ввода также упаковываем слева, сразу после '''lbl1'''.<br />
<br />
Метод '''bind()''' <nowiki>задаёт реакцию поля на нажатие клавиши [Enter] (a.k.a. Return) – будет вызван метод </nowiki>'''pressBtn().'''<br />
<br />
lbl2 = Label(fcmd, text=' in section ')<br />
lbl2.pack(side=LEFT)<br />
slt = OptionMenu(fcmd, sectno,<br />
'---Auto---',<br />
'1 User Utilities',<br />
'2 System Calls',<br />
'3 Library Functions',<br />
'4 Devices & Kernel Interfaces',<br />
'5 File Formats',<br />
'6 Games',<br />
'7 Macros & SQL Commands',<br />
'8 System Utilities',<br />
'9 X Window',<br />
'n Built-In commands',<br />
)<br />
<br />
slt.pack(side=LEFT)<br />
<br />
Ещё одна текстовая метка, и далее – выпадающий список. В нём мы перечисляем имеющиеся разделы справки, к которым будет относиться наш запрос.<br />
<br />
btn1 = Button(fcmd, command=win.quit, text='Quit')<br />
btn1.pack(side=RIGHT)<br />
btn2 = Button(fcmd, command=self.pressBtn, text='Open')<br />
btn2.pack(side=RIGHT)<br />
<br />
Две кнопки – завершающая работу и отображающая запрошенную man-страницу. Обратите внимание, что здесь упаковка выполняется по правому краю, т.е. Quit будет самой правой, а Open – чуть левее. Важнейший параметр – command, задает функцию-обработчик. В первом случае используется встроенный метод '''quit''', завершающий работу, во втором – наш метод '''pressBtn'''. Обратите внимание, что здесь происходит не вызов метода, а даётся ссылка на него, т.е. имя указывается без скобок.<br />
<br />
fview = Frame(win)<br />
fview.pack(side=BOTTOM, fill=BOTH, expand=YES)<br />
sb = Scrollbar(fview)<br />
tx = Text(fview, relief=SUNKEN)<br />
sb.config(command=tx.yview)<br />
tx.config(yscrollcommand=sb.set)<br />
sb.pack(side=RIGHT, fill=Y)<br />
tx.pack(side=TOP, expand=YES, fill=BOTH)<br />
<br />
Во втором фрейме, который мы прикрепляем к нижнему краю и растягиваем во все стороны, заполняя всё доступное пространство, размещается текстовое поле, где мы будем отображать содержимое man-страницы. Поскольку последняя может быть достаточно длинной, понадобится скроллинг (элемент Scrollbar). Обратите внимание на то, как методами '''config()''' мы обеспечиваем взаимную привязку текстового поля и полосы прокрутки. Параметр relief в описании текстового поля задаёт вид рамки вокруг поля.<br />
<br />
Настало время поговорить о '''pressBtn'''<nowiki>. Как вы видели, этот метод будет выполняться в двух случаях – по щелчку на кнопке Open и при нажатии [Enter] в поле ввода. Получив цифру раздела справки и очистив текущее содержимое поля </nowiki>'''tx''' от начала ('''1,0''') до конца (END), вызываем метод '''getman()''' и вставляем результат в '''tx'''.<br />
<br />
win.mainloop()<br />
<br />
Всё выше было просто подготовкой. А вот этой командой мы запускаем наше окно в работу. С этого момента управление передаётся ''Tkinterо''бъекту, и влиять на его поведение можно только с помощью описанных ранее обработчиков событий.<br />
<br />
if __name__ == '__main__':<br />
test = ManReader()<br />
test.Drawface()<br />
<br />
Ну, это должно быть понятно – если скрипт запускается автономно, а не экспортируется в другой, то создаём объект нашего класса и запускаем его в работу.<br />
<br />
Фух… Кажется, мы сделали это. Результат наших трудов можно наблюдать на рисунке (Рисунок 1). Как видите, «виджеты» имеют свой уникальный дизайн и будут резко выделяться на фоне остального интерфейса, внешний вид которого вы с такой любовью выбирали среди десятков различных стилей. Но это работает (причём одинаково) и в KDE, и в Gnome, и даже в Windows и Mac OS X.<br />
<br />
Теперь, когда мы разобрались с основами, самое время перейти к «родным» для Linux-окружения средствам – ''PyQt ''и ''PyGTK''. Прежде чем приступать к работе, проверьте, есть ли в вашей системе нужные модули.<br />
<br />
=== Пара слов про PyGTK ===<br />
<br />
Мощь библиотеки ''GTK ''позволяет создавать таких «гигантов графики», как ''Gimp''. Отрадно, что мы можем в значительной степени воспользоваться её возможностями и в сценариях ''Python''. В каталоге '''/usr/share/doc/pygtk<версия>/examples''' вы найдёте массу примеров её использования. Здесь приведём простейший вариант:<br />
<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# -*- coding: utf-8 -*-<br />
<br />
import gtk<br />
<br />
# Создаём основное окно<br />
win = gtk.Window()<br />
def main():<br />
<br />
# Задаём параметры окна (размер, заголовок и т.д.)<br />
win.set_default_size(300, 50)<br />
win.set_border_width(10)<br />
win.connect('destroy', gtk.main_quit)<br />
win.set_title('Небольшой пример') <br />
txtvar = 'Библиотека ''GTK ''привносит в ''Python ''небывалую мощь и высокий уровень интеграции в среду Gnome.' <br />
<br />
# Текстовая метка – не забывайте применять метод show() к каждому объекту, чтобы он был видимым<br />
txt = gtk.Label(txtvar)<br />
txt.show()<br />
# Кнопка. На неё «привязываем» завершение работы<br />
btn = gtk.Button('Закрыть')<br />
btn.connect('pressed', lambda button: gtk.main_quit())<br />
btn.show()<br />
# объект Window может содержать только один элемент, так что им будет HBox, <br />
# который служит контейнером для остальных<br />
box = gtk.HBox()<br />
box.pack_start(txt)<br />
box.pack_start(btn)<br />
win.add(box)<br />
box.show()<br />
win.show()<br />
<br />
gtk.main()<br />
main()<br />
</source><br />
<br />
Результат представлен на рисунке 2.<br />
<br />
[[Изображение:Img 80 98 1.jpg|left|thumb|400px|Рисунок 2. Именно так вы и должны представлять себе GTK-<br />
приложение.]]<br />
<br />
=== Фанатам KDE посвящается ===<br />
Если для вас Linux не мыслим без KDE, то ''PyQt ''– как раз тот инструмент, который способен обеспечить «бесшовную» интеграцию ваших ''Pythonс''ценариев с окружением рабочего стола. (Впрочем, ''Qt ''работает не только в Linux, так что ваши решения по-прежнему сохранят определённый уровень переносимости.) К тому же, для разработки интерфейса к вашим услугам ''Qt Designer''. А создадим мы программу для просмотра запущенных в системе процессов. Итак, в путь!<br />
<br />
[[Изображение:Img 80 99 1.jpg|thumb|400px|Рисунок 3. Мышкой влево, мышкой вправо – а зачем вам<br />
вообще клавиатура?]]<br />
<br />
=== Делай «РАЗ»! ===<br />
Первым делом, запустите Qt Designer и откройте новый проект типа Widget. Для кнопки создайте соединение с формой (Connect Signal/ Slots) и объявите новый слот, назвав его showPs. В дальнейшем это будет наш обработчик нажатия кнопки. Сохранив интерфейс (Рисунок 3) под именем psview.ui, нужно конвертировать его в ''Python''-код:<br />
<br />
$ pyuic psview.ui > psview.py<br />
<br />
Теперь в '''psview.py''' описан класс, отвечающий за наш интерфейс. Если вы посмотрите на него, то увидите, что всё не намного сложнее, чем в ''Tkinter'', так что при желании интерфейс можно создать и вручную.<br />
<br />
=== Делай «ДВА»! ===<br />
Далее, нам нужно довести всё это до ума. Непосредственное редактирование созданного сценария – самое плохое решение, поскольку вы лишитесь возможности корректировать интерфейс в ''Qt Designer'', т.к. все изменения при этом будут потеряны. Поэтому правильно будет оставить psview.py в неприкосновенном виде, а для работы описать ещё один класс на базе созданного, пользуясь возможностями наследования. Просто создать экземпляр класса Form1 тоже не совсем хорошо – ведь нам нужно будет изменить поведение класса, отредактировав обработчик нажатия кнопки. Полученный код с переопределённой функциейобработчиком будет выглядеть так:<br />
<br />
<nowiki>#!/usr/bin/Python</nowiki><br />
<nowiki># -*- coding: utf-8 -*import os, sys</nowiki><br />
<nowiki># Импортируем модуль, созданный автоматически</nowiki><br />
from psview import *<br />
class PsForm(Form1): # наследуем от Form1<br />
def __init__(self):<br />
<nowiki># Помните, что при наследовании инициализацию</nowiki><br />
<nowiki>#- родительского класса нужно делать вручную?</nowiki><br />
Form1.__init__(self)<br />
def showPs(self):<br />
<nowiki># </nowiki>Формируем нужные ключи команды ps<br />
if self.radioButton1.isChecked():<br />
all = 'a'<br />
else:<br />
all = ''<br />
<nowiki># Выводимые поля описываются после ключа «o»</nowiki><br />
<nowiki>#- Очень важно не допускать пробелов!</nowiki><br />
fields = 'o'<br />
if self.checkBox1.isChecked():<br />
fields += 'pid,'<br />
if self.checkBox2.isChecked():<br />
fields += 'user,'<br />
if self.checkBox3.isChecked():<br />
fields += 'stat,'<br />
if self.checkBox4.isChecked():<br />
fields += 'command,'<br />
<nowiki>fields = fields[:-1] # </nowiki>последнюю запятую – долой!<br />
filter = self.lineEdit1.text()<br />
<nowiki># Если есть фильтр, то заголовок придётся забирать</nowiki><br />
<nowiki>#- отдельной командой (head – чтобы не выводить лишнее)</nowiki><br />
if filter:<br />
cmd = 'ps %sx%s | grep %s | grep -v grep' % (all, fields,<br />
filter)<br />
head = os.popen('ps %s | head' % fields).readline()<br />
body = os.popen(cmd).read()<br />
else:<br />
cmd = 'ps %sx%s' % (all, fields)<br />
pspipe = os.popen(cmd)<br />
head = pspipe.readline() # первая строка<br />
body = pspipe.read() # всё остальное<br />
pspipe.close()<br />
self.tl_header.setText(head)<br />
self.te_body.setText(body)<br />
<nowiki># </nowiki>Создаём объект-приложение<br />
app = QApplication(sys.argv)<br />
form = PsForm()<br />
app.setMainWidget(form)<br />
form.show()<br />
app.exec_loop()<br />
<br />
[[Изображение:Img 80 99 2.jpg|thumb|400px|Рисунок 4. Ну, нравится мне стиль Redmond – ничего с собой<br />
поделать не могу…]]<br />
<br />
Небольшое пояснение: чтобы при прокрутке заголовок был всегда на виду, мы вынесли его в отдельную текстовую метку '''(tl_header)'''. Поскольку при использовании ''grep ''заголовок теряется, придётся приложить чуточку усилий, чтобы всё-таки обеспечить его вывод (см. фрагмент «'''if filter – else'''»).<br />
<br />
=== Делай «ТРИ»! ===<br />
<br />
Всё! Можно запускать нашу оболочку, не забыв сделать скрипт исполняемым (Рисунок 4). Конечно, это не верх совершенства – из множества доступных полей поддерживаются только четыре, нет проверки выражения фильтра на безопасность (а что, если пользователь введёт '''«root; rm -Rf /»'''?). Впрочем, это беда любого графического интерфейса – неизбежная потеря функциональности и гибкости в угоду сомнительным удобствам. Тем не менее, наша задача была всё-таки в другом – показать пример разработки ГИП, с чем мы успешно справились.<br />
<br />
Как видите, изложенное сегодня – всего лишь основа. Но, надеюсь, этого будет достаточно, чтобы приступить к быстрой и эффективной разработке графических интерфейсов. На этом мы завершаем «Уроки P''ython''», но не прощаемся – со следующего номера начинается серия «''Python ''для профессионалов», где мы поговорим о многопоточных приложениях, обработке сетевых протоколов, взаимодействии с СУБД и библиотеке ''Python Image Library''.</div>Assaronhttp://wiki.linuxformat.ru/wiki/%D0%91%D0%BE%D0%B8_%D0%B7%D0%B0_%D0%90%D0%B2%D0%B4%D0%B5%D0%B5%D0%B2%D0%BA%D1%83_2023Бои за Авдеевку 20232008-12-22T17:29:51Z<p>Assaron: /* Очарование простоты */</p>
<hr />
<div>{{цикл/Python}}<br />
УЧЕБНИК ''Python''<br />
<br />
ПРОГРАММИРОВАНИЕ СЦЕНАРИЕВ<br />
<br />
== Уроки Python ==<br />
'''ЧАСТЬ 6 '''<br />
<br />
''Теперь, когда мы научились обрабатывать строки, самое время показать наше умение другим. Но кто будет смотреть сценарий «без лица», пусть даже в Konsole с красивым прозрачным фоном? Для тех, кто не Квентин Тарантино, '''Сергей Супрунов '''покажет, как создаются на ''Python ''графические интерфейсы.''<br />
<br />
Чем больше работаешь с ''Python'', тем сильнее ощущаешь его мощь и гибкость. Порой кажется, что для этого языка нет ничего невозможного. Это же можно отнести и к разработке графического интерфейса пользователя (хотя здесь ''Python ''просто использует возможности мощных библиотек). Как вы увидите, ''Python и в'' этом вопросе сохраняет простоту и эффективность, становясь весьма удобным инструментом для решения таких задач, как создание «обёрток» к различным консольным утилитам, разработка графических конфигураторов для ваших любимых инструментов, проектирование прототипов программ (куда проще за пару дней согласовать с заказчиком все «интерфейсные» моменты разрабатываемого проекта, меняя внешний вид прямо на переговорах, чем спустя месяцы напряжённой работы переделывать многомегабайтный проект на C++, только лишь потому, что заказчик счёл интерфейс слишком сложным для освоения), и т.д. Кстати, инсталлятор Gentoo, появившийся в версии 2006.0 и наделавший столько шума, тоже написан на ''Python''.<br />
<br />
Для ''Python ''разработано множество инструментов, отличающихся по гибкости, сложности, степени интеграции с вашим окружением. В рамках одного урока сложно рассмотреть всё это многообразие даже в общих чертах. Поэтому мы ограничимся кратким знакомством с модулем ''Tkinter ''и «привязками» ''PyQt ''и ''PyGTK''.<br />
<br />
Есть ещё достаточно мощный и переносимый модуль wx''Python, я''вляющийся привязкой к графической библиотеке wxWidgets, который тоже заслуживает внимания, но думаю, вы сможете разобраться с ним самостоятельно.<br />
<br />
Начнём со старого доброго ''Tkinter'', который, несмотря на некоторую «неказистость» и плохую интеграцию с системой, всё же остаётся стандартным модулем, входящим в поставку практически всех дистрибутивов. К тому же его простота – идеальная особенность для учебных целей. А разобравшись с ним, вы без труда освоите и другие инструменты.<br />
<br />
=== Очарование простоты ===<br />
Проверить наличие ''Tkinter'' в вашем дистрибутиве достаточно просто (он должен быть, но всякое случается): запустите ''Python'' и выполните команду import ''Tkinter'' (обратите внимание на заглавную первую букву). Если вам не повезло и вы увидели сообщение об ошибке, придётся установить этот модуль отдельно. Проконсультируйтесь с вашим менеджером пакетов, не знает ли он случайно про ''Tkinter'' (имя пакета, в отличие от модуля, обычно записывается только маленькими буквами). Если он окажется из партизанской семьи, то прямая дорога вам на http://www.python.org/ (который с новым дизайном смотрится очень даже приятно).<br />
<br />
[[Изображение:Img 80 98 2.jpg|thumb|400px|Рисунок 1. Tkinter – лучший способ сделать не так, как у других!]]<br />
<br />
Разобравшись с модулем, приступим сразу к серьёзной работе. В качестве примера выберем такую задачу: разработать графический интерфейс для просмотра man-страниц. Хорош он тем, что логика предельно проста, и нам почти не придётся отвлекаться от основной задачи. Итак, сразу код (по мере необходимости, будем прерывать его некоторыми пояснениями):<br />
<br />
<source lang="python"><br />
#!/usr/bin/Python<br />
# -*- coding: utf-8 -*-<br />
import os, re<br />
from Tkinter import *<br />
</source><br />
<br />
Здесь мы объявили кодировку нашей локали и подключили необходимые для работы модули (не забывайте следить за регистром символов). Далее опишем класс, который будет отвечать за работу нашего приложения. Использование объектного подхода позволит в дальнейшем, если потребуется, легко создавать модифицированные приложения на базе этого класса или интегрировать его функциональность в другие объекты:<br />
<br />
<source lang="python"><br />
class ManReader:<br />
def getman(self, manpage, sn):<br />
bschar = '\b'<br />
tmp = os.popen('man %s %s 2>/dev/null' % (sn, manpage)).read()<br />
if tmp:<br />
tmp = re.sub(r'(.)%c\1' % bschar, r'\1', tmp)<br />
tmp = tmp.replace('_%c' % bschar, '')<br />
else:<br />
tmp = 'Page "%s" in section "%s" not found' % (manpage, sn or 'Auto')<br />
return tmp<br />
</source><br />
<br />
Метод с украинским именем '''getman()''' отвечает за то, чтобы вернуть запрошенную man-страницу в виде текстовой переменной. Для этого используем уже знакомую нам функцию '''popen()''' модуля '''os'''. Нужно учитывать, что man-страницы могут содержать специальное форматирование: подсветку и подчёркивание. Чтобы не усложнять наш пример, мы просто избавляемся от ненужных символов, оставляя чистый текст. Пожалуй, нужно пояснить, как мы это делаем.<br />
<br />
Если вы посмотрите на результат работы команды man в «натуральном» виде, например, сохранив его в файл, то заметите, что яркость создаётся следующим приёмом: символ выводится, стирается символом «'''\b'''» (Backspace) и затем снова выводится. Подчёркивание достигается выводом и последующим стиранием символа «_». То есть вы можете увидеть что-то такое: '''N\bNA\bAM\bME\bE = NAME,_\b/_\be_\bt_\bc = /etc'''. С удалением подчёркиваний всё понятно. А вот для удаления «символов яркости» мы используем регулярное выражение, причём на первый фрагмент, взятый в скобки, мы можем в дальнейшем ссылаться с помощью '''\1'''. То есть регулярное выражение «'''(.)!\1'''» означает два одинаковых символа, разделённых восклицательным знаком. Мы же используем символ '''\b'''. Обратите внимание, что его нельзя указывать непосредственно в регулярном выражении, иначе он будет трактоваться как граница слова. Вернёмся к нашим «виджетам»:<br />
<br />
def pressBtn(self):<br />
<nowiki>sn = sectno.get()[0]</nowiki><br />
if sn == '-': sn = ''<br />
tx.delete(1.0, END)<br />
tx.insert(1.0, self.getman(ent.get(), sn))<br />
<br />
Это – функция-обработчик нажатия на кнопку (которую мы пока не нарисовали). Ещё ничего не понятно, но мы вернёмся к ней чуть позже, а сейчас перейдем к самому важному методу нашего класса, собственно и рисующему рабочее окно. Разберём его подробнее:<br />
<br />
def Drawface(self):<br />
global ent, tx, sectno<br />
<br />
Объявляем некоторые переменные как глобальные, чтобы мы могли обращаться к ним из других функций (в нашем примере – из '''pressBtn''').<br />
<br />
win = Tk()<br />
win.title('Просмотрщик man-страниц')<br />
<br />
Так мы создаём объект, который будет являться нашим рабочим окном. Заодно задаём ему заголовок.<br />
<br />
sectno = StringVar()<br />
sectno.set('---Auto---')<br />
<br />
В будущем нам понадобится эта переменная – она должна представлять собой особый объект, поэтому и создаём её как экземпляр класса '''Tkinter.StringVar()'''.<br />
<br />
fcmd = Frame(win)<br />
fcmd.pack(side=TOP, fill=X)<br />
<br />
В основном окне создаём фрейм, который будет являться контейнером для элементов управления. Второй строчкой «упаковываем» его, т.е. указываем место расположения на главном окне. Параметры метода '''pack()''' означают: прикрепить элемент к верхнему краю (TOP) и растянуть по горизонтальной оси.<br />
<br />
Здесь нужно сказать, что '''pack''' – не единственный метод упаковки в T''kinter''. В ряде случаев удобнее использовать упаковку «по сетке» – '''grid''' (подробности можно узнать в документации). Приступаем к созданию графических элементов:<br />
<br />
lbl1 = Label(fcmd, text='Read about ')<br />
lbl1.pack(side=LEFT)<br />
<br />
Это – обычная текстовая метка. Она размещается во фрейме (первый аргумент метода) и упакована по левому краю.<br />
<br />
ent = Entry(fcmd)<br />
<nowiki>ent.bind('<Return>', (lambda event: self.pressBtn()))</nowiki><br />
ent.pack(side=LEFT)<br />
<br />
Текстовое поле ввода также упаковываем слева, сразу после '''lbl1'''.<br />
<br />
Метод '''bind()''' <nowiki>задаёт реакцию поля на нажатие клавиши [Enter] (a.k.a. Return) – будет вызван метод </nowiki>'''pressBtn().'''<br />
<br />
lbl2 = Label(fcmd, text=' in section ')<br />
lbl2.pack(side=LEFT)<br />
slt = OptionMenu(fcmd, sectno,<br />
'---Auto---',<br />
'1 User Utilities',<br />
'2 System Calls',<br />
'3 Library Functions',<br />
'4 Devices & Kernel Interfaces',<br />
'5 File Formats',<br />
'6 Games',<br />
'7 Macros & SQL Commands',<br />
'8 System Utilities',<br />
'9 X Window',<br />
'n Built-In commands',<br />
)<br />
<br />
slt.pack(side=LEFT)<br />
<br />
Ещё одна текстовая метка, и далее – выпадающий список. В нём мы перечисляем имеющиеся разделы справки, к которым будет относиться наш запрос.<br />
<br />
btn1 = Button(fcmd, command=win.quit, text='Quit')<br />
btn1.pack(side=RIGHT)<br />
btn2 = Button(fcmd, command=self.pressBtn, text='Open')<br />
btn2.pack(side=RIGHT)<br />
<br />
Две кнопки – завершающая работу и отображающая запрошенную man-страницу. Обратите внимание, что здесь упаковка выполняется по правому краю, т.е. Quit будет самой правой, а Open – чуть левее. Важнейший параметр – command, задает функцию-обработчик. В первом случае используется встроенный метод '''quit''', завершающий работу, во втором – наш метод '''pressBtn'''. Обратите внимание, что здесь происходит не вызов метода, а даётся ссылка на него, т.е. имя указывается без скобок.<br />
<br />
fview = Frame(win)<br />
fview.pack(side=BOTTOM, fill=BOTH, expand=YES)<br />
sb = Scrollbar(fview)<br />
tx = Text(fview, relief=SUNKEN)<br />
sb.config(command=tx.yview)<br />
tx.config(yscrollcommand=sb.set)<br />
sb.pack(side=RIGHT, fill=Y)<br />
tx.pack(side=TOP, expand=YES, fill=BOTH)<br />
<br />
Во втором фрейме, который мы прикрепляем к нижнему краю и растягиваем во все стороны, заполняя всё доступное пространство, размещается текстовое поле, где мы будем отображать содержимое man-страницы. Поскольку последняя может быть достаточно длинной, понадобится скроллинг (элемент Scrollbar). Обратите внимание на то, как методами '''config()''' мы обеспечиваем взаимную привязку текстового поля и полосы прокрутки. Параметр relief в описании текстового поля задаёт вид рамки вокруг поля.<br />
<br />
Настало время поговорить о '''pressBtn'''<nowiki>. Как вы видели, этот метод будет выполняться в двух случаях – по щелчку на кнопке Open и при нажатии [Enter] в поле ввода. Получив цифру раздела справки и очистив текущее содержимое поля </nowiki>'''tx''' от начала ('''1,0''') до конца (END), вызываем метод '''getman()''' и вставляем результат в '''tx'''.<br />
<br />
win.mainloop()<br />
<br />
Всё выше было просто подготовкой. А вот этой командой мы запускаем наше окно в работу. С этого момента управление передаётся ''Tkinterо''бъекту, и влиять на его поведение можно только с помощью описанных ранее обработчиков событий.<br />
<br />
if __name__ == '__main__':<br />
test = ManReader()<br />
test.Drawface()<br />
<br />
Ну, это должно быть понятно – если скрипт запускается автономно, а не экспортируется в другой, то создаём объект нашего класса и запускаем его в работу.<br />
<br />
Фух… Кажется, мы сделали это. Результат наших трудов можно наблюдать на рисунке (Рисунок 1). Как видите, «виджеты» имеют свой уникальный дизайн и будут резко выделяться на фоне остального интерфейса, внешний вид которого вы с такой любовью выбирали среди десятков различных стилей. Но это работает (причём одинаково) и в KDE, и в Gnome, и даже в Windows и Mac OS X.<br />
<br />
Теперь, когда мы разобрались с основами, самое время перейти к «родным» для Linux-окружения средствам – ''PyQt ''и ''PyGTK''. Прежде чем приступать к работе, проверьте, есть ли в вашей системе нужные модули.<br />
<br />
=== Пара слов про PyGTK ===<br />
<br />
Мощь библиотеки ''GTK ''позволяет создавать таких «гигантов графики», как ''Gimp''. Отрадно, что мы можем в значительной степени воспользоваться её возможностями и в сценариях ''Python''. В каталоге '''/usr/share/doc/pygtk<версия>/examples''' вы найдёте массу примеров её использования. Здесь приведём простейший вариант:<br />
<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# -*- coding: utf-8 -*-<br />
<br />
import gtk<br />
<br />
# Создаём основное окно<br />
win = gtk.Window()<br />
def main():<br />
<br />
# Задаём параметры окна (размер, заголовок и т.д.)<br />
win.set_default_size(300, 50)<br />
win.set_border_width(10)<br />
win.connect('destroy', gtk.main_quit)<br />
win.set_title('Небольшой пример') <br />
txtvar = 'Библиотека ''GTK ''привносит в ''Python ''небывалую мощь и высокий уровень интеграции в среду Gnome.' <br />
<br />
# Текстовая метка – не забывайте применять метод show() к каждому объекту, чтобы он был видимым<br />
txt = gtk.Label(txtvar)<br />
txt.show()<br />
# Кнопка. На неё «привязываем» завершение работы<br />
btn = gtk.Button('Закрыть')<br />
btn.connect('pressed', lambda button: gtk.main_quit())<br />
btn.show()<br />
# объект Window может содержать только один элемент, так что им будет HBox, <br />
# который служит контейнером для остальных<br />
box = gtk.HBox()<br />
box.pack_start(txt)<br />
box.pack_start(btn)<br />
win.add(box)<br />
box.show()<br />
win.show()<br />
<br />
gtk.main()<br />
main()<br />
</source><br />
<br />
Результат представлен на рисунке 2.<br />
<br />
[[Изображение:Img 80 98 1.jpg|left|thumb|400px|Рисунок 2. Именно так вы и должны представлять себе GTK-<br />
приложение.]]<br />
<br />
=== Фанатам KDE посвящается ===<br />
Если для вас Linux не мыслим без KDE, то ''PyQt ''– как раз тот инструмент, который способен обеспечить «бесшовную» интеграцию ваших ''Pythonс''ценариев с окружением рабочего стола. (Впрочем, ''Qt ''работает не только в Linux, так что ваши решения по-прежнему сохранят определённый уровень переносимости.) К тому же, для разработки интерфейса к вашим услугам ''Qt Designer''. А создадим мы программу для просмотра запущенных в системе процессов. Итак, в путь!<br />
<br />
[[Изображение:Img 80 99 1.jpg|thumb|400px|Рисунок 3. Мышкой влево, мышкой вправо – а зачем вам<br />
вообще клавиатура?]]<br />
<br />
=== Делай «РАЗ»! ===<br />
Первым делом, запустите Qt Designer и откройте новый проект типа Widget. Для кнопки создайте соединение с формой (Connect Signal/ Slots) и объявите новый слот, назвав его showPs. В дальнейшем это будет наш обработчик нажатия кнопки. Сохранив интерфейс (Рисунок 3) под именем psview.ui, нужно конвертировать его в ''Python''-код:<br />
<br />
$ pyuic psview.ui > psview.py<br />
<br />
Теперь в '''psview.py''' описан класс, отвечающий за наш интерфейс. Если вы посмотрите на него, то увидите, что всё не намного сложнее, чем в ''Tkinter'', так что при желании интерфейс можно создать и вручную.<br />
<br />
=== Делай «ДВА»! ===<br />
Далее, нам нужно довести всё это до ума. Непосредственное редактирование созданного сценария – самое плохое решение, поскольку вы лишитесь возможности корректировать интерфейс в ''Qt Designer'', т.к. все изменения при этом будут потеряны. Поэтому правильно будет оставить psview.py в неприкосновенном виде, а для работы описать ещё один класс на базе созданного, пользуясь возможностями наследования. Просто создать экземпляр класса Form1 тоже не совсем хорошо – ведь нам нужно будет изменить поведение класса, отредактировав обработчик нажатия кнопки. Полученный код с переопределённой функциейобработчиком будет выглядеть так:<br />
<br />
<nowiki>#!/usr/bin/Python</nowiki><br />
<nowiki># -*- coding: utf-8 -*import os, sys</nowiki><br />
<nowiki># Импортируем модуль, созданный автоматически</nowiki><br />
from psview import *<br />
class PsForm(Form1): # наследуем от Form1<br />
def __init__(self):<br />
<nowiki># Помните, что при наследовании инициализацию</nowiki><br />
<nowiki>#- родительского класса нужно делать вручную?</nowiki><br />
Form1.__init__(self)<br />
def showPs(self):<br />
<nowiki># </nowiki>Формируем нужные ключи команды ps<br />
if self.radioButton1.isChecked():<br />
all = 'a'<br />
else:<br />
all = ''<br />
<nowiki># Выводимые поля описываются после ключа «o»</nowiki><br />
<nowiki>#- Очень важно не допускать пробелов!</nowiki><br />
fields = 'o'<br />
if self.checkBox1.isChecked():<br />
fields += 'pid,'<br />
if self.checkBox2.isChecked():<br />
fields += 'user,'<br />
if self.checkBox3.isChecked():<br />
fields += 'stat,'<br />
if self.checkBox4.isChecked():<br />
fields += 'command,'<br />
<nowiki>fields = fields[:-1] # </nowiki>последнюю запятую – долой!<br />
filter = self.lineEdit1.text()<br />
<nowiki># Если есть фильтр, то заголовок придётся забирать</nowiki><br />
<nowiki>#- отдельной командой (head – чтобы не выводить лишнее)</nowiki><br />
if filter:<br />
cmd = 'ps %sx%s | grep %s | grep -v grep' % (all, fields,<br />
filter)<br />
head = os.popen('ps %s | head' % fields).readline()<br />
body = os.popen(cmd).read()<br />
else:<br />
cmd = 'ps %sx%s' % (all, fields)<br />
pspipe = os.popen(cmd)<br />
head = pspipe.readline() # первая строка<br />
body = pspipe.read() # всё остальное<br />
pspipe.close()<br />
self.tl_header.setText(head)<br />
self.te_body.setText(body)<br />
<nowiki># </nowiki>Создаём объект-приложение<br />
app = QApplication(sys.argv)<br />
form = PsForm()<br />
app.setMainWidget(form)<br />
form.show()<br />
app.exec_loop()<br />
<br />
[[Изображение:Img 80 99 2.jpg|thumb|400px|Рисунок 4. Ну, нравится мне стиль Redmond – ничего с собой<br />
поделать не могу…]]<br />
<br />
Небольшое пояснение: чтобы при прокрутке заголовок был всегда на виду, мы вынесли его в отдельную текстовую метку '''(tl_header)'''. Поскольку при использовании ''grep ''заголовок теряется, придётся приложить чуточку усилий, чтобы всё-таки обеспечить его вывод (см. фрагмент «'''if filter – else'''»).<br />
<br />
=== Делай «ТРИ»! ===<br />
<br />
Всё! Можно запускать нашу оболочку, не забыв сделать скрипт исполняемым (Рисунок 4). Конечно, это не верх совершенства – из множества доступных полей поддерживаются только четыре, нет проверки выражения фильтра на безопасность (а что, если пользователь введёт '''«root; rm -Rf /»'''?). Впрочем, это беда любого графического интерфейса – неизбежная потеря функциональности и гибкости в угоду сомнительным удобствам. Тем не менее, наша задача была всё-таки в другом – показать пример разработки ГИП, с чем мы успешно справились.<br />
<br />
Как видите, изложенное сегодня – всего лишь основа. Но, надеюсь, этого будет достаточно, чтобы приступить к быстрой и эффективной разработке графических интерфейсов. На этом мы завершаем «Уроки P''ython''», но не прощаемся – со следующего номера начинается серия «''Python ''для профессионалов», где мы поговорим о многопоточных приложениях, обработке сетевых протоколов, взаимодействии с СУБД и библиотеке ''Python Image Library''.</div>Assaronhttp://wiki.linuxformat.ru/wiki/LXF78:PythonLXF78:Python2008-12-22T16:00:41Z<p>Assaron: поправил отступы</p>
<hr />
<div>{{цикл/Python}}<br />
''Часть 4. Мы вплотную подошли к наиболее интересной теме – реализации объектно-ориентированного подхода в языке Python. Репортаж с места событий ведет '''Сергей Супрунов.''' ''<br />
<br />
===Немного терминологии===<br />
ООП (объектно-ориентированное программирование), наряду с анализом и дизайном – это важнейшая часть очень популярного объектного подхода к разработке программного обеспечения. В данном случае в дизайне программы выделяются так называемые объекты, которые обладают атрибутами (свойствами) и методами (способами изменения свойств).<br />
<br />
Новые объекты создаются на базе классов, которые являются своего рода шаблонами, описывающими общие характеристики объектов (также называемых экземплярами класса).<br />
<br />
Для ООП характерны следующие принципы:<br /><br />
* инкапсуляция: экземпляр класса рассматривается как «чёрный ящик», когда его внутреннее устройство скрыто, а всё взаимодействие с объектом выполняется через предусмотренный для этого интерфейс, т.е. набор методов. Нужно заметить, что в Python программист, в принципе, имеет возможность непосредственно воздействовать на свойства объекта, минуя интерфейс – здесь инкапсуляция реализована не на уровне ограничений языка, а на уровне соглашений. <br />
* наследование: вы можете создавать новые классы на базе существующих, при этом они будут получать все атрибуты и методы, описанные для родительских классов. Наследование – это наиболее естественный путь разработки классов, подобных родительским, но с дополнительным набором свойств.<br />
<br />
Есть и другие принципы (полиморфизм, аггрегация и т.д.), с которыми вы сможете глубже познакомиться в специальной литературе.<br />
<br />
===Реализация ООП в Python===<br />
<br />
В отличие от Perl, в Python объектный подход заложен в основу этого языка. Здесь почти всё является объектами, даже строки и числа. Не верите? Смотрите сами:<br />
<br />
<source lang="python"><br />
>>> print 1.0.__add__(5)<br />
6.0<br />
</source><br />
<br />
То есть мы применили к числу 1.0 (объекту) метод __add__(), который является «внутренней» реализацией операции сложения. Нужно заметить, что здесь из-за ограничений синтаксиса мы вынуждены использовать число с плавающей запятой, поскольку первая точка воспринимается интерпретатором именно как разделитель целой и дробной частей, а вот вторая уже отделяет объект от имени метода. Уже известная нам функция dir(3) вернёт ещё 52 метода, которые вы можете применять к числу.<br />
<br />
Для определения класса используется специальный оператор class:<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# file: сtest.py<br />
class User:<br />
def __init__(self, username):<br />
self.username = username<br />
self.password = ''<br />
def setpass(self, value=''):<br />
self.password = value<br />
def checkpass(self, typed):<br />
if typed == self.password:<br />
return 1<br />
else:<br />
return 0<br />
</source><br />
<br />
Здесь мы определили класс, экземпляры которого будут хранить информацию о пользователях (имя и пароль). Специальный метод '''__init__()''', играющий роль конструктора, автоматически исполняется при создании нового объекта. В нём мы присваиваем атрибуту username значение, переданное конструктору как параметр, и создаём атрибут password с пустым значением.<br />
<br />
Обратите внимание на обязательность использования параметра '''self''', который должен быть первым в описании любого метода. При работе с конкретным объектом здесь будет указываться его идентификатор, чтобы интерпретатор мог определить, с каким же объектом он имеет дело.<br />
<br />
Описанные далее методы setpass() и checkpass() служат для того, чтобы установить значение атрибута password и сравнить с ним значение, введённое пользователем. На практике работа с нашим классом может выглядеть таким образом:<br />
<br />
<source lang="python"><br />
u1 = User('Vasya')<br />
u1.setpass('qwerty')<br />
userpass = raw_input('Enter your password:')<br />
if u1.checkpass(userpass):<br />
print 'Password is OK'<br />
else:<br />
print 'Password is wrong'<br />
u1.password = 'sasdf'<br />
</source><br />
<br />
В первой строке мы создали объект (экземпляр класса User). Затем мы задали ему пароль и чуть позже сравнили его с тем, который пользователь ввёл по запросу сценария.<br />
<br />
Последняя строка демонстрирует, что пароль можно изменить и непосредственно. Это является нарушением принципа инкапсуляции. Язык Python предусматривает два соглашения для решения этой проблемы: во-первых, если имя атрибута или метода начинается с символа подчёркивания, это является признаком того, что они предназначены для «внутреннего потребления» и не должны использоваться непосредственно. Однако сам Python никак не ограничивает это. Если вы желаете получить более жёсткий контроль, используйте имена, начинающиеся двумя символами подчёркивания. Такое имя уже не будет доступно через пространство имён объекта (хотя обходной путь всё же есть).<br />
<br />
Особое место занимают специальные методы (такие как показанный выше метод '''__init__()'''). Эти методы реализуют ряд «сервисных» функций (например, конструктор '''__init__()''', '''деструктор __del__()''', отвечающий за корректное удаление объекта, и т.п.), а также позволяют переопределить поведение, заложенное в интерпретаторе. Например, метод '''__add__()''' исполняется при обработке оператора сложения «+».<br />
Определим его для некоторого класса:<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# file: stest.py<br />
class Test:<br />
def __init__(self, value):<br />
self.value = value<br />
def __add__(self, other):<br />
return self.value – other<br />
</source><br />
<br />
И теперь, вместо ожидаемого сложения, мы увидим вычитание:<br />
<br />
<source lang="python"><br />
>>> from stest import Test<br />
>>> a = Test(5)<br />
>>> print a + 3<br />
2<br />
</source><br />
<br />
Естественно, этим не стоит злоупотреблять, но вы должны знать, что очень многие «стереотипы» становятся весьма условными, если речь заходит о Python.<br />
<br />
Нужно заметить, что вы можете задавать атрибуты объекта динамически, что называется, «на лету». Продолжим предыдущий пример:<br />
<br />
<source lang="python"><br />
>>> a.description = 'Описание'<br />
>>> print a.description <br />
Описание<br />
</source><br />
<br />
Хотя атрибут '''description''' отсутствует в классе Test, мы можем работать с ним, как с обычным. Таким образом можно задать и атрибуты, имена которых начинаются с двух подчёркиваний. Это создаёт видимость того, что никаких ограничений на эти имена нет, но на самом деле получится совершенно иной атрибут, нежели определённый в описании класса. Так что не увлекайтесь подчёркиваниями.<br />
<br />
===Наследование===<br />
<br />
Несколько слов нужно сказать о наследовании. Рассмотрим пример:<br />
<br />
<source lang="python"><br />
#!/usr/bin/python<br />
# file: ntest.py<br />
from ctest import User<br />
class ShellUser(User):<br />
def __init__(self, username):<br />
User.__init__(self, username)<br />
self.shell = '/bin/sh'<br />
def setshell(self, newshell):<br />
self.shell = newshell<br />
u2 = ShellUser('Petya')<br />
u2.setpass('12345')<br />
print u2.password, u2.shell<br />
</source><br />
<br />
Как видите, дочерний класс ShellUser получает все свойства родительского (имена родительских классов перечисляются в скобках в определении class). Теперь мы можем расширить этот класс новыми атрибутами и методами, и в дальнейшем использовать их наряду с определёнными в родительских классах.<br />
<br />
Обратите внимание, что при создании экземпляра нового класса исполняется только его метод '''__init__()''', инициализацию родительского класса нужно вызывать явно.<br />
<br />
===Классы в стандартных модулях===<br />
Классы очень широко используются в стандартных модулях Python. В качестве примера рассмотрим один достаточно полезный модуль –'''StringIO'''. С его помощью вы можете применять к строкам методы работы с файлами ('''read()''', '''write()''' и т.д.). Это может быть необходимо в тех случаях, когда другой метод или функция может обрабатывать только файлы. Например, класс Message модуля '''rfc822''', с помощью которого, в частности, можно разбирать сообщения электронной почты, умеет работать только с файлом, содержащим текст сообщения.<br />
<br />
Рассмотрим пример:<br />
<source lang="python"><br />
#!/usr/bin/python<br />
import rfc822, StringIO<br />
mailstr = """\<br />
From: user@domain.ru<br />
To: me@mysite.ru<br />
Subject: Test Message<br />
Hello!<br />
It is a test message.<br />
"""<br />
fileobj = StringIO.StringIO(mailstr)<br />
message = rfc822.Message(fileobj)<br />
print "%s wrote:\n" % message.getheader('From')<br />
bodyfrom = message.startofbody<br />
fileobj.seek(bodyfrom)<br />
body = fileobj.read()<br />
print body<br />
</source><br />
<br />
Итак, что здесь происходит? Переменная mailstr содержит текст в формате почтового сообщения. Поскольку модуль '''rfc822''' умеет работать только с файловыми объектами, мы создаём экземпляр класса '''StringIO''' под именем '''fileobj'''. На его основе уже создаётся объект message класса Message, описанного в модуле rfc822.<br />
<br />
С помощью метода getheader() мы распечатываем значение поля «From», а узнав из атрибута startofbody объекта message, где начинается тело сообщения (отделённое от заголовка пустой строкой), мы видим, что нашему объекту fileobj не чужды никакие методы настоящих файлов – можно позиционировать указатель (метод '''seek()'''), считывать содержимое от текущей позиции до конца файла ('''read()'''), и т.д.<br />
<br />
===Документируй это!===<br />
<br />
Хотя в языке Python инкапсуляция, т.е. сокрытие внутреннего устройства класса, не реализована в чистом виде (вы можете обращаться напрямую к любому атрибуту, за небольшим исключением), но всё же хорошим тоном считается работать с объектами классов только через предоставляемый ими интерфейс. И в этих условиях особое значение приобретает документация. В Python реализован очень удобный способ, скажем так, «онлайновой» документации – первая текстовая строка класса (или функции) воспринимается как его описание, которое может быть в любое время получено с помощью атрибута '''__doc__''':<br />
<source lang="python"><br />
>>> class Cla:<br />
... "Документация класса"<br />
... pass<br />
...<br />
>>> print Cla.__doc__<br />
Документация класса<br />
</source><br />
<br />
Для многострочных описаний удобно использовать утроенные кавычки.<br />
<br />
На этом мы завершим наше знакомство с классами Python. Естественно, на таких небольших примерах их мощь и удобство остались за кадром, хотя при работе со стандартными модулями в любом случае необходимо понимать, как всё это работает. Настоящий же эффект от использования объектно-ориентированного подхода вы ощутите при работе над большими проектами.</div>Assaronhttp://wiki.linuxformat.ru/wiki/LXF74-75:PythonLXF74-75:Python2008-12-22T13:44:01Z<p>Assaron: Добавил отсупы в "первую программу"</p>
<hr />
<div>{{цикл/Python}}<br />
''ЧАСТЬ 1 Язык программирования Python становится достаточно популярным и уже практически на равных может бороться за благосклонность пользователей с таким бывалым соперником, как Perl. Поэтому с ним стоит познакомиться, считает '''Сергей Супрунов'''.''<br />
<br />
== Немного истории ==<br />
Началось все в далеком 1990 году, когда Гвидо ван Россум<br />
(Guido van Rossum) разработал первую версию языка Python,<br />
который появился в свободном доступе в 1991 году. Первоначально созданный как язык сценариев для разрабатываемой Россумом системы<br />
Amoeba, он оказался настолько хорош и переносим, что достаточно быстро получил распространение на самых различных операционных системах.<br />
<br />
Сейчас этот язык входит в поставку практически всех популярных<br />
дистрибутивов Linux; его, наряду с Perl, поддерживают Apache<br />
(модуль mod_python), PostgreSQL (процедурный язык PL/Python) и<br />
многие другие программы.<br />
<br />
== Отличительные особенности ==<br />
Почему же Python столь стремительно завоевывает популярность?<br />
<br />
Прежде всего, будучи интерпретируемым языком, Python не требует компиляции, компоновки и прочих «премудростей» — сценарий на<br />
Python может быть запущен практически сразу же после редактирования. Это существенно снижает время разработки и делает Python весьма удобным для таких задач как создание прототипов программ, проверка работоспособности того или иного алгоритма, а также для администрирования, где время исполнения — гораздо менее важный фактор,<br />
чем удобство модификации сценария.<br />
<br />
Вторым большим плюсом этого языка является уже упоминавшаяся<br />
переносимость между различными системами. Если вы не используете<br />
специфические особенности платформы (например, команду fork для<br />
ветвления процессов), то с большой долей вероятности ваш код, разработанный в Linux, сможет работать и во FreeBSD, и даже в Windows.<br />
<br />
Далее, поскольку Python — язык высокого уровня, он позволяет<br />
вам сосредоточиться на алгоритме, на логике программы, не отвлекаясь на описание переменных, выделение памяти и т. п.<br />
<br />
Ну и самое главное — Python разрабатывается как проект opensource,<br />
то есть вы можете не только использовать его в своей работе<br />
без каких-либо ограничений, но и активно участвовать в его развитии.<br />
<br />
Все это делает Python хорошим выбором для самого широкого<br />
спектра задач — от автоматизации рутинных операций по администрированию и разработки графических «оберток» для консольных утилит<br />
до web-программирования и разработки прототипов в крупных<br />
проектах.<br />
<br />
== Где его взять? ==<br />
Большинству пользователей Linux об этом беспокоиться не нужно -<br />
Python входит почти во все популярные дистрибутивы. Откройте окно<br />
терминала, наберите там команду python, и если в ответ увидите сообщение о версии интерпретатора и приглашение «>>>», значит, он у вас<br />
уже есть. Если же вам не повезло и вы увидели сообщение «Command<br />
not found», то придется озаботиться его установкой.<br />
<br />
Свежую версию Python (архив с исходными кодами) всегда можно<br />
найти на официальной странице: http://www.python.org/download/ .<br />
Кроме того, вы наверняка сможете найти пакет, подготовленный для<br />
вашей операционной системы.<br />
<br />
== Знакомство с интерактивной оболочкой и основы синтаксиса ==<br />
Для первоначального изучения очень удобен интерактивный<br />
режим — вы сразу же получаете результат выполнения введенной<br />
команды, и в случае ошибки можете проанализировать причины ее<br />
возникновения и исправиться.<br />
<br />
Так что вводите команду python в окне терминала, и — вперед! Для<br />
начала потренируемся с математикой (после ввода команды в строке<br />
приглашения, которая отмечается символами «>>>» следует нажимать<br />
[Enter], чтобы увидеть результат):<br />
<source lang="python"><br />
>>> 1+3<br />
4<br />
>>> a=5<br />
>>> 3 + a<br />
8<br />
>>> a / 3<br />
1<br />
</source><br />
Ага, вот и первый сюрприз. Как и в языке C, при делении целого<br />
числа на целое результат — тоже целое число, дробная часть отбрасывается. Чтобы обойти эту особенность, один из операндов нужно сделать числом с плавающей запятой:<br />
<source lang="python"><br />
>>> a / 3.0<br />
1.6666666666666667<br />
>>> (a * 1.0) / 3<br />
1.6666666666666667<br />
</source><br />
Этот пример показывает, что в Python существует понятие «тип<br />
переменной», и если преобразование между типами не лишено смысла, то оно выполняется прозрачно для программиста. Впрочем, можно<br />
использовать и явное преобразование:<br />
<source lang="python"><br />
>>> float(a) / 3<br />
1.6666666666666667<br />
</source><br />
В текущей версии языка для целочисленного деления существует<br />
отдельный оператор — «//». Сейчас выражения 3/2 и 3//2 вернут один<br />
и тот же результат, но в будущих версиях поведение «традиционного»<br />
оператор деления («/») может быть приведено к привычному для человека, когда 3/2 = 1.5.<br />
<br />
Операция сложения допустима и для строк (которые могут быть<br />
заключены как в апострофы, так и в кавычки):<br />
<source lang="python"><br />
>>> 'Hello, ' + "world!"<br />
'Hello, world!'<br />
>>> "127" + 5<br />
Traceback (most recent call last):<br />
File "<stdin>", line 1, in ?<br />
TypeError: cannot concatenate 'str' and 'int' objects<br />
</source><br />
А вот смешивать символьные типы с числовыми нельзя — это вам<br />
не Perl! Еще одна интересная операция для строк — повторение:<br />
<source lang="python"><br />
>>> '=' * 25<br />
'========================='<br />
</source><br />
При выводе динамически формируемых текстовых строк важное<br />
значение имеют символы подстановки:<br />
<source lang="python"><br />
>>> language = 'Python'<br />
>>> print "I like %s" % language<br />
I like Python<br />
</source><br />
Здесь мы в выводимом тексте создаем так называемое «знакоместо» %s, которое затем заполняется значением переменной, стоящей<br />
после оператора %. В последующих статьях цикла мы познакомимся с<br />
ними более подробно. Ну а оператор print, думаю, пояснять не нужно -<br />
он просто выводит строку на экран (обратите внимание на отсутствие<br />
апострофов в выводе команды).<br />
<br />
Чтобы выйти из командной оболочки, нажмите [Ctrl]+[D].<br />
<br />
== Первая программа ==<br />
Настало время написать первую настоящую программу. Откройте ваш<br />
любимый текстовый редактор. Я предпочитаю работать в vi, но вы<br />
вполне можете выбрать и более привычный инструмент; кроме того,<br />
существует немало редакторов, обладающих рядом сервисных функций, таких как подсветка синтаксиса, автоотступ и т. д. Один из них, Eric,<br />
рассматривается в разделе HotPicks. Вы также можете обратить внимание на KDevelop и IDLE, редактор, поставляемый вместе с Python.<br />
Наберите приведенный ниже код. Немного нарушив традицию, мы сразу приступим к сравнительно сложному примеру, который позволит<br />
познакомиться с большинством синтаксических особенностей языка.<br />
Да и «Hello, World!» мы фактически уже написали, когда были в интерактивной оболочке. Еще одна просьба — когда будете вводить код, не<br />
спешите читать его описание далее в этой статье. Постарайтесь догадаться сами, что делает этот сценарий — это совсем не сложно. Итак,<br />
код (сохраните его в файле test1.py):<br />
<source lang="python"><br />
#!/usr/bin/python<br />
prompt = '> '<br />
cmd = 'nocmd'<br />
while cmd != 'exit':<br />
cmd = raw_input(prompt)<br />
if cmd == 'about':<br />
print 'It is my first python script'<br />
elif cmd == 'help':<br />
print '''Commands:<br />
about – about this program<br />
help – this information<br />
exit – quit the program'''<br />
elif cmd =='exit':<br />
pass # пустая команда – не делает ничего<br />
else:<br />
print 'Command not found. Type "help"'<br />
print 'Bye.'<br />
</source><br />
Для удобства чтения строковые константы выделены в тексте курсивом. Надеюсь, вы уже поняли, что приведенный скрипт должен<br />
будет имитировать работу простейшей командной оболочки, запрашивая у пользователя команды и выводя результат их исполнения. Таких<br />
команд три:<br />
* about: вывести информацию о программе;<br />
* help: напечатать краткую справку по работе с программой;<br />
* exit: команда выхода из программы.<br />
<br />
В этом фрагменте мы встречаемся с одним из циклов (while, другой популярный цикл — for — рассмотрим в следующей статье) и оператором ветвления if-elif-else. Если вы знакомы с каким-нибудь языком программирования, то все вам будет понятно. Но обратите внимание на синтаксис: в Python неотъемлемой частью программы являются<br />
отступы. Именно они показывают интерпретатору, где заканчивается<br />
тот или иной блок кода. В приведенном выше примере последняя строка будет выполнена при выходе из цикла, поскольку она не имеет<br />
отступа и потому телу цикла while не принадлежит.<br />
<br />
Особо следует пояснить отсутствие отступа в строках, заключенных<br />
в тройные апострофы (фрагмент elif, отвечающий за выполнение<br />
команды «help»). Такие строки могут содержать в себе и символы<br />
перевода строки, и табуляцию. Интерпретатор воспринимает все это<br />
как одну строковую константу — на экран все будет выведено именно<br />
так, как набрано в коде сценария. (Чтобы было проще понять программу, замените на первых порах эту строку обычной, — тогда она не будет<br />
сбивать вас с толку и лучше будет видно структуру сценария).<br />
<br />
На Unix-подобных системах очень важную роль играет самая первая строка сценария — в ней следует указать путь к интерпретатору<br />
python (обычно это /usr/bin/python или /usr/local/bin/python). В<br />
остальных строках символ «#» означает начало комментария — все,<br />
что за ним следует, интерпретатором игнорируется.<br />
<br />
Осталось сделать наш файл исполняемым (chmod +x test1.py)<br />
и убедиться, что все работает правильно:<br />
<source lang="text"><br />
serg$ ./test1.py<br />
> hello<br />
Command not found. Type "help"<br />
> help<br />
Commands:<br />
about – about this program<br />
help – this information<br />
exit – quit the program<br />
> about<br />
It is my first python script<br />
> exit<br />
Bye.<br />
</source><br />
В случае ошибок проверяйте синтаксис, особенно отступы. И не<br />
забудьте убедиться, что вы используете именно тот путь к интерпретатору, который принят в вашем дистрибутиве.<br />
<br />
Ну что ж. Для первого урока, думаю, достаточно. В следующий раз<br />
мы рассмотрим так называемые последовательности (списки, кортежи<br />
и словари) — очень важные типы данных, делающие Python столь мощным языком программирования, а также затронем основы работы<br />
с функциями и модулями.</div>Assaron