I am electrical/power electronic engineer and I am trying to develop extremely simple-to-extend engineering calculator based on SymPy for equation solving and PyQt for simple user interface.
Now I am very stuck trying to achieve readability and correct execution in following stages:
- Generate widgets (and setup properties, connect signals to update values method) from dictionary using exec to achieve predictable names in few (easy understandable) lines of code. Works nicely.
self.variable_list = {
"R_1": 'R_1',
"R_2": 'R_2',
"R_load": 'R_load',
...
}
self.var_list = list(self.variable_list.keys())
self.var_names = list(self.variable_list.values())
for name in self.var_list:
exec("self.%s_input = gui_common.input_checked_field(\"%s\")" %(name, name))
exec("self.%s_input.setValidator(QtGui.QDoubleValidator())" %(name))
exec("self.%s_input.editingFinished.connect(self.update)" %(name))
exec("main_layout.addWidget(self.%s_input)" %(name))
- Initialize symbolic variables from same variables list in update function. Works nicely. Generates all variables as SymPy ones from single list.
for i in range(len(self.var_list)):
temp_string = "vars(sys.modules[__name__])[\'%s\'] = sp.symbols(\'%s\')" %(self.var_list[i], self.var_names[i])
exec(temp_string )#, globals(), locals()
exec("print(%s)" %(self.var_list[i]) )
- Update these already defined variables with numerical values before solving equation system. And these stage fails miraculously, since variables are not changed in exec. Why? Because it is not possible, as far as I understand, since CPython is optimizing something with local function variables. Setting variables with exec inside a function Variable not define after exec('variable = value')
for name in self.var_list:
temp_string = name + " = update_if_checked(" + name + ", self."+name+"_input)"
exec(temp_string) #can be supplied with globals() argument, what leads to self.* names not defined
Function update_if_checked is very simple, since it only checks if corresponding input field has its numeric value lock checkbox checked and if so modifies the variable.
def update_if_checked(var_in, field_in):
if field_in._checkbox.isChecked():
print("updating variable")
#nonlocal var_in
var_in = float(field_in._field.text())
return var_in
I have done research and understood that my MCU C with macro preprocessor background is completely out of water in this case, since using exec and eval in Python is heavily unrecommended. And has severe limitations such as inability to update variables in functions.
Can someone push me in right direction with this usecase, please? Any notes on the most Pythonic way to achieve readable batch variables operation are very welcomed. I am not afraid of learning something new, or researching complex case. But I am completely stuck since everything I have came upon severely damages symbolic equation parts readability. Exec-based approach allows to have native symbolic/numerical variables in equation system, allowing non-software-coders write equations efficiently:
system_without_load = [
I_in - U_in/(R_1+R_2),
U_out - (U_in - I_in*R_1),
P_div - I_in**2*(R_2+R_1),
Ratio - U_out/U_in,
]
If I understand correctly, one can define variables as calc's custom widget class fields and use them via "self." prefix, or one can define them as input field's custom widget field and use them with ".var" post-fix, both of which lead to equation system over-complication like:
system_without_load = [
I_in.var - U_in.var/(R_1.var+R_2.var),
U_out.var - (U_in.var - I_in.var*R_1.var),
P_div.var - I_in.var**2*(R_2.var+R_1.var),
Ratio.var - U_out.var/U_in.var,
]
Thanks everyone who reaches this far, reading this puzzle-headed from non-programmer engineer.