[prev in list] [next in list] [prev in thread] [next in thread] 

List:       pykde
Subject:    Re: [PyQt] How to make a circular button?
From:       Maurizio Berti <maurizio.berti () gmail ! com>
Date:       2020-03-03 13:22:11
Message-ID: CAPn+-XQ1Cf+v3ixyy8bM-79ABFPUa8qko6M5fcTEYE68FKAX-Q () mail ! gmail ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


You'll have to subclass QPushButton, provide a way to set/change the
radius, and at least override the paintEvent to actually draw the circle
and possibly the sizeHint to report the preferred size of the button (based
on the radius), unless you to set a fixed size when setting the radius.

In the following example I've used two gradients to get a round button with
borders that have a "shadow" effect: it's composed of a conical gradient
with a superimposed radial gradient to "mask" the center of the conical.

class CircleButton(QtWidgets.QPushButton):
    def __init__(self, radius=None, parent=None):
        super().__init__(parent)
        if radius is None:
            radius = self.font().pointSize() * 2
        self.setRadius(radius)

        # use the palette as source for the colors
        palette = self.palette()
        light = palette.color(palette.Light)
        midlight = palette.color(palette.Midlight)
        mid = palette.color(palette.Mid)
        dark = palette.color(palette.Dark)

        # a radial gradient for the "shadow effect" when button is unpressed
        self.backgroundUp = QtGui.QConicalGradient(.5, .5, 135)

self.backgroundUp.setCoordinateMode(self.backgroundUp.ObjectBoundingMode)
        self.backgroundUp.setStops([
            (0.0, light),
            (0.3, dark),
            (0.6, dark),
            (1.0, light),
        ])

        # the same as above, but inverted for pressed state
        self.backgroundDown = QtGui.QConicalGradient(.5, .5, 315)

self.backgroundDown.setCoordinateMode(self.backgroundDown.ObjectBoundingMode)
        self.backgroundDown.setStops(self.backgroundUp.stops())

        # a "mask" for the conical gradient
        self.ringShapeDown = QtGui.QRadialGradient(.5, .5, .5)

self.ringShapeDown.setCoordinateMode(self.ringShapeDown.ObjectBoundingMode)
        self.ringShapeDown.setStops([
            (0.7536231884057971, midlight),
            (0.7960662525879917, QtCore.Qt.transparent),
        ])

        self.ringShapeUp = QtGui.QRadialGradient(.5, .5, .5)

self.ringShapeUp.setCoordinateMode(self.ringShapeUp.ObjectBoundingMode)
        self.ringShapeUp.setStops([
            (0.7536231884057971, mid),
            (0.7960662525879917, QtCore.Qt.transparent),
            (0.9627329192546584, QtCore.Qt.transparent),
        ])

    def getButtonRect(self):
        # just a helper function to avoid repetitions
        size = min(self.width(), self.height()) - 1
        rect = QtCore.QRect(0, 0, size, size)
        rect.moveCenter(self.rect().center())
        return rect

    def mousePressEvent(self, event):
        # ensure that the click happens within the circle
        path = QtGui.QPainterPath()
        path.addEllipse(QtCore.QRectF(self.getButtonRect()))
        if path.contains(event.pos()):
            super().mousePressEvent(event)

    def setRadius(self, radius):
        self.radius = radius
        # notify the layout manager that the size hint has changed
        self.updateGeometry()

    def sizeHint(self):
        return QtCore.QSize(self.radius, self.radius)

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width):
        return width

    def paintEvent(self, event):
        qp = QtGui.QPainter(self)
        qp.setRenderHints(qp.Antialiasing)
        qp.translate(.5, .5)
        qp.setPen(QtCore.Qt.NoPen)
        rect = self.getButtonRect()
        if self.isDown() or self.isChecked():
            qp.setBrush(self.backgroundDown)
            qp.drawEllipse(rect)
            qp.setBrush(self.ringShapeDown)
            qp.drawEllipse(rect)
        else:
            qp.setBrush(self.backgroundUp)
            qp.drawEllipse(rect)
            qp.setBrush(self.ringShapeUp)
            qp.drawEllipse(rect)

Il giorno mar 3 mar 2020 alle ore 13:08 Souvik Dutta Chowdhury <
souvik.viksou@outlook.com> ha scritto:

