jim mako
jim mako

Reputation: 561

PyQt - Managing variable names and values through code

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!

Upvotes: 2

Views: 1662

Answers (1)

ekhumoro
ekhumoro

Reputation: 120628

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.

Upvotes: 1

Related Questions