Различия между версиями 10 и 32 (по 22 версиям)
Версия 10 от 2010-06-08 19:31:41
Размер: 10540
Редактор: SOL-FTTB
Комментарий:
Версия 32 от 2010-08-11 11:46:58
Размер: 101067
Редактор: 80
Комментарий:
Удаления помечены так. Добавления помечены так.
Строка 6: Строка 6:
Строка 8: Строка 7:

Это введение в программирование с использованием !PyQt4. Это руководство предназначенно для того что Вы представляли себе что такое !PyQt4. Весь код приведенный в этом руководстве был протестирован под управлением ОС Linux.
Это введение в программирование с использованием !PyQt4. Это руководство предназначено для того что Вы представляли себе что такое !PyQt4. Весь код приведенный в этом руководстве был протестирован под управлением ОС Linux.
Строка 12: Строка 10:

!PyQt4 представляет собой инструмент для создания GUI приложений. !PyQt представляет собой сочитания языка программирования Python и превосходной библиотеки Qt. Qt библиотека является одной из самых лучших библиотек в мире, если не самой лучшей. Официальный сайт проекта !PyQt находится по адресу www.riverbankcomputing.co.uk. Он был разработан Фил Томпсоном. 

!PyQt реализована в виде набора модулей Python. Он насчитывает более 300 классов, и почти 6000 функций и методов. Он представляет собой платформенно-независимый инструмент. То есть он работает на всех основных операционных систем (Unix, Windows и Mac). !PyQt распространяется под двумя видами лицензией. Разработчики могут выбирать между GPL и коммерческой лицензией. Ранее, GPL версия была доступна только для Unix. Но начиная с 4-ой версии, GPL лицензии доступна на всех поддерживаемых платформах. 

Поскольку Есть много классов, они были разнесены на несколько модулей. 
!PyQt4 представляет собой инструмент для создания GUI приложений. !PyQt представляет собой сочетание языка программирования Python и превосходной библиотеки Qt. Qt библиотека является одной из самых лучших библиотек в мире, если не самой лучшей. Официальный сайт проекта !PyQt находится по адресу www.riverbankcomputing.co.uk. Он был разработан Фил Томпсоном.

!PyQt реализована в виде набора модулей Python. Он насчитывает более 300 классов, и почти 6000 функций и методов. Он представляет собой платформо-независимый инструмент. То есть он работает на всех основных операционных систем (Unix, Windows и Mac). !PyQt распространяется под двумя видами лицензией. Разработчики могут выбирать между GPL и коммерческой лицензией. Ранее, GPL версия была доступна только для Unix. Но начиная с 4-ой версии, GPL лицензии доступна на всех поддерживаемых платформах.

Поскольку есть много классов, они были разнесены на несколько модулей.
Строка 23: Строка 20:
Модуль !QtCore содержит функционал не связанный с GUI. Этот модуль используется для работы со временем, файлами и каталогами, различными типами данных, потоками, URL, MIME типами, процессами и тредами. Модуль !QtGui содерждит компоненты отвечающие за графические компоненты а также взаимодействующие с ними классы. К ним относятся, например кнопки, окна, статус бары, панели, слайдеры, изображения, цвета, шрифты и т.д. Модуль !QtNetwork содержит классы для сетевого программирования. Благодаря этому вы можете создать свои TCP/IP и UDP клиентские и серверные приложения. Благодаря этому классу ваши программы будут более простые и небольшие по размеру. Модуль !QtXml содержатся классы для работы с xml файлами. Этот модуль обеспечивает связь как с SAX так и DOM API. Модуль !QtSvg модуль содержит компоненты для работы с SVG файлами. Масштабируемая векторная графика (SVG) является языком для работы с двумерной графикой и графическими приложениями XML. В QtOpenGL модуль используется для рендеринга 3D и 2D графики с использованием библиотеки OpenGL. Этот модуль дает возможность бесшовной интеграции с модудем !QtGui и библиотекой OpenGL. В !QtSql модуль предоставляет классы для работы с базами данных.  Модуль !QtCore содержит функционал не связанный с GUI. Этот модуль используется для работы со временем, файлами и каталогами, различными типами данных, потоками, URL, MIME типами, процессами и тредами. Модуль !QtGui содержит компоненты отвечающие за графические компоненты а также взаимодействующие с ними классы. К ним относятся, например кнопки, окна, статус бары, панели, слайдеры, изображения, цвета, шрифты и т.д. Модуль !QtNetwork содержит классы для сетевого программирования. Благодаря этому вы можете создать свои TCP/IP и UDP клиентские и серверные приложения. Благодаря этому классу ваши программы будут более простые и небольшие по размеру. Модуль !QtXml содержатся классы для работы с xml файлами. Этот модуль обеспечивает связь как с SAX так и DOM API. Модуль !QtSvg модуль содержит компоненты для работы с SVG файлами. Масштабируемая векторная графика (SVG) является языком для работы с двумерной графикой и графическими приложениями XML. В QtOpenGL модуль используется для рендеринга 3D и 2D графики с использованием библиотеки OpenGL. Этот модуль дает возможность бесшовной интеграции с модулем !QtGui и библиотекой OpenGL. В !QtSql модуль предоставляет классы для работы с базами данных.
Строка 26: Строка 23:

{{attachment:pythonlogo (1).png}}

Python является прекрасным скриптовым языком. Автором этого языка является Гвидо ван Россум. Первая версия этого языка программирования увидела свет в 1991 году. Python является высокроуровневым платформо-независмым и интерпретируемым языком программирования. Одним из свойств этого языка программирования является отстуствие в нем блоков выделенных как либо (например фигурными скобками). Для выделения блоков используются отступы. Текущая версия языка программирования является 2.5 (не совсем свежее руководство :)), которая была выпущена в сентябре 2006 года. Сегодня в разработке этого языка программирования участвует много людей живущих в разных странах.

Сообщество программистов TIOBE публикует рейтинги популярности языков программирования. На первом месте находится Java. C++ немного уступает Java. Но стоит заметь что ближайшие десятелетие С++ будет по-прежнему востребован на рынке программистов, и альтернатив ему нету. Также из этого рейтина мы можем узнать о попоулярности языков программирования в том или ином секторе решаемых задач. Java используется в промышленных решениях, гле требуется платформо-независимость. C незаменим при написании системных программ (драйвера устройств, ОС, небольшие утилиты). Php - небольшие веб-сайты. JavaScript используется при написании клиентских веб-приложений.

||'''Position'''||'''Language'''||'''Ratings'''||
||1||Java||21.7%||
||2||C||14.9%||
||3||Visual Basic||10.7%||
||4||PHP||10.2%||
||5||C++||9.9%||
||6||Perl||5.4%||
||7||C#||3.4%||
||8||Python||3.0%||
||9||JavaScript||2.7%||
||10||Ruby||2.0%||

Python находится на 8 месте в этом рейтинге (в настоящее время Python находится на 6 месте потеснив С# и Perl). Также в этом рейтинге находятся ближайшие конкуренты Python: Ruby и Python.
{{attachment:pythonlogo (1).png|pythonlogo (1).png}}

Python является прекрасным скриптовым языком. Автором этого языка является Гвидо ван Россум. Первая версия этого языка программирования увидела свет в 1991 году. Python является высокроуровневым платформо-независимым и интерпретируемым языком программирования. Одним из свойств этого языка программирования является отсутствие в нем блоков выделенных как либо (например фигурными скобками). Для выделения блоков используются отступы. Текущая версия языка программирования является 2.5 (не совсем свежее руководство :)), которая была выпущена в сентябре 2006 года. Сегодня в разработке этого языка программирования участвует много людей живущих в разных странах.

Сообщество программистов TIOBE публикует рейтинги популярности языков программирования. На первом месте находится Java. C++ немного уступает Java. Но стоит заметь что ближайшие десятилетие С++ будет по-прежнему востребован на рынке программистов, и альтернатив ему нету. Также из этого рейтигна мы можем узнать о популярности языков программирования в том или ином секторе решаемых задач. Java используется в промышленных решениях, где требуется платформо-независимость. С незаменим при написании системных программ (драйвера устройств, ОС, небольшие утилиты). PHP - небольшие веб-сайты. !JavaScript используется при написании клиентских веб-приложений.
||'''Position''' ||'''Language''' ||'''Ratings''' ||
||1 ||Java ||21.7% ||
||2 ||C ||14.9% ||
||3 ||Visual Basic ||10.7% ||
||4 ||PHP ||10.2% ||
||5 ||C++ ||9.9% ||
||6 ||Perl ||5.4% ||
||7 ||C# ||3.4% ||
||8 ||Python ||3.0% ||
||9 ||!JavaScript ||2.7% ||
||10 ||Ruby ||2.0% ||




Python находится на 8 месте в этом рейтинге (в настоящее время Python находится на 6 месте потеснив С# и Perl). Также в этом рейтинге находятся ближайшие конкуренты Python: Ruby и Perl.
Строка 48: Строка 46:

Для создания GUI приложений на Python программисты могут выбирать между тремя замечательными библиотеками: PyGTK, wxPython и PyQt. Какую библиотеку выберете Вы, завист от Вас. Существует также еще библиотека для создания GUI приложений: TkInter. Буду краток - не использовать.
Для создания GUI приложений на Python программисты могут выбирать между тремя замечательными библиотеками: PyGTK, wxPython и !PyQt. Какую библиотеку выберете Вы, зависит от Вас. Существует также еще библиотека для создания GUI приложений: !TkInter. Буду краток - не использовать.
Строка 52: Строка 49:

В этой части руководства PyQt4 мы познакомимся с некоторыми основновными функциями. Объъяснение будет детальным как для ребенка. Помните, нет глупых людей. Есть ленивые или не достаточно упрямые люди.
В этой части руководства !PyQt4 мы познакомимся с некоторыми основными функциями. Объяснение будет детальным как для ребенка. Помните, нет глупых людей. Есть ленивые или не достаточно упрямые люди.
Строка 56: Строка 52:

Код нашего примера очень прост. Он показывает только небольшое окно. И не смотря на его простоту, мы можем с ним сделать много действий. Мы можем изменить его размер. Мы можем минимизировать его. Мы мжем распохнуть его во весь экран. При создании программы с нуля - это потребовало бы большой объем кода. Но в нашем случае - кто то уже заложил всю эту функциональность. То есть мы создали приложение на основе чьего то кода. Так какданные функции являются стандартными, то мы просто их вызываем в своем приложении. PyQt является высокоуровневым инструментом (библиотекой). Более низкоуровневые библиотеки требуют для создания аналогичного приложения несколько десятков строчек кода.
Код нашего примера очень прост. Он показывает только небольшое окно. И не смотря на его простоту, мы можем с ним сделать много действий. Мы можем изменить его размер. Мы можем минимизировать его. Мы можем распахнуть его во весь экран. При создании программы с нуля - это потребовало бы большой объем кода. Но в нашем случае - кто то уже заложил всю эту функциональность. То есть мы создали приложение на основе чьего то кода. Так как данные функции являются стандартными, то мы просто их вызываем в своем приложении. !PyQt является высокоуровневым инструментом (библиотекой). Более низкоуровневые библиотеки требуют для создания аналогичного приложения несколько десятков строчек кода.
Строка 76: Строка 71:
{{{#!highlight python
import sys
from PyQt4 import QtGui
}}}
Сначала мы подключаем необходимые нам библиотеки. Основным графическим интерфейсом для работы с виджетами является модуль !QtGui.

{{{#!highlight python
app = QtGui.QApplication(sys.argv)
}}}
Каждое !PyQt4 приложение должно создать основной объект приложения. Объект приложения находится в модуле !QtGui. При его создании мы передаем в него параметры командной строки sys.argv. Таким образом наш скрипт может запускаться из командной строки. Таким образом мы можем контролировать его работу.

{{{#!highlight python
widget = QtGui.QWidget()
}}}
QWidget является базовым классом для всех виджетов пользовательского интерфейса !PyQt4. Мы используем стандартный конструктор для создания объекта QWidget. По умолчанию виджет не имеет родителя. Такой виджет называется окном.

{{{#!highlight python
widget.resize(250, 150)
}}}
Метод resize() изменяет размер виджета. В нашем коде 250 пикселей ширина и 150 высота.

{{{#!highlight python
widget.setWindowTitle('simple')
}}}
Эта строчка определяет название нашего окна. Название отображается в заголовке.

{{{#!highlight python
widget.show()
}}}
Метод show() отображает наше окно на экране.

{{{#!highlight python
sys.exit(app.exec_())
}}}
И наконец мы запускаем основной цикл нашей программы. Обработка сообщений начинается с этой строчки. В основном цикле происходит получение и обработка событий окна системы и последующая передача их виджетам. При окончании работы главного цикла, наша программа прекращает свою работу. С помощью sys.exit() мы обеспечиваем чистый выход. То есть мы передадим в ОС информацию что наше приложение закончило свою работу.

{{attachment:simple.jpg}}

'''Рисонок: Пример'''

=== Иконка приложения ===
Иконка приложения это картинка, которая как правило отображается в верхнем левом углу заголовка приложения. В этом примере мы покажем как привязать иконку в приложение с помощью !PyQt4. Также мы рассмотрим несколько новых методов.

{{{#!highlight python
#!/usr/bin/python

# icon.py

import sys
from PyQt4 import QtGui


class Icon(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Icon')
        self.setWindowIcon(QtGui.QIcon('icons/web.png'))


app = QtGui.QApplication(sys.argv)
icon = Icon()
icon.show()
sys.exit(app.exec_())
}}}
Предыдущий пример был написан используя структурный стиль. Язык программирования Python поддерживает как структурный стиль, так и объективно-ориентированный стиль программирования. При использовании !PyQt4 лучше использовать объектно-ориентированный стиль.

{{{#!highlight python
class Icon(QtGui.QWidget):
     def __init__(self, parent=None):
         QtGui.QWidget.__init__(self, parent)
}}}
Объектно-ориентированное программирование подразумевает три важные вещи: класс, данные и методы. В этих строчках кода мы создаем класс с именем Icon. Этот класс мы объявляем производным от класса !QtGui.QWidget. Теперь мы вызываем конструктор класса-родителя и передаем туда в качестве параметров указатель на наш класс и указатель на родителя класса (???).

{{{#!highlight python
self.setGeometry(300, 300, 250, 150)
self.setWindowTitle('Icon')
self.setWindowIcon(QtGui.QIcon('icons/web.png'))
}}}
Теперь мы рассмотрим три метода которые мы унаследовали от !QtGui.QWidget. Метод QWidget.setGeometry(300, 300, 250, 150) задает местоположение нашего окна и его размеры. Первые два значения задают X и Y координаты его расположение, и последние два значения ширину и высоту. Второй метод мы рассматривали в предыдущем примере. Третий метод мы используем для определение иконки нашего приложения. В метод QIcon мы передаем путь где находится наша картинка.

{{attachment:icon1.jpg}}

'''Рисонок: иконка'''

=== Посмотрим на контекстную подсказку ===
Мы можем отобразить контекстную подсказку в любом из наших виджетов.

{{{#!highlight python
#!/usr/bin/python

# tooltip.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class Tooltip(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Tooltip')

        self.setToolTip('This is a <b>QWidget</b> widget')
        QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))


app = QtGui.QApplication(sys.argv)
tooltip = Tooltip()
tooltip.show()
app.exec_()
}}}
В этом примере мы рассмотрим контекстную подсказку на примере виджета на базе класса QWidget.

{{{#!highlight python
self.setToolTip('This is a <b>QWidget</b> widget')
}}}
Сначала создадим контекстную подсказку, для этого вызовем метод setTooltip(). Для форматирования текста мы можем использовать теги.

{{{#!highlight python
QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))
}}}
По умолчанию шрифт установленный для контекстной подсказки неудачный, поэтому исправим его.

{{attachment:tooltip (1).jpg|tooltip (1).jpg}}

'''Рисонок: контекстная подсказка'''

=== Закрытие окна ===
Самый простой способ закрыть наше приложение, это щелкнуть по крестику в заголовке нашего окна. В этом примере мы рассмотрим как закрыть окно программным путем. Мы вкратце рассмотрим сигналы и слоты. Ниже приведен вызов конструктора для создания кнопки.

{{{#!highlight python
QPushButton(string text, QWidget parent = None)
}}}
Параметр text передает в конструктор надпись на кнопки. Параметр parent передает указатель на родительский класс. В нашем случае это QWidget.

