Давно хотел попробовать Python в качестве инструмента разработки приложений под декстоп. Выбор GUI-библиотеки из доступных GTK, QT, wxPython и Tkinter был не сложен, т.к. рабочим столом у меня является GNOME и как известно он построен на GTK+, то я решил остановится на PyGTK - прослойки между GTK+ и Python. Приложения написанные на GTK+ могут жить не только под одними Linux, если поставить необходимые библиотеки то можно будет запускать приложения под MacOS и Windows, один плюсов библиотеки - кроссплатформенной.
В качестве примера напишем приложение-каталогизатор фильмов для киномана. Все элементы интерфейса будут на GTK+, фильмы будут храниться в SQLite, для общения с базой данных будет использоваться ORM - SQLAlchemy.
Коротко про GTK+
GTK+ (GIMP Toolkit) представляет собой инструментарий, набор библиотек, которые используются для разработки GUI-приложений под Linux, OSX, Windows и другие платформы на которых доступна библиотека GTK+. Думаю не открою Америку, если скажу, что на GTK+ написаны такие известные приложения, как GIMP (растровый графический редактор) и GNOME (среда рабочего стола для Unix-подобных операционных систем). Сама библиотека написана на C, используемая лицензия - LGPL.
PyGTK является оберткой над GUI-библиотекой GTK+ и предоставляет возможности творить GUI-приложения на Python. PyGTK состоит из нескольких компонентов:
Если у вас рабочий стол GNOME, то в системе уже установлен PyGTK, иначе идем на страницу загрузки и качаем версию для вашей платформы.
Проверим установленные версии GTK+ и PyGTK
>>> import gtk >>> gtk.pygtk_version >>> (2, 17, 0) >>> gtk.gtk_version >>> (2, 20, 1)
Все приложения на PyGTK состоят из визуальных компонентов - виджетов. Которые могут быть либо видимыми (кнопки, комбобоксы и т.д.) и не видимыми (например, лейауты). Большинство виджетов могут реагировать на определенные события, генерируемые X-сервером. Когда пользователь кликает по кнопке - генерируется сигнал, который порождает событие - clicked, программисту остается только написать обработчик на событие. Всеми событиями в приложение управляет главный цикл обработки событий, запускаемый во время инициализация приложения.
Для построения интерфейса приложения можно использовать два подхода: вручную описывать все виджеты и порядок их размещение на форме, либо воспользоваться Glade - так называемый RAD (rapid application development), GUI-инструмент для визуального проектирования интерфейса. Процесс сводится к набрасыванию виджетов на форму (как в Delphi или QT Designer) и назначению имен для обработчиков событий. После этого весь результат сохраняется в XML-файл который парсится с помощью libglade в конструкторе главного окна приложения. В результате мы получаем готовый интерфейс. Хорошее описание работы с Glade есть тут.
Я использовал первый подход - вручную описывал и размещал виджеты на форме, так лучше формируется понимание внутренней кухни, а уже потом можно переходить к Glade для минимизирования времени на проектирование интерфейса.
Если с виджетами все понятно, есть кнопка - нажимай, есть комбобокс - выбирай что-то из списка, то остается вопрос как их размещать на форме, для этого существуют разные лейауты (layout), или еще PyGTK документация называет этот процесс - упаковка виджетов. Каждый из этих лейаутов умеет размещать на себе виджеты в определенной последовательности. Список возможных лейаутов в PyGTK:
Рассмотрим пример простого приложения на PyGTK, каждое действие прокомментировано:
# coding=utf-8 import gtk # создадим класс основного окна, которое будет отображаться при запуске приложения class App(gtk.Window): def __init__(self): super(App, self).__init__() # параметры основного окна # установим размер окна self.set_size_request(200, 150) # установим title self.set_title('Hello world') # установим внутренние отступы self.set_border_width(20) # установим расположение окна по центру экрана self.set_position(gtk.WIN_POS_CENTER) # назначим обработчик закрытия окна, без него не будет реакции на попытки закрыть окно self.connect('destroy', gtk.main_quit) # создадим виджет Button self.button = gtk.Button("Click me") # назначим обработчик на событие 'clicked' self.button.connect('clicked', self.helloworld, None) # добавим кнопку на форму основного окна self.add(self.button) # дадим команду отобразить все виджеты self.show_all() def helloworld(self, widget, data=None): """ Обработчик события clicked для кнопки self.button """ # изменим текст на кнопке widget.set_label('Hello world') # создадим экземпляр класса App() # запустим главный цикл обработки событий gtk.main()
Коротко про SQLAlchemy
SQLAlchemy — это программное обеспечение с открытым исходным кодом для работы с базами данных при помощи языка SQL. Оно реализует технологию программирования ORM (Object-Relational Mapping), которая связывает базы данных с концепциями объектно-ориентированных языков программирования. SQLAlchemy позволяет описывать структуры баз данных и способы взаимодействия с ними прямо на языке Python. источник
Установим SQLAlchemy
pip install SQLAlchemy
Проверим результат установки
>>> import sqlalchemy >>> sqlalchemy.__version__ >>> '0.6.3'
Для создания ORM с существующей БД будем использовать декларативный подход, который сводит к минимуму процесс установки связи. Рассмотрим пример работы SQLAlchecmy и SQLite на примере модели будущего приложения.
Импортируем используемые методы и типы полей:
from sqlalchemy.ext.declarative import declarative_base from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy import Column, Integer, Unicode, Date, MetaData, Boolean
Создаем объект отвечающие за работу с БД и указываем выводить в консоль все SQL-запросы - удобно для отладки.
engine = create_engine('sqlite:///films.db', echo=True)
Создаем объект отвечающий за выполнение транзакциq и выполнение запросов
Session = scoped_session(sessionmaker(bind=engine))
Создадим базовый класс, от которого потом будем наследоваться модель 'Фильмы'
Base = declarative_base()
Собственно сама модель, в атрибуте tablename указывается имя таблицы, в которой будет хранится список фильмов. Остальные атрибуты представляют собой характеристики модели и по совместительству будут колонками таблицы:
class Film(Base): """ Модель 'Фильм' """ __tablename__ = 'films' fid = Column(Integer, primary_key=True) title = Column(Unicode) genre = Column(Unicode) release = Column(Date) is_viewed = Column(Boolean) def __init__(self, title, genre, release, is_viewed): self.title = title self.genre = genre self.release = release self.is_viewed = is_viewed def __repr__(self): return "" % (self.title)
Создадим файл с базой данных и необходимыми таблицами
Base.metadata.create_all(engine)
Попробуем создать запись о новом фильме
>>> from models import * >>> import datetime >>> session = Session() >>> film = Film('Iron Man', 'action', datetime.date.today(), True) >>> session.add(film) >>> session.commit()
А теперь выберем все фильмы из БД:
>>> from models import * >>> session = Session() >>> films = session.query(Film).all() >>> for film in films: >>> ... print film.title
Приложение MyFilms
Полный исходник приложения можно скачать в самом низу поста, внутри исходников есть комментарии почти ко всем действия, думаю разобраться в самом процессе не составит большого труда. Ниже я опишу только пару интересных, на мой взгляд, моментов, но сначала внешний вид приложения:
В начале я вскользь коснулся расположения виджетов на форме с помощью лейаутов, сейчас опишу чуть подробнее один из них - VBox, на нем все виджеты располагаются вертикально, один за другим.
Создается этот лейаут так:
vbox = gtk.VBox(False, 8)
первый параметр конструктора, homogeneous, значение которого в данном случае равно False, означает что каждый виджет, расположенный на этом лейауте, может занимать разную высоту по вертикале, если установить True то для всех виджетов будет установлена одинаковая высота (общая высота зависит от родителя). Второй параметр выставляет отступы, в пикселях, между вложенными блоками.
Виджеты могут добавляться на лейаут с двух сторон, в случаи VBox сверху (метод pack_start) и снизу (метод pack_end).
vbox.pack_start(toolbar, False, False, 0)
сигнатура метода pack_start:
box.pack_start(child, expand, fill, padding)
Для хранения табличных данных используется виджет gtk.TreeView с хранилищем данных (gtk.ListStore). Ниже мы создадим экземпляр виджета и передадим ему уже наполненное хранилище данных. Наполнение хранилища происходит банальной итерацией по всем записям в БД и добавлением в хранилище с помощью метода append, которому передается список значений (см. исходники в прикрепленном ниже файле).
self.treeView = gtk.TreeView(self.store)
Следующим этапом мы создадим колонки для виджета TreeView, за это отвечает метод create_columns
... def create_columns(self, treeView): """ Метод отвечает за создание колонок для грида. """ rendererText = gtk.CellRendererText() column = gtk.TreeViewColumn("ID", rendererText, text=0) column.set_sort_column_id(0) treeView.append_column(column) ...
Для виджета TreeView назначены два обработчика события:
После выбора строки в гриде, мы можем получить выбранные значения, которые хранятся в хранилище данных (gtk.ListStore)
tree_model, tree_iter = self.treeView.get_selection().get_selected()
возвращается кортеж из двух объектов
Сорцы приложения MyFilms - myfilms_pygtk.tar.gz
Дополнительное чтиво