01 июня 2011

тестовое приложение на PyQt4


17.04.2001 01:05 я написал свое первое приложение на Python с использованием QT. Великолепное ощущение, я вам скажу.
 Под рукой были только электронное издание "Разработка графического интерфейса с помощью библиотеки Qt3" и примеры, идущие в комплекте с PyQt4. Впрочем обо всем по порядку...
 QT выбрана по простой причине - я прочитал недавно о окончании поддержки PyGTK. Именно PyGTK я собирался использовать для освоения GUI под Python. К тому же хотелось кроссплатформенности и изящества решения. Минус выбора вижу только один - в типе лицензии. У GTK она более свободная. Впрочем, коммерческое использование QT для меня пока далеко.
 Приложения типа "Hello World" меня никогда не вдохновляли. Всегда хотелось решить какую-то микрозадачу. Потому выбор задачи пал на портирование примера с Хабра, написанного на PyGtk на PyQt.

Используемые инструменты:
ОС: Windows XP, язык: Python 2.6, библиотека PyQt4, IDE Aptana (standalone версия) с плагином PyDev
 Задача определена, настала очередь алгоритма решения. Начал с основ QT. Тут пригодилась книга "Разработка графического интерфейса с помощью библиотеки Qt3". Несмотря на отличие от используемой версии, для понимания самое то. Труднее всего было въехать в концепцию "слотов". Объясню что это:
 - если учесть, что QT является объектно-ориентированной системой отбражения информации (так написано в книге), то виджет (элемент GUI) можнл считать объектом, а слот - его методом. Для тех, кто в танке, объясню вообще на пальцах. Абстрактный пример - тот самый работник, которого спросили что он умеет делать, на что работник ответил:
 - могу копать, а могу не копать.
Получается, что работник  имеет (как объект с точки зрения объектно-ориентированного программирования) несколько функций:
        - копать()
        - не копать()
В эти функции можно передавать параметры: ров, окоп, канава, грядка. Исторически сложилось так, что эти функции называют методами. То есть вызов метода объекта, это как сказать: - "Объект такой-то, сделай то-то". На Python команда работнику копать будет скорее всего выглядеть так:
       работник.копать(яма).
Все просто. Загляните в Qt Designer, и вы увидите в редакторе сигналов/слотов, колонки под названием "получатель" и "слот". Примитивно: получатель - это объект, а слот - его метод. Например, у объекта (виджета) "Form" есть метод close(), и, для того, чтобы закрыть его, нужно просто вызвать этот метод. Удобно. Там еще очень много любопытного.
Qt Designer - это инструмент для создания интерфейсов, причем очень простой в управлении. Причем, чтобы пользоваться им, вам не нужно качать весь SDK, все есть в поставке PyQt4. На выходе получается XML файл, с описанием созданного интерфейса.
Выкладываю несколько решений выбранной мной задачи.

1. С использованием дизайнера.

  Прикольно, но для решения данного вопроса с помощью дизайнера практически не писал код на Python, все сделал Qt Designer и утилита pyuic4.
Открываем Qt Designer, выбираем создание Widget. Кидаем на виджет два QTextEdit и два QPushButton. кликаем по форме, жмем по иконке "скомпоновать по сетке" (на верхней панели).
Должно получится следующее:


В редакторе свойств (правая колонка) заменяем стандартные тексты на элементах нашего Gui, как на картинке. Обязательно переименуйте имена объектов (Редактор Свойств -> objectName). Персонализируйте их, иначе при написании кода можно запутаться, где располагается QPushButton2, и за что он отвечает. Приучите себя к этому. В моем примере имена следующие:


Далее - самое интересное. Мы начинаем соединять сигналы со слотами. Переходим к "Редактору Сигналов/Слотов" в правой колонке.
делаем как у меня:


Сохраняем полученное описание Gui в файл ui_example.ui. Находим пакет pyuic. У меня он находится в директории  Python26\Lib\site-packages\PyQt4 (это справедливо для python 2.6, для остальных версий путь может отличаться). Кстати, эту директорию я прописал в системную переменную PATH, что позволяет запускать пакеты этой библиотеки без указания пути. Далее из командной строки генерируем файл интерфейса, понятный Python.