{{{#!highlight python
#!/usr/bin/python

# quitbutton.py

import sys
from PyQt4 import QtGui, QtCore


class QuitButton(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Icon')

        quit = QtGui.QPushButton('Close', self)
        quit.setGeometry(10, 10, 60, 35)

        self.connect(quit, QtCore.SIGNAL('clicked()'),
            QtGui.qApp, QtCore.SLOT('quit()'))


app = QtGui.QApplication(sys.argv)
qb = QuitButton()
qb.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
quit = QtGui.QPushButton('Close', self)
quit.setGeometry(10, 10, 60, 35)
}}}
Мы создаем кнопку и определяем ее местоположение и размер относительно нашего виджета.

{{{#!highlight python
self.connect(quit, QtCore.SIGNAL('clicked()'), QtGui.qApp, QtCore.SLOT('quit()'))
}}}
Система обработки событий в !PyQt4 строится с использованием механизма слотов и сигналов. Если мы нажимаем на кнопку, то посылается сигнал clicked(). Любой слой !PyQt4 может принять этот сигнал и обработаь его. Метод !QtCore.QObject.connect() отвечает за связь между слотами и сигналами. В нашем случае мы связываем сигнал clicked() со слотом приложения и его функцией quit(). То есть в такой связи участвует два объекта: отправитель и получатель. Отправитель - кнопка, получатель - объект приложения.

{{attachment:quitbutton (1).jpg|quitbutton (1).jpg}}

'''Рисунок: кнопка выхода'''

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

{{{#!highlight python
#!/usr/bin/python

# messagebox.py

import sys
from PyQt4 import QtGui


class MessageBox(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('message box')


    def closeEvent(self, event):
        reply = QtGui.QMessageBox.question(self, 'Message',
            "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

        if reply == QtGui.QMessageBox.Yes:
            event.accept()
        else:
            event.ignore()

app = QtGui.QApplication(sys.argv)
qb = MessageBox()
qb.show()
sys.exit(app.exec_())
}}}
Если Вы попробуете закрыть это приложение, то будет сгенерированно событие QCloseEvent. Чтобы изменить поведение нашего виджета, необходимо переопределить обработчик события closeEvent().

{{{#!highlight python
reply = QtGui.QMessageBox.question(self, 'Message',
     "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
}}}
В этих строчках мы показываем окно с сообщением с просьбой подтвердить выход и с двумя кнопками: Да и Нет. Возвращаемое значение нажатой кнопки хранится в переменной.

{{{#!highlight python
if reply == QtGui.QMessageBox.Yes:
    event.accept()
else:
   event.ignore()
}}}
Теперь мы смотрим что у нас в этой переменной. Если вы кликнули на кнопку Да, то наше приложение закроется, в противоположном случае событие игнорируется.

{{attachment:messagebox (1).jpg|messagebox (1).jpg}}

'''Рисунок: окно с сообщением.'''

=== Расположение окна в центре дисплея ===
Следующий скрипт который мы рассмотрим, ответит нам на вопрос, как расположить наше окно в центре дисплея.

{{{#!highlight python
#!/usr/bin/python

# center.py

import sys
from PyQt4 import QtGui


class Center(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('center')
        self.resize(250, 150)
        self.center()

    def center(self):
        screen = QtGui.QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)


app = QtGui.QApplication(sys.argv)
qb = Center()
qb.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
self.resize(250, 150)
}}}
Определяем размер нашего виджета: 250 пикселей ширина и 150 пикселей высота.

{{{#!highlight python
screen = QtGui.QDesktopWidget().screenGeometry()
}}}
Получаем разрешение нашего дисплея.

{{{#!highlight python
size = self.geometry()
}}}
Получаем размер нашего виджета.

{{{#!highlight python
self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
}}}
Теперь перемещаем наше окно в центр экрана.
Строка 78: Строка 359:
== Главное окно ==
Класс QMainWindow служит для создания главного окна приложения. Он позволяет создавать скелет классического приложения с меню и панелями.

== Панель состояния ==
Панель состояния является виджетом, который используется для отображения различной информации о состоянии приложения.

{{{#!highlight python
!/usr/bin/python

# statusbar.py

import sys
from PyQt4 import QtGui

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.resize(250, 150)
        self.setWindowTitle('statusbar')

        self.statusBar().showMessage('Ready')


app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
self.statusBar().showMessage('Ready')
}}}
Для создания панели состояния мы вызываем метод statusBar() класса QApplication.

== Меню ==
Меню является основным инструментом для работы c графическим приложением. Меню представляет стобой набор команд, расположенных в различных меню. Если при работе с консолью Вы должны запомнить все команды с их параметрами, то в меню Вы группируете команды по их назначению. Существуют стандарты группировки команд в меню, благодаря которым пользователи сокращают свое время при изучении новых приложений.

{{{#!highlight python
#!/usr/bin/python

# menubar.py

import sys
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.resize(250, 150)
        self.setWindowTitle('menubar')

        exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        exit.setShortcut('Ctrl+Q')
        exit.setStatusTip('Exit application')
        self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))

        self.statusBar()

        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(exit)

app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
menubar = self.menuBar()
 file = menubar.addMenu('&File')
 file.addAction(exit)
}}}
Сначала для создание меню мы используем метод menuBar(), входящий в класс QMainWindow. Для добавление в меню разделов, используем метод addMenu(). Ну и в завершении мы привязываем к разделу действие.

== Панель инструментов ==
Меню группирует все команды приложения. Панель инструментов обеспечивает быстрый доступ к основным командам.

{{{#!highlight python
#!/usr/bin/python

# toolbar.py

import sys
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.resize(250, 150)
        self.setWindowTitle('toolbar')

        self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        self.exit.setShortcut('Ctrl+Q')
        self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))

        self.toolbar = self.addToolBar('Exit')
        self.toolbar.addAction(self.exit)


app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
self.exit.setShortcut('Ctrl+Q')
}}}
GUI приложения могут управляться с помощью команд. Вызов команд может осуществляться из меню, контекстного меню, панели инструментов или с помощью сочитания клавиш. PyQt упрощает разработку приложений, предлагая использовать "действия". Объект "действия" может иметь текст, значок, комбинацию клавиш, текст статуса, "What is this" текст и подсказку. В нашем примере мы определяем значок действия, текст и комбинацию клавиш.

{{{#!highlight python
self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
}}}
Прежде чем подключить наше действие к панели управления, мы связываем сигнал triggered() со слотом exit().

{{{#!highlight python
self.toolbar = self.addToolBar('Exit')
self.toolbar.addAction(self.exit)
}}}
Теперь мы создаем панель управления и добавляем туда наше действие.

{{attachment:toolbar.jpg}}

'''Рисунок: Панель управления'''

== Объединим все ==
В этой главе мы рассмотрели создание меню, панели инструментов и панели статуса. Теперь мы объединим все виджеты и добавим виджет текстового редактора.

{{{#!highlight python
#!/usr/bin/python

# mainwindow.py

import sys
from PyQt4 import QtGui, QtCore

class MainWindow(QtGui.QMainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)

        self.resize(350, 250)
        self.setWindowTitle('mainwindow')

        textEdit = QtGui.QTextEdit()
        self.setCentralWidget(textEdit)

        exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
        exit.setShortcut('Ctrl+Q')
        exit.setStatusTip('Exit application')
        self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))

        self.statusBar()

        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(exit)

        toolbar = self.addToolBar('Exit')
        toolbar.addAction(exit)


app = QtGui.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
textEdit = QtGui.QTextEdit()
self.setCentralWidget(textEdit)
}}}
Тут мы создаем вджет для редактирования текста. Мы размещаем этот виджет в центре виджета QMainWindow. Центральный виджет заполняет все свободное пространство.

{{attachment:mainwindow.jpg}}

'''Рисунок: Панель управления'''
Строка 80: Строка 538:

= События и Сигналы =
Важным понятием программирования является менеджер компоновки. Менеджер компоновки отвечает за расположение виджетов в окне. Расположением виджетов может осуществляться двумя способами. Либо используя абсолютное позиционирование, либо используя компоновочные классы.

== Абсолютное позиционирование ==
При абсолютном позиционировании вы определяете размер и положение каждого виджета в пикселях. При использовании абсолютного позиционирования вы должны учитывать три фактора:

 * размер и местоположение виджетов не изменяется при изменении размеров окна
 * вид программы может зависить от ОС на которой она будет зарущена
 * Изменение шрифта в программе может испортить ее внешний вид
 * Если Вы решите изменить внешний вид виджета, то Вам придется изменить местопложение всех виджетов, что может занять у вас много времени

{{{#!highlight python
#!/usr/bin/python

# absolute.py

import sys
from PyQt4 import QtGui


class Absolute(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('Communication')

        label = QtGui.QLabel('Couldn\'t', self)
        label.move(15, 10)

        label = QtGui.QLabel('care', self)
        label.move(35, 40)

        label = QtGui.QLabel('less', self)
        label.move(55, 65)

        label = QtGui.QLabel('And', self)
        label.move(115, 65)

        label = QtGui.QLabel('then', self)
        label.move(135, 45)

        label = QtGui.QLabel('you', self)
        label.move(115, 25)

        label = QtGui.QLabel('kissed', self)
        label.move(145, 10)

        label = QtGui.QLabel('me', self)
        label.move(215, 10)

        self.resize(250, 150)

app = QtGui.QApplication(sys.argv)
qb = Absolute()
qb.show()
sys.exit(app.exec_())
}}}
Мы просто вызываем метод move() для указания расположения виджетов. Мы указываем координаты X и Y расположения. Начало системы координат находится в левом верхнем углу. То есть координата X растет слева направо, а координата Y сверху вниз.

{{attachment:absolute.jpg}}

'''Рисунок: Абсолютное позиционирование'''

== Компоновка макета ==
Компоновка макета с помощью классов является более гибкой и удобной. Это более предпочтительный способ размещения виджетов на экране. Основными классами для такой компоновки являются QHBoxLayout и QVBoxLayout. Они размещают виджеты по горизонтали и вертикали.

Допустим мы хотим разместить две кнопки в правом нижнем углу. Чтобы это сделать, мы должны использовать вертикальный и горизонтальный макет. Для создания необходимого пустого пространства мы будем использовать "коэффициент притяжения".

{{{#!highlight python
#!/usr/bin/python

# boxlayout.py

import sys
from PyQt4 import QtGui


class BoxLayout(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('box layout')

        ok = QtGui.QPushButton("OK")
        cancel = QtGui.QPushButton("Cancel")

        hbox = QtGui.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(ok)
        hbox.addWidget(cancel)

        vbox = QtGui.QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.resize(300, 150)

app = QtGui.QApplication(sys.argv)
qb = BoxLayout()
qb.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
ok = QtGui.QPushButton("OK")
cancel = QtGui.QPushButton("Cancel")
}}}
Просто создаем две кнопки.

{{{#!highlight python
hbox = QtGui.QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(ok)
hbox.addWidget(cancel)
}}}
Создаем горизонтальный макет компоновки. Затем добавляем коэффициент притяжения и 2 кнопки.

{{{#!highlight python
vbox = QtGui.QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)
}}}
Что бы разместить виджеты в нужном формате, мы размещаем вертикальный макет в горизонтальном.

{{{#!highlight python
self.setLayout(vbox)
}}}
И в заключении мы определяем основную компоновку.

{{attachment:boxlayout.jpg}}

'''Рисунок: Компоновка макета'''

== QGridLayout ==
Наиболее универсальным менеджером компоновки является менеджер размещающей виджеты в сетки. Эта компоновка подразумевает что вы делите пространство на столбцы и строчки. Для создания такой компоновки мы используем класс QGridLayout.

{{{#!highlight python
#!/usr/bin/python

# gridlayout.py

import sys
from PyQt4 import QtGui


class GridLayout(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('grid layout')

        names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/',
            '4', '5', '6', '*', '1', '2', '3', '-',
            '0', '.', '=', '+']

        grid = QtGui.QGridLayout()

        j = 0
        pos = [(0, 0), (0, 1), (0, 2), (0, 3),
                (1, 0), (1, 1), (1, 2), (1, 3),
                (2, 0), (2, 1), (2, 2), (2, 3),
                (3, 0), (3, 1), (3, 2), (3, 3 ),
                (4, 0), (4, 1), (4, 2), (4, 3)]

        for i in names:
            button = QtGui.QPushButton(i)
            if j == 2:
                grid.addWidget(QtGui.QLabel(''), 0, 2)
            else: grid.addWidget(button, pos[j][0], pos[j][1])
            j = j + 1

        self.setLayout(grid)



app = QtGui.QApplication(sys.argv)
qb = GridLayout()
qb.show()
sys.exit(app.exec_())
}}}
В нашем примере мы создали сетку с кнопками. Для заполнения пустого места, мы использовали пустой виджет метки.

{{{#!highlight python
grid = QtGui.QGridLayout()
}}}
Создаем компоновочную сетку.

{{{#!highlight python
if j == 2:
    grid.addWidget(QtGui.QLabel(''), 0, 2)
else: grid.addWidget(button, pos[j][0], pos[j][1])
}}}
Добавляем виджеты в сетку с помощью метода addWidget(). Аргументами, передаваемыми в этот метод, являются виджет, строчка и столбец.

{{attachment:gridlayout.jpg}}

'''Рисунок: сеточная компоновка'''

Виджеты могут занимать несколько столбцов и строк. Следующей пример иллюстрирует это.

{{{#!highlight python
#!/usr/bin/python

# gridlayout2.py

import sys
from PyQt4 import QtGui


class GridLayout2(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('grid layout')

        title = QtGui.QLabel('Title')
        author = QtGui.QLabel('Author')
        review = QtGui.QLabel('Review')

        titleEdit = QtGui.QLineEdit()
        authorEdit = QtGui.QLineEdit()
        reviewEdit = QtGui.QTextEdit()

        grid = QtGui.QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)

        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)

        grid.addWidget(review, 3, 0)
        grid.addWidget(reviewEdit, 3, 1, 5, 1)


        self.setLayout(grid)
        self.resize(350, 300)


app = QtGui.QApplication(sys.argv)
qb = GridLayout2()
qb.show()
sys.exit(app.exec_())
}}}
{{{#!highlight python
grid = QtGui.QGridLayout()
grid.setSpacing(10)
}}}
Создаем клетчатаю компоновку и устанавливаем расстояние между ячейками.

{{{#!highlight python
grid.addWidget(reviewEdit, 3, 1, 5, 1)
}}}
При добавление виджета к ячейке мы можем указать сколько столбцов и строчек он будет занимать. В нашем случае мы выделили reviewEdit 1 столбец и 5 строчек.

{{attachment:gridlayout2.jpg}}

'''Рисунок: сеточная компоновка (2)'''

= События и сигналы =
В этой части руководства мы рассмотрим сигналы и события происходящие в приложении.

== События ==
События являютя важным элементом в любой графической программе. События могут генерироваться программой или пользователем. Когда мы вызываем в нашей программе метод exec_(), то запускаем рутинный механизм. Основной цикл выбирает события и отправляет их на объекты. Trolltech внедрила уникальную технологию слотов и сигналов.

== Сигналы и слоты ==
Гененирование сигнала происходит на любое действие пользователя, например, он нажимает на кнопку, перетаскивает ползунок и т.д. Сигнал может также генерироваться самой программой. Например, когда наступает определенное время. Слоты предназначены для обработки сигнала и представляют собой методы. В Python любой метод может быть слотом.

{{{#!highlight python
#!/usr/bin/python

# sigslot.py

import sys
from PyQt4 import QtGui, QtCore


class SigSlot(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('signal & slot')

        lcd = QtGui.QLCDNumber(self)
        slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)

        vbox = QtGui.QVBoxLayout()
        vbox.addWidget(lcd)
        vbox.addWidget(slider)

        self.setLayout(vbox)
        self.connect(slider, QtCore.SIGNAL('valueChanged(int)'), lcd,
                QtCore.SLOT('display(int)') )

        self.resize(250, 150)


app = QtGui.QApplication(sys.argv)
qb = SigSlot()
qb.show()
sys.exit(app.exec_())
}}}
В этом примере мы показываем ползунок и число в стиле ЖК-часов. Мы можем изменять число с помощью передвижения ползунка.

{{{#!highlight python
self.connect(slider, QtCore.SIGNAL('valueChanged(int)'), lcd, QtCore.SLOT('display(int)') )
}}}
В этой строчке мы соединяем сигнал ползунка valueChanged() со слотом числа display().

В методе connect() используется 4 параметра. Отправитель - объект, посылающий сигнал. Сигнал - то что посылает объект. Приемник - объект получающая сигнал. Слот - метод, реагирующий на сигнал.

{{attachment:signalslot.jpg}}

'''Рисунок: сигнал и слоты'''

== Переопределение обработчика событий ==
Обработка событий в PyQt4 в основном осуществляется с помощью переопределения обработчика событий.

{{{#!highlight python
#!/usr/bin/python

# escape.py

import sys
from PyQt4 import QtGui, QtCore

class Escape(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('escape')
        self.resize(250, 150)
        self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )


    def keyPressEvent(self, event):
        if event.key() == QtCore.Qt.Key_Escape:
            self.close()

app = QtGui.QApplication(sys.argv)
qb = Escape()
qb.show()
sys.exit(app.exec_())
}}}
В этом примере мы переопределили обработчик событий keyPressEvent().

{{{#!highlight python
def keyPressEvent(self, event):
     if event.key() == QtCore.Qt.Key_Escape:
         self.close()
}}}
При нажатии на клавишу Esc наше приложение закроется.

== Генерирование сигнала ==
Рассмотрим объекты, которые образованы от !QtCore. QObject может генерировать сигналы. Например если вы нажмете на кнопку, будет сгенерирован сигнал clicked(). В следующем примере мы посмотрим как можно послать сигнал.

{{{#!highlight python
#!/usr/bin/python

# emit.py

import sys
from PyQt4 import QtGui, QtCore


class Emit(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setWindowTitle('emit')
        self.resize(250, 150)
        self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )

    def mousePressEvent(self, event):
        self.emit(QtCore.SIGNAL('closeEmitApp()'))

app = QtGui.QApplication(sys.argv)
qb = Emit()
qb.show()
sys.exit(app.exec_())
}}}
Мы создали и послали сигнал closeEmitApp(). Этот сигнал вызывается, когда мы кликаем мышкой.

{{{#!highlight python
def mousePressEvent(self, event):
     self.emit(QtCore.SIGNAL('closeEmitApp()'))
}}}
Генерация сигнала происходит с помощью метода emit().

{{{#!highlight python
self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
}}}
Самостоятельно соединяем созданный сигнал closeEmitApp() со слотом close().
Строка 84: Строка 934:
Диалоговые окна являются неотъемлемой частью большинства современных GUI приложений. В обычной жизни диалог - это общение между двумя и более лицами. В компьютерном приложении диалоговое окно обеспечивает "общение" пользователя с приложением. То есть с помощью диалоговых окон пользователь может вводить данные, редактировать их и т.д.

