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
Related
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.
This question already has answers here:
How can you dynamically create variables? [duplicate]
(8 answers)
Closed 4 years ago.
I have a class for which I want to create instances through a function, but I also want to be able to name the instances with the value of a Tkinter.Entry widget.
The simplified version of that I am trying to achieve is the following:
class vtdiagram():
IO=0.0
IC=0.0
EO=0.0
EC=0.0
IGA=0.0
def printvtvalues(self):
print self.IO
print self.IC
print self.EO
print self.EC
print self.IGA
def createvtinstance():
global Nametemp
Nametemp=vtdiagram()
If I run this code, then I can call Nametemp.printvtvalues() and get all values printed, so it works fine.
I am now trying to change the name of the instance Nametemp to the string that is on the Tkinter entry widget. Basically, if engine1 is written on the entry box when I createvtinstance(), I would like to then call the instance by:
engine1.printvtvalues()
and get the values.
I imagine the function should look something like this:
def createvtinstance():
global Nametemp
Nametemp=vtdiagram()
Nametemp._command_to_change_the_name_=stringinentrybox.get()
Do you guys have know of a command that can do such a thing?
Or is there a way that I could achieve the same effect, maybe using a dictionary?
***edit: The reason I need to name the variables is for the following (in plain English): I am creating an 'engine simulator'.
The idea is that the user will enter engine parameters -plus its name- in a GUI and this is the vtdiagram class.
The reason for using a class is that I have the characteristics of 'engine1, engine2...' saved as an instance of the class but I also need to have functions attached to it. This is because I want to generate graphs and diagrams of saved engines but only when called. So I can compare engine1 and engine2, but then get 'forget' engine2 from the GUI to compare 1 and 3.
Please keep in mind I am quite new to python :) ***
Many thanks!
Juan
I wouldn't recommend changing the name of a variable based on user input.
You could "achieve the same effect" like this:
Objects=[]
Names=[]
def createvtinstance(Object=4,Name="engine1"):
global Nametemp
global Objects
global Names
Nametemp=Object # I'll just use an int to demonstrate.
Objects+=[Nametemp]
Names+=[Name]
def Use(Name="engine1"):print(Objects[Names.index(Name)]) # Or: Objects[Names.index(Name)].SomeFunction()
If you REALLY want to alter the name of a variable based on user input, then you could do it like this:
def createvtinstance(Name="engine1"):
if (not Name[0]in"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") or False in(i in"1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"for i in Name) or Name in("tkinter","createvtinstance","Name","vtdiagram",):return "Invalid name." # This should make the code more "robust".
try:exec("global "+Name+"\n"+Name+"=vtdiagram()")
except SyntaxError:return "Invalid name."
Or this:
def createvtinstance(Name="engine1"):
if (not Name[0]in"qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM") or False in(i in"1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"for i in Name) or Name in("tkinter","createvtinstance","Name","vtdiagram",):raise NameError("The name "+Name+" does not comply to validation rules.") # This should make the code more "robust".
try:exec("global "+Name+"\n"+Name+"=vtdiagram()")
except SyntaxError:raise NameError(Name+" is a reserved keyword.")
The top example shows how you would use a list to find an object in another list; using a string. This is what I'd probably do in this situation, however a dictionary could be better.
The bottom examples show how you would actually name a variable based on user input. This is NOT RECOMMENDED. Everyone seems to agree that using exec is counterproductive, and should be avoided. Python can't compile code in exec statements until execution, and won't be able to colour code your code.
People have been suggesting the use of python dictionaries, so I decided to research them. Dictionaries (dict) seem to be a data type similar to lists, except they can be indexed using strings (or other "immutable" data types). Here is a version of my first example that uses a dictionary instead of lists:
Objects={}
def createvtinstance(Object=4,Name="engine1"):
global Objects
Objects[Name]=Object
def Use(Name="engine1"):print(Objects[Name]) # Or: Objects[Name].SomeFunction()
Python seems to have a built in dictionary called globals, which stores all your variables, so you could probably do:
def createvtinstance(Object=4,Name="engine1"):
globals()[Name]=Object # Or globals()[Name]=vtdiagram()
However, this will allow the user to break your program, if they use a name like createvtinstance or tkinter.
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()
Forgive this rather basic Python question, but I literally have very little Python experience. I'm create a basic Python script for use with Kodi:
http://kodi.wiki/view/List_of_built-in_functions
Example code:
import kodi
variable = "The value to use in PlayMedia"
kodi.executebuiltin("PlayMedia(variable)")
kodi.executebuiltin("PlayerControl(RepeatAll)")
Rather than directly providing a string value for the function PlayMedia, I want to pass a variable as the value instead. The idea is another process may modify the variable value with sed so it can't be static.
Really simple, but can someone point me in the right direction?
It's simple case of string formatting.
template = "{}({})"
functionName = "function" # e.g. input from user
arg = "arg" # e.g. input from user
formatted = template.format(functionName, arg)
assert formatted == "function(arg)"
kodi.executebuiltin(formatted)
OK as far as I get your problem you need to define a variable whose value could be changed later, so the first part is easier, defining a variable in python is as simple as new_song = "tiffny_avlord_I_love_u", similarly you can define another string as new_video = "Bohemia_on_my_feet", the thing to keep in mind is that while defining variables as strings, you need to encapsulate all the string inside the double quotes "..." (However, single quotes also work fine)
Now the issue is how to update it's value , the easiest way is to take input from the user itself which can be done using raw_input() as :
new_song = raw_input("Please enter name of a valid song: ")
print "The new song is : "+new_song
Now whatever the user enters on the console would be stored in the variable new_song and you could use this variable and pass it to any function as
some_function(new_song)
Try executing this line and you will understand how it works.
Being new at programming in general, and new with Python in particular, I'm having some beginner's troubles.
I'm trying out a function from NLTK called generate:
string.generate()
It returns what seems like a string. However, if I write:
stringvariable = string.generate()
or
stringvariable = str(string.generate())
… the stringvariable is always Empty.
So I guess I'm missing something here. Can the text output generated, that I see on the screen, be something else than a string output? And if so, is there any way for me to grab that output and put it into a variable?
Briefly put, how to I get what comes out of string.generate() into stringvariable, if not as described above?
you can rewrite generate. The only disadvantage is that it can change and your code might not be updated to reflect these changes:
from nltk.util import tokenwrap
def generate_no_stdout(self, length=100):
if '_trigram_model' not in self.__dict__:
estimator = lambda fdist, bins: LidstoneProbDist(fdist, 0.2)
self._trigram_model = NgramModel(3, self, estimator=estimator)
text = self._trigram_model.generate(length)
return tokenwrap(text)
then "a.generate()" becomes "generate_no_stdout(a)"
generate() prints its output rather than returning a string, so you need to capture it.