Журнал LinuxFormat - перейти на главную

LXF134:Python

Материал из Linuxformat
Перейти к: навигация, поиск
'Python Ре­аль­ные про­ек­ты, от­та­чи­ваю­щие на­вы­ки ха­ке­ра

Содержание

Python: Бу­дем грузить подкасты

По­зна­комь­тесь с пре­крас­ным ин­ст­ру­мен­том для соз­дания и вы­пуска при­ло­жений от коман­ды Ubuntu. Ник Вейч на­страи­ва­ет­ся на лю­би­мый под­каст…

На этом эпи­че­ском и зре­лищ­ном уро­ке на­ша за­да­ча, ни мно­го ни ма­ло, напи­сать пол­но­цен­ное на­столь­ное при­ло­жение, соз­дать для него па­кет Debian и вы­пустить его в боль­шой мир. Ду­мае­те, невоз­мож­но соз­дать пол­но­цен­ное ра­бо­таю­щее на­столь­ное при­ло­жение, ко­то­рое и вправ­ду что-то де­ла­ет, в рам­ках че­ты­рех­странич­но­го уро­ка? В чем-то вы пра­вы; вот мы и рас­ши­ри­ли урок это­го но­ме­ра в два раза, до вось­ми стра­ниц. И на сей раз у нас есть силь­ный со­юзник – Quickly.

Quickly [англ. «бы­ст­ро»] – это на­бор средств раз­ра­бот­ки, с при­ла­гае­мы­ми шаб­ло­на­ми, для соз­дания по­до­бия RAD-сре­ды (Rapid Application Development – бы­ст­рая раз­ра­бот­ка при­ло­жений). Оно до­воль­но ско­ро­ст­ное: мож­но соз­дать и за­пустить при­ло­жение за несколь­ко се­кунд – про­смот­ри­те далее по­ша­го­вое ру­ко­во­дство и убе­ди­тесь на­гляд­но. Од­на­ко, хоть мы и мо­жем соз­дать ра­бо­таю­щее при­ло­жение, в нем не бу­дет ни грам­ма функ­цио­на­ла, необ­хо­ди­мо­го для ме­га­заг­ру­чи­ка под­кастов TuxRadar (а его-то мы и со­би­ра­ем­ся де­лать). И все же про­бе­ги­тесь по тек­сту, что­бы по­нять, как все ра­бо­та­ет, за­тем вернитесь сю­да – и мы раз­бе­рем­ся с под­каста­ми.

Что нам пона­добится

Наш урок ос­но­ван на Quickly, ин­ст­ру­мен­те раз­ра­бот­ки Ubuntu; по­это­му бу­дет на­мно­го про­ще, ес­ли вы возь­ме­те Ubuntu (на­при­мер, с дис­ка LXF133). То­гда про­сто ус­та­но­ви­те па­ке­ты quickly и python-feedparser из ре­по­зи­то­рия, и все бу­дет го­то­во.

Ес­ли вы рас­па­ко­ва­ли код с LXFDVD в удоб­ное ме­сто, мо­же­те ис­поль­зо­вать для уро­ка его – на­стро­ен­ное Quickly уже го­то­во к ра­бо­те, и мож­но ис­поль­зо­вать ко­ман­ды Quickly.

Соз­да­ем при­ло­жение

Итак, мы оз­на­ко­ми­лись с ба­зо­вы­ми ме­ханиз­ма­ми ра­бо­ты Quickly; по­ра сде­лать что-то по­лез­ное. Как уже ска­за­но, мы на­ме­ре­ны соз­дать за­груз­чик под­кастов. По­нят­но, при­дет­ся по­во­зить­ся с но­во­ст­ны­ми лен­та­ми и то­му по­доб­ным, но для на­ча­ла при­па­сем ме­сто для хранения за­гру­жае­мых фай­лов. Мы обя­за­ны дать поль­зо­ва­те­лям воз­мож­ность вы­брать ме­сто хранения (или соз­дать но­вое, ес­ли на­до); зна­чит, необ­хо­дим диа­лог, в ко­то­ром они смо­гут это сде­лать.

Весь­ма удоб­но, что шаб­лон на­ше­го при­ло­жения со­дер­жит ре­дак­тор па­ра­мет­ров. В нем еще ниче­го нет, но это уже хо­ро­шее на­ча­ло. В та­ких си­туа­ци­ях обыч­но луч­ше на­чать с ин­тер­фей­са поль­зо­ва­те­ля, чем с са­мо­го ко­да – хо­тя бы по­то­му, что это по­мо­жет уз­нать име­на объ­ек­тов и их сиг­на­лы до напи­сания ко­да, объ­е­ди­няю­ще­го их. Итак, за­пусти­те Glade коман­дой quickly design в ка­та­ло­ге при­ло­жения.

От­кро­ет­ся Glade с глав­ным UI-фай­лом про­ек­та, но нам-то ну­жен дру­гой, так что от­крой­те пра­виль­ный, с именем PreferencesMyAppDialog.ui. По­сле его от­кры­тия вы уви­ди­те по­се­ре­дине при­ят­ную пусто­ту. Пер­вым де­лом раз­местим в ней эле­мент vbox, вер­тикаль­ный кон­тейнер – он раз­ме­ща­ет свои до­черние эле­мен­ты по вер­тика­ли. Щелкните в ле­вой панели на икон­ке vbox, а за­тем в се­рой об­ласти, что­бы раз­местить его. Всплы­вет диа­лог, за­пра­ши­ваю­щий, сколь­ко эле­мен­тов вы хо­ти­те раз­местить – оставь­те их 3. По­ка мы упот­ре­бим толь­ко два из них, но это не бе­да – один бу­дет про за­пас, по­сколь­ку, ве­ро­ятнее все­го, он по­на­до­бит­ся нам позднее. Но не за­па­сай­те слиш­ком мно­го: до­ба­вить эле­мен­ты в вер­тикаль­ный кон­тейнер про­сто, а вот уда­лить – боль­шая мо­ро­ка.

В верхней части раз­местим мет­ку, что­бы поль­зо­ва­тель по­нимал, что про­ис­хо­дит. Вы­бе­ри­те эле­мент Label в панели сле­ва и щелкните в верхней части vbox. Сей­час в ней напи­са­но просто «Label», но текст мож­но из­менить при по­мо­щи ин­спек­то­ра свойств спра­ва внизу.


По­местив бо­лее осмыс­лен­ный текст в свой­стве Мет­ка, до­бавь­те ка­кой-нибудь спо­соб вы­бо­ра ка­та­ло­га. Та­ко­вых мож­но при­ду­мать мно­го, но GTK со­дер­жит спе­ци­аль­ную кноп­ку имен­но для этой це­ли, и бы­ло бы глу­по ею не восполь­зо­вать­ся. Вы­бе­ри­те объ­ект FileDialogButton в панели сле­ва и по­мести­те его в сре­дин­ный слот.

Нам нуж­но из­менить неко­то­рые свой­ства. На вклад­ке «Основ­ные» про­кру­чи­вай­те спи­сок вниз, по­ка не на­ткнетесь на пункт «Дей­ствие». Из­мените его на «Вы­бор ка­та­ло­га». Стандарт­ное дей­ствие – это вы­бор фай­ла, но это не то, что нам нуж­но. По­сле вы­бо­ра это­го дей­ствия, щел­чок на кноп­ке при­ве­дет к за­полнению спи­ска со­от­вет­ствую­щим со­дер­жи­мым. Мы мо­жем оп­ро­бо­вать ее уже в Glade – про­сто щелкните на икон­ке у пра­во­го края кноп­ки.