== Стандартные диалоги ==
QInputDialog представляет собой стандартный диалог, чтобы получить одно значения от пользователя. Значение может быть целым числом, строкой или элементом списка.

{{{#!highlight python
#!/usr/bin/python

# inputdialog.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class InputDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 350, 80)
        self.setWindowTitle('InputDialog')

        self.button = QtGui.QPushButton('Dialog', self)
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)

        self.button.move(20, 20)
        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
        self.setFocus()

        self.label = QtGui.QLineEdit(self)
        self.label.move(130, 22)


    def showDialog(self):
        text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')

        if ok:
            self.label.setText(unicode(text))


app = QtGui.QApplication(sys.argv)
icon = InputDialog()
icon.show()
app.exec_()
}}}
В этом примере у нас имеется виджет с кнопкой и строкой для редактирования. При нажатии на кнопку мы увидим диалоговое окно, куда можем ввести значение. После того как мы ввели текст и нажали кнопку ввода, мы получим в строке редактирования виждета введенное значение.

{{{#!highlight python
text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
}}}
Эта строка отображает диалогове окно. Первое значение - это название диалогового окна, второе значение - сообщение в диалоговом окне. Диалог возвращает введенный текст и булево значение. Если Вы нажали кнопку Ок, то мы получим булево значение True, в противном случае - False.

{{attachment:inputdialog.jpg}}

'''Рисунок: ввод в диалоговое окно'''

== QColorDialog ==
QColorDialog предоставляет виджет в виде диалоговго окна, где пользователь может выбрать цвет.

{{{#!highlight python
#!/usr/bin/python

# colordialog.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class ColorDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        color = QtGui.QColor(0, 0, 0)

        self.setGeometry(300, 300, 250, 180)
        self.setWindowTitle('ColorDialog')

        self.button = QtGui.QPushButton('Dialog', self)
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)
        self.button.move(20, 20)

        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
        self.setFocus()

        self.widget = QtGui.QWidget(self)
        self.widget.setStyleSheet("QWidget { background-color: %s }"
            % color.name())
        self.widget.setGeometry(130, 22, 100, 100)


    def showDialog(self):
        color = QtGui.QColorDialog.getColor()

        self.widget.setStyleSheet("QWidget { background-color: %s }"
            % color.name())

app = QtGui.QApplication(sys.argv)
cd = ColorDialog()
cd.show()
app.exec_()
}}}
Пример отображает кнопку и пустой виджет. Цвет вижета по умолчанию устанавливается в черный цвет. Используя QColorDialog, мы можем изменить его фон.

{{{#!highlight python
color = QtGui.QColorDialog.getColor()
}}}
Эта строчка вызывает диалоговое окно QColorDialog.

{{{#!highlight python
self.widget.setStyleSheet("QWidget { background-color: %s }"
     % color.name())
}}}
Тут мы изменяем фоновый цвет нашего виджета.

{{attachment:colordialog.jpg}}

'''Рисунок: диалоговое окно выбора цвета'''

== QFontDialog ==
Диалоговое окно QFontDialog предназначенно для выбора шрифта.

{{{#!highlight python
#!/usr/bin/python

# fontdialog.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class FontDialog(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        hbox = QtGui.QHBoxLayout()

        self.setGeometry(300, 300, 250, 110)
        self.setWindowTitle('FontDialog')

        button = QtGui.QPushButton('Dialog', self)
        button.setFocusPolicy(QtCore.Qt.NoFocus)
        button.move(20, 20)

        hbox.addWidget(button)

        self.connect(button, QtCore.SIGNAL('clicked()'), self.showDialog)

        self.label = QtGui.QLabel('Knowledge only matters', self)
        self.label.move(130, 20)

        hbox.addWidget(self.label, 1)
        self.setLayout(hbox)


    def showDialog(self):
        font, ok = QtGui.QFontDialog.getFont()
        if ok:
            self.label.setFont(font)


app = QtGui.QApplication(sys.argv)
cd = FontDialog()
cd.show()
app.exec_()
}}}
В этом примере у нас есть метка и строчка. С помощью QFontDialog мы можеи изменить тип шрифта метки.

{{{#!highlight python
hbox.addWidget(self.label, 1)
}}}
Мы создали метку с изменяемым размером. Это необходимо, так как при изменении шрифта ширина надписи может измениться. В случае если ширина станет больше, то весь тект может не поместиться в ней.

{{{#!highlight python
font, ok = QtGui.QFontDialog.getFont()
}}}
Здесь мы вызываем диалоговое окно для выбора шрифта.

{{{#!highlight python
if ok:
     self.label.setFont(font)
}}}
Если мы нажали Ok, то изменяем тип шрифта метки.

{{attachment:fontdialog.jpg}}

'''Рисунок: диалоговое окно выбора шрифта'''

== QFileDialog ==
QFileDialog - диалоговое окно, которое позволяет пользователю выбирать файлы или директории. Выбранный файл может быть выбран для его открытия.

{{{#!highlight python
#!/usr/bin/python

# openfiledialog.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class OpenFile(QtGui.QMainWindow):
    def __init__(self, parent=None):
        QtGui.QMainWindow.__init__(self, parent)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('OpenFile')

        self.textEdit = QtGui.QTextEdit()
        self.setCentralWidget(self.textEdit)
        self.statusBar()
        self.setFocus()

        exit = QtGui.QAction(QtGui.QIcon('open.png'), 'Open', self)
        exit.setShortcut('Ctrl+O')
        exit.setStatusTip('Open new File')
        self.connect(exit, QtCore.SIGNAL('triggered()'), self.showDialog)

        menubar = self.menuBar()
        file = menubar.addMenu('&File')
        file.addAction(exit)

    def showDialog(self):
        filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file',
                    '/home')
        file=open(filename)
        data = file.read()
        self.textEdit.setText(data)

app = QtGui.QApplication(sys.argv)
cd = OpenFile()
cd.show()
app.exec_()
}}}
В этом примере мы создаем меню - виджет, где можно вводить текст, и строку состояния. В строке состояния отображается подсказка над каким действием в меню находится курсор. При выборе действия в меню, вызывается диалоговое окно QFileDialog, где мы можем выбрать файл. Содержимое файла загружается в виджет редактирования текста.

{{{#!highlight python
class OpenFile(QtGui.QMainWindow):
...
        self.textEdit = QtGui.QTextEdit()
        self.setCentralWidget(self.textEdit)
}}}
Этот пример построен на базе виджета QMainWindow, потому что нам надо расположить централизованно текстовый редактор. Это удобно сделать с помощью QMainWindow не прибегая к помощи менеджера компоновки.

{{{#!highlight python
filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file',
                    '/home')
}}}
Мы открываем диалогове окно QFileDialog. Первый параметр метода getOpenFileName() - название окна. Второй параметр определяет рабочую директорию (дирктория которая по умолчанию будет выбрана). По умолчанию, фильтр показывает все файлы (*).

{{{#!highlight python
file=open(filename)
data = file.read()
self.textEdit.setText(data)
}}}
Читаем выбранный файл и передаем его содержимое в виджет для редактирования текста.

{{attachment:filedialog.jpg}}

'''Рисунок: диалоговое окно для открытий файла'''
Строка 86: Строка 1197:
Виджеты являются кубиками для построения графического приложения. В PyQt4 предоставлен широкий выбор виджетов. Кнопки, флажки, ползунки, выпадающий список и т.д. Таким образом програмист может удовлетворить все свои потребности. В этом разделе руководства мы опишем несколько полезных виджетов.

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

{{{#!highlight python
#!/usr/bin/python

# checkbox.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class CheckBox(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Checkbox')

        self.cb = QtGui.QCheckBox('Show title', self)
        self.cb.setFocusPolicy(QtCore.Qt.NoFocus)
        self.cb.move(10, 10)
        self.cb.toggle();
        self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)

    def changeTitle(self, value):
        if self.cb.isChecked():
            self.setWindowTitle('Checkbox')
        else:
            self.setWindowTitle('')

app = QtGui.QApplication(sys.argv)
icon = CheckBox()
icon.show()
app.exec_()
}}}
В этом примере мы создаем переключатель, который в зависимости от того включен он или выключен, влияет на отображение заголовка окна.

{{{#!highlight python
self.cb = QtGui.QCheckBox('Show title', self)
}}}
Это конструктор переключателя.

{{{#!highlight python
self.cb.setFocusPolicy(QtCore.Qt.NoFocus)
}}}
По умолчанию, фокус наводится на переключатель. Он представляет собой тонкую линию прямоугольника вокруг переключателя. Мне кажется что это линия выглядит некрасиво, и поэтому я ее отключил, установив настройку фокуса Qt.NoFocus.

{{{#!highlight python
self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)
}}}
Мы соединяем наш метод changeTitle() с сигналом stateChanged(). Метод changeTitle() изменяет нам заголовок окна.

{{{#!highlight python
self.cb.toggle();
}}}
По умолчанию переключатель находится в выключенном положении. Для того что бы его включить мы используем метод toggle().

{{attachment:checkbox.jpg}}

'''Рисунок: QCheckBox'''

== ToggleButton ==
В PyQt4 нет виджета с названием !ToggleButton. Для создания этого виджета мы используем QPushButton в специальном формате. ToggleButton это кнопка которая может находиться в двух состояниях: нажата и не нажата. Вы можете переключаться кнопку из положения в положения просто щелкнув по ней. Существуют задачи, когда использование такого механизма очень удобно.

{{{#!highlight python
#!/usr/bin/python

# togglebutton.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class ToggleButton(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.color = QtGui.QColor(0, 0, 0)

        self.setGeometry(300, 300, 280, 170)
        self.setWindowTitle('ToggleButton')

        self.red = QtGui.QPushButton('Red', self)
        self.red.setCheckable(True)
        self.red.move(10, 10)

        self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)

        self.green = QtGui.QPushButton('Green', self)
        self.green.setCheckable(True)
        self.green.move(10, 60)

        self.connect(self.green, QtCore.SIGNAL('clicked()'), self.setGreen)

        self.blue = QtGui.QPushButton('Blue', self)
        self.blue.setCheckable(True)
        self.blue.move(10, 110)

        self.connect(self.blue, QtCore.SIGNAL('clicked()'), self.setBlue)

        self.square = QtGui.QWidget(self)
        self.square.setGeometry(150, 20, 100, 100)
        self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())

        QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))

    def setRed(self):
        if self.red.isChecked():
            self.color.setRed(255)
        else: self.color.setRed(0)

        self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())

    def setGreen(self):
        if self.green.isChecked():
            self.color.setGreen(255)
        else: self.color.setGreen(0)

        self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())

    def setBlue(self):
        if self.blue.isChecked():
            self.color.setBlue(255)
        else: self.color.setBlue(0)

        self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())


app = QtGui.QApplication(sys.argv)
tb = ToggleButton()
tb.show()
app.exec_()
}}}
В этом примере мы создали три ToggleButton. Мы также создали QWidget. Мы сделали его фон черным. ToggleButton включает и выключает красную, голубую и зеленную составляющую в значении фона QWidget'а. Соответственно, цвет фона меняется в зависимости от нажатия или отжатия кнопки.

{{{#!highlight python
self.color = QtGui.QColor(0, 0, 0)
}}}
Это строчка инициализирует начальное значение фона. Отсуствие красного, зеленого и голубого цвета приводит к тому, что мы видим черный квадрат. Теоритически говоря, черный цвет - это отсуствие всех цветов.

{{{#!highlight python
self.red = QtGui.QPushButton('Red', self)
self.red.setCheckable(True)
}}}
Тут мы создаем togglebutton и включаем режим переключение состояния кнопки с помощью метода setCheckable().

{{{#!highlight python
self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)
}}}
Связываем сигнал clicked() с нашим методом setRed.

{{{#!highlight python
QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))
}}}
Я установил в настройках стиля - стиль 'cleanlooks'. Дело в том, что по умолчанию используется стиль 'plastique', в котором из за ошибки реализации нельзя понять: нажата кнопка или отжата (в текущей версии это ошибка исправлена). И потом стиль 'cleanlooks', как мне кажется, более красивый.

{{{#!highlight python
if self.red.isChecked():
    self.color.setRed(255)
else: self.color.setRed(0)
}}}
Мы проверяем, нажата или отжата кнопка, и в зависимости от этого добавляем цвет или убавляем.

{{{#!highlight python
self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
}}}
Для изменения цвета фона, мы используем таблицу стилей.

{{attachment:togglebutton.jpg}}

'''Рисунок: ToggleButton'''

== QSlider, QLabel ==
QSlider - это виджет, который имеет ползунок для управления. Передвигая ползунок мы можем менять значение в большую или меньшую сторону. Таким образом мы можем задавать значения. В ряде случаев использование ползунка для определения значения является более удобным, чем установка значения вручную или с помощью списка значений. QLabel является просто текстовой или графической меткой.

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

{{{#!highlight python
#!/usr/bin/python

# slider-label.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class SliderLabel(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('SliderLabel')

        self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
        self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
        self.slider.setGeometry(30, 40, 100, 30)
        self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)


        self.label = QtGui.QLabel(self)
        self.label.setPixmap(QtGui.QPixmap('mute.png'))
        self.label.setGeometry(160, 40, 80, 30)


    def changeValue(self, value):
        pos = self.slider.value()

        if pos == 0:
            self.label.setPixmap(QtGui.QPixmap('mute.png'))
        elif pos > 0 and pos <= 30:
            self.label.setPixmap(QtGui.QPixmap('min.png'))
        elif pos > 30 and pos < 80:
            self.label.setPixmap(QtGui.QPixmap('med.png'))
        else:
            self.label.setPixmap(QtGui.QPixmap('max.png'))

app = QtGui.QApplication(sys.argv)
icon = SliderLabel()
icon.show()
app.exec_()
}}}
В этом примере мы моделируем работу регулятора громкости. Передвигая ползунок, мы меняем метку.

{{{#!highlight python
self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
}}}
Мы создали горизонтальный ползунок.

{{{#!highlight python
self.label = QtGui.QLabel(self)
self.label.setPixmap(QtGui.QPixmap('mute.png'))
}}}
Мы создали QLabel. И установили значение по умолчанию файл mute.png.

{{{#!highlight python
self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
}}}
Мы связали сигнал valueChanged(int) с нашей функцией self.changeValue.

{{{#!highlight python
pos = self.slider.value()
}}}
Мы получаем значение ползунка вызвав метод value(). В зависимости от значения меняем изображение метки.

{{attachment:sliderlabel.jpg}}

'''Рисунок: QSlider и QLabel'''

== QProgressBar ==
QProgressBar - это виджет, который используется, когда необходимо показать уровень выполнения задачи. Это анимированный виджет, позволяющий видеть пользователю, что задача выполняется. QProgressBar можно сделать как вертикальным, так и горизонтальным. При создании этого виджета программист должен задать минимальное и максимальное значение. По умолчанию, эти значения равны 0 и 99 соотвественно.

{{{#!highlight python
#!/usr/bin/python

# progressbar.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class ProgressBar(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('ProgressBar')

        self.pbar = QtGui.QProgressBar(self)
        self.pbar.setGeometry(30, 40, 200, 25)

        self.button = QtGui.QPushButton('Start', self)
        self.button.setFocusPolicy(QtCore.Qt.NoFocus)
        self.button.move(40, 80)

        self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onStart)

        self.timer = QtCore.QBasicTimer()
        self.step = 0;


    def timerEvent(self, event):
        if self.step >= 100:
            self.timer.stop()
            return
        self.step = self.step + 1
        self.pbar.setValue(self.step)

    def onStart(self):
        if self.timer.isActive():
            self.timer.stop()
            self.button.setText('Start')
        else:
            self.timer.start(100, self)
            self.button.setText('Stop')


app = QtGui.QApplication(sys.argv)
icon = ProgressBar()
icon.show()
app.exec_()
}}}
В этом примере мы создали горизонтальный уровень выполнения задачи и кнопку. Кнопка запускает и останавливает работу уровня выполнения задачи.

{{{#!highlight python
self.pbar = QtGui.QProgressBar(self)
}}}
Конструктор QProgressBar.

{{{#!highlight python
self.timer = QtCore.QBasicTimer()
}}}
Создали объект таймера, который будет влиять на уровень выполнения задачи.

{{{#!highlight python
self.timer.start(100, self)
}}}
Для запуска таймера необходимо вызвать метод start(). Этот метод получает два параметра: тайм-аут и объект, который будет получать событие о его срабатывании.

{{{#!highlight python
def timerEvent(self, event):
    if self.step >= 100:
        self.timer.stop()
        return
    self.step = self.step + 1
    self.pbar.setValue(self.step)
}}}
Каждый потомок QObject имеет обработчик события timerEvent. Для того что бы мы могли отреагировать на это событие, нам надо его переопределить в нашем объекте.

{{attachment:progressbar.jpg}}

'''Рисунок: QProgressBar'''

== QCalendarWidget ==
QCalendarWidget позволяет работать с календарем. Благодаря ему пользователь может выбрать дату интуитивно понятным способом.

{{{#!highlight python
#!/usr/bin/python

# calendar.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore


class Calendar(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Calendar')

        self.cal = QtGui.QCalendarWidget(self)
        self.cal.setGridVisible(True)
        self.cal.move(20, 20)
        self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)


        self.label = QtGui.QLabel(self)
        date = self.cal.selectedDate()
        self.label.setText(str(date.toPyDate()))
        self.label.move(130, 260)


    def showDate(self):
        date = self.cal.selectedDate()
        self.label.setText(str(date.toPyDate()))


app = QtGui.QApplication(sys.argv)
icon = Calendar()
icon.show()
app.exec_()
}}}
В этом примере мы создали календарь и метку. Выбирая дату, мы отображаем ее в метке.