pyuic4 ui_example.ui -o ui_example.py
где первый параметр -  файл, полученный из Qt Designer, а второй - опция "-o" которая указывает что нужно вывести описание интерфейса в наш файл ui_example.py.

Все, интерфейс получен, теперь можно писать приложение. Создаем файл example.py


# -*- coding: utf-8 -*-
# Пример с использованием QtDesigner
# Импортируем нужные библиотеки
from PyQt4 import QtCore
from PyQt4 import QtGui
# Импортируем интерфейс из файла ui_example.py (расширение не указываем)
from ui_example import Ui_Form
# Создаем класс, отвечающий за вывод интерфейса
class Ui(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Ui,self).__init__(parent)
        # Так как интерфейс определен в ui_example.py классом Ui_Form,
        # то работаем с этим классом
        self.ui = Ui_Form()
        self.ui.setupUi(self)

if __name__=='__main__':
    import sys
    # создаем QApplication, это обязательное условие Qt
    app = QtGui.QApplication(sys.argv)
    exampleWindow = Ui()
    exampleWindow.show()
    sys.exit(app.exec_())

Все!!! Можно запускать приложение. Оно работает.


2. Без использования дизайнера

На этот раз мы будем  определять интерфейс непосредственно в конструкторе класса.
Создаем файл example2.py.


# -*- coding: utf-8 -*-
# пример без использования дизайнера, все Gui определяется тут
from PyQt4 import QtCore
from PyQt4 import QtGui
# создадим "алиас" для этого метода, чтобы писать меньше кода
fromUtf8 = QtCore.QString.fromUtf8

class Ui(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Ui,self).__init__(parent)
        # Меняем заголовок у окна, обратите внимание,
        # что кириллицу нельзя указывать напрямую
        # только через QString, тут мы и исползуем
        # алиас fromUtf8
        self.setWindowTitle(fromUtf8('Пример'))
        # Определяем компоновщик элементов, с ним
        # приложение будет опрятнее, в данном случае
        # используется табличный компоновщик, в который
        # мы позже разместим элементы интерфейса
        self.layout = QtGui.QGridLayout()
        # создаем два виджета текстового редактирования
        # левый и правый
        self.leftText = QtGui.QTextEdit()
        self.rightText = QtGui.QTextEdit()
        # Создаем виджет - кнопку
        self.buttonForward = QtGui.QPushButton()
        # меняем ее текст
        self.buttonForward.setText(fromUtf8('туда ->'))
        self.buttonBack = QtGui.QPushButton()
        self.buttonBack.setText(fromUtf8('<- сюда'))
        # Располагаем в компоновщике элементы
        # представьте себе таблицу 2х2
        # в первой ячейке размещается leftText, который
        # занимает одну ячейку
        self.layout.addWidget(self.leftText,  0, 0, 1, 1)
        self.layout.addWidget(self.rightText,  0, 1, 1, 1)
        self.layout.addWidget(self.buttonForward, 1, 0, 1, 1)
        self.layout.addWidget(self.buttonBack, 1, 1, 1, 1)
        # применяем компоновку
        self.setLayout(self.layout)
        # соединяем сигналы со слотами
        # в конкретном случае мы сами определили слоты forward и back
        self.connect(self.buttonForward, QtCore.SIGNAL("clicked()"), self.forward)
        self.connect(self.buttonBack, QtCore.SIGNAL("clicked()"), self.back)
        # в forward() и back() мы просто работаем с "родными методами" Qt
    def forward(self):
        self.leftText.selectAll()
        self.leftText.cut()
        self.rightText.clear()
        self.rightText.paste()
    def back(self):
        self.rightText.selectAll()
        self.rightText.cut()
        self.leftText.clear()
        self.leftText.paste()
     
if __name__=='__main__':
    import sys
    app = QtGui.QApplication(sys.argv)
    exampleWindow = Ui()
    exampleWindow.show()
    sys.exit(app.exec_())



На сегодня все. В следующей статье мы соберем этот проект в EXE файл






С уважением, Ваш В.А

4 комментария:

  1. красавчик... скоро пойду по твоим стопам :)

    ОтветитьУдалить
  2. Спасибо за этот пример, жду с нетерпением следующей статьи про сборку exe

    ОтветитьУдалить
  3. то Юрышев:
    Спасибо за отзыв. Скоро будет

    ОтветитьУдалить