Connect with us

Обучение

Интерфейс на Python: пишем с помощью PyQt

На языке Python написаны такие гиганты, как YouTube и Instagram. Если у вас в планах написать свой клевый сервис, то пора познакомиться с азами разработки UI.

Ранее мы уже познакомились с библиотекой PyQt и рассмотрели несколько простых примеров. Так же мы упомянули, что на Python написать код можно двумя способами: с помощью утилиты QtDesigner или вручную. Если вы выбрали второй вариант, то сегодня мы продолжим напишем простой интерфейс с обработчиками и событиями. В прошлый раз мы пробовали написать небольшое окно с тулбаром

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

Интерфейс — это связь между программой и пользователем. Главное, что нужно знать при разработке на PyQT — это такие определения как сигналы и слоты. Сигнал — это какое-либо событие, которые вы сделали в интерфейсе: нажали на кнопку мыши, выбрали какой-то элемент из выпадающего списка, изменили что-то или удалили. Создать сигнал можно при помощи ключевого слова emit().

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

Какие бывают сигналы?

  • clicked — Данный сигнал испускается при активизации кнопки (т.е. когда нажатая кнопка отпускается при нахождении указателя мыши внутри кнопки)
  • pressed — Событие генерируется при нажатии кнопки мыши
  • released — Событие генерируется при отпускании кнопки мыши (которая была нажата раньше)
  • textChanged — генерация сигнала, когда происходит изменение текста (само изменение текста — это событие),
  • valueChanged — генерация сигнала, когда происходит изменение значения

Что может служить слотом?

  • Функция
  • Метод класса
  • Объект с методом вызова (__call__)
  • Анонимная функция (lambda-функция)
  • Слот класса (декоратор pyqtSignal — о нем ниже)

Пример:

Наша задача — создать объект и связать его с сигналом и реализовать обработчик этого самого сигнала. В теории это выглядит так

  1. Кнопка (список, элемент интерфейса). В общем, объект.
  2. Далее идёт событие (нажатие, клик).
  3. При наступлении п.2 (события) срабатывает сигнал
  4. Когда срабатывает п.3 (сигнал) вызывается обработчик

Сигнал может быть и встроенный, но мы напишем свой. Итак, у нас есть MainWindow — главное окно программы. Он наследуется от класса QMainWindow — встроенного класса, который импортируется из QWidget.  Так же у нас есть класс для объектов — AnyObjects, который в свою очередь наследуется от QObject. Внутри него pyqtSignal() — это конструктор, который позволяет создать собственный сигнал

В MainWindow не забываем прописывать метод super().__init__(). Как мы помним это позволяет работать с атрибутами класса родителя. Обратите внимание на функцию set_params, вызываемой внутри класса. В ней мы и прописываем наш сигнал в виде объекта — self.ao = AnyObjects()

Когда мы кликаем на объект срабатывает событие (метод) mousePressEvent. Функция emit() служит как раз для генерации сигнала. Обработчиком в нашем примере является вызов функции on_clicked, которую мы написали ниже — она просто выводит сообщение в консоль. Функция on_clicked является слотом.

Обратите внимание, что в данном примере срабатывает два сигнала — собственный, который мы создали и встроенным — стандартным (self.close — вызов функции закрытия окна). Именно поэтому при клике мы видим сообщение в консоли и наше окно закрываются.

import sys
from PyQt5.QtCore import pyqtSignal, QObject
from PyQt5.QtWidgets import QMainWindow, QApplication

# базовый класс для всех объектов модуля
class AnyObjects(QObject):
    # создаем свой сигнал
    own_signal = pyqtSignal()

# создаем главное окно
class MainWindow(QMainWindow):

    def __init__(self):
        super().__init__()
        self.set_params()

    # метод, который срабатывает при нажатии на окно
    def mousePressEvent(self, event):
        # генерируем сигнал
        self.ao.own_signal.emit()
        self.close()

    def set_params(self):
        self.ao = AnyObjects()
        # обработчик сигнала, связанного с объектом
        self.ao.own_signal.connect(self.on_clicked)
        # параметры главного окна
        self.setGeometry(900, 300, 290, 150)
        self.setWindowTitle('Пример работы самописного сигнала')
        self.show()

    def on_clicked(self):
        print('Тут сообщение')

if __name__ == '__main__':
    app = QApplication(sys.argv)
    mw = MainWindow()
    sys.exit(app.exec_())

Результат при запуске:

Результат после клика по окну (1 — оно закрылось, 2 — вывелось сообщение)

Создание слайдера и прогресс-бара

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

Файлы имеют расширение UI (UserInterface) и являются обычным XML-кодом. Их можно программно перевести в py-файлы — мы их не создаем вручную

Данный интерфейс сохраняется в файл с расширением .ui. Как выглядит файл .ui, если его открыть в текстовом редакторе:

Таким образом, можно создавать простенькие элементы интерфейса, а затем релизить их в py-модули с помощью PyQt5 UI code generator.

Итак, что у нас в главном скрипте. Прежде всего, создадим сигнал ползунка — changed_value = pyqtSignal(int). Т.е. когда мы будем двигать ползунок будет генерироваться сигнал, а после него, соответственно, сработает обработчик. Функция on_changed_value начинает срабатывать, когда меняется значение, тогда и генерируется сигнал. В ней передается параметр value, значение которого постоянно меняется. Далее у нас идёт класс прогресс бара. Здесь нужно обратить внимание на специальный декоратор @pyqtSlot(int) — это объект (слот), который «ловит» наш сигнал, если точнее int (целое число). Таким образом наш прогресс бар устанавливает это значение.

import sys
from PyQt5.QtWidgets import QDialog, QWidget, QApplication
from PyQt5.QtCore import Qt, pyqtSignal, pyqtSlot
import slider
import progress

class SliderClass(QDialog):
    # Добавляем Qt—сигнал как атрибут класса
    changed_value = pyqtSignal(int)

    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.sd = slider.Ui_SliderDialog()
        self.sd.setupUi(self)

        # Связываем оригинальный сигнал слайдера с функцией данного класса
        # при передвигании слайдер будет срабатывать ф-ция on_changed_value
        self.sd.horizontalSlider.valueChanged.connect(self.on_changed_value)

    def on_changed_value(self, value):
        # В этой ф-ции on_changed_value мы генерируем сигнал
        self.changed_value.emit(value)


class ProgressClass(QDialog):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.ui = progress.Ui_ProgressDialog()
        self.ui.setupUi(self)

    def make_connection(self, slider_object):
        # Связываем "свой" сигнал со "своим" слотом
        slider_object.changed_value.connect(self.get_slider_value)

    # Создаем Qt—слот
    @pyqtSlot(int)
    def get_slider_value(self, val):
        self.ui.progressBar.setValue(val)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    slider = SliderClass()
    progress = ProgressClass()
    # Непосредственное связывание ProgressBar'а и Sliser'а
    progress.make_connection(slider)
    # отображаем слайдер
    slider.show()
    # отображаем прогресс-бар
    progress.show()
    sys.exit(app.exec_())

Результат :

Перехват событий

Перехват событий в Qt поможет «поймать» и выполнять обработку событий в режиме реального времени. Как это выглядит? В нашем примере мы создаем классы, которые наследуется от QWidget, а значит мы можем создать модальное окно, менять его размер и так далее. Самое главное здесь — функция event с параметром e на входе. В ней мы используем класс QEvent, где объекты являются событиями. Например, QEvent.MouseButtonPress — это клик мышкой. Мы перехватываем события и обрабатываем

import sys
from PyQt5 import QtCore
from PyQt5.QtWidgets import QWidget, QApplication

class WindowClass(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        self.resize(300, 100)

    def event(self, e):
        if e.type() == QtCore.QEvent.KeyPress:
            print("Вы нажали клавишу на клавиатуре")
            print("Код:", e.key(), ", текст:", e.text())
        elif e.type() == QtCore.QEvent.Close:
            print("Вы закрыли окно")
        elif e.type() == QtCore.QEvent.MouseButtonPress:
            print("Совершен клик мышью. Координаты:", e.x(), e.y())

        # Событие отправляется дальше
        return QWidget.event(self, e)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    wc = WindowClass()
    wc.show()
    sys.exit(app.exec_())

Результат:

Click to comment

Leave a Reply

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Телефон не заряжается. Что делать?

Гаджеты

Wink Интерактивное ТВ на Android: Настройка, цена, решение проблем

Телевидение

Расшифровка SMART у HDD. Как читать ошибки жесткого диска?

Гаджеты

Библиотека Python: Работа с PyQt

Обучение



  • Яндекс.Метрика

Карьера, тренды, диджитал, образование, разработка, DevOps

Connect