{{{#!highlight python
self.cal = QtGui.QCalendarWidget(self)
}}}
Конструктор QCalendar.

{{{#!highlight python
self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)
}}}
Если пользователь выбрал дату, то посылается сигнал selectionChanged(). Мы соединяем его с нашим методом showDate.

{{{#!highlight python
def showDate(self):
     date = self.cal.selectedDate()
     self.label.setText(str(date.toPyDate()))
}}}
Мы получаем выбранную дату с помощью метода selectedDate(). Следующим нашим шагом будет преобразование ее в строку и затем передача в метод который определяет значение метки.

{{attachment:calendar.jpg}}

'''Рисунок: QCalendarWidget'''
Строка 88: Строка 1602:
В этой части руководства PyQt4 мы рассмотрим Drag & Drop.

При работе с графическим интерфейсом на компьютере Drag & Drop это технология которая позволяет выбрав виртуальный объект перетащить его на другое место или на другой виртуальный объект. В целом эта технология может использоваться в различных задачах где требуется создать связать абстракные объекты (Википедия).

Drag & Drop является одним из самых ярких возможностей графического интерфейса. Перетаскивание объектов дает возможность пользователям работать интуитивно со сложными вещами.

Как правило мы можем перетаскивать два вида объектов: данные или графические. Если мы перетаскиваем изображение из одного приложения в другое, то мы перетаскиваем двоичные данные. Если мы перетаскиваем к примеру вкладку Firefox - мы перетаскиваем графически компонент.

== Пример Drag & Drop ==
В этом примере мы используем виджеты QLineEdit и QPushButton. Мы будем перетаскивать текст из виджета строки редактирования на виджет кнопки.

{{{#!highlight python
#!/usr/bin/python

# dragdrop.py

import sys
from PyQt4 import QtGui

class Button(QtGui.QPushButton):
    def __init__(self, title, parent):
        QtGui.QPushButton.__init__(self, title, parent)
        self.setAcceptDrops(True)

    def dragEnterEvent(self, event):
        if event.mimeData().hasFormat('text/plain'):
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
            self.setText(event.mimeData().text())


class DragDrop(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)

        self.resize(280, 150)
        self.setWindowTitle('Simple Drag & Drop')

        edit = QtGui.QLineEdit('', self)
        edit.setDragEnabled(True)
        edit.move(30, 65)

        button = Button("Button", self)
        button.move(170, 65)


        screen = QtGui.QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width()-size.width())/2,
            (screen.height()-size.height())/2)

app = QtGui.QApplication(sys.argv)
icon = DragDrop()
icon.show()
app.exec_()


class Button(QtGui.QPushButton):
     def __init__(self, title, parent):
         QtGui.QPushButton.__init__(self, title, parent)
}}}
Для того чтобы мы могли менять текст на кнопке, мы должны переопределить некоторые методы. Для этого мы создаем свой собственный класс кнокпи, который является потомком виджета QPushButton. Мы включаем режим обработки событий для QPushButton.

{{{#!highlight python
def dragEnterEvent(self, event):
     if event.mimeData().hasFormat('text/plain'):
         event.accept()
     else:
         event.ignore()
}}}
Первый метод, который мы переопределяем - это dragEnterEvent(). Мы проверяем какого типа пришли данные, и если они текстовые, то принимаем их.

{{{#!highlight python
def dropEvent(self, event):
     self.setText(event.mimeData().text())
}}}
Затем мы переопределяем метод dropEvent(), в котором мы определяем, что мы будем делать после того как произойдет событие. Затем мы изменяем текст на кнопки.

{{{#!highlight python
edit = QtGui.QLineEdit('', self)
edit.setDragEnabled(True)
}}}
Нам необходимо включить поддержку для операций связанных с перетаскиванием. Для этого нам необходимо вызвать метод setDragEnabled().

{{attachment:simpledd.png}}

'''Рисунок: Пример Drag & Drop'''

== Drag & Drop с кнопкой ==
В следующем примере мы покажем как можно перетаскивать виджет кнопки.

{{{#!highlight python
#!/usr/bin/python

# dragbutton.py

import sys
from PyQt4 import QtGui
from PyQt4 import QtCore

class Button(QtGui.QPushButton):
    def __init__(self, title, parent):
        QtGui.QPushButton.__init__(self, title, parent)

    def mouseMoveEvent(self, event):

        if event.buttons() != QtCore.Qt.RightButton:
            return

        mimeData = QtCore.QMimeData()

        drag = QtGui.QDrag(self)
        drag.setMimeData(mimeData)
        drag.setHotSpot(event.pos() - self.rect().topLeft())

        dropAction = drag.start(QtCore.Qt.MoveAction)

        if dropAction == QtCore.Qt.MoveAction:
            self.close()

    def mousePressEvent(self, event):
        QtGui.QPushButton.mousePressEvent(self, event)
        if event.button() == QtCore.Qt.LeftButton:
            print 'press'



class DragButton(QtGui.QDialog):
    def __init__(self, parent=None):
        QtGui.QDialog.__init__(self, parent)

        self.resize(280, 150)
        self.setWindowTitle('Click or Move')
        self.setAcceptDrops(True)

        self.button = Button('Close', self)
        self.button.move(100, 65)


        screen = QtGui.QDesktopWidget().screenGeometry()
        size = self.geometry()
        self.move((screen.width()-size.width())/2,
            (screen.height()-size.height())/2)


    def dragEnterEvent(self, event):
         event.accept()

    def dropEvent(self, event):

        position = event.pos()
        button = Button('Close', self)
        button.move(position)
        button.show()

        event.setDropAction(QtCore.Qt.MoveAction)
        event.accept()


app = QtGui.QApplication(sys.argv)
db = DragButton()
db.show()
app.exec_()
}}}
В этом примере мы можемувидеть кнопку расположенную в окне. Если мы по ней кликнем левой клавишой мышки, то в консоли выведется надпись "press". Если мы нажмем правую кнопку мышки, то сможем переместить кнопку в любое место нашего окна.

{{{#!highlight python
class Button(QtGui.QPushButton):
    def __init__(self, title, parent):
        QtGui.QPushButton.__init__(self, title, parent)
}}}
Мы создали класс Button, который определили как наследника QPushButton. Мы также переопределяем два метода класса QPushButton: mouseMoveEvent() и mousePressEvent(). Метод mousePressEvent() который позволит нам осущесвить drag & drop.

{{{#!highlight python
if event.buttons() != QtCore.Qt.RightButton:
     return
}}}
Здесь мы определяем, что с помощью правой клавиши мы можем перетаскивать кнопку. Левая кнопка служит нам для нажатия на нее.

{{{#!highlight python
 mimeData = QtCore.QMimeData()

 drag = QtGui.QDrag(self)
 drag.setMimeData(mimeData)
 drag.setHotSpot(event.pos() - self.rect().topLeft())
}}}
Здесь мы создаем объет drag.

{{{#!highlight python
dropAction = drag.start(QtCore.Qt.MoveAction)

 if dropAction == QtCore.Qt.MoveAction:
     self.close()
}}}
Метод start() начинает операцию связанную с drag & drop. Если мы нажмем на правую кнопку, то мы удалим виджет кнопки. То есть технически мы уничтожаем виджет кнопки, и создаем ее в новом месте.

{{{#!highlight python
 def mousePressEvent(self, event):
     QtGui.QPushButton.mousePressEvent(self, event)
     if event.button() == QtCore.Qt.LeftButton:
         print 'press'
}}}
Мы печатает в консоли 'press' если пользователь нажимает левую кнопку. Обратите внимание, что мы вызываем родительский метод. В противном случае мы бы не увидели кнопку во время нажатия (???).

{{{#!highlight python
position = event.pos()
button = Button('Close', self)
button.move(position)
button.show()
}}}
В метод dropEvent() мы вставляем код, который выполняется после того как пользователь отпускает кнопку и заканчивает ее перемещение. В нашем случае мы создаем новую кнопку в месте указателя мыши.

{{{#!highlight python
 event.setDropAction(QtCore.Qt.MoveAction)
 event.accept()
}}}
Мы определяем тип действия для перемещения. В нашем случае это перемещение (тафтология drop и move).
Строка 90: Строка 1824:
Мы рисуем, когда мы хотим или видоизменить или расширить существуеющий виджет, а также в случае создание виджета с нуля. Что бы нарисовать что то, мы будем использовать API, которые есть в !PyQt4.

Рисование происходит внутри метода paintEvent(). Сами команды рисование располагаются между методов start() и end(), определенных в объекте QPainter.

== Рисуем текст ==
Для начала мы нарисуем несколько строчект текста в кодировке Unicode в окне нашего приложения.

{{{#!highlight python numbers=off
#!/usr/bin/python

# drawtext.py

import sys
from PyQt4 import QtGui, QtCore


class DrawText(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Draw Text')

        self.text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439: \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'



    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)
        paint.setPen(QtGui.QColor(168, 34, 3))
        paint.setFont(QtGui.QFont('Decorative', 10))
        paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
        paint.end()


app = QtGui.QApplication(sys.argv)
dt = DrawText()
dt.show()
app.exec_()
}}}
В этом примере мы нарисовали несколько строчек кириллицы. Разместив их по середине вертикали и горизонтали.

{{{#!highlight python numbers=off
def paintEvent(self, event):
}}}
Рисуем внутри события отрисовки.

{{{#!highlight python numbers=off
paint = QtGui.QPainter()
paint.begin(self)
...
paint.end()
}}}
Класс Qpainter содержит низкоуровневые методы рисования. Все командя рисования находятся между методами begin() и end().

{{{#!highlight python numbers=off
paint.setPen(QtGui.QColor(168, 34, 3))
paint.setFont(QtGui.QFont('Decorative', 10))
}}}
Здесь мы определяем шрифт и цвет пера, которым мы будем рисовать текст.

{{{#!highlight python numbers=off
paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
}}}
Метод drawText() нарисует нам текст в нашем окне.

{{attachment:drawtext.jpg}}

'''Рисунок: Рисуем текст'''

== Рисуем точки ==
Точки это простейшие объекты для рисования. Это небольшие пятна на экране.

{{{#!highlight python numbers=off
#!/usr/bin/python

# points.py

import sys, random
from PyQt4 import QtGui, QtCore


class Points(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Points')

    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)
        paint.setPen(QtCore.Qt.red)
        size = self.size()
        for i in range(1000):
            x = random.randint(1, size.width()-1)
            y = random.randint(1, size.height()-1)
            paint.drawPoint(x, y)
        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Points()
dt.show()
app.exec_()
}}}
В этом примере мы нарисовали в случайно выбранных местах нашего окна 1000 красных точек.

{{{#!highlight python numbers=off
paint.setPen(QtCore.Qt.red)
}}}
Здесь мы определили цвет пера которым будем рисовать - красный. Мы использовали предопределнную константу.

{{{#!highlight python numbers=off
size = self.size()
}}}
Каждый раз когда мы изменяем размер окна, точки заново перерисовываются. Мы получаем текущий размер окна с помощью метода size().

{{{#!highlight python numbers=off
paint.drawPoint(x, y)
}}}
Мы рисуем точки с помощью метода drawPoint().

{{attachment:points.jpg}}

'''Рисунок: Точки'''

== Цвета ==
Цвет - это объект представляющий собой комбинацию трех цветов: красного, зеленого и синего (RGB). Каждый цвет может представлять собой значение от 0 до 255. Мы можем определять конечный цвет по разному. Наиболее распространенным вариантом является использование десятиричных или шестнадцатиричных значений цветов RGB. Также мы можем использовать цветовую схему RGBA, значения которой состоит из красного, голубого, зеленого и так называемого альфа-канала. Альфа канал - это значение определяющие прозрачность цвета и принимающего значения от 0 (цвет невидим) до 255 (цвет непрозрачен).

{{{#!highlight python numbers=off
#!/usr/bin/python

# colors.py

import sys, random
from PyQt4 import QtGui, QtCore


class Colors(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 350, 280)
        self.setWindowTitle('Colors')

    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)

        color = QtGui.QColor(0, 0, 0)
        color.setNamedColor('#d4d4d4')
        paint.setPen(color)

        paint.setBrush(QtGui.QColor(255, 0, 0, 80))
        paint.drawRect(10, 15, 90, 60)

        paint.setBrush(QtGui.QColor(255, 0, 0, 160))
        paint.drawRect(130, 15, 90, 60)

        paint.setBrush(QtGui.QColor(255, 0, 0, 255))
        paint.drawRect(250, 15, 90, 60)

        paint.setBrush(QtGui.QColor(10, 163, 2, 55))
        paint.drawRect(10, 105, 90, 60)

        paint.setBrush(QtGui.QColor(160, 100, 0, 255))
        paint.drawRect(130, 105, 90, 60)

        paint.setBrush(QtGui.QColor(60, 100, 60, 255))
        paint.drawRect(250, 105, 90, 60)

        paint.setBrush(QtGui.QColor(50, 50, 50, 255))
        paint.drawRect(10, 195, 90, 60)

        paint.setBrush(QtGui.QColor(50, 150, 50, 255))
        paint.drawRect(130, 195, 90, 60)

        paint.setBrush(QtGui.QColor(223, 135, 19, 255))
        paint.drawRect(250, 195, 90, 60)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Colors()
dt.show()
app.exec_()
}}}
В этом примере мы нарисовали 9 разноцветных прямоугольников. В верхней строке нарисованы красные прямоугольники с разной степенью прозрачности.

{{{#!highlight python numbers=off
color = QtGui.QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')
}}}
Здесь мы определяем цвет в шестнадцатеричной нотации.

{{{#!highlight python numbers=off
paint.setBrush(QtGui.QColor(255, 0, 0, 80));
paint.drawRect(10, 15, 90, 60)
}}}
Здесь мы определяем цвет кисти которым будем рисовать, и затем рисуем прямоугольник. Кисть является элементарным графическим объектом, который используется для заполнения фона. Метод drawRect() принимает 4 параметра. Первые два - являются значение X и Y по оси, третий и четвертый определяет ширину и высоту. Метод рисует прямоугольник используя перо и текущую кисть.

{{attachment:colors.jpg}}

'''Рисунок: Цвета'''

== QPen ==
QPen является элементарным графическим объектом. Он используется для рисование линий, кривых и контуров прямоугольников, эллипсов, многоугольников и других форм.

{{{#!highlight python numbers=off
#!/usr/bin/python

# penstyles.py

import sys
from PyQt4 import QtGui, QtCore


class PenStyles(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 280, 270)
        self.setWindowTitle('penstyles')

    def paintEvent(self, event):
        paint = QtGui.QPainter()

        paint.begin(self)

        pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)

        paint.setPen(pen)
        paint.drawLine(20, 40, 250, 40)

        pen.setStyle(QtCore.Qt.DashLine)
        paint.setPen(pen)
        paint.drawLine(20, 80, 250, 80)

        pen.setStyle(QtCore.Qt.DashDotLine)
        paint.setPen(pen)
        paint.drawLine(20, 120, 250, 120)

        pen.setStyle(QtCore.Qt.DotLine)
        paint.setPen(pen)
        paint.drawLine(20, 160, 250, 160)

        pen.setStyle(QtCore.Qt.DashDotDotLine)
        paint.setPen(pen)
        paint.drawLine(20, 200, 250, 200)

        pen.setStyle(QtCore.Qt.CustomDashLine)
        pen.setDashPattern([1, 4, 5, 4])
        paint.setPen(pen)
        paint.drawLine(20, 240, 250, 240)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = PenStyles()
dt.show()
app.exec_()
}}}
В нашем примере мы нарисовали 6 линий. Эти 6 линий нарисованы в разном стиле. Мы можем увидеть 5 прерывистых линий. Также мы можем создавать собственные стили. Последняя линия - нарисована в стиле созданным нами.

{{{#!highlight python numbers=off
pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)
}}}
Мы создали QPen объект. Цвет черный. Толщину устанавливаем в два пикселя, так что мы увидим различия между различными стильями перьев. !QtCore.Qt.!SolidLine является одним из предопределенных стилей перьев.

{{{#!highlight python numbers=off
pen.setStyle(QtCore.Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
paint.setPen(pen)
}}}
В этой части кода мы определяем собственный стиль пера. Мы устанавливаем стиль пера !QtCore.Qt.!CustomDashLine и затем вызываем метод setDashPattern(). Перечень чисел определяет стиль. В этот метод мы должны передать четное число значений. Нечетные значение определяеют линии, четный пропуски. В нашем случае мы опредеяем линия длиной 1 пиксель, 4 пикселя пропуск, 5 пикселей линия, 4 пикселя пропуск.

{{attachment:penstyles.jpg}}

'''Рисунок: Стили перьев.'''

== QBrush ==
QBrash является элементарным графическим объектом. Он используется для закрашивания фона графических форм, таких как прямоугольники, эллипсы и многоугольники. Кисть может быть различных типов. Предопределенные кисти различаются градиентами и текстурами.

{{{#!highlight python numbers=off
#!/usr/bin/python

# brushes.py

import sys
from PyQt4 import QtGui, QtCore


class Brushes(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 355, 280)
        self.setWindowTitle('Brushes')

    def paintEvent(self, event):
        paint = QtGui.QPainter()

        paint.begin(self)

        brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense1Pattern)
        paint.setBrush(brush)
        paint.drawRect(130, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense2Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense3Pattern)
        paint.setBrush(brush)
        paint.drawRect(10, 105, 90, 60)

        brush.setStyle(QtCore.Qt.DiagCrossPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense5Pattern)
        paint.setBrush(brush)
        paint.drawRect(130, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense6Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense7Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 105, 90, 60)

        brush.setStyle(QtCore.Qt.HorPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 195, 90, 60)

        brush.setStyle(QtCore.Qt.VerPattern)
        paint.setBrush(brush)
        paint.drawRect(130, 195, 90, 60)

        brush.setStyle(QtCore.Qt.BDiagPattern)
        paint.setBrush(brush)
        paint.drawRect(250, 195, 90, 60)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Brushes()
dt.show()
app.exec_()
}}}
В нашем примере мы нарисовали 9 различных прямоугольников.