Од­на­ко пре­ж­де чем со­хранить наш но­вый диа­лог, сде­лай­те еще од­ну важ­ную вещь. Когда кто-нибудь вы­би­ра­ет ка­та­лог, мы долж­ны как-то уз­нать, что же он вы­брал. Для это­го мы по­да­ем сиг­нал от объ­ек­та, со­об­щаю­щий, что некое зна­чение из­менилось. С точ­ки зрения ко­да, нуж­но напи­сать ме­тод-об­ра­бот­чик, ра­бо­таю­щий с дан­ны­ми, но об­ра­бот­чик необ­хо­ди­мо ука­зать здесь при ди­зайне UI-фай­ла. Щелкните на вклад­ке «Сиг­на­лы». Вы уви­ди­те, что сиг­на­лов, к ко­то­рым мож­но под­клю­чить­ся, пол­но. Нас ин­те­ре­су­ет сиг­нал из раз­де­ла ‘GTKFileChooser’ – от­крой­те его. В пер­вом столб­це два­ж­ды щелкните спра­ва от на­звания ‘current-folder-changed’ и вве­ди­те имя об­ра­бот­чи­ка ‘folder_changed’. Те­перь, сто­ит поль­зо­ва­те­лю вы­брать дру­гой ка­та­лог, мы уз­на­ем об этом и из­меним со­от­вет­ствую­щий па­ра­метр при­ло­жения.

Не за­будь­те со­хранить файл пе­ред вы­хо­дом из Glade. Те­перь при­шло вре­мя вве­сти quickly save ‘prefs dialog’, что пе­ре­даст ва­ши но­вые фай­лы систе­ме управ­ления вер­сия­ми (см. со­вет Quickly Со­хра­няй­те по­ча­ще).

Что в име­ни тво­ем?

Не ис­клю­че­но, что вы счи­тае­те UI-фай­лы Glade про­сто XML-ко­дом, од­на­ко вы не смо­же­те про­сто пе­ре­не­сти их из од­но­го про­ек­та в дру­гой без зна­чи­тель­но­го ре­фак­то­рин­га. Они на­пич­ка­ны ссыл­ка­ми на имя про­ек­та, так что вы не смо­же­те да­же про­сто им­пор­ти­ро­вать их в ваш про­ект – луч­ше по­зво­лить Quickly соз­дать для вас ба­зо­вые UI-фай­лы.

Храните здесь

Пре­ж­де чем по­гру­зить­ся в код, что­бы сде­лать этот ше­девр ин­тер­фей­са ра­бо­чим, при­кинем, где мы бу­дем все раз­ме­щать: ведь глав­ный смысл на­стро­ек в том, что они со­хра­ня­ют­ся на бу­ду­щее. Так что тре­бу­ет­ся ме­сто для хранения. Дан­ные бу­дут ти­па на­стро­ек при­ло­жения, лен­т, на ко­то­рые мы под­пи­шем­ся, и, воз­мож­но, мно­гого дру­гого, о чем мы еще не за­ду­мы­ва­лись. Луч­шее все­го вы­брать нечто лег­ко­доступ­ное. Име­ет­ся мно­же­ство по­пу­ляр­ных и хо­ро­шо до­ку­мен­ти­ро­ван­ных мо­ду­лей Python для со­хранения на­стро­ек, вро­де ConfigObj, ConfigParser и т. д., но мы ис­поль­зу­ем нечто иное: ба­зу дан­ных под на­званием CouchDB.

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

На са­мом де­ле в про­ект вклю­че­на не толь­ко ба­за, но и код для досту­па к ней; оста­ет­ся до­ба­вить там и сям несколь­ко строк. Ес­ли вы же­лае­те спер­ва оз­нко­мить­ся с CouchDB, про­смот­ри­те раздел Что та­кое CouchDB?

За­пусти­те Gedit коман­дой quickly edit, и все фай­лы с ко­дом от­кро­ют­ся ав­то­ма­ти­че­ски. Най­ди­те PreferencesMyAppDialog.py с ко­дом об­ра­бот­ки на­стро­ек. Неваж­но, понимае­те ли вы весь этот код или нет, но чтение его вам по­мо­жет. Здесь про­ис­хо­дит несколь­ко ве­щей: несколь­ко функ­ций инициа­ли­за­ции, генери­рую­щих ок­но диа­ло­га, и некий код для ра­бо­ты с ба­зой дан­ных. К сча­стью, в нем име­ют­ся ком­мен­та­рии, и най­ти его бу­дет не слож­но. При вы­полнении дан­ный код про­ве­ря­ет, не за­да­ны ли уже неко­то­рые на­строй­ки; ес­ли нет, он от­кры­ва­ет ба­зу дан­ных (и за­пуска­ет CouchDB, ес­ли она еще не за­пу­ще­на) и пы­та­ет­ся за­гру­зить их из нее. Ес­ли там их нет, необ­хо­ди­мо вве­сти в код уста­нов­ку зна­чений по умол­чанию, что мы да­лее и сде­ла­ем. Здесь есть неболь­шой код для кно­пок ‘OK’ и ‘Cancel’, ко­то­рые вско­ре поя­вят­ся.

Что за шту­ка – CouchDB?

  • Итак, я понимаю, что CouchDB — еще од­на ба­за дан­ных, как SQL и вся­кое та­кое?
Не со­всем. CouchDB – ско­рее дви­жок хранили­ща до­ку­мен­тов. В от­ли­чие от ба­зы дан­ных, он про­сто хранит све­дения, ко­то­рые вы ему пе­ре­дае­те, поч­ти их не струк­ту­ри­руя, лег­ким для досту­па спо­со­бом. Же­ст­кой схе­мы дан­ных нет (все ор­ганизо­ва­но в ви­де пар ключ:зна­чение, по ти­пу объ­ек­та-сло­ва­ря в Python), что весь­ма удоб­но для нас.
  • Он мо­жет хранить толь­ко текст? А как на­счет дру­гих дан­ных, ко­то­рые я за­хо­чу туда по­местить?
Зна­чение клю­ча мо­жет быть прак­ти­че­ски лю­бым ти­пом дан­ных Python – чис­лом, стро­кой и на­бо­ром раз­лич­ных объ­ек­тов, спи­ском и да­же дру­гим сло­ва­рем. Вы мо­же­те до­бав­лять дру­гие объ­ек­ты – на­при­мер, фай­лы с изо­бра­жения­ми – в ка­че­стве при­ло­жений к ба­зе дан­ных, но бо­лее эф­фек­тив­но хранить их дру­гим спо­со­бом.
  • Зна­чит, тот, кто возь­мет мое при­ло­жение для сво­ей ра­бо­ты, дол­жен за­пустить еще один сер­вер ба­зы данных?
Нет, об этом в Python по­за­бо­тит­ся биб­лио­те­ка Desktopcouch. Она за­пуска­ет ба­зу дан­ных по лю­бо­му тре­бо­ванию. CouchDB раз­ра­бо­та­н нетре­бо­ва­тель­ным к ре­сур­сам, так что ско­рее все­го вы да­же не по­чув­ствуе­те, что он за­пусти­лся. Ес­ли, конеч­но, вы не сле­ди­те за ним…
  • То есть как это — «сле­дить за ним»?
