Home > Blockchain >  Qt Custom CheckBox, help about PaintEvent
Qt Custom CheckBox, help about PaintEvent

Time:03-28

I am trying to create a custom checkbox. I am using paintEvent function to create my special checkbox. It's design:

Design

The result on Qt:

Qt

First of all, rounded should be added and the junction of the lines should be a smoother transition. I need a more professional solution. Which is pretty looking. Thanks! Code:

import sys, os, time
from PySide6 import QtCore, QtWidgets, QtGui
from PySide6.QtWidgets import *
from PySide6.QtCore import *
from PySide6.QtGui import *
    
class ECheckBoxData(object):
    Radius = 10
    AnimationTime = 600  # ms
    FontSize, FontSpacing = 16, 0
    Color = {
        "CORNER": QColor(239, 239, 239),
        "BASE_BACKGROUND": QColor(255, 125, 51),
        "BASE_FOREGROUND": QColor(255, 152, 91),

        "BASE_HOVER_BACKGROUND" :QColor(255, 152, 91),
        "BASE_HOVER_FOREGROUND": QColor(247, 247, 250),

    }
    TextElide = Qt.ElideMiddle
    CheckWidth, CheckHeight = 128, 128    


class ECheckBox(QCheckBox):
    CheckBoxData = ECheckBoxData()
   
    def __init__(self, CheckBoxData=ECheckBoxData()):
        super(ECheckBox, self).__init__(None)
        self.CheckBoxData = CheckBoxData
            
        self.myfont = QFont("Times New Roman", 16, weight=QFont.Bold)
        self.myfont.setWordSpacing(self.CheckBoxData.FontSpacing)
        self.myfont.setStyleHint(QFont.Monospace)
        self.myfontMetrics = QFontMetrics(self.myfont)
        # font.setStyleHint(QFont.Times, QFont.PreferAntialias)
        self.setFont(self.myfont)
        self.setFixedHeight(self.CheckBoxData.CheckHeight 2)
        self.setMinimumWidth(self.CheckBoxData.CheckWidth 8)
       
    def paintEvent(self, event: QPaintEvent):
        pt = QPainter(self)
        pt.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing )
        border = QPainterPath()
        pt.setBrush(self.CheckBoxData.Color["BASE_BACKGROUND"])
        pt.setPen(QPen(self.CheckBoxData.Color["CORNER"],5))
        border.addRoundedRect(QRectF(2,2,self.CheckBoxData.CheckWidth-2, self.CheckBoxData.CheckHeight-2),self.CheckBoxData.Radius, self.CheckBoxData.Radius)

        pt.drawPath(border)
        pt.setClipPath(border)

        pt.setPen(QPen(Qt.white,self.CheckBoxData.CheckWidth/10))
        pt.setBrush(Qt.white)
        path2 = QPainterPath()
        arrow_width, arrow_height = self.width()/4, self.height()/ (66/8)
        center_width, center_height = int(self.width()/2), int(self.height()/2)

        #path2.moveTo((self.width() - arrow_width * 2) / 2, (center_height   2))
        #path2.lineTo(QPoint((self.width() - arrow_width) / 2   2, (center_height)   arrow_height   1))
        #path2.lineTo(QPoint((self.width()-arrow_width), (center_height)-arrow_height))
        path2.addPolygon(QPolygonF([
            QPoint((self.width()-arrow_width*2)/2, (center_height 2)), QPoint((self.width()-arrow_width)/2 2, (center_height) arrow_height 1)
        ]))
        path2.addPolygon(QPolygonF([QPoint((self.width()-arrow_width)/2 2, (center_height) arrow_height 1), QPoint((self.width()-arrow_width-12), (center_height)-arrow_height)]))
        
        pt.drawPath(path2)
          


if __name__ == "__main__":
    app = QApplication(sys.argv)
    wind = QMainWindow()
    wind.setStyleSheet("QMainWindow{background-color:rgb(247,247,250)}")
    wind.resize(221, 150)
    wid = QWidget()
    lay = QHBoxLayout(wid)
    lay.setAlignment(Qt.AlignCenter)    
    Data = ECheckBoxData()
    e = ECheckBox(Data)
    e.setChecked(True)    
    lay.addWidget(e)
    wind.setCentralWidget(wid)
    wind.show()
    sys.exit(app.exec())

CodePudding user response:

In order to create a smooth and curved outline, you need to properly set the QPen cap and join style.

Using a polygon to draw the outline is obviously not a valid solution, as that outline will be drawn with the pen, but what you need is a path that will be painted with a thick pen and the preferred cap and join styles.

Also, in order to be able to draw a good icon at different sizes, you should not rely on fixed sizes (even if properly computed), but use the current size as a reference instead.

    def paintEvent(self, event: QPaintEvent):
        pt = QPainter(self)
        pt.setRenderHints(QPainter.Antialiasing | QPainter.TextAntialiasing)

        size = min(self.width(), self.height())
        border = max(1, size / 32)
        rect = QRectF(0, 0, size - border, size - border)
        # move the square to the *exact* center using a QRectF based on the
        # current widget; note: it is very important that you get the center
        # using a QRectF, because the center of QRect is always in integer
        # values, and will almost always be imprecise at small sizes
        rect.moveCenter(QRectF(self.rect()).center())

        borderPath = QPainterPath()
        # with RelativeSize we can specify the radius as 30% of half the size
        borderPath.addRoundedRect(rect, 30, 30, Qt.RelativeSize)

        pt.setBrush(self.CheckBoxData.Color["BASE_BACKGROUND"])
        pt.setPen(QPen(self.CheckBoxData.Color["CORNER"], border * 2.5))
        pt.drawPath(borderPath)

        pt.setPen(QPen(Qt.white, size * .125, 
            cap=Qt.RoundCap, join=Qt.RoundJoin))
        arrow_path = QPainterPath()
        arrow_path.moveTo(size * .25, size * .5)
        arrow_path.lineTo(size * .40, size * .65)
        arrow_path.lineTo(size * .7, size * .325)
        pt.drawPath(arrow_path.translated(rect.topLeft()))
  • Related