{{{#!highlight python numbers=off
brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
paint.setBrush(brush)
paint.drawRect(10, 15, 90, 60)
}}}
Мы определили объект кисти. И затем нарисовали прямоугольник вызвав метод drawRect().

{{attachment:brushes.jpg}}

'''Рисунок: Кисти.'''
Строка 92: Строка 2196:
Строка 94: Строка 2197:

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

Введение в среду PyQt4

Об этом руководстве

Это введение в программирование с использованием PyQt4. Это руководство предназначено для того что Вы представляли себе что такое PyQt4. Весь код приведенный в этом руководстве был протестирован под управлением ОС Linux.

О PyQt4

PyQt4 представляет собой инструмент для создания GUI приложений. PyQt представляет собой сочетание языка программирования Python и превосходной библиотеки Qt. Qt библиотека является одной из самых лучших библиотек в мире, если не самой лучшей. Официальный сайт проекта PyQt находится по адресу www.riverbankcomputing.co.uk. Он был разработан Фил Томпсоном.

PyQt реализована в виде набора модулей Python. Он насчитывает более 300 классов, и почти 6000 функций и методов. Он представляет собой платформо-независимый инструмент. То есть он работает на всех основных операционных систем (Unix, Windows и Mac). PyQt распространяется под двумя видами лицензией. Разработчики могут выбирать между GPL и коммерческой лицензией. Ранее, GPL версия была доступна только для Unix. Но начиная с 4-ой версии, GPL лицензии доступна на всех поддерживаемых платформах.

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

modules.jpg

Рисунок 1: Модули PyQt4

Модуль QtCore содержит функционал не связанный с GUI. Этот модуль используется для работы со временем, файлами и каталогами, различными типами данных, потоками, URL, MIME типами, процессами и тредами. Модуль QtGui содержит компоненты отвечающие за графические компоненты а также взаимодействующие с ними классы. К ним относятся, например кнопки, окна, статус бары, панели, слайдеры, изображения, цвета, шрифты и т.д. Модуль QtNetwork содержит классы для сетевого программирования. Благодаря этому вы можете создать свои TCP/IP и UDP клиентские и серверные приложения. Благодаря этому классу ваши программы будут более простые и небольшие по размеру. Модуль QtXml содержатся классы для работы с xml файлами. Этот модуль обеспечивает связь как с SAX так и DOM API. Модуль QtSvg модуль содержит компоненты для работы с SVG файлами. Масштабируемая векторная графика (SVG) является языком для работы с двумерной графикой и графическими приложениями XML. В QtOpenGL модуль используется для рендеринга 3D и 2D графики с использованием библиотеки OpenGL. Этот модуль дает возможность бесшовной интеграции с модулем QtGui и библиотекой OpenGL. В QtSql модуль предоставляет классы для работы с базами данных.

Python

pythonlogo (1).png

Python является прекрасным скриптовым языком. Автором этого языка является Гвидо ван Россум. Первая версия этого языка программирования увидела свет в 1991 году. Python является высокроуровневым платформо-независимым и интерпретируемым языком программирования. Одним из свойств этого языка программирования является отсутствие в нем блоков выделенных как либо (например фигурными скобками). Для выделения блоков используются отступы. Текущая версия языка программирования является 2.5 (не совсем свежее руководство :)), которая была выпущена в сентябре 2006 года. Сегодня в разработке этого языка программирования участвует много людей живущих в разных странах.

Сообщество программистов TIOBE публикует рейтинги популярности языков программирования. На первом месте находится Java. C++ немного уступает Java. Но стоит заметь что ближайшие десятилетие С++ будет по-прежнему востребован на рынке программистов, и альтернатив ему нету. Также из этого рейтигна мы можем узнать о популярности языков программирования в том или ином секторе решаемых задач. Java используется в промышленных решениях, где требуется платформо-независимость. С незаменим при написании системных программ (драйвера устройств, ОС, небольшие утилиты). PHP - небольшие веб-сайты. JavaScript используется при написании клиентских веб-приложений.

Position

Language

Ratings

1

Java

21.7%

2

C

14.9%

3

Visual Basic

10.7%

4

PHP

10.2%

5

C++

9.9%

6

Perl

5.4%

7

C#

3.4%

8

Python

3.0%

9

JavaScript

2.7%

10

Ruby

2.0%

Python находится на 8 месте в этом рейтинге (в настоящее время Python находится на 6 месте потеснив С# и Perl). Также в этом рейтинге находятся ближайшие конкуренты Python: Ruby и Perl.

Инструментарий Python

Для создания GUI приложений на Python программисты могут выбирать между тремя замечательными библиотеками: PyGTK, wxPython и PyQt. Какую библиотеку выберете Вы, зависит от Вас. Существует также еще библиотека для создания GUI приложений: TkInter. Буду краток - не использовать.

Первые программы

В этой части руководства PyQt4 мы познакомимся с некоторыми основными функциями. Объяснение будет детальным как для ребенка. Помните, нет глупых людей. Есть ленивые или не достаточно упрямые люди.

Простая программа

Код нашего примера очень прост. Он показывает только небольшое окно. И не смотря на его простоту, мы можем с ним сделать много действий. Мы можем изменить его размер. Мы можем минимизировать его. Мы можем распахнуть его во весь экран. При создании программы с нуля - это потребовало бы большой объем кода. Но в нашем случае - кто то уже заложил всю эту функциональность. То есть мы создали приложение на основе чьего то кода. Так как данные функции являются стандартными, то мы просто их вызываем в своем приложении. PyQt является высокоуровневым инструментом (библиотекой). Более низкоуровневые библиотеки требуют для создания аналогичного приложения несколько десятков строчек кода.

   1 #!/usr/bin/python
   2 
   3 # simple.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 app = QtGui.QApplication(sys.argv)
   9 
  10 widget = QtGui.QWidget()
  11 widget.resize(250, 150)
  12 widget.setWindowTitle('simple')
  13 widget.show()
  14 
  15 sys.exit(app.exec_())

   1 import sys
   2 from PyQt4 import QtGui

Сначала мы подключаем необходимые нам библиотеки. Основным графическим интерфейсом для работы с виджетами является модуль QtGui.

   1 app = QtGui.QApplication(sys.argv)

Каждое PyQt4 приложение должно создать основной объект приложения. Объект приложения находится в модуле QtGui. При его создании мы передаем в него параметры командной строки sys.argv. Таким образом наш скрипт может запускаться из командной строки. Таким образом мы можем контролировать его работу.

   1 widget = QtGui.QWidget()

QWidget является базовым классом для всех виджетов пользовательского интерфейса PyQt4. Мы используем стандартный конструктор для создания объекта QWidget. По умолчанию виджет не имеет родителя. Такой виджет называется окном.

   1 widget.resize(250, 150)

Метод resize() изменяет размер виджета. В нашем коде 250 пикселей ширина и 150 высота.

   1 widget.setWindowTitle('simple')

Эта строчка определяет название нашего окна. Название отображается в заголовке.

   1 widget.show()

Метод show() отображает наше окно на экране.

   1 sys.exit(app.exec_())

И наконец мы запускаем основной цикл нашей программы. Обработка сообщений начинается с этой строчки. В основном цикле происходит получение и обработка событий окна системы и последующая передача их виджетам. При окончании работы главного цикла, наша программа прекращает свою работу. С помощью sys.exit() мы обеспечиваем чистый выход. То есть мы передадим в ОС информацию что наше приложение закончило свою работу.

simple.jpg

Рисонок: Пример

Иконка приложения

Иконка приложения это картинка, которая как правило отображается в верхнем левом углу заголовка приложения. В этом примере мы покажем как привязать иконку в приложение с помощью PyQt4. Также мы рассмотрим несколько новых методов.

   1 #!/usr/bin/python
   2 
   3 # icon.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class Icon(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setGeometry(300, 300, 250, 150)
  14         self.setWindowTitle('Icon')
  15         self.setWindowIcon(QtGui.QIcon('icons/web.png'))
  16 
  17 
  18 app = QtGui.QApplication(sys.argv)
  19 icon = Icon()
  20 icon.show()
  21 sys.exit(app.exec_())

Предыдущий пример был написан используя структурный стиль. Язык программирования Python поддерживает как структурный стиль, так и объективно-ориентированный стиль программирования. При использовании PyQt4 лучше использовать объектно-ориентированный стиль.

   1 class Icon(QtGui.QWidget):
   2      def __init__(self, parent=None):
   3          QtGui.QWidget.__init__(self, parent)

Объектно-ориентированное программирование подразумевает три важные вещи: класс, данные и методы. В этих строчках кода мы создаем класс с именем Icon. Этот класс мы объявляем производным от класса QtGui.QWidget. Теперь мы вызываем конструктор класса-родителя и передаем туда в качестве параметров указатель на наш класс и указатель на родителя класса (???).

   1 self.setGeometry(300, 300, 250, 150)
   2 self.setWindowTitle('Icon')
   3 self.setWindowIcon(QtGui.QIcon('icons/web.png'))

Теперь мы рассмотрим три метода которые мы унаследовали от QtGui.QWidget. Метод QWidget.setGeometry(300, 300, 250, 150) задает местоположение нашего окна и его размеры. Первые два значения задают X и Y координаты его расположение, и последние два значения ширину и высоту. Второй метод мы рассматривали в предыдущем примере. Третий метод мы используем для определение иконки нашего приложения. В метод QIcon мы передаем путь где находится наша картинка.

icon1.jpg

Рисонок: иконка

Посмотрим на контекстную подсказку

Мы можем отобразить контекстную подсказку в любом из наших виджетов.

   1 #!/usr/bin/python
   2 
   3 # tooltip.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class Tooltip(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 250, 150)
  15         self.setWindowTitle('Tooltip')
  16 
  17         self.setToolTip('This is a <b>QWidget</b> widget')
  18         QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))
  19 
  20 
  21 app = QtGui.QApplication(sys.argv)
  22 tooltip = Tooltip()
  23 tooltip.show()
  24 app.exec_()

В этом примере мы рассмотрим контекстную подсказку на примере виджета на базе класса QWidget.

   1 self.setToolTip('This is a <b>QWidget</b> widget')

Сначала создадим контекстную подсказку, для этого вызовем метод setTooltip(). Для форматирования текста мы можем использовать теги.

   1 QtGui.QToolTip.setFont(QtGui.QFont('OldEnglish', 10))

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

tooltip (1).jpg

Рисонок: контекстная подсказка

Закрытие окна

Самый простой способ закрыть наше приложение, это щелкнуть по крестику в заголовке нашего окна. В этом примере мы рассмотрим как закрыть окно программным путем. Мы вкратце рассмотрим сигналы и слоты. Ниже приведен вызов конструктора для создания кнопки.

   1 QPushButton(string text, QWidget parent = None)

Параметр text передает в конструктор надпись на кнопки. Параметр parent передает указатель на родительский класс. В нашем случае это QWidget.

   1 #!/usr/bin/python
   2 
   3 # quitbutton.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 
   9 class QuitButton(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setGeometry(300, 300, 250, 150)
  14         self.setWindowTitle('Icon')
  15 
  16         quit = QtGui.QPushButton('Close', self)
  17         quit.setGeometry(10, 10, 60, 35)
  18 
  19         self.connect(quit, QtCore.SIGNAL('clicked()'),
  20             QtGui.qApp, QtCore.SLOT('quit()'))
  21 
  22 
  23 app = QtGui.QApplication(sys.argv)
  24 qb = QuitButton()
  25 qb.show()
  26 sys.exit(app.exec_())

   1 quit = QtGui.QPushButton('Close', self)
   2 quit.setGeometry(10, 10, 60, 35)

Мы создаем кнопку и определяем ее местоположение и размер относительно нашего виджета.

   1 self.connect(quit, QtCore.SIGNAL('clicked()'), QtGui.qApp, QtCore.SLOT('quit()'))

Система обработки событий в PyQt4 строится с использованием механизма слотов и сигналов. Если мы нажимаем на кнопку, то посылается сигнал clicked(). Любой слой PyQt4 может принять этот сигнал и обработаь его. Метод QtCore.QObject.connect() отвечает за связь между слотами и сигналами. В нашем случае мы связываем сигнал clicked() со слотом приложения и его функцией quit(). То есть в такой связи участвует два объекта: отправитель и получатель. Отправитель - кнопка, получатель - объект приложения.

quitbutton (1).jpg

Рисунок: кнопка выхода

Окно с сообщением

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

   1 #!/usr/bin/python
   2 
   3 # messagebox.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class MessageBox(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setGeometry(300, 300, 250, 150)
  14         self.setWindowTitle('message box')
  15 
  16 
  17     def closeEvent(self, event):
  18         reply = QtGui.QMessageBox.question(self, 'Message',
  19             "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)
  20 
  21         if reply == QtGui.QMessageBox.Yes:
  22             event.accept()
  23         else:
  24             event.ignore()
  25 
  26 app = QtGui.QApplication(sys.argv)
  27 qb = MessageBox()
  28 qb.show()
  29 sys.exit(app.exec_())

Если Вы попробуете закрыть это приложение, то будет сгенерированно событие QCloseEvent. Чтобы изменить поведение нашего виджета, необходимо переопределить обработчик события closeEvent().

   1 reply = QtGui.QMessageBox.question(self, 'Message',
   2      "Are you sure to quit?", QtGui.QMessageBox.Yes, QtGui.QMessageBox.No)

В этих строчках мы показываем окно с сообщением с просьбой подтвердить выход и с двумя кнопками: Да и Нет. Возвращаемое значение нажатой кнопки хранится в переменной.

   1 if reply == QtGui.QMessageBox.Yes:
   2     event.accept()
   3 else:
   4    event.ignore()

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

messagebox (1).jpg

Рисунок: окно с сообщением.

Расположение окна в центре дисплея

Следующий скрипт который мы рассмотрим, ответит нам на вопрос, как расположить наше окно в центре дисплея.

   1 #!/usr/bin/python
   2 
   3 # center.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class Center(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('center')
  14         self.resize(250, 150)
  15         self.center()
  16 
  17     def center(self):
  18         screen = QtGui.QDesktopWidget().screenGeometry()
  19         size =  self.geometry()
  20         self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)
  21 
  22 
  23 app = QtGui.QApplication(sys.argv)
  24 qb = Center()
  25 qb.show()
  26 sys.exit(app.exec_())

   1 self.resize(250, 150)

Определяем размер нашего виджета: 250 пикселей ширина и 150 пикселей высота.

   1 screen = QtGui.QDesktopWidget().screenGeometry()

Получаем разрешение нашего дисплея.

   1 size =  self.geometry()

Получаем размер нашего виджета.

   1 self.move((screen.width()-size.width())/2, (screen.height()-size.height())/2)

Теперь перемещаем наше окно в центр экрана.

Меню и панели

Главное окно

Класс QMainWindow служит для создания главного окна приложения. Он позволяет создавать скелет классического приложения с меню и панелями.

Панель состояния

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

   1 !/usr/bin/python
   2 
   3 # statusbar.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 class MainWindow(QtGui.QMainWindow):
   9     def __init__(self):
  10         QtGui.QMainWindow.__init__(self)
  11 
  12         self.resize(250, 150)
  13         self.setWindowTitle('statusbar')
  14 
  15         self.statusBar().showMessage('Ready')
  16 
  17 
  18 app = QtGui.QApplication(sys.argv)
  19 main = MainWindow()
  20 main.show()
  21 sys.exit(app.exec_())

   1 self.statusBar().showMessage('Ready')

Для создания панели состояния мы вызываем метод statusBar() класса QApplication.

Меню

Меню является основным инструментом для работы c графическим приложением. Меню представляет стобой набор команд, расположенных в различных меню. Если при работе с консолью Вы должны запомнить все команды с их параметрами, то в меню Вы группируете команды по их назначению. Существуют стандарты группировки команд в меню, благодаря которым пользователи сокращают свое время при изучении новых приложений.

   1 #!/usr/bin/python
   2 
   3 # menubar.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 class MainWindow(QtGui.QMainWindow):
   9     def __init__(self):
  10         QtGui.QMainWindow.__init__(self)
  11 
  12         self.resize(250, 150)
  13         self.setWindowTitle('menubar')
  14 
  15         exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
  16         exit.setShortcut('Ctrl+Q')
  17         exit.setStatusTip('Exit application')
  18         self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
  19 
  20         self.statusBar()
  21 
  22         menubar = self.menuBar()
  23         file = menubar.addMenu('&File')
  24         file.addAction(exit)
  25 
  26 app = QtGui.QApplication(sys.argv)
  27 main = MainWindow()
  28 main.show()
  29 sys.exit(app.exec_())

   1 menubar = self.menuBar()
   2  file = menubar.addMenu('&File')
   3  file.addAction(exit)

Сначала для создание меню мы используем метод menuBar(), входящий в класс QMainWindow. Для добавление в меню разделов, используем метод addMenu(). Ну и в завершении мы привязываем к разделу действие.

Панель инструментов

Меню группирует все команды приложения. Панель инструментов обеспечивает быстрый доступ к основным командам.

   1 #!/usr/bin/python
   2 
   3 # toolbar.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 class MainWindow(QtGui.QMainWindow):
   9     def __init__(self):
  10         QtGui.QMainWindow.__init__(self)
  11 
  12         self.resize(250, 150)
  13         self.setWindowTitle('toolbar')
  14 
  15         self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
  16         self.exit.setShortcut('Ctrl+Q')
  17         self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
  18 
  19         self.toolbar = self.addToolBar('Exit')
  20         self.toolbar.addAction(self.exit)
  21 
  22 
  23 app = QtGui.QApplication(sys.argv)
  24 main = MainWindow()
  25 main.show()
  26 sys.exit(app.exec_())

   1 self.exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
   2 self.exit.setShortcut('Ctrl+Q')

GUI приложения могут управляться с помощью команд. Вызов команд может осуществляться из меню, контекстного меню, панели инструментов или с помощью сочитания клавиш. PyQt упрощает разработку приложений, предлагая использовать "действия". Объект "действия" может иметь текст, значок, комбинацию клавиш, текст статуса, "What is this" текст и подсказку. В нашем примере мы определяем значок действия, текст и комбинацию клавиш.

   1 self.connect(self.exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))

