PyQt. Автоматическая генерация кода

Вообще в программировании принято большие и сложные программы разбивать на несколько функциональных уровней.

Так например, различают уровень представления (иногда его называют фронт-энд), уровень бизнес-логики (бэк-энд), уровень данных. (Слово «бизнес» следует понимать не в узком смысле только экономики, а более широко. «Бизнес» — это производство чего-то, это какое-то дело. То есть, это именно «то», ради чего затеялась движуха.)

Разбивка программы на уровни может быть самая разнообразная. Но что это дает? Прежде всего это дает гибкость в реализации. Так же следует отметить, что разные уровни могут писаться параллельно разными разработчиками, что ускорит выход программы в свет. Ну и не надо забывать про принцип «разделяй и властвуй»!

Отделение бизнес-логики от представления дает очень мощную позицию. Представьте себе, что вы пишите в программе уровень бизнес-логики. Кто-то в вашей команде пишет уровень представления. Вам не нужно заботиться как будут выглядеть ваши данные на экране — в виде таблиц, в виде графиков или вообще будут появляться в текстовом виде на каком-нибудь терминале. Вы сосредоточиваете свои усилия и внимание только на вопросах обработки данных. Как эти данные будут отображаться — это не ваша забота!

И наоборот, ваш коллега пишет уровень представления. Его не волнуют вопросы как работает программа, как она вычисляет Фурье-преобразование. Он получает готовые данные, и его задача — отобразить их на экране в заданном виде.

Вы улавливаете суть разделения программы на уровни?

Если с написанием кода для уровня бизнес-логики более-менее нормально, то с написанием кода для уровня представления не все так хорошо.

Проблема состоит в том, что программа на любом языке «понимает» (оперирует) вызовы функций, изменение данных… собственно, это и составляет её код. Но как объяснить программе, что она должна нарисовать серый прямоугольник размерами 100 х 20 пиксел и поместить его от края окна программы слева 25 пиксел, сверху — 35?

Конечно, можно это написать в виде кода типа:

Rectangle(100, 20, 25, 35, GRAY)

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

Здравый рассудок подсказывает, что внешний вид программы удобнее выполнять «программируя мышкой». То есть с помощью мышки расположить виджеты и установить их размеры на макете окна. А потом этот топометрический порядок виджетов превратить в программый код.

Вот как раз для этой цели и предназначена программа Qt 4 дизайнер.

А теперь давайте перейдем от разговоров к практическим занятиям!

Если у вас открыто окно псевдо-терминала, то наберите в нем команду

$ designer

(Необязательно набирать полное слово! Вы ведь помните про клавишу табуляции, которая помогает вам набрать остаток команды?)

Если у вас окно псевдотерминала закрыто — ну что ж! — придется перекладывать руку на мышку и топать в системное меню:

Приложения / Программирование / Qt 4 дизайнер

В любом случае у вас на экране должно появиться окно Qt-Дизайнера:

pyqt-designer-1

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

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

В средине Дизайнера находится окно «Новая форма»

pyqt4-designer-new

Найдите в его левой части в списке строку «Dialog withot Buttons» («Диалог без кнопок») и нажмите кнопку «Создать».

Окно «Новая форма» закроется а на его месте появится заготовка для окна нашей программы:

 

pyqt4-designer-dialog-1

Пока окно нашей будущей программы совершенно пустое:

pyqt4-designer-dialog-1a

и наша задача стоит в том, чтобы на него переместить виджеты из списка, который слева, и правильно их расположить. Но сначала давайте разберёмся с самим окном.

Во первых, нам нужно дать осознанное наименование объекту диалогового окна. Это ведь но просто окно, а объект, с которым должна будет «сотрудничать» наша программа. А поскольку у каждого объекта есть имя, то и объекту диалогового окна нужно дать имя.

Вообще-то имя по умолчанию уже есть — «Dialog», но оно ничего не выражает. Давайте назовём наш объект «MyDialog». Для этого найдите с правой стороны Дизайнера в списке свойство objectName и измените у него значение на MyDialog:

pyqt4-designer-1

По этому имени (MyDialog) мы будем из программы обращаться к объекту нашего окна.