Ну, од­но из са­мых за­ме­ча­тель­ных свойств этой ба­зы дан­ных – то, что она соз­да­на с при­це­лом на web. Она бы­ла раз­ра­бо­та­на для ра­бо­ты с неслож­ны­ми API, реа­ли­зо­ван­ны­ми че­рез HTTP, что­бы сде­лать ее по­лез­ной для web-при­ло­жений. Ба­зи­ру­ясь на прин­ци­пах ACID (для фа­на­тов аб­бре­виа­тур: Atomicity, Consistency, Isolation и Durability – Ато­мар­ность, Со­гла­со­ван­ность, Изо­ли­ро­ван­ность, Дол­го­веч­ность), она соз­да­ва­лась с це­лью быть по­сто­ян­но в ра­бо­чем со­стоянии, что­бы со­про­тив­лять­ся сбо­ям или от­клю­чению. Она мо­жет вы­пол­нять вся­кие по­лез­ные дей­ствия, вро­де P2P-ре­п­ли­ка­ций. Для на­ших це­лей, сле­ду­ет лишь на­пра­вить брау­зер по ад­ре­су file:///home/<имя_поль­зо­ва­те­ля>/.local/share/desktop-couch/couchdb.html – там вы об­на­ру­жи­те все ба­зы дан­ных, ис­поль­зуе­мые desktop-couch.
  • Вы­хо­дит, это нечто вро­де MySQL-Admin?
По­хо­же, толь­ко работать с этим про­ще. Ис­поль­зо­вание web-ин­тер­фей­са для из­менения дан­ных в лю­бой ба­зе весь­ма про­сто и, конеч­но же, от­ла­жи­вать тран­зак­ции ба­зы дан­ных так­же лег­ко.

Сброс CouchDB

Ко­гда на­ше при­ло­же­ние ищет со­хра­нен­ные на­строй­ки при стар­те, оно по­слуш­но за­гру­жа­ет то, что на­хо­дит. Но ес­ли вы нач­не­те до­бав­лять в раз­ные ве­щи код, их мо­жет не ока­зать­ся в ба­зе дан­ных, и ни­че­го не про­изой­дет. В этом слу­чае про­ще все­го уда­лить ба­зу дан­ных CouchDB из брау­зе­ра. Пе­рей­ди­те в file:///home/<имя_поль­зо­ва­те­ля>/.local/share/desktop-couch/couchdb.html и про­сто вы­де­ли­те и уда­ли­те ба­зу дан­ных ва­ше­го при­ло­же­ния. При сле­дую­щем за­пус­ке она бу­дет соз­да­на из стан­дарт­ных зна­че­ний.

Со­хра­няй­те по­ча­ще

Ино­гда нам слу­ча­ет­ся на­пор­та­чить. Для фай­лов ис­ход­ных тек­стов это обыч­но не про­бле­ма, но для фай­лов ин­тер­фей­са Glade прак­ти­че­ски фа­таль­но, по­то­му что в них мно­го не­об­ра­ти­мых дей­ст­вий.

По этой при­чи­не Quickly име­ет встро­ен­ную сис­те­му управ­ле­ния вер­сия­ми. Это не про­сто спо­соб упа­ков­ки и пуб­ли­ка­ции про­ек­та на Launchpad; это по­лез­ная вещь по су­ти для все­го. Ес­ли вве­сти ко­ман­ду quickly save ‘ком­мен­та­рий к но­вой вер­сии’, то но­вая вер­сия ва­ше­го про­ек­та за­не­сет­ся в сис­те­му кон­тро­ля вер­сий Bazaar. Про­ве­рить со­хра­нен­ные вер­сии мож­но, ско­ман­до­вав bzr log – вы­ве­дет­ся спи­сок всех вы­пол­нен­ных тран­зак­ций, вме­сте с за­мет­ка­ми и да­той-вре­ме­нем. Ес­ли уг­ро­би­лось во­об­ще все, вве­ди­те в глав­ном ка­та­ло­ге при­ло­же­ния bzr revert – и файл с с ис­ход­ны­ми тек­ста­ми, гра­фи­че­ский ин­тер­фейс и лю­бые дру­гие фай­лы вос­ста­но­вят­ся из пре­ды­ду­щей вер­сии.

Под­сказ­ка-бо­нус: при же­ла­нии вос­ста­но­вить лишь часть про­ек­та, вы­пол­ни­те quickly design и quickly edit, ука­жи­те ко­ман­ду revert и сохра­ни­те фай­лы, ко­то­рые хо­ти­те ос­та­вить как бы­ли.

Со­хра­ня­ем вво­ди­мые зна­чения

Итак, мы ре­ши­ли со­хра­нять в на­строй­ках ка­та­лог, ку­да бу­дут за­гру­жать­ся фай­лы. Со­глас­но ко­ду, на­строй­ки хра­нят­ся в объ­ек­те-сло­ва­ре с именем self._preferences (часть ‘self’ оз­на­ча­ет, что это часть дан­ных для объ­ек­та-диа­ло­га, соз­да­вае­мо­го при вы­зо­ве это­го ко­да). Здесь есть да­же под­хо­дя­щее ме­сто для раз­ме­щения умол­чаний – в ме­то­де с именем _load_preferences, а так­же име­ет­ся ком­мен­та­рий TODO, со­об­щаю­щий, что здесь-то и сле­ду­ет раз­местить зна­чения по умол­чанию. Здесь и вве­ди­те

self._preferences[“savedir”]=”~/”

(Убе­ди­тесь, что в ко­де долж­ное ко­ли­че­ство от­сту­пов, ина­че по­лу­чи­те ошиб­ку). Это ку­со­чек ма­гии со­кра­щений в Linux. Сим­вол «тиль­да» (~) в запи­си пу­ти к фай­лу рас­по­зна­ет­ся как путь к до­машней ди­рек­то­рии поль­зо­ва­те­ля, так что эта стро­ка ав­то­ма­ти­че­ски уста­нав­ли­ва­ет его в ка­че­стве ка­та­ло­га со­хранений, ес­ли поль­зо­ва­тель не из­менит это.

