I have a ton of different labels that are going to have different numbers on them. I'm creating a running app where you write down how much you run each day. But for some reason doing it dynamically like below isn't working. Do I need to create some sort of id object?
def mileageinput(self, mileagevalue):
global curmileage
global runs
try:
curmileage = float(mileagevalue)
runs[activeday] = curmileage
calendar = self.manager.get_screen('calendar')
label = "mileagerun" + str(activeday)
print(label)
# Changing the identifier like this isn't working:
# calendar.ids.label.text = str(curmileage) + " miles"
# This works, but I have tons of different labels and I want it
# to work dynamically:
calendar.ids.mileagerun2.text = str(curmileage) + " miles"
except:
curmileage = 0
Your commented-out line is trying to access an id named label, but you want to access the id whose name is the value of the variable label.
ids can be accessed by dot notation or with an index in brackets -- that is, ids.foo is the same thing as ids['foo'], and in this case the latter is easier to work with it. Try this:
calendar.ids[label].text = str(curmileage) + " miles"
(Note that this is ids[label], NOT ids['label']. The latter would be equivalent to your current code.)
For completeness's sake, here's one more way to do it. Kivy intentionally makes ids available in both ways for your convenience. But what if you were working with something else, that is only available as attributes, and not as indices? Suppose that, after the code below, you want to somehow retrieve the value of menu.spam dynamically, using the value of item.
menu = object()
menu.spam = False
menu.eggs = True
item = 'spam'
The way to do this is with a builtin function, getattr:
print(getattr(menu, item))
>>> False
Thus, your code could also use:
getattr(calendar.ids, label).text = str(curmileage) + " miles"
In this case the first option is, I think, much more readable, but getattr is good to know about in other, similar cases.
And one final note: you might enjoy trying out f-strings, which can make it much more convenient to insert variables into strings. For instance, str(curmileage) + " miles" could instead be:
f"{curmileage} miles"
It handles turning things into strings for you automatically, and you can insert as many variables as you need all into the same string.
Related
This seems like a really simple question, but has me stumped. I've got a UI that has multiple QLineEdits for names, start, and end times. For example:
clipName1, clipStart1, clipEnd1
clipName2, clipStart2, clipEnd2
clipName2, clipStart3, clipEnd3
These are not dynamically built on the fly. They are static. I wish to access the values from these by going through a loop. I am not sure how I can append an integer onto the variable name and still be able to access the value. I've tried this which I know doesn't work:
clipTotal = 4
for i in range(1, clipTotal+1):
clipName = self.clipName+str(i)+.text()
Answer provided by ekhumoro in comments above:
clipName = getattr(self, 'clipName%d' % i).text()
I want to read some input, which contains python assignment statements like this string:
"VARIABLE = 'something' + OTHER_VAR"
So I use one of these:
exec("VARIABLE = 'something' + OTHER_VAR")
exec("VARIABLE = 'something' + OTHER_VAR", globals(), locals())
I want to use this variable in other code, but after exec(...) it is not in current namespace.
It is possible to get the variable value like this:
locals()['VARIABLE']
however, if I dont know the name of variable it is not solution for me.
So how to get that new variable into my namespace?
UPDATE:
My data for exec are like this:
COMPBLOCK = StringLeft(MyPlatform.HierarchicalName,6) +"_IADI." + CMP + "." + BLOCK ;
SetCustomPropertyValue("DEVLM2",COMPBLOCK + ".DEVLM2",false);
SetCustomPropertyValue("CRIT",COMPBLOCK + ".CRIT",false);
SetCustomPropertyValue("UNACK",COMPBLOCK + ".UNACK",false);
SetCustomPropertyValue("ALMSTA_26",COMPBLOCK + ".ALMSTA#B26",false);
I defined functions SetCustomPropertyValue and StringLeft. I want to avoid some complicated translation of this script to python with all possible inputs. Exec() seems to be very quick solution, but after reading this post - Modifying locals in python I am little bit stuck.
pay attention to the comments warning about how dangerous it is to execute arbitrary code from a foreign source.
if the statements have a consistent format, for example like the one in the example, you could easly parse it and extract the variable name:
varname = stmt.split('=')[0].strip()
or something more sophisticated using regular expressions
if the statement always introduces exactly one new variable, you could compare locals() before and after execution and check which new variable has been added:
old_locals = set(locals().keys())
exec(stmt)
new_locals = set(locals().keys())
varname = (new_locals-old_locals).pop()
How about using a small domain specific language as found in configparser to declare your new variables?
This way you don't need to run untrusted code, and get simple things like variable expansion (though probably in a slightly different syntax).
E.g. considering the following input
FOO = world
BAR = hello, #FOO#
as simple parser could look like:
lines=["FOO = world", "BAR = hello, #FOO#" ]
vars={}
# store variables in dictionary, with expansion
for line in lines:
key, value = [x.strip() for x in line.split('=', 1)]
for k in vars:
value=value.replace('#%s#' % (k), str(vars[k]))
vars[key]=value
# make variables available in local namespace
for k in vars:
locals()[k]=vars[k]
## use new variable
print(BAR)
There is some issues around locals() in Python 3 (see this post) so generally speaking, changes in locals() during runtime is not allowed.
So I made some workaround, where I defined my own namespace dictonary my_dict_locals and my_dict_globals. To my_dict_globals I copied some necessary definitions from current namespace (like SetCustomPropertyValue function definition ...). Then I just called
exec(each, my_dict_globals, my_dict_locals)
where each could be like one of following:
COMPBLOCK = StringLeft(MyPlatform.HierarchicalName,6) +"_IADI." + CMP + "." + BLOCK ;
SetCustomPropertyValue("DEVLM2",COMPBLOCK + ".DEVLM2",false);
SetCustomPropertyValue("CRIT",COMPBLOCK + ".CRIT",false);
SetCustomPropertyValue("UNACK",COMPBLOCK + ".UNACK",false);
SetCustomPropertyValue("ALMSTA_26",COMPBLOCK + ".ALMSTA#B26",false);`
This works for me as I expected and I have in my_dict_locals all variables from above script defined.
For a set of games I'm creating I have a line a code that allows a question to be answered only once. If it has been answered, it adds points to a player's score (the code below sits inside of an if function that checks the answer) and then shuts off the ability to answer the question again. Here's the code I'm currently using:
while self.game1_question1_not_answered:
self.game1_player1_score += self.score_increment
self.game1_question1_not_answered = False`
I would like to use the 'game1' in the code as a generic identifier that can be used to identify any one of the multiple games I'm creating. I tried using a variable called game_name (e.g., game_name = game1) and inserting the variable into the code using an eval function but haven't gotten the code to work. In addition, I realize the eval function has some security concerns. What function could I use to get this to work? The code I've tried that doesn't work looks like this:
while eval('self.' + game_name + 'question1_not_answered'):
eval('self.' + game_name + 'player1_score') += self.score_increment
eval('self.' + game_name + 'question1_not_answered') = False
Is there another function I could use instead of eval to get this to work?
You should use a dict instead. That will allow you to create dynamically-named variables, as it were, to store that information.
self.game_dict = {}
self.game_dict[game_name + 'question1_not_answered'] = True
Then you can modify it as above, and access it in a couple ways:
>>> game_obj.game_dict.get(game_name + 'question1_not_answered')
True
>>> game_obj.game_dict[game_name + 'question1_not_answered']
True
But as jonrsharpe said, your variable names should not include data. The best solution would be to make multiple game objects, each with variables like question1_not_answered. Then if you need to, assign all of those objects to variables in whatever self is in this case.
I'd actually follow #jonrsharpe's comment, but if you really need to have them in one instance, you can use getattr and setattr builtins:
game_name = "game1"
question1_na_line = "{}_question1_not_answered"
player1_sc_line = "{}_player1_score"
question1_na = question1_na_line.format(game_name)
player1_sc = player1_sc_line.format(game_name)
while getattr(self, question1_na):
setattr(self,
player1_sc,
getattr(self, player1_sc) + self.score_increment)
setattr(self, question1_na, False)
The usual way to access attributes by name is the getattr(obj, name) function. Now given the more specific problem you describe I think you'd be better using a dict (or dict of dicts etc) to store your games states (scores etc).
OK.
So I've got a database where I want to store references to other Python objects (right now I'm using to store inventory information for person stores of beer recipe ingredients).
Since there are about 15-20 different categories of ingredients (all represented by individual SQLObjects) I don't want to do a bunch of RelatedJoin columns since, well, I'm lazy, and it seems like it's not the "best" or "pythonic" solution as it is.
So right now I'm doing this:
class Inventory(SQLObject):
inventory_item_id = IntCol(default=0)
amount = DecimalCol(size=6, precision=2, default=0)
amount_units = IntCol(default=Measure.GM)
purchased_on = DateCol(default=datetime.now())
purchased_from = UnicodeCol(default=None, length=256)
price = CurrencyCol(default=0)
notes = UnicodeCol(default=None)
inventory_type = UnicodeCol(default=None)
def _get_name(self):
return eval(self.inventory_type).get(self.inventory_item_id).name
def _set_inventory_item_id(self, value):
self.inventory_type = value.__class__.__name__
self._SO_set_inventory_item_id(value.id)
Please note the ICKY eval() in the _get_name() method.
How would I go about calling the SQLObject class referenced by the string I'm getting from __class__.__name__ without using eval()? Or is this an appropriate place to utilize eval()? (I'm sort of of the mindset where it's never appropriate to use eval() -- however since the system never uses any end user input in the eval() it seems "safe".)
To get the value of a global by name; Use:
globals()[self.inventory_type]
I'm trying to dynamically create buttons at runtime with PyQT4.7
However, this being my first python program I'm not sure how to get the functionality I want.
I would like to be able to substitute a text string for an attribute name:
i.e.
for each in xrange(4):
myname = "tab1_button%s" % each #tab1_button0, tab1_button1, tab1_button2
#self.ui.tab1_button0 = QtGui.QPushButton(self.ui.tab) <--normal code to create a named button
setattr(self.ui,myname,QtGui.QPushButton(self.ui.tab)) #rewrite of line above to dynamicly generate a button
#here's where I get stuck. this code isn't valid, but it shows what i want to do
self.ui.gridLayout.addWidget(self.ui.%s) % myname
#I need to have %s be tab1_button1, tab1_button2, etc. I know the % is for string substituion but how can I substitute the dynamically generated attribute name into that statement?
I assume there's a basica language construct I'm missing that allows this. Since it's my first program, please take it easy on me ;)
If I interpreted this correctly, I think what you want is this:
self.ui.gridLayout.addWidget(getattr(self.ui,myname))
Give that a go. In Python the following two statements are functionally equivalent (from the link below):
value = obj.attribute
value = getattr(obj, "attribute-name")
For extra context:
http://effbot.org/zone/python-getattr.htm
Just assign the button to a variable so you can both set the attribute and add the widget.
for i in range(4):
name = 'button%d' % i
button = QtGui.QPushButton(...)
setattr(self, name, button)
self.ui.gridLayout.addWidget(button)
Personally I would add the buttons to a list instead of giving them different names.
I think you might benefit from knowledge of lists (commonly called arrays in other languages)
self.buttons = [None, None, None, None]
for each in xrange(4):
self.buttons[each] = QtGui.QPushButton(self.ui.tab)
self.ui.gridLayout.addWidget(self.buttons[each])
For a tutorial on Python lists:
http://effbot.org/zone/python-list.htm