Search code examples
pythonpyqt5qt-designer

How to change values in QDoubleSpinBoxes using with QDoubleSpinBoxes?


Below is my example and my attempt of code:

from PyQt5 import QtCore, QtGui, QtWidgets

class Ui_MainWindow(QtWidgets.QMainWindow):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName("MainWindow")
        MainWindow.resize(469, 348)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.gridLayout_3 = QtWidgets.QGridLayout(self.centralwidget)
        self.gridLayout_3.setObjectName("gridLayout_3")
        self.frame = QtWidgets.QFrame(self.centralwidget)
        self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
        self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
        self.frame.setObjectName("frame")
        self.gridLayout_2 = QtWidgets.QGridLayout(self.frame)
        self.gridLayout_2.setObjectName("gridLayout_2")
        self.total_spin = QtWidgets.QDoubleSpinBox(self.frame)
        self.total_spin.setObjectName("total_spin")
        self.gridLayout_2.addWidget(self.total_spin, 0, 1, 1, 1)
        self.totalLabel = QtWidgets.QLabel(self.frame)
        self.totalLabel.setObjectName("totalLabel")
        self.gridLayout_2.addWidget(self.totalLabel, 0, 0, 1, 1)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.SpinBox_two = QtWidgets.QDoubleSpinBox(self.frame)
        self.SpinBox_two.setObjectName("SpinBox_two")
        self.gridLayout.addWidget(self.SpinBox_two, 1, 0, 1, 1)
        self.two_amount = QtWidgets.QDoubleSpinBox(self.frame)
        self.two_amount.setObjectName("two_amount")
        self.gridLayout.addWidget(self.two_amount, 1, 1, 1, 1)
        self.five_amount = QtWidgets.QDoubleSpinBox(self.frame)
        self.five_amount.setObjectName("five_amount")
        self.gridLayout.addWidget(self.five_amount, 4, 1, 1, 1)
        self.SpinBox_one = QtWidgets.QDoubleSpinBox(self.frame)
        self.SpinBox_one.setObjectName("SpinBox_one")
        self.gridLayout.addWidget(self.SpinBox_one, 0, 0, 1, 1)
        self.three_amount = QtWidgets.QDoubleSpinBox(self.frame)
        self.three_amount.setObjectName("three_amount")
        self.gridLayout.addWidget(self.three_amount, 2, 1, 1, 1)
        self.one_amount = QtWidgets.QDoubleSpinBox(self.frame)
        self.one_amount.setObjectName("one_amount")
        self.gridLayout.addWidget(self.one_amount, 0, 1, 1, 1)
        self.SpinBox_three = QtWidgets.QDoubleSpinBox(self.frame)
        self.SpinBox_three.setObjectName("SpinBox_three")
        self.gridLayout.addWidget(self.SpinBox_three, 2, 0, 1, 1)
        self.SpinBox_four = QtWidgets.QDoubleSpinBox(self.frame)
        self.SpinBox_four.setObjectName("SpinBox_four")
        self.gridLayout.addWidget(self.SpinBox_four, 3, 0, 1, 1)
        self.four_amount = QtWidgets.QDoubleSpinBox(self.frame)
        self.four_amount.setObjectName("four_amount")
        self.gridLayout.addWidget(self.four_amount, 3, 1, 1, 1)
        self.SpinBox_five = QtWidgets.QDoubleSpinBox(self.frame)
        self.SpinBox_five.setObjectName("SpinBox_five")
        self.gridLayout.addWidget(self.SpinBox_five, 4, 0, 1, 1)
        self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 2)
        self.gridLayout_3.addWidget(self.frame, 0, 0, 1, 1)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 469, 21))
        self.menubar.setObjectName("menubar")
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtWidgets.QStatusBar(MainWindow)
        self.statusbar.setObjectName("statusbar")
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        ####
        self.one_amount.setReadOnly(True)
        self.two_amount.setReadOnly(True)
        self.three_amount.setReadOnly(True)
        self.four_amount.setReadOnly(True)
        self.five_amount.setReadOnly(True)
        
        self.total_spin.setMaximum(99999999.99)
        self.one_amount.setMaximum(99999999.99)
        self.two_amount.setMaximum(99999999.99)
        self.three_amount.setMaximum(99999999.99)
        self.four_amount.setMaximum(99999999.99)
        self.five_amount.setMaximum(99999999.99)
        
        self.SpinBox_one.setMaximum(100.00)
        self.boxes = [self.SpinBox_one, self.SpinBox_two, self.SpinBox_three, self.SpinBox_four, self.SpinBox_five]

        self.SpinBox_one.valueChanged.connect(self.limit)
        self.SpinBox_two.valueChanged.connect(self.limit)
        self.SpinBox_three.valueChanged.connect(self.limit)
        self.SpinBox_four.valueChanged.connect(self.limit)
        ####
    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.totalLabel.setText(_translate("MainWindow", "total"))
        ###
    def limit(self, value):
        i = self.boxes.index(self.sender())
        total = value + sum(box.value() for box in self.boxes[:i])
        for box in self.boxes[i + 1:]:
            box.blockSignals(True) # Avoid extra calls to limit()
            box.setMaximum(100 - total)
            box.blockSignals(False)
            total += box.value()

        if self.SpinBox_one.valueChanged:
            self.one_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_one.value())
        if self.SpinBox_two.valueChanged:
            self.two_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_two.value())
        if self.SpinBox_three.valueChanged:
            self.three_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_three.value())
        if self.SpinBox_four.valueChanged:
            self.four_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_four.value())
        if self.SpinBox_five.valueChanged:
            self.five_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_five.value())
        ###
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    MainWindow = QtWidgets.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

