Метою цієї статті є надання практичного довідника для вивчення основ створення за допомогою бібліотеки Qt графічних додатків на мові Python.
Для прочитання даної статті, бажано мати базові знанні мови python та бібліотеки Qt, однак, вони не є обов'язковими. Я використовую Linux у нижченаведених прикладах, і сподіваюсь, що ви вже маєте встановлені робочі інсталяції інтерпретатора python та бібліотеки pyqt. Для того, щоб перевірити це, запустіть командний процесор python, просто виконавши команду python в консолі і виконайте у ньому:
>>> import PyQt4
Якщо не побачили ніяких помилок, можете радіти. Приклади у цій статті на стільки прості, на скільки це взагалі можливо, і показують прадатні до використання способи написання і структурування програм. Для вас дуже важливо читати джерельні тексти цих прикладів, більшість з яких гарно коментовані. Використовуйте приклади, і намагайтесь їх модифікувати, побавтесь з ними. Це найкращий спосіб вивчити PyQt.
Hello, world!
Давайте розпочнемо з дуже простого прикладу. Покажемо вікно, і відобразимо на ньому текстову мітку «Hello world!»:
#!/usr/bin/env python
import sys
from PyQt4 import QtGui
# Ми ініціалізуємо QApplication, передаючи йому аргументи скрипта:
app = QtGui.QApplication(sys.argv)
# Додаємо до алпікації простий віджет:
# Першим аргументом є текст, який відображатиме QWidget, а другим -- батьківський
# віджет. Скілький наш "hello" єдиний віджет, який ми маємо (він також має назву
# "MainWidget"), він не матиме батьківського вджету.
hellolabel = QtGui.QLabel("Hello, world!'", None)
hellolabel.show()
sys.exit(app.exec_())
Кілька рядків коду, і все на стільки просто, на скільки це взагалі можливо.
Кнопка
Давайте додамо до нашої програми якусь взаємодії з користувачем! Замінимо нашу текстову мітку «Hello, World!» кнопкою, і додамо до неї обробник на подію «кнопка натиснута». Це робиться шляхом під'єднання сигналу, події, яка відбуватиметься при натисканні на кнопку до слоту, який являє собою дію, котра, за звичай, є простою function, котра викликається після того, як подія відбулась:
#!/usr/bin/env python
import sys
from PyQt4 import QtGui, QtCore, Qt
app = QtGui.QApplication(sys.argv)
# Наша функція, яка викликатиметься у відповідь на натиснення кнопки
def button_clicked():
print "Hello, world!"
# Ініціалізуємо кнопку
hellobutton = QtGui.QPushButton("Hello, world!'", None)
# З'єднуємо дію "sayHello" з подією "кнопку було натиснено"
app.connect(hellobutton, Qt.SIGNAL("clicked()"), button_clicked)
# Про це нам вже відомо...
hellobutton.show()
sys.exit(app.exec_())
Ви можете зауважити, що подібний кодинг — це не зовсім той спосіб, який би ви хотіли використовувати. Тож, давайте зробимо наш код більше «пайтонівським», додавши структуру, і використавши ООП. Створимо клас нашого додатку, який успадковується від QApplication, і трохи змінимо наш додаток: створимо метод для створення віджета, і слот, який виконуватиметься при отримання сигналу:
#!/usr/bin/env python
import sys
from PyQt4 import Qt
class HelloApplication(Qt.QApplication):
def __init__(self, args):
""" У конструкторі ми робимо усе для того, щоб наша програма запустилась,
створючи просту QApplication, за допомогою її методу __init__, а
тоді додаючи наш віджет, і в кінці, запускаючиexec_loop.
"""
Qt.QApplication.__init__(self, args)
self.addWidgets()
self.exec_()
def addWidgets(self):
""" У даному методі, ми додаємо, і під'єднуємо сигнали нашого віджета до
методів нашого класу, які звуться "сигналами"
"""
self.hellobutton = Qt.QPushButton("Say 'Hello world!'",None)
self.connect(self.hellobutton, Qt.SIGNAL("clicked()"), self.slotSayHello)
self.hellobutton.show()
def slotSayHello(self):
""" Це приклад слоту, методу, який виконуватиметься при отриманні
сигналу """
pprint "Hello, World!"
if __name__ == "__main__":
app = HelloApplication(sys.argv)
Кодинг ГКІ — відстій
#… тому ми хочемо використовувати Qt3 Designer для створення ГКІ. На знімку екрану ви можете побачити простий графічний додаток, на якому зеленими літерами позначені назви віджетів. Ми компілюємо з файлу .ui, котрий створює Qt designer код у класи python.
# Ми є підкласом, який використовує свій батьківський клас, mainWidget
Таким чином, ми можемо згодом змінювати графічний інтерфейс, за допомогою Qt Designer, без будь яких змін коду.
pyuic testapp_ui.ui -o testapp_ui.py
перетворить файл .ui на пайтонівський код, з яким ми зможемо працювати.
# Спосіб, у який працює наша програма можна описати наступним чином: ми заповнюємо lineedit.
# Натискання на кнопку hellobutton буде приєднано до методу, який читатиме текст з lineedit, робить listviewitem і додаємо його в кінець віджету listview.
# Натискаючи кнопку deletebutton ми видаляємо виділений елемент віджету listview.
Ось гарно коментований код:
#!/usr/bin/env python
from testapp_ui import TestAppUI
from qt import *
import sys
class HelloApplication(QApplication):
def __init__(self, args):
""" У конструкторі ми робимо усе для того, щоб наша програма запустилась,
створючи просту QApplication, за допомогою її методу __init__, а
тоді додаючи наш віджет, і в кінці, запускаючиexec_loop.
"""
QApplication.__init__(self,args)
self.maindialog = TestApp(None)
self.setMainWidget(self.maindialog)
self.maindialog.show()
self.exec_loop()
class TestApp(TestAppUI):
def __init__(self,parent):
# Виконуємо батьківський конструктор, і з'єднуємо слоти з методами.
TestAppUI.__init__(self,parent)
self._connectSlots()
self.deletebutton.setEnabled(False)
def _connectSlots(self):
# Під'єднуємо наші два методи до сигналів.
self.connect(self.addbutton,SIGNAL("clicked()"),self._slotAddClicked)
self.connect(self.deletebutton,SIGNAL("clicked()"),self._slotDeleteClicked)
def _slotAddClicked(self):
# Читаємо текст з lineedit,
text = self.lineedit.text()
# якщо lineedit не пуста,
if len(text):
# вставляємо новий listviewitem ...
lvi = QListViewItem(self.listview)
# з текстом, який міститься у lineedit і...
lvi.setText(0,text)
# очищаємо lineedit.
self.lineedit.clear()
self.deletebutton.setEnabled(True)
def _slotDeleteClicked(self):
# Вилучаємо виділений елемент у listview.
self.listview.takeItem(self.listview.currentItem())
# Пеервіряємо, чи перелік не є пустим - якщо ж так, блокуємо кнопку deletebutton.
if self.listview.childCount() == 0:
self.deletebutton.setEnabled(False)
if __name__ == "__main__":
app = HelloApplication(sys.argv)
Не погоджуся
По-перше, ui-файли — згенеровані програмою, а тому їх ідеологічно неправильно класти в svn. В разі чого, зробити svn merge буде майже неможливо
Друге випливає з першого: відсутня можливість написати нормальний коментар (TODO, наприклад)
По-третє, зайві проблеми з власноруч створеними класами. Наприклад, я написав свій варіант QLabel з розмитими (blur) краями. Щоб використовувати його в UiDesigner, треба буде робити плагін, компілювати його, встановлювати… Воно, в принципі, не важко, але суперечить KISS
Четверте, в дизайнері неможливо об’єднати елементи в один масив. Інколи буває потрібно.
І, нарешті, часу на ручний кодинг витрачається майже стільки ж, скільки на перемикання між різними віконцями у дизайнері
1. Чесно кажучи, зовсім не зрозумів, що Ви мали на увазі. Ну, генерує .ui файли Qt Designer, і, що? Це ж звичайна xml-ка. Чому їх не можна класти на vcs? Як це може зашкодити мердженню?
2. Вручну .ui файли редагуватиме лише повний псих, це відомий анти-патерн. Хоча, інколи таки редагуються, наприклад, щоб поправити якусь проперті, чи внести іншу дрібну правку. Ніколи такого не робіть!
3. Відомий патерн: за допомогою Qt Designer`а виділяється на формі місце, куди вставлятиметься ваша лейбла, а тоді вже програмно її туди запихаєте. Елементарна річ, хоча й трохи марудна (інколи доводиться добренько поколупатись з розмірами, і позицією віджета).
Дякую за гарний коментар. Захочете ще поділитись досвідом, буду вдячним!
1. Саме це й мав. У гіршому, але досить імовірному випадку доведеться вручну розбиратися з невідомо якою хмлкою
2. Їжакові зрозуміло, що вручну нічого редагувати не можна. В тому то й проблема: треба написати коментар, а людської можливості для цього нема
3. А навіщо тоді заводити роботу з дизайнером, якщо потім все одно треба писати? А якщо кастомний віджет — контейнер, типу QScrollArea?
1. Я написав, що не зрозумів Вашої тези, і задав Вам два уточнюючих питання (на які Ви, на жаль, не відповіли): чому не можна звичайну xml`ку класти на svn? як це може зашкодити мердженню, адже це звичайнісінький текстовий файл? Якщо Ви її не покладете з сирцями, програму не можливо буде зкомпілити!
2. Навіщо писати коментарі в .UI файлах? Ви, мабуть, не правильно розумієте принцип, згідно якого слід коментувати увесь код. Може Ви хочете коментувати створені у дизайнері слоти? Допоможіть, будь ласка, мені зрозуміти, що саме Ви мали на увазі.
3. Якщо Вам потрібно написати звичайну формочку з одним QLabel, то користі з дизайнера справді не багато, але, якщо Вам тре нафігачити форму з 30 віджетами, один з яких буде кастомним, ось патерн, яким користуються усі qt-девелопери: у дизайнері вставляєте віджет, який буде батьківським для Вашого (QWidget присутній), і натиснувши на ньому правою кнопкою миші, обираєте пункт «Promote to ...». Успіхів!
Цікава стаття, люблю суміщати приємне з корисним (Qt, Python чи навпаки, кому як довподоби =) ), цікаво було б писати статі на кшталт порад по оптимізації тощо…
Манше з тим, цікаве питання швидкодії, мав щастя працювати з однією софтиною написаною на PyQt4, в певних місцях відверте слайд шоу, так от чи це проблема самого модуля, чи просто кривизна рук у програміста тієї програми???
Коментарі (14)
RSS згорнути / розгорнутиtheasus
sashko
theasus
Не погоджуся
По-перше, ui-файли — згенеровані програмою, а тому їх ідеологічно неправильно класти в svn. В разі чого, зробити svn merge буде майже неможливо
Друге випливає з першого: відсутня можливість написати нормальний коментар (TODO, наприклад)
По-третє, зайві проблеми з власноруч створеними класами. Наприклад, я написав свій варіант QLabel з розмитими (blur) краями. Щоб використовувати його в UiDesigner, треба буде робити плагін, компілювати його, встановлювати… Воно, в принципі, не важко, але суперечить KISS
Четверте, в дизайнері неможливо об’єднати елементи в один масив. Інколи буває потрібно.
І, нарешті, часу на ручний кодинг витрачається майже стільки ж, скільки на перемикання між різними віконцями у дизайнері
whirlwind
2. Вручну .ui файли редагуватиме лише повний псих, це відомий анти-патерн. Хоча, інколи таки редагуються, наприклад, щоб поправити якусь проперті, чи внести іншу дрібну правку. Ніколи такого не робіть!
3. Відомий патерн: за допомогою Qt Designer`а виділяється на формі місце, куди вставлятиметься ваша лейбла, а тоді вже програмно її туди запихаєте. Елементарна річ, хоча й трохи марудна (інколи доводиться добренько поколупатись з розмірами, і позицією віджета).
Дякую за гарний коментар. Захочете ще поділитись досвідом, буду вдячним!
sashko
2. Їжакові зрозуміло, що вручну нічого редагувати не можна. В тому то й проблема: треба написати коментар, а людської можливості для цього нема
3. А навіщо тоді заводити роботу з дизайнером, якщо потім все одно треба писати? А якщо кастомний віджет — контейнер, типу QScrollArea?
whirlwind
2. Навіщо писати коментарі в .UI файлах? Ви, мабуть, не правильно розумієте принцип, згідно якого слід коментувати увесь код. Може Ви хочете коментувати створені у дизайнері слоти? Допоможіть, будь ласка, мені зрозуміти, що саме Ви мали на увазі.
3. Якщо Вам потрібно написати звичайну формочку з одним QLabel, то користі з дизайнера справді не багато, але, якщо Вам тре нафігачити форму з 30 віджетами, один з яких буде кастомним, ось патерн, яким користуються усі qt-девелопери: у дизайнері вставляєте віджет, який буде батьківським для Вашого (QWidget присутній), і натиснувши на ньому правою кнопкою миші, обираєте пункт «Promote to ...». Успіхів!
sashko
бо так ми не розуміємо одне одного :)
whirlwind
taryk
sashko
+ останній приклад коду only works in PyQt 3
taryk
DreammakeR
sashko
Манше з тим, цікаве питання швидкодії, мав щастя працювати з однією софтиною написаною на PyQt4, в певних місцях відверте слайд шоу, так от чи це проблема самого модуля, чи просто кривизна рук у програміста тієї програми???
PyThon
Тільки зареєстровані й авторизовані користувачі можуть залишати коментарі.