Те­перь до­ба­вим об­ра­бот­чик сиг­на­ла, ко­то­рый мы вве­ли при раз­ра­бот­ке ин­тер­фей­са поль­зо­ва­те­ля. Раз­местить его мож­но в лю­бом месте клас­са, но, на­вер­но, луч­шим ва­ри­ан­том бу­дет непо­сред­ствен­но пе­ред стро­кой, на­чи­наю­щей­ся с def ok(self,widget,... – об­ра­бот­чи­ком на­жа­тия кла­ви­ши OK:

def folder_changed(self, widget, data=None):
self._preferences[“savedir”]=widget.get_current_folder()

Все очень про­сто. В PyGTK, вид­жет, по­даю­щий сиг­нал, вклю­ча­ет са­мо­го се­бя в об­рат­ный вы­зов об­ра­бот­чи­ка. Как мы зна­ем, это сиг­нал от FileChooserDialog; мы так­же зна­ем, что у него есть ме­тод до­бы­вания те­ку­ще­го вы­бран­но­го зна­чения (в ви­де стро­ки), и мож­но про­сто со­хранить их в на­шем сло­ва­ре на­стро­ек.

Тут, од­на­ко, вста­ет ин­те­рес­ный во­прос: с ка­ким зна­чением по­яв­ля­ет­ся вид­жет? От­вет та­ков: ес­ли мы ранее ниче­го не оп­ре­де­ли­ли, то за­пуска­ет­ся он с пустым зна­чением. Это мо­жет обеску­ра­жить поль­зо­ва­те­ля; нуж­но за­ранее вклю­чить неко­то­рый код, что­бы кор­рект­но за­дать зна­чение. Для это­го по­тре­бу­ет­ся раз­до­быть ссыл­ку на сам вид­жет.

Код в на­шем фай­ле ис­поль­зу­ет са­мо­стоя­тель­ный сбор­щик при­ло­жения для соз­дания ре­аль­но­го диа­ло­га из фай­ла .ui, ко­то­рый мы со­хранили, так что на гра­фи­че­ский ин­тер­фейс ссыла­ют­ся локаль­но че­рез объ­ект self.builder. Имя вид­же­та мы помнимn (filechooserbutton1), и мо­жем про­сто ра­зы­скать этот объ­ект и со­хранить в нем пе­ре­мен­ную с на­строй­ка­ми при по­мо­щи под­хо­дя­ще­го ме­то­да:

o=self.builder.get_object(‘filechooserbutton1’)
o.set_current_folder(self._preferences[“savedir”])

Луч­ше все­го раз­местить это где-то сра­зу по­сле соз­дания диа­ло­га и за­груз­ки на­стро­ек. К сча­стью, вы вновь об­на­ру­жи­те по­лез­ную за­мет­ку где-то в стро­ке 60: #TODO: code for other initialization actions should be added here [#Сде­лать: код дру­гих дей­ствий инициа­ли­за­ции сле­ду­ет раз­местить здесь], так что до­бавь­те его сра­зу по­сле нее (точ­ный но­мер стро­ки за­ви­сит от дру­гих функ­ций, та­ких как генера­ция ли­цен­зии и про­чее – про­сто при­зо­ви­те свой ин­тел­лект, что­бы най­ти ее!).

Итак, мы соз­да­ли ме­сто для хранения на­стро­ек, при­вя­за­ли сиг­нал к ме­то­ду по­лу­чения но­во­го зна­чения при его из­менении поль­зо­ва­те­лем и не за­бы­ли взять зна­чение и по­местить его в вид­жет при от­кры­тии диа­ло­га. Код для кноп­ки OK по­за­бо­тит­ся о со­хранении лю­бых из­менений. На­до еще что-нибудь?

Что про­изой­дет, ес­ли поль­зо­ва­тель из­менит ка­та­лог, а за­тем пе­ре­ду­ма­ет и на­жмет кноп­ку Cancel [От­ме­на]? Со­глас­но те­ку­ще­му ко­ду, на­строй­ки из­ме­ня­ют­ся (но не со­хра­ня­ют­ся) в мо­мент, когда поль­зо­ва­тель вы­би­ра­ет дру­гой ка­та­лог. Ес­ли он на­жм­ет Cancel, то ка­та­лог хо­тя и не со­хранит­ся, но оста­нет­ся в па­мя­ти как вы­бран­ный па­ра­метр на­строй­ки. Сле­ду­ет учесть это и дать знать основ­но­му при­ло­жению об из­менении па­ра­мет­ров, про­сто пе­ре­за­гру­зив их из ба­зы дан­ных при за­кры­тии диа­ло­га. Для это­го нуж­но из­менить глав­ный файл MyApp.py (ну или как вы там его на­зва­ли), а так­же обес­пе­чить, что­бы на­строй­ки за­гру­жа­лись при стар­те при­ло­жения. Код, вы­пол­няю­щий это, прак­ти­че­ски тот же, и не будь он все­го из двух строк, его сле­до­ва­ло бы офор­мить в ви­де функ­ции:

dlg = PreferencesPlopDialog.PreferencesPlopDialog()
self.preferences = dlg.get_preferences()

Здесь про­сто соз­да­ет­ся (но не по­ка­зы­ва­ет­ся) диа­лог, а за­тем из него бе­рут­ся на­строй­ки. Ме­сто для это­го ко­да в основ­ном фай­ле – при­бли­зи­тель­но око­ло стро­ки 90 (по­сле ком­мен­та­рия # Code for other initialization actions should be added here [осталь­ной код инициа­ли­за­ции раз­местить здесь]) и стро­ки 103 (по­сле ком­мен­та­рия # Make any updates based on changed preferences here [Лю­бые из­менения на осно­ве па­ра­мет­ров на­строй­ки вы­пол­-нять здесь]).

Это­го долж­но хватить. Те­перь за­пусти­те при­ло­жение и по­смот­ри­те, как оно ра­бо­та­ет. Про­тести­руй­те вы­бор ка­та­ло­га и за­крой­те и от­крой­те при­ло­жение вновь, пре­ж­де чем мы двинем­ся даль­ше.

ОК, до сих пор мы вас ща­ди­ли, по­сколь­ку вы, ско­рее все­го, но­вич­ки в Quickly, но те­перь вы знае­те до­ро­гу, и нам сле­ду­ет уско­рить­ся, ес­ли мы хо­тим за­вер­шить наш за­груз­чик под­кастов – уж из­вините, ка­ж­дое на­жа­тие кноп­ки раз­же­вы­вать не бу­дем. Ес­ли вы в чем-то не уве­ре­ны, все­гда мож­но об­ра­тить­ся к фай­лам про­ек­та на LXFDVD!

На­ша сле­дую­щая за­да­ча – соз­дать основ­ной ин­тер­фейс, так что на­бе­ри­те quickly design и за­гру­зи­те со­от­вет­ствую­щий файл. Уда­ли­те имею­щие­ся там объ­ек­ты, но вер­тикаль­ный кон­тейнер мож­но оста­вить.

Нам ну­жен спо­соб по­ка­зать спи­сок источников и спи­сок фай­лов вы­бран­но­го источника. Это мож­но сде­лать по-раз­но­му, но в на­шем при­ло­жении мы восполь­зу­ем­ся про­смот­ром в го­ри­зон­таль­ной панели, что­бы два спи­ска раз­де­ля­лись вер­тикаль­ной чер­той. Дей­ствуй­те: раз­мести­те ее в пустом кон­тейнере.

Внутрь ка­ж­дой панели сле­ду­ет по­местить кон­тейнер Про­кру­чи­вае­мое ок­но (возь­мем его в том же раз­де­ле па­лит­ры сле­ва), это по­мо­жет про­смат­ри­вать спи­ски боль­шой дли­ны.

Те­перь в пра­вой панели раз­мести­те объ­ект Де­ре­во. Это по­хо­же на спи­сок, и мы бу­дем при­ме­нять его для ото­бра­жения гра­фи­ки, иден­ти­фи­ци­рую­щей под­касты, на ко­то­рые мы подпи­са­ны. При раз­ме­щении объ­ек­та Де­ре­во Glade по­тре­бу­ет ука­зать его мо­дель. Про­сто соз­дай­те но­вую. Мо­дель – это обыч­ный спи­сок ти­пов дан­ных и ме­ток, ис­поль­зую­щих­ся для манипу­ля­ций с дан­ны­ми или ото­бра­жения их в ин­тер­фей­се поль­зо­ва­те­ля. Мы начнем с че­ты­рех зна­чений – guint (це­лое без зна­ка) с именем source_id, gchararray (мас­сив сим­во­лов – стро­ка) с именем source_name, еще один gchararray с именем source_url и GdkPixBuf (мас­сив пик­се­лей) с именем source_img.

Что это бы­ло?

По­следний тип – изо­бра­жение; по умол­чанию, его-то мы и бу­дем ото­бра­жать спра­ва. Щелкните по объ­ек­ту Де­ре­во в дре­во­вид­ном спи­ске объ­ек­тов и вы­бе­ри­те пункт ме­ню Edit, что­бы ре­дак­ти­ро­вать свой­ства объ­ек­та бо­лее под­роб­но в от­дель­ном окне. Про­кру­чи­вай­те вниз, по­ка не уви­ди­те пункт «Стол­бец всплы­ваю­щей под­сказ­ки», и вве­ди­те зна­чение «1». Этим мы вы­би­ра­ем пер­вый стол­бец толь­ко что соз­дан­но­го спи­ска (мо­де­ли) в ка­че­стве всплы­ваю­щей под­сказ­ки к ка­ж­дой запи­си. Те­перь пе­рей­ди­те на вклад­ку Ие­рар­хия и на­жми­те внизу спи­ска кноп­ку До­ба­вить, и до­бавь­те од­ну ко­лон­ку. Спра­ва за­да­ет­ся за­го­ло­вок и на­бор свойств. Те­перь щелкните на до­бав­лен­ном столб­це пра­вой кноп­кой мы­ши и в поя­вив­шем­ся ме­ню вы­бе­ри­те ‘До­ба­вить до­черний эле­мент Изо­бра­жение’. В панели спра­ва уста­но­ви­те для пунк­та Объ­ект зна­чок (Pixbuf) в зна­чение 3, или вы­бе­ри­те source_img в вы­па­даю­щем спи­ске. Это по­зво­лит ото­бра­жать на­ше изо­бра­жения в спи­ске в ячей­ках.

Пе­ред вы­хо­дом до­бавь­те ссыл­ку на об­ра­бот­чик сиг­на­ла. На вклад­ке Сиг­на­лы объ­ек­та Де­ре­во, най­ди­те пункт ‘row-activated’ и до­бавь­те запись с именем ‘row_chosen’. Со­храните все!

При­шло вре­мя по­ра­бо­тать с ко­дом. Пре­ж­де все­го нам ну­жен хит­рый ме­тод для из­вле­чения изо­бра­жения-икон­ки для кон­крет­ной лен­ты. Мы мо­жем ука­зать URL и из­влечь изо­бра­жение Pixbuf. Лег­ко! (Это ес­ли вы сле­ди­ли за на­шей се­ри­ей.)

 def fetch_image(self,url):
  import feedparser,urllib
  #най­дем имя изо­бра­же­ния
  f=feedparser.parse(url)
  name = f.feed.image.href
  imgfile,data = urllib.urlretrieve(name)
  img=gtk.gdk.pixbuf_new_from_file(imgfile)
  img=img.scale_simple(150,150,gtk.gdk.INTERP_BILINEAR)
  return img

На до­су­ге мож­но по­ду­мать и о кэ­ши­ро­вании изо­бра­жений (помните: име­на фай­лов мо­гут кон­флик­то­вать, так что не помешает до­бав­лять к ним и хэш URL’а); но по­ка оста­вим это. Они, конеч­но, бу­дут за­гру­жать­ся при ка­ж­дом за­про­се, но благодаря их ма­лому раз­ме­ру вы ед­ва ли по­чув­ствуе­те серьезное снижение ско­ро­сти.

Объ­е­дините этот ме­тод с дру­ги­ми в глав­ном фай­ле MyApp.py, и раз уж файл от­крыт, про­смот­ри­те код, сле­дую­щий по­сле ком­мен­та­рия # Code for other initialization actions should be added here [осталь­ной код инициа­ли­за­ции раз­местить здесь]. До­бавь­те сле­дую­щий код:

 store1=self.builder.get_object(“liststore1”)
 for index, sub in enumerate(self.preferences[‘subs’]):
   # струк­ту­ра дан­ных subs долж­на
   # со­дер­жать name и url
   img=self.fetch_image(sub[1])
   store1.append([index,sub[0],sub[1],img])

Пе­ре­мен­ная store1 со­дер­жит объ­ект liststore, генери­руе­мый в UI, и мы мо­жем за­полнить его дан­ны­ми. Я так ви­жу, что дан­ные о на­ших лен­тах бу­дут хранить­ся в на­строй­ках в ви­де спи­ска: имя лен­ты и URL (мы сде­ла­ем это че­рез ми­ну­ту). Ис­поль­зуя URL, мы до­бу­дем изо­бра­жение и по­местим весь на­бор в хранили­ще спи­ска. Про­смотр в цик­ле (по спи­ску спи­сков) соз­да­ет до­полнитель­ную пе­ре­мен­ную – но­мер ите­ра­ции; его мы ис­поль­зу­ем для соз­дания иден­ти­фи­ка­то­ра ID. Оста­лось толь­ко вер­нуть­ся в файл PreferencesMyAppDialog.py и до­ба­вить сле­дую­щее к пе­ре­мен­ным по умол­чанию:

self._preferences[“subs”]=[[“Tux Radar podcast (ogg) “http:// www.tuxradar.com/files/podcast/podcast_ogg.rss”]]

По­мести­те это сра­зу по­сле умол­чаний savedir, вве­ден­ных на­ми ранее. Со­храните все опять и вновь за­пусти­те при­ло­жение. Те­перь вы долж­ны уви­деть за­гру­жен­ный ло­го­тип TuxRadar в ле­вой панели!

Соз­дание при­ло­жений с гра­фи­че­ским ин­тер­фей­сом на­чи­на­ет на­по­ми­нать уп­ражнение в про­клад­ке труб. В на­шем пер­вом спи­ске необ­хо­ди­мо что-то сде­лать, ес­ли кто-то вы­бе­рет в нем эле­мент. Ме­ханизм дей­ствий та­кой: пе­ре­хва­ты­ва­ем сиг­нал, по­да­вае­мый спи­ском, а за­тем де­ла­ем нечто с дру­гим спи­ском; на­до лишь со­единить ме­ж­ду со­бой части ко­да. Нас не вол­ну­ет, когда они бу­дут ис­пол­нять­ся – это де­ло поль­зо­ва­те­ля; глав­ное – про­ду­мать все по­след­ствия пред­принимае­мых дей­ствий.

Поль­зуй­тесь жур­на­лом

Древ­ний при­ем на­шпи­го­вы­вать про­грам­му опе­ра­то­ра­ми вы­во­да раз­лич­ных зна­че­ний и объ­ек­тов для про­вер­ки – это от­стой. Оно, ко­неч­но, по­мо­га­ет, но по­сле от­лад­ки ка­кой-ли­бо час­ти ко­да их нуж­но вы­чи­щать, не то ваш код раз­бух­нет и при ка­ж­дом за­пус­ке кон­соль бу­дет за­пол­нять­ся му­со­ром. Ос­нов­ной файл про­грам­мы, соз­да­вае­мый Quickly, ини­циа­ли­зи­ру­ет жур­нал, соз­да­вае­мый со­от­вет­ст­вую­щим мо­ду­лем Python, ес­ли вы за­пус­ти­те при­ло­же­ние с клю­чом -v (это мож­но де­лать и в Quickly). Так что для вы­во­да со­об­ще­ний про­сто ис­поль­зуй­те:

logging.debug(“ prefs= %s”,self.preferences[‘subs’])

Этот ме­тод сле­ду­ет стан­дарт­ным пра­ви­лам под­ста­нов­ки; его пре­лесть в том, что вы­вод он про­из­во­дит, толь­ко ес­ли его об этом просят.

Тру­бо­про­во­ды

Пре­ж­де чем под­клю­чать­ся ко вто­ро­му спи­ску, не ху­до уз­нать, что в нем со­дер­жит­ся. От­крой­те Glade сно­ва и раз­мести­те Де­ре­во во вто­ром про­кру­чи­вае­мом окне. В соз­дан­ном для него пред­став­лении спи­ском сле­ду­ет соз­дать несколь­ко столб­цов – boolean (ло­ги­че­ское) с именем downloaded [за­гру­же­но], gfloat (чис­ло с пла­ваю­щей точ­кой) с именем progress [про­гресс] и во­семь gchararrays – их я на­завл title [на­звание], subtitle [под­за­го­ло­вок], summary [опи­сание], tags [тэ­ги], size [раз­мер], duration [дли­тель­ность], media_type [тип со­дер­жи­мо­го] и media_url [URL со­дер­жи­мо­го]. Их на­зна­чение яс­но из имен, и, че­ст­но го­во­ря, де­ло до них ско­рее все­го не дой­дет.

Есте­ствен­но бы­ло бы хранить все эти дан­ные в объ­ек­те и ко­пи­ро­вать его в мо­дель спи­ска при необ­хо­ди­мо­сти, но в ито­ге у вас по­лу­чит­ся две ко­пии всех дан­ных, и за­бо­та об их син­хрониза­ции станет про­бле­мой – на­ко­пив опыт, вы при­де­те к вы­во­ду, что про­ще хранить дан­ные в спи­ске-пред­став­лении и ссылать­ся на не­го при необ­хо­ди­мо­сти, а не оп­ра­ши­вать пе­ре­мен­ные на ка­ж­дом ша­гу. При до­бав­лении столб­цов в са­мо Де­ре­во, нет ну­ж­ды до­бав­лять все, од­на­ко пер­вым до­бавь­те cellrendererprogress и свя­жи­те его с дан­ны­ми о про­грес­се вы­полнения.

Для это­го Де­ре­ва же­ла­тель­но убе­дить­ся, что в мет­ке Об­щие для свой­ства Со­бы­тия уста­нов­ле­но зна­чение по­лу­чать все. Так­же до­бавь­те сиг­нал с именем file_chosen в row-activated. Мы соз­да­дим для него ме­тод об­ра­бот­ки двой­но­го щелч­ка на фай­ле.

Для из­вле­чения дан­ных и со­дер­жи­мо­го в ваш спи­сок-пред­став­ление до­бавь­те та­кой код в глав­ный файл:

  def row_chosen(self,treeview,selection,treeviewcolumn):
     “”” Обработчик сигнала для выбора элемента в строке источника”””
     import feedparser
     listmodel=treeview.get_model()
     store2=self.builder.get_object(“liststore2”)
     store2.clear()
    savedir=self.preferences[“savedir”]
    index=listmodel.get_iter(selection[0])
    source_url =listmodel.get_value(index,2)
    # получаем список файлов
    f=feedparser.parse(source_url)
    for entry in f.entries:
      media_url=entry.enclosures[0][“href”]
      logging.debug(“media url = %s”,media_url)
      fname=os.path.split(media_url)[­1]
      #проверяем что файл уже загру жен
      downloaded=os.path.exists(os.path.join(savedir,fname))
      progress=0.0
      if downloaded:
         progress=100
      title= entry.title if entry.has_key(“title”) else “Unnamed”
      subtitle= entry.subtitle if entry.has_key(“subtitle”) else “no information”
      summary=entry.summary if entry.has_key(“summary”) else “no information”
      tags = ‘’
      if entry.has_key(“tags”):
         for item in tags:
           tags+=item.term
      size= entry.enclosures[0].length
      duration= entry.itunes_duration if entry.has_key(‘itunes_duration’) else??:??”
      media_type= entry.enclosures[0].type
      logging.debug(“data:%s”,[downloaded,progress,title,subtitle,summary,tags,size,duration,media_type,media_url])
      store2.append([downloaded,progress,title,subtitle,summary,tags,size,duration,media_type,media_url])


Здесь нет ниче­го сверх­слож­но­го. Мы рас­смат­ри­ва­ли чтение RSS-лент в пре­ды­ду­щих учебниках: при лю­бой об­ра­бот­ке RSS-лент, в фор­ма­те Atom или нет, неза­чем искать до­б­ра от до­б­ра, а точнее – за­ме­ча­тель­но­го мо­ду­ля Feedparser от Мар­ка Пил­гри­ма [Mark Pilgrim] (http://www.feedparser.org). По скры­тым от че­ло­ве­че­ства при­чи­нам этот мо­дуль не вклю­чен в стан­дарт­ный пан­те­он при уста­нов­ке при­ло­жений Ubuntu, но до­ба­вить его про­ще про­сто­го: sudo apt-get install python-feedparser. Под­лин­ная кра­со­та это­го мо­ду­ля в том, что он про­че­сы­ва­ет все мыс­ли­мые фор­ма­ты лент и вы­да­ет при­ят­ный объ­ект Python, с ко­то­рым уже мож­но ра­бо­тать, не пу­та­ясь со вся­ки­ми уз­ла­ми, про­вер­ка­ми ис­поль­зуе­мой вер­сии RSS и про­чим. Пе­ре­дае­те ему URL лен­ты – по­лу­чае­те чет­кую струк­ту­ру. Для лен­ты под­кастов воз­вра­щае­мый объ­ект бу­дет со­дер­жать раз­лич­ные кон­тейнеры entry [запись], с ин­фор­ма­ци­ей о ка­ж­дом доступ­ном эпи­зо­де. Они раз­би­ва­ют­ся на несколь­ко стан­дарт­ных по­лей – за­го­ло­вок, опи­сание, тэ­ги, а так­же ин­фор­ма­цию о да­те и URL для ме­диа-со­дер­жи­мо­го, что вполне доста­точ­но для соз­дания таб­ли­цы эпи­зо­дов.

Для вы­бо­ра фай­ла…

С этим ра­зо­бра­лись; те­перь об­ра­бо­та­ем сиг­нал, по­лу­чае­мый при вы­бо­ре фай­ла. До­бавь­те сле­дую­щий ме­тод:

  def file_chosen(self, treeview,selection,column):
    import thread
    listmodel=treeview.get_model()
    index=listmodel.get_iter(selection[0])
    downloaded=listmodel.get_value(index,0)
    if downloaded:
       self.file_play(listmodel.get_value(index,9))
    else:
       self.file_download(listmodel,index)

Он определяет, какой файл выбран, и проверяет, не загру­жен ли он. Если да, подставляется ссылка на другой обработчик:

  def file_play(self,url):
    “”” Инициа лизирует PlayerDialog и проиг рывает за данный файл”””
    logging.debug(“caught signal for file play”)
    filename=os.path.split(url)[­1]
    filename=os.path.join(self.preferences[“savedir”],filename)
    player=PlayerDialog.PlayerDialog()
    player.set_playfile(filename)
    player.run()
    player.destroy()

Во­об­ще-то здесь об­ра­ща­ют­ся к но­во­му диа­ло­гу. Что­бы все ра­бо­та­ло, до­бавь­те диа­лог че­рез Quickly, вклю­чив его в опи­са­ние про­ек­та. Для это­го в ка­та­ло­ге про­ек­та вве­ди­те quickly add dialog Player Об осталь­ном по­за­бо­тит­ся Quickly. За­тем вы мо­же­те от­крыть эти фай­лы и из­менить их при по­мо­щи quickly edit и quickly design. Наш ин­тер­фейс диа­ло­га – это все­го лишь сиг­нал, по­ка­зы­ваю­щий: что-то слу­чи­лось, и мы от­реа­ги­ро­ва­ли. Ес­ли че­ст­но, то и в ко­де про­иг­ры­ва­те­ля ма­ло что про­ис­хо­дит. Это про­сто бы­ст­рый спо­соб из­дать звук.

В кон­це ме­то­да finish_initializing нуж­но соз­дать про­иг­ры­ва­тель из мо­ду­ля gst (ах да, необ­хо­ди­мо до­ба­вить в на­ча­ло фай­ла import gst):

self.player=gst.element_factory_make(“playbin”,”PodPlayer”)

За­тем мы про­сто реа­ли­зу­ем ме­тод про­иг­ры­ва­ния фай­ла:

 def set_playfile(self,filename):
  #по­лу­ча­ет путь к фай­лу и за­пус­ка­ет про­иг­ры­ва­тель
  uri =”file://”+filename
  print uri
  self.player.set_property(“uri”,uri)
  self.player.set_state(gst.STATE_PLAYING)


Да не за­будь­те оста­но­вить про­иг­ры­ва­тель. Не оболь­щай­тесь, что объ­ект бу­дет унич­то­жен вме­сте с мо­даль­ным диа­ло­гом: звук в Linux – шту­ка ка­приз­ная, и вы неод­но­крат­но мо­же­те столк­нуть­ся с си­туа­ци­ей упор­но­го про­иг­ры­вания MP3‑фай­лов, хо­тя пред­по­ла­га­лось, что воспро­из­ве­дение оста­нов­ле­но. Мож­но про­сто по­местить вы­зов оста­нов­ки про­иг­ры­ва­те­ля в об­ра­бот­чик кно­пок OK и Cancel.

self.player.set_state(gst.STATE_PAUSED)

Этот код по­за­бо­тит­ся о том, что­бы под­кас­ты не пе­ре­тру­ди­лись.

Оста­лось по­за­бо­тить­ся о са­мой за­груз­ке ме­диа-фай­лов. Для по­лу­чения фай­лов восполь­зу­ем­ся urllib.urlretrieve(): это часть стан­дарт­но­го па­ке­та Python, и она ра­бо­та­ет доста­точ­но хо­ро­шо. В этом ко­де есть два хит­рых трю­ка, и его сле­ду­ет немно­го по­яснить.

 def file_download(self,listmodel,index):
   import urllib,thread
   url=listmodel.get_value(index,9)
   savename=os.path.split(url)[-1]
   savename=os.path.join(self.preferences[“savedir”],savename)
   reporthook = lambda a,b,c : self.progress_update(listmodel,index,a,b,c)
   thread.start_new_thread( urllib.urlretrieve,(url,savename,reporthook))

Ме­тод urlretrieve пре­ду­смат­ри­ва­ет об­рат­ный вы­зов. Бе­да в том, что по­следний воз­вра­ща­ет лишь дан­ные о за­гру­жен­ных бло­ках, и ниче­го о за­гру­жае­мом фай­ле. За­пустив сра­зу несколь­ко за­гру­зок, мы уже не смо­жем рас­смат­ри­вать их по от­дель­но­сти. Я в об­щем про­тив при­менения лям­бда-функ­ций (функ­ций, встро­ен­ных в Python) – они неред­ко услож­ня­ют понимание ко­да. Од­на­ко в дан­ном слу­чае ис­поль­зо­вание лям­бда-функ­ций в ка­че­стве про­кси оз­на­ча­ет, что мы мо­жем до­бав­лять по­лез­ную ин­фор­ма­цию к от­кли­ку про­цес­са за­груз­ки и пе­ре­да­вать ее в сле­дую­щую функ­цию. Пре­ж­де чем брать­ся за это, рас­смот­рим еще од­ну ис­поль­зуе­мую здесь улов­ку – а имен­но, по­то­ки.

При­ят­ные по­то­ки

Про­стой слу­чай по­то­ков (см. по­след­нюю стро­ку) оз­на­ча­ет, что мож­но за­пускать по­ток, пред­ста­вив функ­цию и ее ар­гу­мен­ты в ви­де кор­те­жа. Без по­то­ков ра­бо­та при­ло­жения бу­дет приоста­нов­ле­на до тех пор, по­ка файл не за­гру­зит­ся – оно не бу­дет ни на что от­кли­кать­ся, и поль­зо­ва­тель мо­жет ре­шить, что оно за­вис­ло. Вдо­ба­вок наш удоб­ный ин­ди­ка­тор про­грес­са бу­дет бес­по­ле­зен. Для соз­дания об­рат­ной свя­зи нам необ­хо­дим вто­рой ме­тод:

 def progress_update(self, listmodel,index,a,b,c):
  “”” Функ­ция об­рат­но­го вы­зо­ва, от­ра­жаю­щая про­гресс в таб­ли­це. Не­об­хо­ди­мы по­то­ки!”””
  blocks = float(c/b)
  progress=float(a/blocks)*100
  if progress > 100:
   progress = 100
   listmodel.set(index,0,True)
 listmodel.set(index,1,progress)

Здесь вы­чис­ля­ет­ся про­цент за­вер­шен­но­сти и со­от­вет­ствен­но об­нов­ля­ет­ся мо­дель пред­став­ления спи­ска. Мож­но за­пустить несколь­ко за­гру­зок од­но­вре­мен­но, и при­ло­жение бу­дет об­нов­лять их все. Ис­ход­ная идея бы­ла в том, что­бы ин­ди­ка­тор про­грес­са ото­бра­жал­ся под ка­ж­дым эле­мен­том на всю ши­ри­ну ок­на. К со­жа­лению, GTK не под­дер­жи­ва­ет такого на­пря­мую (по крайней ме­ре, по­ка), но ес­ли вы же­лае­те соз­дать ваш соб­ствен­ный ва­ри­ант от­ри­сов­ки, то сде­лай­те это и дай­те нам знать, по­жа­луй­ста!

Что­бы за­ста­вить по­то­ки ра­бо­тать, при­пи­ши­те сле­дую­щее в конец основ­но­го фай­ла, сра­зу по­сле import optparse:

import gobject
gobject.threads_init()

Ес­ли это­го не сде­лать, GTK-часть при­ло­жения не по­ла­дит с мо­ду­лем по­то­ков Python.

Ле­ти­те на во­лю

Ес­ли вы про­бо­ва­ли при­ло­жение по хо­ду уро­ка, то те­перь уви­ди­те, что оно вполне го­то­во к упот­реб­лению. Конеч­но, до­ра­бо­тать еще есть что, и, кста­ти, в ис­ход­ном ко­де в се­ти или на DVD раз­бро­са­но мно­же­ство при­ме­чаний и за­дач [TODO]. Для на­ча­ла, несколь­ко пунк­тов ме­ню не ра­бо­та­ют! Ох, и еще од­на за­гвозд­ка – мы так и не да­ли поль­зо­ва­те­лю воз­мож­ность до­бав­лять свои лен­ты под­кастов (это ес­ли им ма­ло од­но­го TuxRadar...).

Од­на­ко мы долж­ны вы­полнить по­след­нюю часть на­шей мис­сии, то есть вы­пустить при­ло­жение на во­лю. Коман­да­ми Quickly это де­ла­ет­ся лег­ко, нуж­на толь­ко учет­ная запись на Launchpad. Ес­ли у вас та­кой нет, соз­дать ее недол­го; но что­бы вы­гру­жать дан­ные в PPA, по­тре­бу­ет­ся так­же иметь ключ GPG и от­кры­тый ключ SSH, для про­вер­ки ва­ших фай­лов.

Ну, а для про­сто­го соз­дания Deb-фай­ла и то­го не на­до. Про­сто вве­ди­те коман­ду quickly package, и скрип­ты по­тру­дят­ся за вас, соз­дав го­то­вый к рас­про­странению Deb-па­кет. Ес­ли вы упор­но же­лае­те вы­гру­зить­ся на Launchpad, сде­лай­те это коман­дой quickly release, про­сто сле­дуя ее ин­ст­рук­ци­ям. Од­на­ко хо­ро­шей иде­ей бу­дет сна­ча­ла пред­ста­вить­ся Bazaar, а за­тем убе­дить­ся, что ваш поч­то­вый ад­рес со­от­вет­ству­ет учет­ной запи­си Launchpad:

bzr whoami ‘My Name <email@example.com>’

Quickly раз­ме­ща­ет­ся на Launchpad (https://launchpad.net/quickly); соз­дав свою учет­ную за­пись, вы смо­же­те за­хо­дить, за­да­вать во­про­сы и ту­со­вать­ся с кру­ты­ми ко­де­ра­ми.

Шаг за шагом: Соз­да­ем про­стое при­ло­же­ние Quickly

Шаг 1

  • 1 Соз­да­ем в Quickly
Что­бы на­чать соз­да­вать свое при­ло­жение, от­крой­те тер­ми­нал и соз­дай­те но­вый ка­та­лог для всех ва­ших Quickly-про­ек­тов коман­дой mkdir Quickly. Вой­ди­те в этот ка­та­лог (cd Quickly) – и вы го­то­вы к соз­данию и сбор­ке ва­ше­го пер­во­го при­ло­жения. Вве­ди­те quickly create ubuntuapplication MyApp, и все за­кру­тит­ся. Quickly соз­даст ка­та­лог и сгенери­ру­ет в нем все необ­хо­ди­мые фай­лы, а имен­но – фай­лы ин­тер­фей­са GTK, фай­лы Python, ба­зы дан­ных и систе­мы управ­ления вер­сия­ми. По за­вер­шении при­ло­жение за­пустит­ся.

Шаг 2

  • 2 Про­ек­ти­ру­ем в Quickly
Вой­ди­те в ка­та­лог про­ек­та myapp и скоман­дуй­те quickly design. За­пустит­ся Glade, ре­дак­тор гра­фи­че­ских ин­тер­фей­сов для GTK, и в нем ав­то­ма­ти­че­ски от­кро­ет­ся глав­ное ок­но ва­ше­го при­ло­жения. Все фай­лы ин­тер­фей­са поль­зо­ва­те­ля хра­нят­ся в од­ном и том же месте, так что лю­бой диа­лог ва­ше­го при­ло­жения мож­но лег­ко из­менить. Немно­го по­рез­ви­тесь в Glade, ес­ли ранее вы с ним не стал­ки­ва­лись. Свой­ства ка­ж­до­го объ­ек­та лег­ко из­менить при по­мо­щи ин­ст­ру­мен­тов, рас­по­ло­жен­ных в пра­вом нижнем уг­лу. Здесь мы ме­ня­ли текст в на­шей мет­ке. Не за­будь­те со­хранить про­ект пе­ред вы­хо­дом!

Шаг 3

  • 3 Ре­дак­ти­ру­ем в Quickly
Quickly ис­поль­зу­ет ре­дак­тор Gedit из Gnome, пре­крас­но под­хо­дя­щий для неин­тен­сив­ной ра­бо­ты. Кро­ме то­го, он от­сле­жи­ва­ет фай­лы ва­ше­го про­ек­та, по­это­му для их из­менения, на­хо­дясь в тер­ми­нале в глав­ном ка­та­ло­ге при­ло­жения, про­сто вве­ди­те quickly edit. Gedit поя­вит­ся и от­кро­ет все Python-фай­лы, свя­зан­ные с про­ек­том, так что вы мо­же­те на­чать соз­дание но­вых функ­ций. Из дру­гих фай­лов вы мо­же­те по­же­лать из­менить файл AUTHORS в глав­ном ка­та­ло­ге про­ек­та. Про­сто вве­ди­те крат­кое со­об­щение об ав­то­ре, что­бы все зна­ли, что это ва­ше. Со­храните файл и вый­ди­те.

Шаг 4

  • 4 За­пуска­ем в Quickly
Пе­ред тем, как вы за­пусти­те свое слег­ка из­менен­ное при­ло­жение, сде­лай­те еще кое-что. В команд­ной стро­ке вве­ди­те quickly license. Это соз­даст со­об­щение об ав­тор­ском пра­ве на осно­вании той ин­фор­ма­ции, что вы вве­ли в фай­ле AUTHORS, и уве­дом­ление GPL v3 (по умол­чанию, хо­тя это мож­но из­менить) в за­го­лов­ке ка­ж­до­го фай­ла с ис­ход­ны­ми ко­да­ми; ну, и еще там всякое. Для за­пуска ва­ше­го при­ло­жения вве­ди­те quickly run. Когда оно от­кро­ет­ся, вы­бе­ри­те в ме­ню Help > О про­грам­ме. Вы уви­ди­те, что диа­лог из­менил­ся и те­перь со­дер­жит ва­ше со­об­щение об ав­тор­ском пра­ве и ин­фор­ма­цию о ли­цен­зии!

От­лад­ка в Quickly

Да­же масти­тые ко­де­ры здесь у нас в Башне LXF иногда оши­ба­ют­ся в су­ж­дениях. Во­об­ще-то лю­ди, имею­щие при­выч­ку все оценивать, счи­та­ют, что 80 % вре­мени раз­ра­бот­ки ПО ухо­дит на ис­прав­ление оши­бок. Но ис­прав­ление оши­бок – во­все не ка­тор­га: это путь к от­кры­ти­ям (как мы пы­та­ем­ся убе­дить са­ми се­бя), даю­щий нам ощу­щение востре­бо­ван­но­сти и по­лез­но­сти.

Для Python име­ет­ся несколь­ко пре­крас­ных ин­ст­ру­мен­тов от­лад­ки, но Quickly по­став­ля­ет­ся с одним уже на­стро­ен­ным и го­то­вым к ра­бо­те, так что мы восполь­зу­ем­ся им! Воз­мож­но, ранее вы не стал­ки­ва­лись с Winpdb, а он со­дер­жит все функ­ции для по­ша­го­во­го пе­ре­ме­щения ме­ж­ду точ­ка­ми оста­но­ва, ана­ли­за про­стран­ства имен и про­вер­ки по­ве­дения пе­ре­мен­ных. Он оп­ре­де­лен­но бы­ст­рее стан­дарт­но­го PDB, и уз­нать о нем боль­ше мож­но на http://winpdb.org/docs.

Ис­ход­ные тек­сты

Ис­ход­ные тек­сты име­ют­ся на LXFDVD, но ес­ли вам лень их ис­кать, у вас нет DVD-при­во­да или вы чи­тае­те это в он­лайн-вер­сии, пе­ре­ве­ден­ной на рус­ский, за­тем на поль­ский и вновь на анг­лий­ский, то ис­ход­ные тек­сты мож­но ска­чать на­пря­мую с сай­та Ubuntu Launchpad. По­се­ти­те http://code.launchpad.net/podofile или вос­поль­зуй­тесь Bazaar для по­втор­но­го соз­да­ния ло­каль­но­го ре­по­зи­то­рия ко­ман­дой bzr branch lp:podofile. Ар­хив со­дер­жит так­же фай­лы на­строй­ки Quickly, так что ес­ли вы уже ус­та­но­ви­ли Quickly, то мо­же­те по­иг­рать с ним са­мо­стоя­тель­но.

Персональные инструменты
купить
подписаться
Яндекс.Метрика