Detailed explanation of PyQt6 custom components

PyQt6 custom widgets

PyQt6 already has rich components, but no toolkit can provide all the components developers need to develop applications. Toolkits usually only provide the most common widgets, such as buttons, text widgets, or sliders. If we need a widget for a specific need, we have to create it ourselves.

Custom widgets are created using the drawing tools provided by the toolkit. Basically there are two ways: a programmer can modify or enhance an existing widget, or he can create a custom widget from scratch.

PyQt6 burning parts

This component can be seen in Nero, K3B or other CD/DVD burning software.

# file: burning_widget.py
#!/usr/bin/python

"""
ZetCode PyQt6 tutorial

In this example, we create a custom widget.

Author: Jan Bodnar
Website: zetcode.com
"""

from PyQt6.QtWidgets import (QWidget, QSlider, QApplication,
        QHBoxLayout, QVBoxLayout)
from PyQt6.QtCore import QObject, Qt, pyqtSignal
from PyQt6.QtGui import QPainter, QFont, QColor, QPen
import sys


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):

        MAX_CAPACITY = 700
        OVER_CAPACITY = 750

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

        size = self.size()
        w = size.width()
        h = size.height()

        step = int(round(w / 10))

        till = int(((w / OVER_CAPACITY) * self.value))
        full = int(((w / OVER_CAPACITY) * MAX_CAPACITY))

        if self.value >= MAX_CAPACITY:

            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.PenStyle.SolidLine)

        qp.setPen(pen)
        qp.setBrush(Qt.BrushStyle.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.horizontalAdvance(str(self.num[j]))

            x, y = int(i - fw/2), int(h / 2)
            qp.drawText(x, y, str(self.num[j]))
            j = j + 1


class Example(QWidget):

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

        self.initUI()


    def initUI(self):

        OVER_CAPACITY = 750

        sld = QSlider(Qt.Orientation.Horizontal, self)
        sld.setFocusPolicy(Qt.FocusPolicy.NoFocus)
        sld.setRange(1, OVER_CAPACITY)
        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()


def main():

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec())


if __name__ == '__main__':
    main()

In this example, there is a QSliderand a custom widget - the slider controls the custom widget. This widget graphically displays the total capacity of the media and the available free space. Custom widgets have a minimum value of 1 and a maximum value of OVER_CAPACITY. If the value is reached MAX_CAPACITY, it will turn red, indicating that the data to be burned is greater than the capacity of the medium.

The programming part is located at the bottom of the window. Implemented with QHBoxLayoutand QVBoxLayout.

class BurningWidget(QWidget):

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

The burning component is based on QWidget.

self.setMinimumSize(1, 30)

Set the height of the widget, the default height is a bit small.

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

A smaller font size is used here, which looks more suitable for our needs.

size = self.size()
w = size.width()
h = size.height()

step = int(round(w / 10))


till = int(((w / OVER_CAPACITY) * self.value))
full = int(((w / OVER_CAPACITY) * MAX_CAPACITY))

Widgets are rendered dynamically. The larger the window, the larger the widget, and vice versa. So we need to dynamically calculate the size of the widget has to calculate the size of the widget on which the custom widget is drawn. The parameter tilldetermines the total size of the widget, this value comes from the slider widget, it is a ratio relative to the whole area. The parameter fullis the starting point of the red patch.

Drawing consists of three steps, first drawing a rectangle with yellow or red and yellow, then drawing a vertical line to divide the widget into parts, and finally drawing a number representing the capacity of the media.

metrics = qp.fontMetrics()
fw = metrics.horizontalAdvance(str(self.num[j]))

x, y = int(i - fw/2), int(h / 2)
qp.drawText(x, y, str(self.num[j]))

We use a font material to draw the text, so the width of the text must be known in order to center it vertically.

def changeValue(self, value):

    self.c.updateBW.emit(value)
    self.wid.repaint()

When the slider is moved, changeValuethe method is called. Inside the method, trigger a custom signal with a parameter updateBW, the parameter is the current value of the slider, and this value is also used to calculate the capacity of the widget to be drawn Burning, so that the widget is drawn.

insert image description here

Figure: Burning Components

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132700692