It has total_spin box that value will be set manually, And 5 QDoubleSpinBoxes are named self.SpinBox_one, self.SpinBox_two, self.SpinBox_three, self.SpinBox_four, self.SpinBox_five will be set values manually with limit of 100 percent all together using below code:

    def limit(self, value):
        i = self.boxes.index(self.sender())
        total = value + sum(box.value() for box in self.boxes[:i])
        for box in self.boxes[i + 1:]:
            box.blockSignals(True) # Avoid extra calls to limit()
            box.setMaximum(100 - total)
            box.blockSignals(False)
            total += box.value()

and another 5 QDoubleSpinBoxes are named self.one_amount, self.SpinBox_three, self.SpinBox_four, self.SpinBox_five will be set the values automatically when self.SpinBox_one, self.SpinBox_two, self.SpinBox_three, self.SpinBox_four, self.SpinBox_five values changed. For example: if self.SpinBox_one value changed that will do self.total_spin.value() / 100 * self.SpinBox_one.value() and will be set the calculated value in to self.one_amount. And remaining are also happening link this. But last one(self.SpinBox_five) is not working. If self.SpinBox_five.valueChanged value changed its not caliculating and not setting the value. self.five_amount.setValue(self.total_spin.value() / 100 * self.SpinBox_five.value()). How to solve above issue and second one is: it should be not overwrite percentage values: for exampe: if i am setting 5 self.SpinBox_one, self.SpinBox_two, self.SpinBox_three, self.SpinBox_four, self.SpinBox_five values(percentage) as 50, 20, 10, 10, 10 and if i change the first value from 50 to 60 its changing last box 10 to 0. In this case if 100 percent packed that should be not changable in every QDoubleSpinBoxes. and if the values(percentage) are 50, 20, 10, 10 and last one is empty. In this case one of above self.SpinBox_one, self.SpinBox_two, self.SpinBox_three, self.SpinBox_four can be added remaining 10 percentage. after that should be pack. How to do this both.


Solution

  • If I understand correctly, you want all the spin boxes to lock when the total value is at 100, and not allow a spin box to overwrite the values of those below it. In that case the limit function will be simpler to implement, but consequently when the total value reaches 100, one of the spin boxes will need to be decreased before any of them can be increased.

    Additionally these statements — if self.SpinBox_one.valueChanged: — are not comparing if that spin box emitted the signal (use QObject.sender() to do that). The valueChanged property points to a pyqtBoundSignal of that object and will always evaluate to True simply because it exists. A better structure now is to place your spin boxes in a dict.

    self.boxes = {self.SpinBox_one: self.one_amount,
                  self.SpinBox_two: self.two_amount,
                  self.SpinBox_three: self.three_amount,
                  self.SpinBox_four: self.four_amount,
                  self.SpinBox_five: self.five_amount}
    

    For each box set its maximum to its current value + the remaining amount out of 100. And the value for the spin box corresponding to the sender can be assigned without any comparisons.

    def limit(self, value):
        remainder = 100 - sum(box.value() for box in self.boxes)
        for box in self.boxes:
            box.setMaximum(box.value() + remainder)
    
        self.boxes[self.sender()].setValue(self.total_spin.value() / 100 * value)
    

    And just a reminder to connect SpinBox_five, as mentioned in the comments.

    self.SpinBox_five.valueChanged.connect(self.limit)