Reputation: 561
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
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