Reputation: 1495
I know the brute force way to do what I want but I am pretty sure there is a much more elegant way to accomplish my task. So I’m looking for help on an approach that’s better than the brute force way.
I have a spreadsheet like application with 21 rows and 5 columns on a grid. The first columns in the first row simply take user entered weight values (w1, w2, w3, w4). The 5th column sums the weight values. I have this working fine and don’t need much help.
The complexity comes in for rows 2 to 20. For each row, the user enters values in columns 1 : 4 and then a weighted average for the row is calculated in column 5 (using the weights from row 1). For example, for any given row, if the user entered values go into QLineEdit widgets named va1, va2, va3, va4, then va_wa= va1*w1 +va2*w2 +va3*w3 +va4*w4.
This is easy to do in code for a single row. But I’m not sure how to accomplish is for another row without copying the code over and over and changing the names for each row (the brute force way).
Here’s my code:
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyForm,self).__init__(parent)
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
self.ui.mdiArea.addSubWindow(self.ui.subwindow)
self.ui.mdiArea.addSubWindow(self.ui.subwindow_2)
QtCore.QTimer.singleShot(10, lambda: self.ui.mdiArea.setActiveSubWindow(self.ui.mdiArea.subWindowList()[0]))
self.ui.wt1.editingFinished.connect(self.runBoth)
self.ui.wt2.editingFinished.connect(self.runBoth)
self.ui.wt3.editingFinished.connect(self.runBoth)
self.ui.wt4.editingFinished.connect(self.runBoth)
self.ui.ca1.editingFinished.connect(self.waCalc)
self.ui.ca2.editingFinished.connect(self.waCalc)
self.ui.ca3.editingFinished.connect(self.waCalc)
self.ui.ca4.editingFinished.connect(self.waCalc)
def runBoth(self):
self.wtResult()
self.waCalc()
def wtResult(self):
if len(self.ui.wt1.text())!=0:
a=float(self.ui.wt1.text())
else:
a=0
if len(self.ui.wt2.text())!=0:
b=float(self.ui.wt2.text())
else:
b=0
if len(self.ui.wt3.text())!=0:
c=float(self.ui.wt3.text())
else:
c=0
if len(self.ui.wt4.text())!=0:
d=float(self.ui.wt4.text())
else:
d=0
sum=a+b+c+d
self.ui.wt_total.setText(str(sum))
def waCalc(self):
if len(self.ui.ca1.text())!=0:
ca1=float(self.ui.ca1.text())
else:
ca1=0
if len(self.ui.ca2.text())!=0:
ca2=float(self.ui.ca2.text())
else:
ca2=0
if len(self.ui.ca3.text())!=0:
ca3=float(self.ui.ca3.text())
else:
ca3=0
if len(self.ui.ca4.text())!=0:
ca4=float(self.ui.ca4.text())
else:
ca4=0
if len(self.ui.wt1.text())!=0:
wt1=float(self.ui.wt1.text())
else:
wt1=0
if len(self.ui.wt2.text())!=0:
wt2=float(self.ui.wt2.text())
else:
wt2=0
if len(self.ui.wt3.text())!=0:
wt3=float(self.ui.wt3.text())
else:
wt3=0
if len(self.ui.wt4.text())!=0:
wt4=float(self.ui.wt4.text())
else:
wt4=0
wa=(wt1*ca1)+(wt2*ca2)+(wt3*ca3)+(wt4*ca4)
self.ui.ca_wa.setText(str(wa))
if __name__ == "__main__":
app = QtGui.QApplication(sys.argv)
myapp=MyForm()
myapp.show()
app.exec_()
So I've shown the example where the row has ca1, ca2, ca3,ca4,ca_wa. What would I do for the next 19 rows (other than copy the wa_Calc code 19 times and change the variables to nx1:4,nx_wa ab1:4,ab_wa,ba1:4,ba_wa ...etc. I know there is a more elegant approach.
Upvotes: 0
Views: 67
Reputation: 52039
This is quite involved, so I''ll just give you an overview and some pointers on how to complete it.
The general outline is this:
Equation
object to record the functional relationship between widgets.Step 1. The Equation class.
Creating a new Equation
object might look like:
eq1 = Equation("wt_total", computeSum, ["wt1", "wt2", "wt3", "wt4"])
eq2 = Equation("ca_wa", computeDot, ["wt1", "wt2", "wt3", "wt4", "ca1", "ca2", "ca3", "ca4"])
computeSum
and computeDor
might look like:
def computeSum(arr):
return sum(arr)
def computDot(arr):
xs = arr[0:3]
ys = arr[4:7]
return sum ([ x*y for (x,y) in zip(xs,ys) ])
You will need the following slots/methods for the Equation
class:
You will need a place to store all of the equations. In the code below
I use self.equations
where self
is a MyForm
object.
Step 2. - Updating a single equation.
A method to update a single equation would look like:
# update a single equation
def update(self, eq):
args = []
for wname in eq.argWidgets():
val = ...lookup and convert value stored in wname...
args.append(val)
result = eq.compute(args)
# store result in target widget eq.target
Step 3. Updated affected equations.
First we develop a method to determine all of the affected equations:
# return the equations affected by a change in widget wname
def affected(self, wname):
return [ e | if e.affected(wname) for e in self.equations ]
The handleEditingFinished method will be called with a widget name:
def handleEditingFinished(self, wname):
eqs = self.affected(wname)
for e in eqs:
self.update(e)
Step 4. Hook up all of the callbacks.
This code is untested, but hopefully the intent is clear.
We just route all of the editingFinished callbacks to our
handleEditingFinished
method with the widget named passed as the
first argument.
from functools import partial
def callback(self, wname):
self.handleEditingFinished(wname)
for e in self.equations:
for wname in e.argWidgets():
w = ... get the widget named wname...
w.editingFinished(partial(callback, self, wname))
Upvotes: 4
Reputation: 42758
Use lists:
class MyForm(QtGui.QMainWindow):
def __init__(self, parent=None):
super(MyForm,self).__init__(parent)
self.ui=Ui_MainWindow()
self.ui.setupUi(self)
self.ui.mdiArea.addSubWindow(self.ui.subwindow)
self.ui.mdiArea.addSubWindow(self.ui.subwindow_2)
QtCore.QTimer.singleShot(10, lambda: self.ui.mdiArea.setActiveSubWindow(self.ui.mdiArea.subWindowList()[0]))
self.ui_weights = [getattr(self.ui,'wt%d'%i) for i in range(1,5)]
self.ui_cas = [
[getattr(self.ui,'ca%d_%d'%(row,i)) for i in range(1,5)]
for row in range(1,21)
]
self.ui_cawas = [getattr(self.ui,'ca_wa_%d'%row) for row in range(1,21)]
for wt in self.ui_weights:
wt.editingFinished.connect(self.runBoth)
for row in self.ui_cas:
for cell in row:
cell.editingFinished.connect(self.waCalc)
def runBoth(self):
self.wtResult()
self.waCalc()
def wtResult(self):
result = sum(float(wt.text() or 0) for wt in self.ui_weights)
self.ui.wt_total.setText(str(result))
def waCalc(self):
for row, wa in zip(self.ui_cas,self.ui_cawas):
result = sum(
float(ca.text() or 0) * float(wt.text() or 0)
for ca,wt in zip(row, self.ui_weights)
)
wa.setText(str(result))
Upvotes: 3