I've written an app that takes some data from the user, queries the API of a website, and returns and processes the data given by the API. I'm trying to allow the user greater control of what happens next by creating checkboxes for each item retrieved.
Because the number of items retrieved will vary on each use, I'm putting the checkboxes and the IntVars for if they're checked or not into lists. However, when I try to set the IntVars to 1 (so they all start out checked) I get a TypeError telling me it wants the IntVar instance as the first arg, but that doesn't make any sense to me as I'm trying to call a method for the IntVar in the first place.
This is a much simplified version of my code that generates an identical error:
import Tkinter as tk
class Application(tk.Frame):
def __init__(self, master=None):
tk.Frame.__init__(self, master)
self.grid()
self.test_names = ["Box 1", "Box 2", "Box 3", "Box 4"]
self.boxes = []
self.box_vars = []
self.box_num = 0
btn_test1 = tk.Button(self, text="Test 1", width = 11, command = self.test1)
btn_test1.grid()
def test1(self):
for name in self.test_names:
self.box_vars.append(tk.IntVar)
self.boxes.append(tk.Checkbutton(self, text = name, variable = self.box_vars[self.box_num]))
self.box_vars[self.box_num].set(1)
self.boxes[self.box_num].grid(sticky = tk.W)
self.box_num += 1
root = tk.Tk()
app = Application(master=root)
app.mainloop()
And the error message when pushing the button:
TypeError: unbound method set() must be called with IntVar instance as first
argument (got int instance instead)
What am I doing wrong?
Classic problem. You add the type IntVar instead of adding an object of type IntVar. Add parentheses like this:
self.box_vars.append(tk.IntVar())
Yes you forgot the brackets. The result will be and not a tkinter intvar instance.
Related
I created entries and labels by loops. If user click the button there will be new entries and lables in two-two seperated row. Now I want to delete them by other button clicking, but labels are not deletable -only entries are.
My other problem is I want to delete just only the last widgets that were added by one clicking. To make it clear: I want to delete the last 18-18 labels and entries by one clicking. Currently, all the added entries so far will be deleted. The labels are not at all.
Here is the relevant code:
self._1thLabel_list = ['Label1', 'Label2', 'Label3', 'Label4', 'Label5',
'Label6', 'Label7', 'Label8', 'Label9']
self._2thLabel_list = ['Label10', 'Label11', 'Label12', 'Label13', 'Label14',
'Label15', 'Label16', 'Label17', 'Label18']
nothingList2 = []
self.col = 4
for j in range(len(self._1thLabel_list)):
self.myLab = Label(root, text=self._1thLabel_list[j]).grid(row=0, column=j+1)
for k in range(1):
self.myEntry_loop = Entry(root)
self.myEntry_loop.grid(row=k + 1, column=j+1, pady=10, padx=10)
self.myEntry_loop_2 = Entry(root)
self.myEntry_loop_2.grid(row=k + 3, column=j + 1, pady=10, padx=10)
nothingList2.append(self.myEntry_loop)
nothingList2.append(self.myEntry_loop_2)
for l in range(len(self._2thLabel_list)):
self.myLab_2 = Label(root, text=self.mylist_2[l]).grid(row=2, column=l + 1)
self.myButton_newWidgets = Button(root, text="Add", command=self.Add)
self.myButton_newWidgets.grid(row=self.col, column=4)
self.myButton_deleteWidgets = Button(root, text="Delete", command=self.deleteThem)
self.myButton_deleteWidgets.grid(row=self.col, column=5)
And here is how try to delete them:
def deleteThem(self):
for v in range(18):
nothingList2.pop(0)
for dele in nothingList2:
dele.destroy() # Here, it is deleting entries but delete all of them. I want just delete the last 18 of them.
for w in range(9):
self._1thLabel_list.pop(0)
for delet in self._1thLabel_list:
delet.destroy() # I got "AttributeError: 'str' object has no attribute 'destroy'" error in this line
for x in range(9):
self._2thLabel_list.pop(0)
for delet2 in self._2thLabel_list:
delet2.destroy()
Here is a solution (explanation below):
from tkinter import Tk, Button
root = Tk()
removable_widget_dict = {}
class RemovableWidget(Button):
def __init__(self, parent, title, key):
Button.__init__(self, parent)
self.key = key
self.config(text=title)
self.pack()
def delete(self):
self.destroy()
removable_widget_dict.pop(self.key)
for i in range(10):
key = f'item{i}'
removable_widget_dict[key] = RemovableWidget(root, f'Button {i}', key)
for key in removable_widget_dict.keys():
if key == 'item5':
removable_widget_dict[key].delete()
break
root.mainloop()
Explanation:
First of I would like to mention that this is something I came up with (at least similar to this) when doing a personal project so all of this comes from experience in a sense that this may not be the best solution but it certainly works.
After doing the basic stuff with tkinter (root = Tk(), root.mainloop()) You create a dictionary which will sort of store classes. Now classes are an important factor here since each instance of a class can act independently which cannot really be achieved with functions at least not as easy.
So then You create a class for whatever widget You need, You can create multiple classes for multiple widgets or somehow merge them all into one (don't know how that would work) but lets stick to one class for one type of widget.
In this class You define all the basic stuff You would for a widget but also You add an important argument "key". This is not as important but certainly is necessary because You do not want to leave unused classes in memory (don't know about the technical aspects but imo it keeps everything more clean especially if You have to read it[dictionary] for some reason)
Then You define the deletion function and here is where the independence of class instances comes in: for each class instance You will be able to call this function which will only affect that class instance. So now in this function You will destroy the widget that class created (in this case the button in self.button) or multiple widgets. and then the cleanup part comes in: You globally define the dictionary and then delete the "key" from it. and the key also makes it easier to access the class instance for other reasons.
The final note. You can access the classes who are stored in dictionaries functions like this: dictionary[key].that_class_function
Okay, so I am trying to make a menu system using Tkinter and am trying to save the string value of the drop down menu to a class variable. I have code to handle that part, but the problem comes with getting that string value to the function I have written. I know that it isn't my function that is the problem as I am using the print function for my example below.
import tkinter as tk
from enum import Enum
class CustomEnum(Enum):
Option1 = 'Option1'
Option2 = 'Option2'
class window():
def __init__(self, root):
self.value = CustomEnum.Option1
test = tk.StringVar()
test.set(self.value.value)
tk.OptionMenu(root, test, *[e.value for e in CustomEnum], command = lambda
content = test.get() : print(content)).pack()
tk.Button(root, text="Save",
command = lambda content = test.get() : print(content)).pack()
root = tk.Tk()
test = window(root)
root.mainloop()
If you run this code, it'll constantly print "Option 1" despite what option you chose or if you add or remove elements (aside from removing Option 1).
The problem lies in this line
tk.Button(root, text="Save",
command = lambda content = test.get() : print(content)).pack()
You are assigning content the value of test.get() which was at that instant (Option1) and it continues to be so unchanged.
Since you want the current value of test.get(), you would have to do this
command = lambda: print(test.get())).pack()
Also, I believe you have misspelt customEnum instead of CustomEnum.
Good Day,
I'm new to this forum (and quite new to programming), so I hope my question is properly formulated.
I've been trying to create a GUI in python using tkinter, and I want to have two buttons calling methods of two different classes. One method is defining an integer, the second one is reporting content. I'd have a list of objects of the latter class, and I want to choose the right instance by the integer. Here's a MWE:
import tkinter as tk
class data:
def __init__(self, content):
self.content = content
def report(self):
print("This is reported as self.content:" + str(self.content)) #This doesnt report the correct value for some reason?
print("The Class does register the correct idx:" + str(Selector.idx))
print("Using the Dict the correct value can be returned:" + str(vocables[Selector.idx].content))
class increment:
def __init__(self):
self.idx = 0
def increase(self):
self.idx += 1
print(self.idx)
vocables[self.idx].report()
root = tk.Tk()
Selector = increment()
vocables = []
for id in range(10):
vocables.append(data(id))
# print(vocables[id].content)
CheckVocable = tk.Button(root, text="Report", command=vocables[Selector.idx].report)
CheckVocable.pack()
NextVocable = tk.Button(root, text="Increase Index", command=Selector.increase)
NextVocable.pack()
root.mainloop()
I do not understand why the print of line 8 always reports the value of the first item in the list (vocabules[0] in this instance) instead of my desired value, which is returned in all other print cases. Am I messing up the work with classes or is the button behavior confusing me?
Thanks in advance!
I have a list named chosenTestHolder (imported from the my_config file) that consists of several objects each with the attribute 'sentence'.
When pressing the button 'Press' for the first time, the attribute 'sentence' of the first object in the chosenTestHolder should be displayed in the text widget. The next time the button 'Press' is pressed the attribute 'sentence' of the second object in chosenTestHolder should be displayed and so on.
I am using lambda event for binding the 'Press' button and tries to use a new sentences as its first arguments after each pressing of the 'Press' button. However, it keeps showing the first sentence.
When searching Stackoverflow I have seen in
Using lambda function to change value of an attribute that you can't use assignments in lambda expressions but by reading that I still have not figured out how to solve my problem.
Grateful for help! Code is below!
main.py
from tkinter import font
import tkinter as tk
import tkinter.ttk as ttk
import my_config
import Testlist as tl
class TestWidgetTest:
def __init__(self):
ram = tk.Frame(root)
ram.grid(in_=root,row=0, column=0)
self.myText = tk.Text(ram, height = 5)
self.myText.grid(row=0,column=1)
my_config.counter = 0
self.myButton = tk.Button(ram, text = 'Press')
self.myButton.grid(row =1, column =0, columnspan =2)
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
def TagConfigure(self, event, arg):
arg[1].delete('1.0',tk.END)
arg[1].insert('1.0',arg[0].sentence)
my_config.counter += 1
root = tk.Tk()
TestWidgetTest()
root.mainloop()
my_config.py
import Testlist as tl
testListHolder = [ ['Fabian was very tired'],
['Thomas light the fire'],
['Anna eat a red apple ']]
chosenTestHolder = []
count = 0
while count <(len(testListHolder)):
chosenTestHolder.append(tl.Testlist(testListHolder[count][0]))
count += 1
counter = 0
Testlist.py
class Testlist:
def __init__(self, sentence):
self.sentence = sentence
Your issue is the assignment of indata.
You do only assign in init.
To get your code working you need to re-configure your sentecte...
indata =[my_config.chosenTestHolder[my_config.counter] , self.myText]
self.myButton.bind('<ButtonRelease-1>',lambda event, arg=indata : self.TagConfigure(event, arg))
I would advise to keep track of the current sentence as an instance variable.
class Test_widget(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, args, kwargs)
self.sentences=["a", "b", "c", "d"] # the data
self.show = tk.StringVar() # the current displayed data
self.show.set("NULL")
self.counter=0 # the indexer
tk.Label(self, textvariable=self.show).grid(row=0)
tk.Button(self, command=self.click).grid(row=1)
def click(self, event):
self.show.set("%s"%self.sentences[self.counter]) # use the indexer to access the data
self.counter = self.counter + 1 # modify the indexer
if self.counter = len(self.sentences): # make sure you dont run in index-err
self.counter = 0
As you see, there is no need at all for the lambdas.
Edit
As to your questions:
The change in your original code was not intended.
I do not see a use case where you can use a lambda for its use inside your code.
At least none where a lambda is necessary.
Please remember to use lambda only and exclusively if there are
no ( == NULL ) other options.
Using inheritance (thats what the mechanism is called), you can inherit functions, "default" behaviour from other classes. It is a common mechanism in programming and not exclusive to python.
It is used like any normal object except you have to call the constructor of the base class (what I do using tk.Frame.__init__(self, args, kwargs) inside the init method. For more information on inheritance please refer to the uncounted manuals and tutorials available for that topic (google is your friend now that you know what the mechanism is called).
I'm working on a GUI for a project in school. All the buttons that I have in my GUI are bound with functions that I have created. These functions call for already predefined functions. For some of the predefined functions, I need one or two arguments and I have solved that with entries. I type in the arguments in the right entries that are connected to the specific button and when I press the button, the function will run with the corresponding arguments.
The thing I want to do is to in some way when I press a button, the function should be saved to a list instead of being executed right away. And when I push the "run" button(a new button that I will create) everything in my list will be executed. I have been thinking about using a list box but I don't know exactly how they work or if its even possible to run a list box that contains a number of functions. Does someone have any ideas or solutions for me? Can I use the list box for this or is there something else that is better to use?
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.entry1 = IntVar()
self.entry2 = IntVar()
def do_something():
value1 = self.entry1.get()
value2 = self.entry2.get()
self.listbox.insert(END, "predefined_function(value1, value2)")
def run_listbox_contents():
pass
self.button = Button(frame, text="Move", command=lambda: do_something())
self.button.pack(side=TOP)
self.entry1.set("value1")
self.entry = Entry(frame, textvariable=self.entry1)
self.entry.pack(side=TOP)
self.entry2.set("value2")
self.entry = Entry(frame, textvariable=self.entry2)
self.entry.pack(side=TOP)
self.listbox = Listbox(master)
self.listbox.pack(side=TOP)
root = Tk()
app = App(root)
root.title("Mindstorms GUI")
root.geometry("800x1200")
root.mainloop()
root.destroy()
Just use a standard list.
something like this
def hest(txt):
print "hest: " +txt
def horse(txt):
print "horse: " + txt
funcList = []
funcList.append(hest)
funcList.append(horse)
for x in funcList:
x("Wow")
This outputs
hest: Wow
horse: Wow
Was this what you wanted?
If I were you, I wouldn't want to save functions to a list. I would suggest another solution for you.
I suppose you have heard of the principle of MVC (Model-View-Controller). In your case, the list box is a part of view, and the process that saves functions and then calls them at once is a part of controller. Separate them.
You might want to save and display any string in the list box to let the users know that the corresponding functions have been enlisted and ready to run. For example, save a string "Function1 aug1 aug2 aug3" or "Funtion2 aug1 aug2" or whatever you like as a handle of the corresponding function.
And for the controller part, write a function (let's say conductor()). It reads the handle strings from the list, parses them and calls the corresponding functions. Where you want to run the enlisted functions, there you just call conductor().
Update:
Due to your comment I understand that you are pretty new to program. Let me show you how to write a simplest parser with your given variable names.
def run_listbox():
to_do_list = #get the list of strings
for handle_string in to_do_list:
#Let's say you got
#handle_string = "Predfined_function1 value1 value2"
#by here
handle = handle_string.split(" ")
#Split the string by space, so you got
#handle = ["Predfined_function1", "value1", "value2"]
#by here
if handle[0] == "Predfined_function1":
Predfined_function1(handle[1], handle[2]) #Call Predfined_function1(value1, value2)
elif handle[0] == "Predfined_function2":
Predfined_function2(handle[1], handle[2])
#elif ...
#...
#elif ...
#...
#elif ...
#...
This is not a perfect parser, but I hope it could let you know what does a parser look like.