2

I would like understand the best way to assign values and do calculations on a number of different variables in a PyQt environment where the variables are already assigned in a QtDesigner ui file.

The variables are named based on different groupings and because each group can have multiple variables, they have a secondary naming reflecting their order.

For example, I have;

{group_name}_range_{number}_start

{group_name}_range_{number}_end

I would like to know how to assign and work with a number of these variables in a more Pythonic way that copying this code multiple times and opening it up to errors.

This is my Qt ui file (single line to save space);

<?xml version="1.0" encoding="UTF-8"?><ui version="4.0"><class>MainWindow</class><widget class="QMainWindow" name="MainWindow"><property name="geometry"><rect><x>0</x><y>0</y><width>390</width><height>261</height></rect></property><property name="windowTitle"><string>MainWindow</string></property><widget class="QWidget" name="centralwidget"><widget class="QLineEdit" name="a_range_1_start"><property name="geometry"><rect><x>30</x><y>30</y><width>113</width><height>41</height></rect></property></widget><widget class="QLineEdit" name="a_range_1_end"><property name="geometry"><rect><x>150</x><y>30</y><width>113</width><height>41</height></rect></property></widget><widget class="QLineEdit" name="a_range_2_start"><property name="geometry"><rect><x>30</x><y>80</y><width>113</width><height>41</height></rect></property></widget><widget class="QLineEdit" name="a_range_2_end"><property name="geometry"><rect><x>150</x><y>80</y><width>113</width><height>41</height></rect></property></widget><widget class="QLineEdit" name="b_range_1_start"><property name="geometry"><rect><x>30</x><y>190</y><width>113</width><height>41</height></rect></property></widget><widget class="QLineEdit" name="b_range_1_end"><property name="geometry"><rect><x>150</x><y>190</y><width>113</width><height>41</height></rect></property></widget><widget class="QLabel" name="a_range_1_sum"><property name="geometry"><rect><x>280</x><y>40</y><width>71</width><height>21</height></rect></property><property name="font"><font><family>Arial</family><pointsize>12</pointsize></font></property><property name="text"><string>TextLabel</string></property></widget><widget class="QLabel" name="a_range_2_sum"><property name="geometry"><rect><x>280</x><y>90</y><width>71</width><height>21</height></rect></property><property name="font"><font><family>Arial</family><pointsize>12</pointsize></font></property><property name="text"><string>TextLabel</string></property></widget><widget class="QLabel" name="b_range_1_sum"><property name="geometry"><rect><x>280</x><y>200</y><width>71</width><height>21</height></rect></property><property name="font"><font><family>Arial</family><pointsize>12</pointsize></font></property><property name="text"><string>TextLabel</string></property></widget></widget></widget><resources/><connections/></ui>

I have the following variables a_range_1_start, a_range_1_end, a_range_2_start, a_range_2_end, b_range_1_start, b_range_1_end which fall into the format I stated above. The python code is as follows to replicate the issue;

import sys
import pandas as pd
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.uic import loadUiType

qtCreatorFile = r'test.ui'
Ui_MainWindow, QtBaseClass = loadUiType(qtCreatorFile)

class MainApp(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None, *args):
        super().__init__()
        self.initUI()

    def initUI(self, parent=None, *args):
        QMainWindow.__init__(self, None)
        Ui_MainWindow.__init__(self)
        self.df1 = pd.DataFrame({'mynumbers': range(10)},
                                index=['a','b','c','d','e','f','g','h','i','j'])
        self.df2 = pd.DataFrame({'values': range(10, 40)})
        self.setupUi(self)
        self.show()

        self.a_range_1_start.textChanged.connect(self.a_range_1_start_function)
        self.a_range_1_end.textChanged.connect(self.a_range_1_end_function)

    def a_range_1_start_function(self):
        # Print replaces other text functions here for example...
        print('start - ' + self.a_range_1_start.text())
        self.calc_a_range_1()

    def a_range_1_end_function(self):
        print('end - ' + self.a_range_1_end.text())
        self.calc_a_range_1()

    def calc_a_range_1(self):
        myvalue = self.df1.loc[self.a_range_1_start.text():self.a_range_1_end.text(),:].mynumbers.sum()
        if myvalue > 0:
            self.a_range_1_sum.setText('{:,.2f}'.format(myvalue))

if __name__ == "__main__":
    app = QApplication(sys.argv)
    w = MainApp()
    w.show()
    sys.exit(app.exec_())

This is just a quick recreation of the problem, the functions and process are not exactly what I am using.

In the end, I would like to have in the a_range_2_start, a_range_2_end variables in the ui to be attached to df1 again (similar to how a_range1_start and a_range_1_end perform calcs on df1) but the display is attached to a_range_2_sum QLabel.

Also I would like to have b_range_1_start and b_range_1_end to be attached to df2 instead and respective QLabel.

Hopefully this is enough to describe the problem. Many thanks!

1 Answer 1

1

There are probably many different ways to solve this kind of problem, but I am going to only suggest one. The idea is to group several objects into one, and provide a simple, unified API for them. This can provide quite good opportunities for code re-use, depending on how the objects are grouped and how much overlap of existing functionality there is.

This approach probably has a well-established Design Pattern name: the Facade Pattern, maybe, or perhaps the Mediator Pattern - I'm not sure.

Anyway - here is how it could be used with your example:

class RangeGroup(QObject):
    def __init__(self, parent, df, r_start, r_end, r_sum):
        super(RangeGroup, self).__init__(parent)
        self.df = df
        self.range_start = r_start
        self.range_end = r_end
        self.range_sum = r_sum
        self.range_start.textChanged.connect(self.range_start_function)
        self.range_end.textChanged.connect(self.range_end_function)

    def range_start_function(self):
        print('start - ' + self.range_start.text())
        self.calc_range()

    def range_end_function(self):
        print('end - ' + self.range_end.text())
        self.calc_range()

    def calc_range(self):
        myvalue = self.df.loc[self.range_start.text():self.range_end.text(),:].mynumbers.sum()
        if myvalue > 0:
            self.range_sum.setText('{:,.2f}'.format(myvalue))

class MainApp(QMainWindow, Ui_MainWindow):
    def __init__(self, parent=None, *args):
        super().__init__()
        self.initUI()

    def initUI(self, parent=None, *args):
        QMainWindow.__init__(self, None)
        Ui_MainWindow.__init__(self)
        self.setupUi(self)

        df1 = pd.DataFrame({'mynumbers': range(10)},
                                index=['a','b','c','d','e','f','g','h','i','j'])
        df2 = pd.DataFrame({'values': range(10, 40)})

        self.range_A1 = RangeGroup(
            self, df1, self.a_range_1_start,
            self.a_range_1_end, self.a_range_1_sum)
        self.range_A2 = RangeGroup(
            self, df1, self.a_range_2_start,
            self.a_range_2_end, self.a_range_2_sum)
        self.range_B1 = RangeGroup(
            self, df2, self.b_range_1_start,
            self.b_range_1_end, self.b_range_1_sum)

        self.show()

I have made the RangeGropup class a QObject so that it provides a parent() method for access to the parent main-window, and also allows custom signals to be defined if necessary.

Sign up to request clarification or add additional context in comments.

1 Comment

Amazing, thank you very much. I am learning these methods and patterns and I very much appreciate your help and also references. Cheers!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.