0

I have an animated toggle widget that i put in a QTableWidget. Now i'm not able to get the signal of what toggle changed in QTable. In this example when i click the toggle its only working with 1 item name so i don't know still what item fired the signal!

This is what i've tried

from PySide6.QtCore import (
    Qt,
    QSize,
    QPoint,
    QPointF,
    QRectF,
    QEasingCurve,
    QPropertyAnimation,
    QSequentialAnimationGroup,
    Slot,
    Property,
)

from PySide6.QtWidgets import QCheckBox
from PySide6.QtGui import QColor, QBrush, QPaintEvent, QPen, QPainter


class AnimatedToggle(QCheckBox):

    _transparent_pen = QPen(Qt.transparent)
    _light_grey_pen = QPen(Qt.lightGray)

    def __init__(
        self,
        parent=None,
        bar_color=Qt.gray,
        checked_color="#00B0FF",
        handle_color=Qt.white,
        pulse_unchecked_color="#44999999",
        pulse_checked_color="#4400B0EE",
    ):
        super().__init__(parent)

        # Save our properties on the object via self, so we can access them later
        # in the paintEvent.
        self._bar_brush = QBrush(bar_color)
        self._bar_checked_brush = QBrush(QColor(checked_color).lighter())

        self._handle_brush = QBrush(handle_color)
        self._handle_checked_brush = QBrush(QColor(checked_color))

        self._pulse_unchecked_animation = QBrush(QColor(pulse_unchecked_color))
        self._pulse_checked_animation = QBrush(QColor(pulse_checked_color))

        # Setup the rest of the widget.

        self.setContentsMargins(8, 0, 8, 0)
        self._handle_position = 0

        self._pulse_radius = 0

        self.animation = QPropertyAnimation(self, b"handle_position", self)
        self.animation.setEasingCurve(QEasingCurve.InOutCubic)
        self.animation.setDuration(200)  # time in ms

        self.pulse_anim = QPropertyAnimation(self, b"pulse_radius", self)
        self.pulse_anim.setDuration(0)  # time in ms
        self.pulse_anim.setStartValue(10)
        self.pulse_anim.setEndValue(20)

        self.animations_group = QSequentialAnimationGroup()
        self.animations_group.addAnimation(self.animation)
        self.animations_group.addAnimation(self.pulse_anim)

        self.stateChanged.connect(self.setup_animation)

    def sizeHint(self):
        return QSize(58, 45)

    def hitButton(self, pos: QPoint):
        return self.contentsRect().contains(pos)

    @Slot(int)
    def setup_animation(self, value):
        self.animations_group.stop()
        if value:
            self.animation.setEndValue(1)
        else:
            self.animation.setEndValue(0)
        self.animations_group.start()

    def paintEvent(self, e: QPaintEvent):

        contRect = self.contentsRect()
        handleRadius = round(0.24 * contRect.height())

        p = QPainter(self)
        p.setRenderHint(QPainter.Antialiasing)

        p.setPen(self._transparent_pen)
        barRect = QRectF(0, 0, contRect.width() - handleRadius, 0.40 * contRect.height())
        barRect.moveCenter(contRect.center())
        rounding = barRect.height() / 2

        # the handle will move along this line
        trailLength = contRect.width() - 2 * handleRadius

        xPos = contRect.x() + handleRadius + trailLength * self._handle_position

        if self.pulse_anim.state() == QPropertyAnimation.Running:
            p.setBrush(self._pulse_checked_animation if self.isChecked() else self._pulse_unchecked_animation)
            p.drawEllipse(QPointF(xPos, barRect.center().y()), self._pulse_radius, self._pulse_radius)

        if self.isChecked():
            p.setBrush(self._bar_checked_brush)
            p.drawRoundedRect(barRect, rounding, rounding)
            p.setBrush(self._handle_checked_brush)

        else:
            p.setBrush(self._bar_brush)
            p.drawRoundedRect(barRect, rounding, rounding)
            p.setPen(self._light_grey_pen)
            p.setBrush(self._handle_brush)

        p.drawEllipse(QPointF(xPos, barRect.center().y()), handleRadius, handleRadius)

        p.end()

    @Property(float)
    def handle_position(self):
        return self._handle_position

    @handle_position.setter
    def handle_position(self, pos):
        """change the property
        we need to trigger QWidget.update() method, either by:
            1- calling it here [ what we're doing ].
            2- connecting the QPropertyAnimation.valueChanged() signal to it.
        """
        self._handle_position = pos
        self.update()

    @Property(float)
    def pulse_radius(self):
        return self._pulse_radius

    @pulse_radius.setter
    def pulse_radius(self, pos):
        self._pulse_radius = pos
        self.update()