Прежде чем подключить наше действие к панели управления, мы связываем сигнал triggered() со слотом exit().

   1 self.toolbar = self.addToolBar('Exit')
   2 self.toolbar.addAction(self.exit)

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

toolbar.jpg

Рисунок: Панель управления

Объединим все

В этой главе мы рассмотрели создание меню, панели инструментов и панели статуса. Теперь мы объединим все виджеты и добавим виджет текстового редактора.

   1 #!/usr/bin/python
   2 
   3 # mainwindow.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 class MainWindow(QtGui.QMainWindow):
   9     def __init__(self):
  10         QtGui.QMainWindow.__init__(self)
  11 
  12         self.resize(350, 250)
  13         self.setWindowTitle('mainwindow')
  14 
  15         textEdit = QtGui.QTextEdit()
  16         self.setCentralWidget(textEdit)
  17 
  18         exit = QtGui.QAction(QtGui.QIcon('icons/exit.png'), 'Exit', self)
  19         exit.setShortcut('Ctrl+Q')
  20         exit.setStatusTip('Exit application')
  21         self.connect(exit, QtCore.SIGNAL('triggered()'), QtCore.SLOT('close()'))
  22 
  23         self.statusBar()
  24 
  25         menubar = self.menuBar()
  26         file = menubar.addMenu('&File')
  27         file.addAction(exit)
  28 
  29         toolbar = self.addToolBar('Exit')
  30         toolbar.addAction(exit)
  31 
  32 
  33 app = QtGui.QApplication(sys.argv)
  34 main = MainWindow()
  35 main.show()
  36 sys.exit(app.exec_())

   1 textEdit = QtGui.QTextEdit()
   2 self.setCentralWidget(textEdit)

Тут мы создаем вджет для редактирования текста. Мы размещаем этот виджет в центре виджета QMainWindow. Центральный виджет заполняет все свободное пространство.

mainwindow.jpg

Рисунок: Панель управления

Менеджер компоновки

Важным понятием программирования является менеджер компоновки. Менеджер компоновки отвечает за расположение виджетов в окне. Расположением виджетов может осуществляться двумя способами. Либо используя абсолютное позиционирование, либо используя компоновочные классы.

Абсолютное позиционирование

При абсолютном позиционировании вы определяете размер и положение каждого виджета в пикселях. При использовании абсолютного позиционирования вы должны учитывать три фактора:

  • размер и местоположение виджетов не изменяется при изменении размеров окна
  • вид программы может зависить от ОС на которой она будет зарущена
  • Изменение шрифта в программе может испортить ее внешний вид
  • Если Вы решите изменить внешний вид виджета, то Вам придется изменить местопложение всех виджетов, что может занять у вас много времени

   1 #!/usr/bin/python
   2 
   3 # absolute.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class Absolute(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('Communication')
  14 
  15         label = QtGui.QLabel('Couldn\'t', self)
  16         label.move(15, 10)
  17 
  18         label = QtGui.QLabel('care', self)
  19         label.move(35, 40)
  20 
  21         label = QtGui.QLabel('less', self)
  22         label.move(55, 65)
  23 
  24         label = QtGui.QLabel('And', self)
  25         label.move(115, 65)
  26 
  27         label = QtGui.QLabel('then', self)
  28         label.move(135, 45)
  29 
  30         label = QtGui.QLabel('you', self)
  31         label.move(115, 25)
  32 
  33         label = QtGui.QLabel('kissed', self)
  34         label.move(145, 10)
  35 
  36         label = QtGui.QLabel('me', self)
  37         label.move(215, 10)
  38 
  39         self.resize(250, 150)
  40 
  41 app = QtGui.QApplication(sys.argv)
  42 qb = Absolute()
  43 qb.show()
  44 sys.exit(app.exec_())

Мы просто вызываем метод move() для указания расположения виджетов. Мы указываем координаты X и Y расположения. Начало системы координат находится в левом верхнем углу. То есть координата X растет слева направо, а координата Y сверху вниз.

absolute.jpg

Рисунок: Абсолютное позиционирование

Компоновка макета

Компоновка макета с помощью классов является более гибкой и удобной. Это более предпочтительный способ размещения виджетов на экране. Основными классами для такой компоновки являются QHBoxLayout и QVBoxLayout. Они размещают виджеты по горизонтали и вертикали.

Допустим мы хотим разместить две кнопки в правом нижнем углу. Чтобы это сделать, мы должны использовать вертикальный и горизонтальный макет. Для создания необходимого пустого пространства мы будем использовать "коэффициент притяжения".

   1 #!/usr/bin/python
   2 
   3 # boxlayout.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class BoxLayout(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('box layout')
  14 
  15         ok = QtGui.QPushButton("OK")
  16         cancel = QtGui.QPushButton("Cancel")
  17 
  18         hbox = QtGui.QHBoxLayout()
  19         hbox.addStretch(1)
  20         hbox.addWidget(ok)
  21         hbox.addWidget(cancel)
  22 
  23         vbox = QtGui.QVBoxLayout()
  24         vbox.addStretch(1)
  25         vbox.addLayout(hbox)
  26 
  27         self.setLayout(vbox)
  28 
  29         self.resize(300, 150)
  30 
  31 app = QtGui.QApplication(sys.argv)
  32 qb = BoxLayout()
  33 qb.show()
  34 sys.exit(app.exec_())

   1 ok = QtGui.QPushButton("OK")
   2 cancel = QtGui.QPushButton("Cancel")

Просто создаем две кнопки.

   1 hbox = QtGui.QHBoxLayout()
   2 hbox.addStretch(1)
   3 hbox.addWidget(ok)
   4 hbox.addWidget(cancel)

Создаем горизонтальный макет компоновки. Затем добавляем коэффициент притяжения и 2 кнопки.

   1 vbox = QtGui.QVBoxLayout()
   2 vbox.addStretch(1)
   3 vbox.addLayout(hbox)

Что бы разместить виджеты в нужном формате, мы размещаем вертикальный макет в горизонтальном.

   1 self.setLayout(vbox)

И в заключении мы определяем основную компоновку.

boxlayout.jpg

Рисунок: Компоновка макета

QGridLayout

Наиболее универсальным менеджером компоновки является менеджер размещающей виджеты в сетки. Эта компоновка подразумевает что вы делите пространство на столбцы и строчки. Для создания такой компоновки мы используем класс QGridLayout.

   1 #!/usr/bin/python
   2 
   3 # gridlayout.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class GridLayout(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('grid layout')
  14 
  15         names = ['Cls', 'Bck', '', 'Close', '7', '8', '9', '/',
  16             '4', '5', '6', '*', '1', '2', '3', '-',
  17             '0', '.', '=', '+']
  18 
  19         grid = QtGui.QGridLayout()
  20 
  21         j = 0
  22         pos = [(0, 0), (0, 1), (0, 2), (0, 3),
  23                 (1, 0), (1, 1), (1, 2), (1, 3),
  24                 (2, 0), (2, 1), (2, 2), (2, 3),
  25                 (3, 0), (3, 1), (3, 2), (3, 3 ),
  26                 (4, 0), (4, 1), (4, 2), (4, 3)]
  27 
  28         for i in names:
  29             button = QtGui.QPushButton(i)
  30             if j == 2:
  31                 grid.addWidget(QtGui.QLabel(''), 0, 2)
  32             else: grid.addWidget(button, pos[j][0], pos[j][1])
  33             j = j + 1
  34 
  35         self.setLayout(grid)
  36 
  37 
  38 
  39 app = QtGui.QApplication(sys.argv)
  40 qb = GridLayout()
  41 qb.show()
  42 sys.exit(app.exec_())

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

   1 grid = QtGui.QGridLayout()

Создаем компоновочную сетку.

   1 if j == 2:
   2     grid.addWidget(QtGui.QLabel(''), 0, 2)
   3 else: grid.addWidget(button, pos[j][0], pos[j][1])

Добавляем виджеты в сетку с помощью метода addWidget(). Аргументами, передаваемыми в этот метод, являются виджет, строчка и столбец.

gridlayout.jpg

Рисунок: сеточная компоновка

Виджеты могут занимать несколько столбцов и строк. Следующей пример иллюстрирует это.

   1 #!/usr/bin/python
   2 
   3 # gridlayout2.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 
   9 class GridLayout2(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('grid layout')
  14 
  15         title = QtGui.QLabel('Title')
  16         author = QtGui.QLabel('Author')
  17         review = QtGui.QLabel('Review')
  18 
  19         titleEdit = QtGui.QLineEdit()
  20         authorEdit = QtGui.QLineEdit()
  21         reviewEdit = QtGui.QTextEdit()
  22 
  23         grid = QtGui.QGridLayout()
  24         grid.setSpacing(10)
  25 
  26         grid.addWidget(title, 1, 0)
  27         grid.addWidget(titleEdit, 1, 1)
  28 
  29         grid.addWidget(author, 2, 0)
  30         grid.addWidget(authorEdit, 2, 1)
  31 
  32         grid.addWidget(review, 3, 0)
  33         grid.addWidget(reviewEdit, 3, 1, 5, 1)
  34 
  35 
  36         self.setLayout(grid)
  37         self.resize(350, 300)
  38 
  39 
  40 app = QtGui.QApplication(sys.argv)
  41 qb = GridLayout2()
  42 qb.show()
  43 sys.exit(app.exec_())

   1 grid = QtGui.QGridLayout()
   2 grid.setSpacing(10)

Создаем клетчатаю компоновку и устанавливаем расстояние между ячейками.

   1 grid.addWidget(reviewEdit, 3, 1, 5, 1)

При добавление виджета к ячейке мы можем указать сколько столбцов и строчек он будет занимать. В нашем случае мы выделили reviewEdit 1 столбец и 5 строчек.

gridlayout2.jpg

Рисунок: сеточная компоновка (2)

События и сигналы

В этой части руководства мы рассмотрим сигналы и события происходящие в приложении.

События

События являютя важным элементом в любой графической программе. События могут генерироваться программой или пользователем. Когда мы вызываем в нашей программе метод exec_(), то запускаем рутинный механизм. Основной цикл выбирает события и отправляет их на объекты. Trolltech внедрила уникальную технологию слотов и сигналов.

Сигналы и слоты

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

   1 #!/usr/bin/python
   2 
   3 # sigslot.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 
   9 class SigSlot(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('signal & slot')
  14 
  15         lcd = QtGui.QLCDNumber(self)
  16         slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
  17 
  18         vbox = QtGui.QVBoxLayout()
  19         vbox.addWidget(lcd)
  20         vbox.addWidget(slider)
  21 
  22         self.setLayout(vbox)
  23         self.connect(slider,  QtCore.SIGNAL('valueChanged(int)'), lcd,
  24                 QtCore.SLOT('display(int)') )
  25 
  26         self.resize(250, 150)
  27 
  28 
  29 app = QtGui.QApplication(sys.argv)
  30 qb = SigSlot()
  31 qb.show()
  32 sys.exit(app.exec_())

В этом примере мы показываем ползунок и число в стиле ЖК-часов. Мы можем изменять число с помощью передвижения ползунка.

   1 self.connect(slider,  QtCore.SIGNAL('valueChanged(int)'), lcd, QtCore.SLOT('display(int)') )

В этой строчке мы соединяем сигнал ползунка valueChanged() со слотом числа display().

В методе connect() используется 4 параметра. Отправитель - объект, посылающий сигнал. Сигнал - то что посылает объект. Приемник - объект получающая сигнал. Слот - метод, реагирующий на сигнал.

signalslot.jpg

Рисунок: сигнал и слоты

Переопределение обработчика событий

Обработка событий в PyQt4 в основном осуществляется с помощью переопределения обработчика событий.

   1 #!/usr/bin/python
   2 
   3 # escape.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 class Escape(QtGui.QWidget):
   9     def __init__(self, parent=None):
  10         QtGui.QWidget.__init__(self, parent)
  11 
  12         self.setWindowTitle('escape')
  13         self.resize(250, 150)
  14         self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
  15 
  16 
  17     def keyPressEvent(self, event):
  18         if event.key() == QtCore.Qt.Key_Escape:
  19             self.close()
  20 
  21 app = QtGui.QApplication(sys.argv)
  22 qb = Escape()
  23 qb.show()
  24 sys.exit(app.exec_())

В этом примере мы переопределили обработчик событий keyPressEvent().

   1 def keyPressEvent(self, event):
   2      if event.key() == QtCore.Qt.Key_Escape:
   3          self.close()

При нажатии на клавишу Esc наше приложение закроется.

Генерирование сигнала

Рассмотрим объекты, которые образованы от QtCore. QObject может генерировать сигналы. Например если вы нажмете на кнопку, будет сгенерирован сигнал clicked(). В следующем примере мы посмотрим как можно послать сигнал.

   1 #!/usr/bin/python
   2 
   3 # emit.py
   4 
   5 import sys
   6 from PyQt4 import QtGui, QtCore
   7 
   8 
   9 class Emit(QtGui.QWidget):
  10     def __init__(self, parent=None):
  11         QtGui.QWidget.__init__(self, parent)
  12 
  13         self.setWindowTitle('emit')
  14         self.resize(250, 150)
  15         self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )
  16 
  17     def mousePressEvent(self, event):
  18         self.emit(QtCore.SIGNAL('closeEmitApp()'))
  19 
  20 app = QtGui.QApplication(sys.argv)
  21 qb = Emit()
  22 qb.show()
  23 sys.exit(app.exec_())

Мы создали и послали сигнал closeEmitApp(). Этот сигнал вызывается, когда мы кликаем мышкой.

   1 def mousePressEvent(self, event):
   2      self.emit(QtCore.SIGNAL('closeEmitApp()'))

Генерация сигнала происходит с помощью метода emit().

   1 self.connect(self, QtCore.SIGNAL('closeEmitApp()'), QtCore.SLOT('close()') )

Самостоятельно соединяем созданный сигнал closeEmitApp() со слотом close().

Диалоговые окна в PyQt4

Диалоговые окна являются неотъемлемой частью большинства современных GUI приложений. В обычной жизни диалог - это общение между двумя и более лицами. В компьютерном приложении диалоговое окно обеспечивает "общение" пользователя с приложением. То есть с помощью диалоговых окон пользователь может вводить данные, редактировать их и т.д.

Стандартные диалоги

QInputDialog представляет собой стандартный диалог, чтобы получить одно значения от пользователя. Значение может быть целым числом, строкой или элементом списка.

   1 #!/usr/bin/python
   2 
   3 # inputdialog.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class InputDialog(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 350, 80)
  15         self.setWindowTitle('InputDialog')
  16 
  17         self.button = QtGui.QPushButton('Dialog', self)
  18         self.button.setFocusPolicy(QtCore.Qt.NoFocus)
  19 
  20         self.button.move(20, 20)
  21         self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
  22         self.setFocus()
  23 
  24         self.label = QtGui.QLineEdit(self)
  25         self.label.move(130, 22)
  26 
  27 
  28     def showDialog(self):
  29         text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')
  30 
  31         if ok:
  32             self.label.setText(unicode(text))
  33 
  34 
  35 app = QtGui.QApplication(sys.argv)
  36 icon = InputDialog()
  37 icon.show()
  38 app.exec_()

В этом примере у нас имеется виджет с кнопкой и строкой для редактирования. При нажатии на кнопку мы увидим диалоговое окно, куда можем ввести значение. После того как мы ввели текст и нажали кнопку ввода, мы получим в строке редактирования виждета введенное значение.

   1 text, ok = QtGui.QInputDialog.getText(self, 'Input Dialog', 'Enter your name:')

Эта строка отображает диалогове окно. Первое значение - это название диалогового окна, второе значение - сообщение в диалоговом окне. Диалог возвращает введенный текст и булево значение. Если Вы нажали кнопку Ок, то мы получим булево значение True, в противном случае - False.

inputdialog.jpg

Рисунок: ввод в диалоговое окно

QColorDialog

QColorDialog предоставляет виджет в виде диалоговго окна, где пользователь может выбрать цвет.

   1 #!/usr/bin/python
   2 
   3 # colordialog.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class ColorDialog(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         color = QtGui.QColor(0, 0, 0)
  15 
  16         self.setGeometry(300, 300, 250, 180)
  17         self.setWindowTitle('ColorDialog')
  18 
  19         self.button = QtGui.QPushButton('Dialog', self)
  20         self.button.setFocusPolicy(QtCore.Qt.NoFocus)
  21         self.button.move(20, 20)
  22 
  23         self.connect(self.button, QtCore.SIGNAL('clicked()'), self.showDialog)
  24         self.setFocus()
  25 
  26         self.widget = QtGui.QWidget(self)
  27         self.widget.setStyleSheet("QWidget { background-color: %s }"
  28             % color.name())
  29         self.widget.setGeometry(130, 22, 100, 100)
  30 
  31 
  32     def showDialog(self):
  33         color = QtGui.QColorDialog.getColor()
  34 
  35         self.widget.setStyleSheet("QWidget { background-color: %s }"
  36             % color.name())
  37 
  38 app = QtGui.QApplication(sys.argv)
  39 cd = ColorDialog()
  40 cd.show()
  41 app.exec_()

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

   1 color = QtGui.QColorDialog.getColor()

Эта строчка вызывает диалоговое окно QColorDialog.

   1 self.widget.setStyleSheet("QWidget { background-color: %s }"
   2      % color.name())

Тут мы изменяем фоновый цвет нашего виджета.

colordialog.jpg

Рисунок: диалоговое окно выбора цвета

QFontDialog

Диалоговое окно QFontDialog предназначенно для выбора шрифта.

   1 #!/usr/bin/python
   2 
   3 # fontdialog.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class FontDialog(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         hbox = QtGui.QHBoxLayout()
  15 
  16         self.setGeometry(300, 300, 250, 110)
  17         self.setWindowTitle('FontDialog')
  18 
  19         button = QtGui.QPushButton('Dialog', self)
  20         button.setFocusPolicy(QtCore.Qt.NoFocus)
  21         button.move(20, 20)
  22 
  23         hbox.addWidget(button)
  24 
  25         self.connect(button, QtCore.SIGNAL('clicked()'), self.showDialog)
  26 
  27         self.label = QtGui.QLabel('Knowledge only matters', self)
  28         self.label.move(130, 20)
  29 
  30         hbox.addWidget(self.label, 1)
  31         self.setLayout(hbox)
  32 
  33 
  34     def showDialog(self):
  35         font, ok = QtGui.QFontDialog.getFont()
  36         if ok:
  37             self.label.setFont(font)
  38 
  39 
  40 app = QtGui.QApplication(sys.argv)
  41 cd = FontDialog()
  42 cd.show()
  43 app.exec_()

В этом примере у нас есть метка и строчка. С помощью QFontDialog мы можеи изменить тип шрифта метки.

   1 hbox.addWidget(self.label, 1)

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

   1 font, ok = QtGui.QFontDialog.getFont()

Здесь мы вызываем диалоговое окно для выбора шрифта.

   1 if ok:
   2      self.label.setFont(font)

Если мы нажали Ok, то изменяем тип шрифта метки.

fontdialog.jpg

Рисунок: диалоговое окно выбора шрифта

QFileDialog

QFileDialog - диалоговое окно, которое позволяет пользователю выбирать файлы или директории. Выбранный файл может быть выбран для его открытия.

   1 #!/usr/bin/python
   2 
   3 # openfiledialog.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class OpenFile(QtGui.QMainWindow):
  11     def __init__(self, parent=None):
  12         QtGui.QMainWindow.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 350, 300)
  15         self.setWindowTitle('OpenFile')
  16 
  17         self.textEdit = QtGui.QTextEdit()
  18         self.setCentralWidget(self.textEdit)
  19         self.statusBar()
  20         self.setFocus()
  21 
  22         exit = QtGui.QAction(QtGui.QIcon('open.png'), 'Open', self)
  23         exit.setShortcut('Ctrl+O')
  24         exit.setStatusTip('Open new File')
  25         self.connect(exit, QtCore.SIGNAL('triggered()'), self.showDialog)
  26 
  27         menubar = self.menuBar()
  28         file = menubar.addMenu('&File')
  29         file.addAction(exit)
  30 
  31     def showDialog(self):
  32         filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file',
  33                     '/home')
  34         file=open(filename)
  35         data = file.read()
  36         self.textEdit.setText(data)
  37 
  38 app = QtGui.QApplication(sys.argv)
  39 cd = OpenFile()
  40 cd.show()
  41 app.exec_()

