Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Создание собственного виджета в PyQt5 [Урок №11]

Создание собственного виджеты в PyQt5

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

Собственные виджеты создаются путём использования инструментов рисования, предоставляемых библиотекой. Существует две основных возможности: программист может изменить или дополнить существующий виджет, или он может создать собственный виджет с нуля.

Виджет записи дисков

Это виджет, который мы можем увидеть в Nero, K3B и другом софте записи CD/DVD.

#!/usr/bin/python3
# -*- coding: utf-8 -*-
 
import sys
from PyQt5.QtWidgets import (QWidget, QSlider, QApplication, QHBoxLayout, QVBoxLayout)
from PyQt5.QtCore import QObject, Qt, pyqtSignal
from PyQt5.QtGui import QPainter, QFont, QColor, QPen
 
class Communicate(QObject):
    updateBW = pyqtSignal(int)
 
class BurningWidget(QWidget):
  
    def __init__(self):      
        super().__init__()
        self.initUI()
        
    def initUI(self):
        self.setMinimumSize(1, 30)
        self.value = 75
        self.num = [75, 150, 225, 300, 375, 450, 525, 600, 675]
 
    def setValue(self, value):
        self.value = value
 
    def paintEvent(self, e):
        qp = QPainter()
        qp.begin(self)
        self.drawWidget(qp)
        qp.end()
      
    def drawWidget(self, qp):
        font = QFont('Serif', 7, QFont.Light)
        qp.setFont(font)
 
        size = self.size()
        w = size.width()
        h = size.height()
 
        step = int(round(w / 10.0))
 
        till = int(((w / 750.0) * self.value))
        full = int(((w / 750.0) * 700))
 
        if self.value >= 700:
            qp.setPen(QColor(255, 255, 255))
            qp.setBrush(QColor(255, 255, 184))
            qp.drawRect(0, 0, full, h)
            qp.setPen(QColor(255, 175, 175))
            qp.setBrush(QColor(255, 175, 175))
            qp.drawRect(full, 0, till-full, h)
            
        else:
            
            qp.setPen(QColor(255, 255, 255))
            qp.setBrush(QColor(255, 255, 184))
            qp.drawRect(0, 0, till, h)
 
        pen = QPen(QColor(20, 20, 20), 1, 
            Qt.SolidLine)
            
        qp.setPen(pen)
        qp.setBrush(Qt.NoBrush)
        qp.drawRect(0, 0, w-1, h-1)
 
        j = 0
 
        for i in range(step, 10*step, step):
          
            qp.drawLine(i, 0, i, 5)
            metrics = qp.fontMetrics()
            fw = metrics.width(str(self.num[j]))
            qp.drawText(i-fw/2, h/2, str(self.num[j]))
            j = j + 1
            
class Example(QWidget):
    
    def __init__(self):
        super().__init__()
        self.initUI()
        
    def initUI(self):      
        sld = QSlider(Qt.Horizontal, self)
        sld.setFocusPolicy(Qt.NoFocus)
        sld.setRange(1, 750)
        sld.setValue(75)
        sld.setGeometry(30, 40, 150, 30)
 
        self.c = Communicate()        
        self.wid = BurningWidget()
        self.c.updateBW[int].connect(self.wid.setValue)
 
        sld.valueChanged[int].connect(self.changeValue)
        hbox = QHBoxLayout()
        hbox.addWidget(self.wid)
        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)
        self.setLayout(vbox)
        
        self.setGeometry(300, 300, 390, 210)
        self.setWindowTitle('Burning widget')
        self.show()
        
    def changeValue(self, value):
        self.c.updateBW.emit(value)        
        self.wid.repaint()
        
if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

В нашем примере, мы имеем QSlider и собственный виджет. Ползунок контролирует наш виджет. Этот виджет показывает графически общий объём носителя и свободное место, доступное для нас. Минимальное значение нашего виджета – 1, максимальное – 750. Если мы достигаем значения 700, мы начинаем рисование в красном цвете. Это обычно означает переполнение. Виджет записи размещается снизу окна. Это достигается путём использования одного QHBoxLayout и одного QVBoxLayout.

class BurningWidget(QWidget):
    def __init__(self):      
        super().__init__()

Виджет записи основывается на виджете QWidget.

self.setMinimumSize(1, 30)

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

font = QFont('Serif', 7, QFont.Light)
qp.setFont(font)

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

size = self.size()
w = size.width()
h = size.height()
 
step = int(round(w / 10.0))
 
till = int(((w / 750.0) * self.value))
full = int(((w / 750.0) * 700))

Мы рисуем виджет динамически. Чем больше окно, тем больше виджет записи, и наоборот. Вот почему мы должны вычислять размер виджета, в котором мы рисуем наш виджет. Параметр till определяет общий размер, что будет рисоваться. Это значение приходит от виджета ползунка. Это доля от полного пространства. Параметр full определяет точку, где мы начинаем рисовать красным цветом. Обратите внимание на использование арифметики с плавающей точкой, чтобы достигнуть большей точности в рисовании.

Фактическое рисование содержит три шага. Мы рисуем жёлтый или красный и желтый прямоугольник. Затем мы рисуем вертикальные линии, которые делят виджет на несколько частей. Наконец, мы рисуем числа, которые показывают ёмкость носителя.

metrics = qp.fontMetrics()
fw = metrics.width(str(self.num[j]))
qp.drawText(i-fw/2, h/2, str(self.num[j]))

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

def changeValue(self, value):
    self.c.updateBW.emit(value)        
    self.wid.repaint()

Когда мы двигаем ползунок, вызывается метод changeValue(). Внутри метода, мы отправляем пользовательский сигнал updateBW с параметром. Параметр – это текущее значение ползунка. Значение позднее используется, чтобы вычислить ёмкость рисуемого виджета записи. Затем, наш виджет перерисовывается.

Виджет прожига CD/DVD

Рисунок: Виджет прожига CD/DVD

В этой части руководства PyQt5, мы создали пользовательский виджет.

Оставьте комментарий!

Используйте нормальные имена.

Имя и сайт используются только при регистрации

Если вы уже зарегистрированы как комментатор или хотите зарегистрироваться, укажите пароль и свой действующий email. При регистрации на указанный адрес придет письмо с кодом активации и ссылкой на ваш персональный аккаунт, где вы сможете изменить свои данные, включая адрес сайта, ник, описание, контакты и т.д., а также подписку на новые комментарии.

(обязательно)