Далее, было бы не плохо изменить заголовок нашего диалогового окна, а то название «Dialog — untitled*» смотрится как какой-то недоделанный полуфабрикат. Пусть в заголовке окна красуется загадочное выражение «Тестер Bluetooth».

Для этого нужно там же найти свойство windowTitle и вписать ему новое значение «Тестер Bluetooth».

Теперь в списке виджетов с левой стороны нужно найти виджет PushButton. Он находится в группе Buttons:

pyqt4-designer-pushbutton-1

Захватите мышкой иконку виджета и перенесите её на заготовку нашего диалогового окна.

Проделайте те же действия с виджетом Label. Этот виджет находится в группе Display Widgets, которая располагается почти в средине списка. Список придётся прокрутить вниз.

pyqt4-designer-label-1

В результате у вас должно получится примерно так:

pyqt4-designer-dialog-2

Теперь приведем в порядок некоторые свойства этих виджетов.

Начнем с лэйблочки. Щелкните по ней (её изображение обрамится синими квадратиками) и перейдите в список свойств, который справа. Найдите там свойство objectName и измените его значение с label на lblHello.

Далее найдите свойство text и измените его значение с TextLabel на Привет, Мир! При этом ширины виджета будет недостаточно чтобы отобразить всю надпись. Чтобы исправить, нужно мышкой «растянуть» виджет.

Теперь поработаем с кнопкой. Изменим у неё свойство objectName — пусть объект кнопки будет называться btnQuit. А свойству text назначим новое значение — Выход.

Теперь можно изменить размеры заготовки окна и более изящно расположить виджеты:

pyqt4-designer-dialog-3

Нам остается сохранить результат нашей работы. Для этого в меню Дизайнера выберите пункт

Файл / Сохранить как…

На экране появится окно:

pyqt4-designer-save-as

В строке «Имя файла:» введите название файла tester_bt.ui, и нажмите кнопку Сохранить.

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

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

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MyDialog</class>
<widget class="QDialog" name="MyDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>252</width>
<height>96</height>
</rect>
</property>
<property name="windowTitle">
<string>Тестер Bluetooth</string>
</property>
<widget class="QPushButton" name="btnQuit">
<property name="geometry">
<rect>
<x>90</x>
<y>50</y>
<width>80</width>
<height>26</height>
</rect>
</property>
<property name="text">
<string>Выход</string>
</property>
</widget>
<widget class="QLabel" name="label">
<property name="geometry">
<rect>
<x>90</x>
<y>20</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Привет, Мир!</string>
</property>
</widget>
</widget>
<resources/>
<connections/>
</ui>

Собственно, это обычный XML-файл.

Ну вот, это пожалуй и всё, что касается «программирования мышкой». Далее нам нужно «познакомить» нашу программу на Питоне с файлом tester_bt.ui.

Вообще существует несколько способов «поженить» полученный файл, отвечающий за пользовательский интерфейс, и файл с бизнес-логикой. Но мы пойдем через компиляцию XML-файла в питоновский код.

Для того чтобы превратить файл tester_bt.ui в код на питоне выполним команду:

$ pyuic4 tester_bt.ui -o tester_bt_ui.py

Здесь pyuic4 — это тот самый компилятор из XML в Python. Ключ «-o» говорит компилятору сохранить результат в файл, имя которого «tester_bt_ui.py«. Если ключ не указывать, то компилятор вывалит результат своей работы на терминал.

В общем, после компиляции получится текстовый файл почти обычной питоновской программы. При желании вы можете глянуть на него, и даже, если сумеете разобраться, — что-нибудь в нём поправить. (Это была шутка! )

Запускать этот файл бесполезно. В нём нет действий (кода уровня бизнес-логики), это всего лишь описание пользовательского интерфейса (код уровня представления). Создание кода бизнес-логики остаётся за нами. И мы сейчас это сделаем!

Давайте выполним команду:

$ mkpy tester_bt

и создадим питоновский файл tester_bt.py. Затем, запустив редактор, напишем в него следующий код:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

''' tester_bt.py '''

from PyQt4 import QtCore, QtGui
from tester_bt_ui import Ui_MyDialog