В этом примере мы создаем меню - виджет, где можно вводить текст, и строку состояния. В строке состояния отображается подсказка над каким действием в меню находится курсор. При выборе действия в меню, вызывается диалоговое окно QFileDialog, где мы можем выбрать файл. Содержимое файла загружается в виджет редактирования текста.

   1 class OpenFile(QtGui.QMainWindow):
   2 ...
   3         self.textEdit = QtGui.QTextEdit()
   4         self.setCentralWidget(self.textEdit)

Этот пример построен на базе виджета QMainWindow, потому что нам надо расположить централизованно текстовый редактор. Это удобно сделать с помощью QMainWindow не прибегая к помощи менеджера компоновки.

   1 filename = QtGui.QFileDialog.getOpenFileName(self, 'Open file',
   2                     '/home')

Мы открываем диалогове окно QFileDialog. Первый параметр метода getOpenFileName() - название окна. Второй параметр определяет рабочую директорию (дирктория которая по умолчанию будет выбрана). По умолчанию, фильтр показывает все файлы (*).

   1 file=open(filename)
   2 data = file.read()
   3 self.textEdit.setText(data)

Читаем выбранный файл и передаем его содержимое в виджет для редактирования текста.

filedialog.jpg

Рисунок: диалоговое окно для открытий файла

Виджеты

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

QCheckBox

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

   1 #!/usr/bin/python
   2 
   3 # checkbox.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class CheckBox(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 250, 150)
  15         self.setWindowTitle('Checkbox')
  16 
  17         self.cb = QtGui.QCheckBox('Show title', self)
  18         self.cb.setFocusPolicy(QtCore.Qt.NoFocus)
  19         self.cb.move(10, 10)
  20         self.cb.toggle();
  21         self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)
  22 
  23     def changeTitle(self, value):
  24         if self.cb.isChecked():
  25             self.setWindowTitle('Checkbox')
  26         else:
  27             self.setWindowTitle('')
  28 
  29 app = QtGui.QApplication(sys.argv)
  30 icon = CheckBox()
  31 icon.show()
  32 app.exec_()

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

   1 self.cb = QtGui.QCheckBox('Show title', self)

Это конструктор переключателя.

   1 self.cb.setFocusPolicy(QtCore.Qt.NoFocus)

По умолчанию, фокус наводится на переключатель. Он представляет собой тонкую линию прямоугольника вокруг переключателя. Мне кажется что это линия выглядит некрасиво, и поэтому я ее отключил, установив настройку фокуса Qt.NoFocus.

   1 self.connect(self.cb, QtCore.SIGNAL('stateChanged(int)'), self.changeTitle)

Мы соединяем наш метод changeTitle() с сигналом stateChanged(). Метод changeTitle() изменяет нам заголовок окна.

   1 self.cb.toggle();

По умолчанию переключатель находится в выключенном положении. Для того что бы его включить мы используем метод toggle().

checkbox.jpg

Рисунок: QCheckBox

ToggleButton

В PyQt4 нет виджета с названием ToggleButton. Для создания этого виджета мы используем QPushButton в специальном формате. ToggleButton это кнопка которая может находиться в двух состояниях: нажата и не нажата. Вы можете переключаться кнопку из положения в положения просто щелкнув по ней. Существуют задачи, когда использование такого механизма очень удобно.

   1 #!/usr/bin/python
   2 
   3 # togglebutton.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class ToggleButton(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.color = QtGui.QColor(0, 0, 0)
  15 
  16         self.setGeometry(300, 300, 280, 170)
  17         self.setWindowTitle('ToggleButton')
  18 
  19         self.red = QtGui.QPushButton('Red', self)
  20         self.red.setCheckable(True)
  21         self.red.move(10, 10)
  22 
  23         self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)
  24 
  25         self.green = QtGui.QPushButton('Green', self)
  26         self.green.setCheckable(True)
  27         self.green.move(10, 60)
  28 
  29         self.connect(self.green, QtCore.SIGNAL('clicked()'), self.setGreen)
  30 
  31         self.blue = QtGui.QPushButton('Blue', self)
  32         self.blue.setCheckable(True)
  33         self.blue.move(10, 110)
  34 
  35         self.connect(self.blue, QtCore.SIGNAL('clicked()'), self.setBlue)
  36 
  37         self.square = QtGui.QWidget(self)
  38         self.square.setGeometry(150, 20, 100, 100)
  39         self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
  40 
  41         QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))
  42 
  43     def setRed(self):
  44         if self.red.isChecked():
  45             self.color.setRed(255)
  46         else: self.color.setRed(0)
  47 
  48         self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
  49 
  50     def setGreen(self):
  51         if self.green.isChecked():
  52             self.color.setGreen(255)
  53         else: self.color.setGreen(0)
  54 
  55         self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
  56 
  57     def setBlue(self):
  58         if self.blue.isChecked():
  59             self.color.setBlue(255)
  60         else: self.color.setBlue(0)
  61 
  62         self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())
  63 
  64 
  65 app = QtGui.QApplication(sys.argv)
  66 tb = ToggleButton()
  67 tb.show()
  68 app.exec_()

В этом примере мы создали три ToggleButton. Мы также создали QWidget. Мы сделали его фон черным. ToggleButton включает и выключает красную, голубую и зеленную составляющую в значении фона QWidget'а. Соответственно, цвет фона меняется в зависимости от нажатия или отжатия кнопки.

   1 self.color = QtGui.QColor(0, 0, 0)

Это строчка инициализирует начальное значение фона. Отсуствие красного, зеленого и голубого цвета приводит к тому, что мы видим черный квадрат. Теоритически говоря, черный цвет - это отсуствие всех цветов.

   1 self.red = QtGui.QPushButton('Red', self)
   2 self.red.setCheckable(True)

Тут мы создаем togglebutton и включаем режим переключение состояния кнопки с помощью метода setCheckable().

   1 self.connect(self.red, QtCore.SIGNAL('clicked()'), self.setRed)

Связываем сигнал clicked() с нашим методом setRed.

   1 QtGui.QApplication.setStyle(QtGui.QStyleFactory.create('cleanlooks'))

Я установил в настройках стиля - стиль 'cleanlooks'. Дело в том, что по умолчанию используется стиль 'plastique', в котором из за ошибки реализации нельзя понять: нажата кнопка или отжата (в текущей версии это ошибка исправлена). И потом стиль 'cleanlooks', как мне кажется, более красивый.

   1 if self.red.isChecked():
   2     self.color.setRed(255)
   3 else: self.color.setRed(0)

Мы проверяем, нажата или отжата кнопка, и в зависимости от этого добавляем цвет или убавляем.

   1 self.square.setStyleSheet("QWidget { background-color: %s }" % self.color.name())

Для изменения цвета фона, мы используем таблицу стилей.

togglebutton.jpg

Рисунок: ToggleButton

QSlider, QLabel

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

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

   1 #!/usr/bin/python
   2 
   3 # slider-label.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class SliderLabel(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 250, 150)
  15         self.setWindowTitle('SliderLabel')
  16 
  17         self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)
  18         self.slider.setFocusPolicy(QtCore.Qt.NoFocus)
  19         self.slider.setGeometry(30, 40, 100, 30)
  20         self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)
  21 
  22 
  23         self.label = QtGui.QLabel(self)
  24         self.label.setPixmap(QtGui.QPixmap('mute.png'))
  25         self.label.setGeometry(160, 40, 80, 30)
  26 
  27 
  28     def changeValue(self, value):
  29         pos = self.slider.value()
  30 
  31         if pos == 0:
  32             self.label.setPixmap(QtGui.QPixmap('mute.png'))
  33         elif pos > 0 and pos <= 30:
  34             self.label.setPixmap(QtGui.QPixmap('min.png'))
  35         elif pos > 30 and pos < 80:
  36             self.label.setPixmap(QtGui.QPixmap('med.png'))
  37         else:
  38             self.label.setPixmap(QtGui.QPixmap('max.png'))
  39 
  40 app = QtGui.QApplication(sys.argv)
  41 icon = SliderLabel()
  42 icon.show()
  43 app.exec_()

В этом примере мы моделируем работу регулятора громкости. Передвигая ползунок, мы меняем метку.

   1 self.slider = QtGui.QSlider(QtCore.Qt.Horizontal, self)

Мы создали горизонтальный ползунок.

   1 self.label = QtGui.QLabel(self)
   2 self.label.setPixmap(QtGui.QPixmap('mute.png'))

Мы создали QLabel. И установили значение по умолчанию файл mute.png.

   1 self.connect(self.slider, QtCore.SIGNAL('valueChanged(int)'), self.changeValue)

Мы связали сигнал valueChanged(int) с нашей функцией self.changeValue.

   1 pos = self.slider.value()

Мы получаем значение ползунка вызвав метод value(). В зависимости от значения меняем изображение метки.

sliderlabel.jpg

Рисунок: QSlider и QLabel

QProgressBar

QProgressBar - это виджет, который используется, когда необходимо показать уровень выполнения задачи. Это анимированный виджет, позволяющий видеть пользователю, что задача выполняется. QProgressBar можно сделать как вертикальным, так и горизонтальным. При создании этого виджета программист должен задать минимальное и максимальное значение. По умолчанию, эти значения равны 0 и 99 соотвественно.

   1 #!/usr/bin/python
   2 
   3 # progressbar.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class ProgressBar(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 250, 150)
  15         self.setWindowTitle('ProgressBar')
  16 
  17         self.pbar = QtGui.QProgressBar(self)
  18         self.pbar.setGeometry(30, 40, 200, 25)
  19 
  20         self.button = QtGui.QPushButton('Start', self)
  21         self.button.setFocusPolicy(QtCore.Qt.NoFocus)
  22         self.button.move(40, 80)
  23 
  24         self.connect(self.button, QtCore.SIGNAL('clicked()'), self.onStart)
  25 
  26         self.timer = QtCore.QBasicTimer()
  27         self.step = 0;
  28 
  29 
  30     def timerEvent(self, event):
  31         if self.step >= 100:
  32             self.timer.stop()
  33             return
  34         self.step = self.step + 1
  35         self.pbar.setValue(self.step)
  36 
  37     def onStart(self):
  38         if self.timer.isActive():
  39             self.timer.stop()
  40             self.button.setText('Start')
  41         else:
  42             self.timer.start(100, self)
  43             self.button.setText('Stop')
  44 
  45 
  46 app = QtGui.QApplication(sys.argv)
  47 icon = ProgressBar()
  48 icon.show()
  49 app.exec_()

В этом примере мы создали горизонтальный уровень выполнения задачи и кнопку. Кнопка запускает и останавливает работу уровня выполнения задачи.

   1 self.pbar = QtGui.QProgressBar(self)

Конструктор QProgressBar.

   1 self.timer = QtCore.QBasicTimer()

Создали объект таймера, который будет влиять на уровень выполнения задачи.

   1 self.timer.start(100, self)

Для запуска таймера необходимо вызвать метод start(). Этот метод получает два параметра: тайм-аут и объект, который будет получать событие о его срабатывании.

   1 def timerEvent(self, event):
   2     if self.step >= 100:
   3         self.timer.stop()
   4         return
   5     self.step = self.step + 1
   6     self.pbar.setValue(self.step)

Каждый потомок QObject имеет обработчик события timerEvent. Для того что бы мы могли отреагировать на это событие, нам надо его переопределить в нашем объекте.

progressbar.jpg

Рисунок: QProgressBar

QCalendarWidget

QCalendarWidget позволяет работать с календарем. Благодаря ему пользователь может выбрать дату интуитивно понятным способом.

   1 #!/usr/bin/python
   2 
   3 # calendar.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 
  10 class Calendar(QtGui.QWidget):
  11     def __init__(self, parent=None):
  12         QtGui.QWidget.__init__(self, parent)
  13 
  14         self.setGeometry(300, 300, 350, 300)
  15         self.setWindowTitle('Calendar')
  16 
  17         self.cal = QtGui.QCalendarWidget(self)
  18         self.cal.setGridVisible(True)
  19         self.cal.move(20, 20)
  20         self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)
  21 
  22 
  23         self.label = QtGui.QLabel(self)
  24         date = self.cal.selectedDate()
  25         self.label.setText(str(date.toPyDate()))
  26         self.label.move(130, 260)
  27 
  28 
  29     def showDate(self):
  30         date = self.cal.selectedDate()
  31         self.label.setText(str(date.toPyDate()))
  32 
  33 
  34 app = QtGui.QApplication(sys.argv)
  35 icon = Calendar()
  36 icon.show()
  37 app.exec_()

В этом примере мы создали календарь и метку. Выбирая дату, мы отображаем ее в метке.

   1 self.cal = QtGui.QCalendarWidget(self)

Конструктор QCalendar.

   1 self.connect(self.cal, QtCore.SIGNAL('selectionChanged()'), self.showDate)

Если пользователь выбрал дату, то посылается сигнал selectionChanged(). Мы соединяем его с нашим методом showDate.

   1 def showDate(self):
   2      date = self.cal.selectedDate()
   3      self.label.setText(str(date.toPyDate()))

Мы получаем выбранную дату с помощью метода selectedDate(). Следующим нашим шагом будет преобразование ее в строку и затем передача в метод который определяет значение метки.

calendar.jpg

Рисунок: QCalendarWidget

Drag & Drop в PyQt4

В этой части руководства PyQt4 мы рассмотрим Drag & Drop.

При работе с графическим интерфейсом на компьютере Drag & Drop это технология которая позволяет выбрав виртуальный объект перетащить его на другое место или на другой виртуальный объект. В целом эта технология может использоваться в различных задачах где требуется создать связать абстракные объекты (Википедия).

Drag & Drop является одним из самых ярких возможностей графического интерфейса. Перетаскивание объектов дает возможность пользователям работать интуитивно со сложными вещами.

Как правило мы можем перетаскивать два вида объектов: данные или графические. Если мы перетаскиваем изображение из одного приложения в другое, то мы перетаскиваем двоичные данные. Если мы перетаскиваем к примеру вкладку Firefox - мы перетаскиваем графически компонент.

Пример Drag & Drop