animated_toggle.py

from PySide6.QtWidgets import (
    QApplication,
    QMainWindow,
    QTableWidgetItem,
    QWidget,
    QHBoxLayout,
    QLabel,
    QTableWidget,
    QFrame,
    QSizePolicy,
    QLayout,
)
from PySide6 import QtCore, QtGui
from PySide6.QtUiTools import QUiLoader
import sys, os
from animated_toggle import AnimatedToggle
from qt_material import apply_stylesheet

os.chdir(os.path.dirname(__file__))


class Ui(QMainWindow):
    def __init__(self):
        super(Ui, self).__init__()
        self.main = QUiLoader().load("main_window.ui", self)
        apply_stylesheet(self.main, theme="my_theme.xml")
        # Define objects
        self.label = self.findChild(QLabel, "label")
        self.label_2 = self.findChild(QLabel, "label_2")
        self.label_3 = self.findChild(QLabel, "label_3")
        self.tableWidget = self.findChild(QTableWidget, "tableWidget")
        self.tableWidget_2 = self.findChild(QTableWidget, "tableWidget_2")
        self.frame = self.findChild(QFrame, "frame")
        self.frame_2 = self.findChild(QFrame, "frame_2")

        # fixing styles
        self.label.setStyleSheet("font-weight: bold")
        self.label_2.setStyleSheet("font-weight: bold")
        self.label_3.setStyleSheet("font-weight: bold")

        self.tableWidget.setStyleSheet(
            "QTableView#tableWidget { background-color: #152249;alternate-background-color: #1a2754;}"
        )
        self.tableWidget.horizontalHeader().setStyleSheet(
            "QHeaderView::section { font-weight: bold; color: #ffb900; border-right: 0px; border-bottom: 0px; border-top:2px }"
        )
        self.tableWidget_2.setStyleSheet("QTableView#tableWidget_2 { background-color: #1d2c5d;}")
        self.frame_2.setStyleSheet("QFrame#frame_2 { border: 2px solid #020b1a;} ")
        self.frame.setStyleSheet("QFrame#frame { border: 2px solid #020b1a;} ")

        self.fill_location_table()

    def fill_location_table(self):
        self.toggles = []
        for i in range(self.tableWidget_2.rowCount()):
            for j in range(self.tableWidget_2.columnCount()):
                name = f"{i}_{j}"
                self.container = QWidget()
                self.mainToggle = AnimatedToggle()
                self.toggles.append(self.mainToggle)
                self.mainToggle.setFixedSize(QtCore.QSize(51, 38))
                # self.mainToggle.stateChanged.connect(lambda: self.uncheck(name))
                layout = QHBoxLayout()
                layout.setContentsMargins(0, 0, 0, 0)
                layout.setSizeConstraint(QLayout.SetMinimumSize)
                self.container.setStyleSheet("QWidget { background-color: #1d2c5d;}")
                self.container.setLayout(layout)
                self.mainToggle.stateChanged.connect(lambda: self.uncheck(name))
                self.container.layout().addWidget(QLabel(name))
                self.container.layout().addWidget(self.mainToggle)
                self.tableWidget_2.setCellWidget(i, j, self.container)

    def uncheck(self, item):
        print(item)


if __name__ == "__main__":
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
    app = QApplication(sys.argv)
    window = Ui()
    window.main.show()
    app.exec()

main.py

thanks to all for the help!

marksoe
  • 58
  • 8
  • `stateChanged.connect(lambda state, name=name: self.uncheck(name))`. Also, setting instance attributes in a for loop is useless, just use local variables. – musicamante Nov 14 '22 at 17:46

0 Answers0