I am trying to create a custom checkbox. I am using paintEvent function to create my special checkbox. It's design:
The result on 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()))