#===============================================================================
if __name__ == "__main__":
  import sys
  app = QtGui.QApplication(sys.argv)
  wnd = QtGui.QWidget()

  ui = Ui_MyDialog()
  ui.setupUi(wnd)

  QtCore.QObject.connect(ui.btnQuit, QtCore.SIGNAL("clicked()"), QtGui.qApp.quit)

  wnd.show()
  sys.exit(app.exec_())

В этом файле мы импортируем файл уровня представления с кодом пользовательского интерфейса:

from tester_bt_ui import Ui_MyDialog

, затем приводим окно нашей программы в соответствие установкам, описанным в файле пользовательского интерфейса:

  ui.setupUi(wnd)

Кроме того, нам нужно наделить нашу программу действиями (наполнить бизнес-логикой). Вся работа (бизнес-логика) нашей программы будет заключаться в реакции на нажатие на кнопку «Выход«.

Кнопка генерирует событие при нажатии на неё мышкой. Событие называется незатейливо — «clicked«. (Вообще-то в Qt это не совсем событие, а функция. Но это уже детали реализации.)

Как правило в событийно-управляемой программе должны быть специальные функции — так называемые «обработчики событий«. Мы могли бы написать свою функцию (обработчик события), но в нашем случае нужно только завершить программу. Поэтому обработчиком события будет не наша функция, а встроенная, которая так и называется — «quit«.

Короче, нам нужно увязать событие с обработчиком. Это делается в строке:

  QtCore.QObject.connect(ui.btnQuit, QtCore.SIGNAL("clicked()"), QtGui.qApp.quit)

Если её задокументировать, программа всё еще будет запускаться, но реагировать на нажатие кнопки уже не будет. Поиграйтесь на досуге!

Собственно, это всё! Мне остается только приложить скриншот работающей программы:

tester_bt

Ну вот, теперь можно считать, что все шасси оторвались от полосы, можно смело набирать высоту!

Я желаю вам чистого неба и приятных полётов!

4 responses to “PyQt. Автоматическая генерация кода

  1. Как обычно, добавлю🙂

    Я выложил на GitHub код Qt клиента, с русификацией, со сборкой в пакет Debian. Точкой входа там является manager.py. В каталоге uis лежат описания окошек, как показано в вашем посте, но я их компилирую на лету. Вся магия реализована в файле ui_dialog.py, а конкретно в строке 23. Затем я просто наследую очередной диалог от моего базового класса диалога и делаю остальные дела, вот так:
    https://github.com/RaD/qtclient/blob/master/src/dialogs/dlg_login.py#L13-L18

    Код я писал лет пять назад, там не всё по феншую, но я уверен, что он является самым компактным и самым подробным примером по работе с Qt, который вам удастся найти в сети (скромно шаркает ножкой).

    • Спасибо, Руслан!

      Я сходил по Вашей ссылке, полистал файлы. Не скромничайте! У вас есть интересные моменты, которым я хотел бы более подробно проанализировать и взять себе на вооружение. Но это как-нибудь потом. Думаю, что клонирую Ваш проект и более плотно поиграюсь с ним. Сейчас у меня немного другие планы.

      Что же касается компиляции XML-файлов пользовательского интерфейса, я об этом знаю. В упомянутой книжке Прохорёнка посвящено этому механизму пара (или около того) страниц. В общем-то проблем с компиляцией нет. Другое дело, что я не уверен, что именно так правильно. Никто не может сказать «как правильно». Программы можно строить и таким способом — предварительной компиляцией XML-файла в питоновский модуль, и компиляцией на лету. И тот и другой способ принципиально — работают. Работают достаточно быстро. Особо сильно ресурсы не сжирают. Поэтому выбор того или другого способа — он как бы не очень критичен. Не сильно очевидно, что одно лучше другого. По крайней мере для моего случая нет большой разницы.

      А за комментарий — спасибо! Думаю, что анонимные читатели блога комменты тоже читают, поэтому должны увидеть, что предложенный в статье вариант создания диалогового окна не единственный.

  2. Вот видео с клиентом, код которого я показал: http://www.youtube.com/watch?v=sDQ3Y8cLgAY

    • С удовольствием посмотрел ролик. Спасибо!

      Труба подкинула к просмотру еще один ваш ролик — первый запуск 3D-принтера, где у Вас всё в проводах. Здорово! Даже завидую Вам белой завистью!

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s