В этом примере мы используем виджеты QLineEdit и QPushButton. Мы будем перетаскивать текст из виджета строки редактирования на виджет кнопки.

   1 #!/usr/bin/python
   2 
   3 # dragdrop.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 
   8 class Button(QtGui.QPushButton):
   9     def __init__(self, title, parent):
  10         QtGui.QPushButton.__init__(self, title, parent)
  11         self.setAcceptDrops(True)
  12 
  13     def dragEnterEvent(self, event):
  14         if event.mimeData().hasFormat('text/plain'):
  15             event.accept()
  16         else:
  17             event.ignore()
  18 
  19     def dropEvent(self, event):
  20             self.setText(event.mimeData().text())
  21 
  22 
  23 class DragDrop(QtGui.QDialog):
  24     def __init__(self, parent=None):
  25         QtGui.QDialog.__init__(self, parent)
  26 
  27         self.resize(280, 150)
  28         self.setWindowTitle('Simple Drag & Drop')
  29 
  30         edit = QtGui.QLineEdit('', self)
  31         edit.setDragEnabled(True)
  32         edit.move(30, 65)
  33 
  34         button = Button("Button", self)
  35         button.move(170, 65)
  36 
  37 
  38         screen = QtGui.QDesktopWidget().screenGeometry()
  39         size =  self.geometry()
  40         self.move((screen.width()-size.width())/2,
  41             (screen.height()-size.height())/2)
  42 
  43 app = QtGui.QApplication(sys.argv)
  44 icon = DragDrop()
  45 icon.show()
  46 app.exec_()
  47 
  48 
  49 class Button(QtGui.QPushButton):
  50      def __init__(self, title, parent):
  51          QtGui.QPushButton.__init__(self, title, parent)

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

   1 def dragEnterEvent(self, event):
   2      if event.mimeData().hasFormat('text/plain'):
   3          event.accept()
   4      else:
   5          event.ignore()

Первый метод, который мы переопределяем - это dragEnterEvent(). Мы проверяем какого типа пришли данные, и если они текстовые, то принимаем их.

   1 def dropEvent(self, event):
   2      self.setText(event.mimeData().text())

Затем мы переопределяем метод dropEvent(), в котором мы определяем, что мы будем делать после того как произойдет событие. Затем мы изменяем текст на кнопки.

   1 edit = QtGui.QLineEdit('', self)
   2 edit.setDragEnabled(True)

Нам необходимо включить поддержку для операций связанных с перетаскиванием. Для этого нам необходимо вызвать метод setDragEnabled().

simpledd.png

Рисунок: Пример Drag & Drop

Drag & Drop с кнопкой

В следующем примере мы покажем как можно перетаскивать виджет кнопки.

   1 #!/usr/bin/python
   2 
   3 # dragbutton.py
   4 
   5 import sys
   6 from PyQt4 import QtGui
   7 from PyQt4 import QtCore
   8 
   9 class Button(QtGui.QPushButton):
  10     def __init__(self, title, parent):
  11         QtGui.QPushButton.__init__(self, title, parent)
  12 
  13     def mouseMoveEvent(self, event):
  14 
  15         if event.buttons() != QtCore.Qt.RightButton:
  16             return
  17 
  18         mimeData = QtCore.QMimeData()
  19 
  20         drag = QtGui.QDrag(self)
  21         drag.setMimeData(mimeData)
  22         drag.setHotSpot(event.pos() - self.rect().topLeft())
  23 
  24         dropAction = drag.start(QtCore.Qt.MoveAction)
  25 
  26         if dropAction == QtCore.Qt.MoveAction:
  27             self.close()
  28 
  29     def mousePressEvent(self, event):
  30         QtGui.QPushButton.mousePressEvent(self, event)
  31         if event.button() == QtCore.Qt.LeftButton:
  32             print 'press'
  33 
  34 
  35 
  36 class DragButton(QtGui.QDialog):
  37     def __init__(self, parent=None):
  38         QtGui.QDialog.__init__(self, parent)
  39 
  40         self.resize(280, 150)
  41         self.setWindowTitle('Click or Move')
  42         self.setAcceptDrops(True)
  43 
  44         self.button = Button('Close', self)
  45         self.button.move(100, 65)
  46 
  47 
  48         screen = QtGui.QDesktopWidget().screenGeometry()
  49         size =  self.geometry()
  50         self.move((screen.width()-size.width())/2,
  51             (screen.height()-size.height())/2)
  52 
  53 
  54     def dragEnterEvent(self, event):
  55          event.accept()
  56 
  57     def dropEvent(self, event):
  58 
  59         position = event.pos()
  60         button = Button('Close', self)
  61         button.move(position)
  62         button.show()
  63 
  64         event.setDropAction(QtCore.Qt.MoveAction)
  65         event.accept()
  66 
  67 
  68 app = QtGui.QApplication(sys.argv)
  69 db = DragButton()
  70 db.show()
  71 app.exec_()

В этом примере мы можемувидеть кнопку расположенную в окне. Если мы по ней кликнем левой клавишой мышки, то в консоли выведется надпись "press". Если мы нажмем правую кнопку мышки, то сможем переместить кнопку в любое место нашего окна.

   1 class Button(QtGui.QPushButton):
   2     def __init__(self, title, parent):
   3         QtGui.QPushButton.__init__(self, title, parent)

Мы создали класс Button, который определили как наследника QPushButton. Мы также переопределяем два метода класса QPushButton: mouseMoveEvent() и mousePressEvent(). Метод mousePressEvent() который позволит нам осущесвить drag & drop.

   1 if event.buttons() != QtCore.Qt.RightButton:
   2      return

Здесь мы определяем, что с помощью правой клавиши мы можем перетаскивать кнопку. Левая кнопка служит нам для нажатия на нее.

   1  mimeData = QtCore.QMimeData()
   2 
   3  drag = QtGui.QDrag(self)
   4  drag.setMimeData(mimeData)
   5  drag.setHotSpot(event.pos() - self.rect().topLeft())

Здесь мы создаем объет drag.

   1 dropAction = drag.start(QtCore.Qt.MoveAction)
   2 
   3  if dropAction == QtCore.Qt.MoveAction:
   4      self.close()

Метод start() начинает операцию связанную с drag & drop. Если мы нажмем на правую кнопку, то мы удалим виджет кнопки. То есть технически мы уничтожаем виджет кнопки, и создаем ее в новом месте.

   1  def mousePressEvent(self, event):
   2      QtGui.QPushButton.mousePressEvent(self, event)
   3      if event.button() == QtCore.Qt.LeftButton:
   4          print 'press'

Мы печатает в консоли 'press' если пользователь нажимает левую кнопку. Обратите внимание, что мы вызываем родительский метод. В противном случае мы бы не увидели кнопку во время нажатия (???).

   1 position = event.pos()
   2 button = Button('Close', self)
   3 button.move(position)
   4 button.show()

В метод dropEvent() мы вставляем код, который выполняется после того как пользователь отпускает кнопку и заканчивает ее перемещение. В нашем случае мы создаем новую кнопку в месте указателя мыши.

   1  event.setDropAction(QtCore.Qt.MoveAction)
   2  event.accept()

Мы определяем тип действия для перемещения. В нашем случае это перемещение (тафтология drop и move).

Рисование

Мы рисуем, когда мы хотим или видоизменить или расширить существуеющий виджет, а также в случае создание виджета с нуля. Что бы нарисовать что то, мы будем использовать API, которые есть в PyQt4.

Рисование происходит внутри метода paintEvent(). Сами команды рисование располагаются между методов start() и end(), определенных в объекте QPainter.

Рисуем текст

Для начала мы нарисуем несколько строчект текста в кодировке Unicode в окне нашего приложения.

#!/usr/bin/python

# drawtext.py

import sys
from PyQt4 import QtGui, QtCore


class DrawText(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Draw Text')

        self.text = u'\u041b\u0435\u0432 \u041d\u0438\u043a\u043e\u043b\u0430\
\u0435\u0432\u0438\u0447 \u0422\u043e\u043b\u0441\u0442\u043e\u0439: \n\
\u0410\u043d\u043d\u0430 \u041a\u0430\u0440\u0435\u043d\u0438\u043d\u0430'



    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)
        paint.setPen(QtGui.QColor(168, 34, 3))
        paint.setFont(QtGui.QFont('Decorative', 10))
        paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)
        paint.end()


app = QtGui.QApplication(sys.argv)
dt = DrawText()
dt.show()
app.exec_()

В этом примере мы нарисовали несколько строчек кириллицы. Разместив их по середине вертикали и горизонтали.

def paintEvent(self, event):

Рисуем внутри события отрисовки.

paint = QtGui.QPainter()
paint.begin(self)
...
paint.end()

Класс Qpainter содержит низкоуровневые методы рисования. Все командя рисования находятся между методами begin() и end().

paint.setPen(QtGui.QColor(168, 34, 3))
paint.setFont(QtGui.QFont('Decorative', 10))

Здесь мы определяем шрифт и цвет пера, которым мы будем рисовать текст.

paint.drawText(event.rect(), QtCore.Qt.AlignCenter, self.text)

Метод drawText() нарисует нам текст в нашем окне.

drawtext.jpg

Рисунок: Рисуем текст

Рисуем точки

Точки это простейшие объекты для рисования. Это небольшие пятна на экране.

#!/usr/bin/python

# points.py

import sys, random
from PyQt4 import QtGui, QtCore


class Points(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Points')

    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)
        paint.setPen(QtCore.Qt.red)
        size = self.size()
        for i in range(1000):
            x = random.randint(1, size.width()-1)
            y = random.randint(1, size.height()-1)
            paint.drawPoint(x, y)
        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Points()
dt.show()
app.exec_()

В этом примере мы нарисовали в случайно выбранных местах нашего окна 1000 красных точек.

paint.setPen(QtCore.Qt.red)

Здесь мы определили цвет пера которым будем рисовать - красный. Мы использовали предопределнную константу.

size = self.size()

Каждый раз когда мы изменяем размер окна, точки заново перерисовываются. Мы получаем текущий размер окна с помощью метода size().

paint.drawPoint(x, y)

Мы рисуем точки с помощью метода drawPoint().

points.jpg

Рисунок: Точки

Цвета

Цвет - это объект представляющий собой комбинацию трех цветов: красного, зеленого и синего (RGB). Каждый цвет может представлять собой значение от 0 до 255. Мы можем определять конечный цвет по разному. Наиболее распространенным вариантом является использование десятиричных или шестнадцатиричных значений цветов RGB. Также мы можем использовать цветовую схему RGBA, значения которой состоит из красного, голубого, зеленого и так называемого альфа-канала. Альфа канал - это значение определяющие прозрачность цвета и принимающего значения от 0 (цвет невидим) до 255 (цвет непрозрачен).

#!/usr/bin/python

# colors.py

import sys, random
from PyQt4 import QtGui, QtCore


class Colors(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 350, 280)
        self.setWindowTitle('Colors')

    def paintEvent(self, event):
        paint = QtGui.QPainter()
        paint.begin(self)

        color = QtGui.QColor(0, 0, 0)
        color.setNamedColor('#d4d4d4')
        paint.setPen(color)

        paint.setBrush(QtGui.QColor(255, 0, 0, 80))
        paint.drawRect(10, 15, 90, 60)

        paint.setBrush(QtGui.QColor(255, 0, 0, 160))
        paint.drawRect(130, 15, 90, 60)

        paint.setBrush(QtGui.QColor(255, 0, 0, 255))
        paint.drawRect(250, 15, 90, 60)

        paint.setBrush(QtGui.QColor(10, 163, 2, 55))
        paint.drawRect(10, 105, 90, 60)

        paint.setBrush(QtGui.QColor(160, 100, 0, 255))
        paint.drawRect(130, 105, 90, 60)

        paint.setBrush(QtGui.QColor(60, 100, 60, 255))
        paint.drawRect(250, 105, 90, 60)

        paint.setBrush(QtGui.QColor(50, 50, 50, 255))
        paint.drawRect(10, 195, 90, 60)

        paint.setBrush(QtGui.QColor(50, 150, 50, 255))
        paint.drawRect(130, 195, 90, 60)

        paint.setBrush(QtGui.QColor(223, 135, 19, 255))
        paint.drawRect(250, 195, 90, 60)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Colors()
dt.show()
app.exec_()

В этом примере мы нарисовали 9 разноцветных прямоугольников. В верхней строке нарисованы красные прямоугольники с разной степенью прозрачности.

color = QtGui.QColor(0, 0, 0)
color.setNamedColor('#d4d4d4')

Здесь мы определяем цвет в шестнадцатеричной нотации.

paint.setBrush(QtGui.QColor(255, 0, 0, 80));
paint.drawRect(10, 15, 90, 60)

Здесь мы определяем цвет кисти которым будем рисовать, и затем рисуем прямоугольник. Кисть является элементарным графическим объектом, который используется для заполнения фона. Метод drawRect() принимает 4 параметра. Первые два - являются значение X и Y по оси, третий и четвертый определяет ширину и высоту. Метод рисует прямоугольник используя перо и текущую кисть.

colors.jpg

Рисунок: Цвета

QPen

QPen является элементарным графическим объектом. Он используется для рисование линий, кривых и контуров прямоугольников, эллипсов, многоугольников и других форм.

#!/usr/bin/python

# penstyles.py

import sys
from PyQt4 import QtGui, QtCore


class PenStyles(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 280, 270)
        self.setWindowTitle('penstyles')

    def paintEvent(self, event):
        paint = QtGui.QPainter()

        paint.begin(self)

        pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)

        paint.setPen(pen)
        paint.drawLine(20, 40, 250, 40)

        pen.setStyle(QtCore.Qt.DashLine)
        paint.setPen(pen)
        paint.drawLine(20, 80, 250, 80)

        pen.setStyle(QtCore.Qt.DashDotLine)
        paint.setPen(pen)
        paint.drawLine(20, 120, 250, 120)

        pen.setStyle(QtCore.Qt.DotLine)
        paint.setPen(pen)
        paint.drawLine(20, 160, 250, 160)

        pen.setStyle(QtCore.Qt.DashDotDotLine)
        paint.setPen(pen)
        paint.drawLine(20, 200, 250, 200)

        pen.setStyle(QtCore.Qt.CustomDashLine)
        pen.setDashPattern([1, 4, 5, 4])
        paint.setPen(pen)
        paint.drawLine(20, 240, 250, 240)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = PenStyles()
dt.show()
app.exec_()

В нашем примере мы нарисовали 6 линий. Эти 6 линий нарисованы в разном стиле. Мы можем увидеть 5 прерывистых линий. Также мы можем создавать собственные стили. Последняя линия - нарисована в стиле созданным нами.

pen = QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine)

Мы создали QPen объект. Цвет черный. Толщину устанавливаем в два пикселя, так что мы увидим различия между различными стильями перьев. QtCore.Qt.SolidLine является одним из предопределенных стилей перьев.

pen.setStyle(QtCore.Qt.CustomDashLine)
pen.setDashPattern([1, 4, 5, 4])
paint.setPen(pen)

В этой части кода мы определяем собственный стиль пера. Мы устанавливаем стиль пера QtCore.Qt.CustomDashLine и затем вызываем метод setDashPattern(). Перечень чисел определяет стиль. В этот метод мы должны передать четное число значений. Нечетные значение определяеют линии, четный пропуски. В нашем случае мы опредеяем линия длиной 1 пиксель, 4 пикселя пропуск, 5 пикселей линия, 4 пикселя пропуск.

penstyles.jpg

Рисунок: Стили перьев.

QBrush

QBrash является элементарным графическим объектом. Он используется для закрашивания фона графических форм, таких как прямоугольники, эллипсы и многоугольники. Кисть может быть различных типов. Предопределенные кисти различаются градиентами и текстурами.

#!/usr/bin/python

# brushes.py

import sys
from PyQt4 import QtGui, QtCore


class Brushes(QtGui.QWidget):
    def __init__(self, parent=None):
        QtGui.QWidget.__init__(self, parent)

        self.setGeometry(300, 300, 355, 280)
        self.setWindowTitle('Brushes')

    def paintEvent(self, event):
        paint = QtGui.QPainter()

        paint.begin(self)

        brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense1Pattern)
        paint.setBrush(brush)
        paint.drawRect(130, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense2Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 15, 90, 60)

        brush.setStyle(QtCore.Qt.Dense3Pattern)
        paint.setBrush(brush)
        paint.drawRect(10, 105, 90, 60)

        brush.setStyle(QtCore.Qt.DiagCrossPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense5Pattern)
        paint.setBrush(brush)
        paint.drawRect(130, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense6Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 105, 90, 60)

        brush.setStyle(QtCore.Qt.Dense7Pattern)
        paint.setBrush(brush)
        paint.drawRect(250, 105, 90, 60)

        brush.setStyle(QtCore.Qt.HorPattern)
        paint.setBrush(brush)
        paint.drawRect(10, 195, 90, 60)

        brush.setStyle(QtCore.Qt.VerPattern)
        paint.setBrush(brush)
        paint.drawRect(130, 195, 90, 60)

        brush.setStyle(QtCore.Qt.BDiagPattern)
        paint.setBrush(brush)
        paint.drawRect(250, 195, 90, 60)

        paint.end()

app = QtGui.QApplication(sys.argv)
dt = Brushes()
dt.show()
app.exec_()

В нашем примере мы нарисовали 9 различных прямоугольников.

brush = QtGui.QBrush(QtCore.Qt.SolidPattern)
paint.setBrush(brush)
paint.drawRect(10, 15, 90, 60)

Мы определили объект кисти. И затем нарисовали прямоугольник вызвав метод drawRect().

brushes.jpg

Рисунок: Кисти.

Делаем виджеты сами в PyQt4

Игра Tetris на PyQt4

Перевод: Олег Плессер

oleg@plesser.ru

Документации/ВведениеВСредуPyQt4 (последним исправлял пользователь 80 2010-08-11 11:46:58)