> I want to make a perfect circular button and want to set a custom radius.
> How to do it?
> _______________________________________________
> PyQt mailing list    PyQt@riverbankcomputing.com
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>


-- 
È difficile avere una convinzione precisa quando si parla delle ragioni del
cuore. - "Sostiene Pereira", Antonio Tabucchi
http://www.jidesk.net

[Attachment #5 (text/html)]

<div dir="ltr"><div dir="ltr"><div dir="ltr">You&#39;ll have to subclass QPushButton, \
provide a way to set/change the radius, and at least override the paintEvent to \
actually draw the circle and possibly the sizeHint to report the preferred size of \
the button (based on the radius), unless you to set a fixed size when setting the \
radius.</div><div dir="ltr"><br></div><div>In the following example I&#39;ve used two \
gradients to get a round button with borders that have a &quot;shadow&quot; effect: \
it&#39;s composed of a conical gradient with a superimposed radial gradient to \
&quot;mask&quot; the center of the conical.</div><div \
dir="ltr"><div><br></div><div><div><font face="monospace">class \
CircleButton(QtWidgets.QPushButton):</font></div><div><font face="monospace">      \
def __init__(self, radius=None, parent=None):</font></div><div><font \
face="monospace">            super().__init__(parent)</font></div><div><font \
face="monospace">            if radius is None:</font></div><div><font \
face="monospace">                  radius = self.font().pointSize() * \
2</font></div><div><font face="monospace">            \
self.setRadius(radius)</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">            # use the \
palette as source for the colors</font></div><div><font face="monospace">            \
palette = self.palette()</font></div><div><font face="monospace">            light = \
palette.color(palette.Light)</font></div><div><font face="monospace">            \
midlight = palette.color(palette.Midlight)</font></div><div><font face="monospace">   \
mid = palette.color(palette.Mid)</font></div><div><font face="monospace">            \
dark = palette.color(palette.Dark)</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">            # a radial \
gradient for the &quot;shadow effect&quot; when button is \
unpressed</font></div><div><font face="monospace">            self.backgroundUp = \
QtGui.QConicalGradient(.5, .5, 135)</font></div><div><font face="monospace">          \
self.backgroundUp.setCoordinateMode(self.backgroundUp.ObjectBoundingMode)</font></div><div><font \
face="monospace">            self.backgroundUp.setStops([</font></div><div><font \
face="monospace">                  (0.0, light),  </font></div><div><font \
face="monospace">                  (0.3, dark),  </font></div><div><font \
face="monospace">                  (0.6, dark),  </font></div><div><font \
face="monospace">                  (1.0, light),  </font></div><div><font \
face="monospace">            ])</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">            # the same \
as above, but inverted for pressed state</font></div><div><font face="monospace">     \
self.backgroundDown = QtGui.QConicalGradient(.5, .5, 315)</font></div><div><font \
face="monospace">            \
self.backgroundDown.setCoordinateMode(self.backgroundDown.ObjectBoundingMode)</font></div><div><font \
face="monospace">            \
self.backgroundDown.setStops(self.backgroundUp.stops())</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">            # a \
&quot;mask&quot; for the conical gradient</font></div><div><font face="monospace">    \
self.ringShapeDown = QtGui.QRadialGradient(.5, .5, .5)</font></div><div><font \
face="monospace">            \
self.ringShapeDown.setCoordinateMode(self.ringShapeDown.ObjectBoundingMode)</font></div><div><font \
face="monospace">            self.ringShapeDown.setStops([</font></div><div><font \
face="monospace">                  (0.7536231884057971, midlight),  \
</font></div><div><font face="monospace">                  (0.7960662525879917, \
QtCore.Qt.transparent),   </font></div><div><font face="monospace">            \
])</font></div><div><font face="monospace"><br></font></div><div><font \
face="monospace">            self.ringShapeUp = QtGui.QRadialGradient(.5, .5, \
.5)</font></div><div><font face="monospace">            \
self.ringShapeUp.setCoordinateMode(self.ringShapeUp.ObjectBoundingMode)</font></div><div><font \
face="monospace">            self.ringShapeUp.setStops([</font></div><div><font \
face="monospace">                  (0.7536231884057971, mid),  \
</font></div><div><font face="monospace">                  (0.7960662525879917, \
QtCore.Qt.transparent),  </font></div><div><font face="monospace">                  \
(0.9627329192546584, QtCore.Qt.transparent),  </font></div><div><font \
face="monospace">            ])</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">      def \
getButtonRect(self):</font></div><div><font face="monospace">            # just a \
helper function to avoid repetitions</font></div><div><font face="monospace">         \
size = min(self.width(), self.height()) - 1</font></div><div><font face="monospace">  \
rect = QtCore.QRect(0, 0, size, size)</font></div><div><font face="monospace">        \
rect.moveCenter(self.rect().center())</font></div><div><font face="monospace">        \
return rect</font></div><div><font face="monospace"><br></font></div><div><font \
face="monospace">      def mousePressEvent(self, event):</font></div><div><font \
face="monospace">            # ensure that the click happens within the \
circle</font></div><div><font face="monospace">            path = \
QtGui.QPainterPath()</font></div><div><font face="monospace">            \
path.addEllipse(QtCore.QRectF(self.getButtonRect()))</font></div><div><font \
face="monospace">            if path.contains(event.pos()):</font></div><div><font \
face="monospace">                  \
super().mousePressEvent(event)</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">      def \
setRadius(self, radius):</font></div><div><font face="monospace">            \
self.radius = radius</font></div><div><font face="monospace">            # notify the \
layout manager that the size hint has changed</font></div><div><font \
face="monospace">            self.updateGeometry()</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">      def \
sizeHint(self):</font></div><div><font face="monospace">            return \
QtCore.QSize(self.radius, self.radius)</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">      def \
hasHeightForWidth(self):</font></div><div><font face="monospace">            return \
True</font></div><div><font face="monospace"><br></font></div><div><font \
face="monospace">      def heightForWidth(self, width):</font></div><div><font \
face="monospace">            return width</font></div><div><font \
face="monospace"><br></font></div><div><font face="monospace">      def \
paintEvent(self, event):</font></div><div><font face="monospace">            qp = \
QtGui.QPainter(self)</font></div><div><font face="monospace">            \
qp.setRenderHints(qp.Antialiasing)</font></div><div><font face="monospace">           \
qp.translate(.5, .5)</font></div><div><font face="monospace">            \
qp.setPen(QtCore.Qt.NoPen)</font></div><div><font face="monospace">            rect = \
self.getButtonRect()</font></div><div><font face="monospace">            if \
self.isDown() or self.isChecked():</font></div><div><font face="monospace">           \
qp.setBrush(self.backgroundDown)</font></div><div><font face="monospace">             \
qp.drawEllipse(rect)</font></div><div><font face="monospace">                  \
qp.setBrush(self.ringShapeDown)</font></div><div><font face="monospace">              \
qp.drawEllipse(rect)</font></div><div><font face="monospace">            \
else:</font></div><div><font face="monospace">                  \
qp.setBrush(self.backgroundUp)</font></div><div><font face="monospace">               \
qp.drawEllipse(rect)</font></div><div><font face="monospace">                  \
qp.setBrush(self.ringShapeUp)</font></div><div><font face="monospace">                \
qp.drawEllipse(rect)</font></div></div></div></div></div><br><div \
class="gmail_quote"><div dir="ltr" class="gmail_attr">Il giorno mar 3 mar 2020 alle \
ore 13:08 Souvik Dutta Chowdhury &lt;<a \
href="mailto:souvik.viksou@outlook.com">souvik.viksou@outlook.com</a>&gt; ha \
scritto:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px \
0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="auto">I want \
to make a perfect circular button and want to set a custom radius. How to do \
it?</div>_______________________________________________<br> PyQt mailing list      \
<a href="mailto:PyQt@riverbankcomputing.com" \
target="_blank">PyQt@riverbankcomputing.com</a><br> <a \
href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt" rel="noreferrer" \
target="_blank">https://www.riverbankcomputing.com/mailman/listinfo/pyqt</a><br> \
</blockquote></div><br clear="all"><div><br></div>-- <br><div dir="ltr" \
class="gmail_signature">È difficile avere una convinzione precisa quando si parla \
delle ragioni del cuore. - &quot;Sostiene Pereira&quot;, Antonio Tabucchi<br><a \
href="http://www.jidesk.net" target="_blank">http://www.jidesk.net</a></div>


[Attachment #6 (text/plain)]

_______________________________________________
PyQt mailing list    PyQt@riverbankcomputing.com
https://www.riverbankcomputing.com/mailman/listinfo/pyqt


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic