error when changing a Tkinter button command method - python

Code:
from tkinter import *
#screen 1
scr1 = Tk()
scr1.configure(bg='#2e3033')
canvas = []
teamCommentsButton = []
#update Vissuals
def updateTeams():
for x in range(6):
onClick = lambda : comments(x+10)
canvas[x].itemconfig(teamCommentsButton[x], command = onClick)
def comments (team):
print(team)
comments = Toplevel(scr1)
for x in range(6):
canvas.append(Canvas(scr1, width = 840, height = 326, bg='#2e3033', highlightthickness=1, highlightbackground='#2e3033'))
teamCommentsButton.append(Button(canvas[x], text='☰', command = lambda : comments(x), width = 2, height = 1))
teamCommentsButton[x].place(x = 20, y = 20)
canvas[x].grid(column = 0 if x < 3 else 1, row = (x if x < 3 else x - 3))
scr1.title('Pit TV - Match')
updateTeams()
scr1.mainloop()
Error:
Traceback (most recent call last):
File "c:\Users\user\Documents\Team 1710\Code\GKC2022\test.py", line 26, in <module>
updateTeams()
File "c:\Users\user\Documents\Team 1710\Code\GKC2022\test.py", line 13, in updateTeams
canvas[x].itemconfig(teamCommentsButton[x], command = onClick)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 2903, in itemconfigure
return self._configure(('itemconfigure', tagOrId), cnf, kw)
File "C:\Users\user\AppData\Local\Programs\Python\Python39\lib\tkinter\__init__.py", line 1636, in _configure
self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
_tkinter.TclError: invalid boolean operator in tag search expression
I want to be able to change the parameter of a command in a Tkinter button but I get this error when I try to do it. I have tried changing the parameter to a constant onClick = lambda : comments(10) and I have tried directly putting the method call as the command command = comments(x+10) but both give me the same error
on top of that when I remove the call to updateTeams() the code runs without errors but prints 5 no matter which button I click. I would expect that it prints a range from 0-5 depending on the button I click because the parameter I set for each button is dependent on x.
here is what the window looks like when I remove updateTeams() window

You have two problems in your Code:
Problem 1
The buttons are not items of the canvases.
You have to treat the button like a regular tkinter widget and use configure:
teamCommentsButton[x].configure(command=onClick)
If you want the button to actually be inside the canvas you have to add it to another frame and add that frame as an item to the window using:
canvas[x].create_window((20, 20), window=buttonFrame)
Problem 2
In Python lambda functions created in loops will execute the same function. That means your lambdas in updateTeams() will always use x = 15. This can be avoided by using an own function for creating lambdas:
def create_lambda(x):
return lambda: comments(x + 10)

Related

Delete item from dictionary and Listbox

I have a program that puts the contents of a dictionary in a Tkinter Listbox, but I'm having trouble deleting it from the Listbox and the dictionary.
from tkinter import *
import ast
f = open("orders.txt", "r")
contents = f.read()
f.close()
things = ast.literal_eval(contents)
secondthing = [things, "test"]
root = Tk()
f = Frame(root).pack()
l = Listbox(root)
b = Button(root, text = "delete selection", command = lambda: delete(l))
b.pack()
l.pack()
for i, j in things.items():
oneitem = i + " " + j
l.insert(END, oneitem)
def delete(listbox):
global things
# Delete from Listbox
selection = l.curselection()
l.delete(selection[0])
# Delete from list that provided it
evaluater = l.get(selection[0])
value = eval(evaluater)
ind = things.index(value)
del(things[ind])
print(things)
root.mainloop()
When I try to delete something it gives me:
Exception in Tkinter callback
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "/Users/mimmo/black_market/aaa.py", line 12, in <lambda>
b = Button(root, text = "delete selection", command = lambda: delete(l))
File "/Users/mimmo/black_market/aaa.py", line 28, in delete
value = eval(evaluater)
File "<string>", line 1
ohhh ohhhhh
^
SyntaxError: unexpected EOF while parsing
Can someone help me because I can delete it from the Listbox, I just have an error when deleting it from the dictionary.
The contents of orders.txt:
{"ayyy" : "ayyyyyy", "ohhh" : "ohhhhh"}
First of all, I would recommend using json or pickle to store contents of the dictionary - it's the most common practice. I don't really understand what do you want to do so I wrote a function which deletes an element from listbox and things by it's index.
An error you are getting is caused by eval function which tries to intepret your listbox item as python code. Of course, it's getting syntax error.
# Deletes element from listbox and thigs by it's index
def delete(listbox, index: int):
global things
item = listbox.get(index)
key = item.split()[0]
del things[key]
listbox.delete(index)

"can only concatenate str (not "int") to str" when zipping list of strings and list of objects

I get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Hunter\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "c:\Users\Hunter\Documents\Programming\Python Scripts\Scripts\spoolClient\menuScript.py", line 46, in <lambda>
updateJsonButton = Button(preferences, text="Save Preferences", command=lambda: updateJson())
File "c:\Users\Hunter\Documents\Programming\Python Scripts\Scripts\spoolClient\menuScript.py", line 17, in updateJson
for i, j in zip(entryNames, entry):
File "C:\Users\Hunter\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1643, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str
When trying to run my script:
from tkinter import *
from tkinter.ttk import *
from tkinter import messagebox
from tkinter import filedialog
import qrMaker
import qrReader
import json
settings = {}
#define vars
preferencesSkippedRows = [1, 3, 5, 7, 9, 11]
def openPreferences():
def updateJson():
print("here")
for i, j in zip(entryNames, entry):
print("loopdie")
value = str(j.get())
settings[i]=value
settingsjson = json.dumps(settings)
print(settingsjson)
f = open("preferences.json","w")
f.write(settingsjson)
f.close()
preferences = Tk()
preferences.title("Preferences")
preferences.iconbitmap(qrMaker.getCurrentPath()+'icon.ico')
preferences.geometry('400x600')
topText = Label(preferences, width=30, text="Filament Spool Client Preferences")
cameraText = Label(preferences, width=30, text="Select Camera Instance:")
cameraEntry = Combobox(preferences, width=30, values=qrReader.getCameras())
qrWidthText = Label(preferences, width=30, text="QR Output Width (in.)")
qrWidthEntry = Entry(preferences, width=30)
qrHeightText = Label(preferences, width=30, text="QR Output Height (in.)")
qrHeightEntry = Entry(preferences, width=30)
text = [cameraText, qrWidthText, qrHeightText]
entry = [cameraEntry, qrWidthEntry, qrHeightEntry]
entryNames = ['cameraEntry', 'qrWidthEntry', 'qrHeightEntry']
updateJsonButton = Button(preferences, text="Save Preferences", command=lambda: updateJson())
for i in preferencesSkippedRows:
preferences.grid_rowconfigure(i, minsize=10)
topText.grid(column = 0, row = 0)
row=2
for text, entry in zip(text, entry):
text.grid(column = 0, row = row)
entry.grid(column = 1, row = row)
row+=2
updateJsonButton.grid(column=1, row=row+2)
preferences.mainloop()
openPreferences() #I call script.openPreferences() in my main program but I left this here for debugging purposes
I can see from the error message that the error occurs somewhere in the line that my zip function occurs, but I have no idea what causes this. Oddly enough, this error goes away if instead of setting updateJson equal to the command value of my Tkinter button state, I set updateJson, which calls the function right as the button object is initialized. I also know what the error is saying, I just don't know where an integer is coming from, and how I can fix this issue. Any help would be appreciated.
Update: I've just found that the actual zipping of the two lists is not the problem, but when I introduce the for loop, the same error occurs.
Answering to close out this thread, answer from "user2357112 supports Monica".
The issue in this script is that for text, entry in zip(text, entry) literally uses "entry" in the for loop, and is executed after the button instance is created, meaning that if updateJson is called during the button object initialization, then there will be no error thrown as entry is still defined as a list. However, after for text, entry in zip(text, entry) executes at startup, entry is now defined as the last object in the list entry, no longer the list entry itself. When the user presses the button and updateJson is called, an error is thrown as entry is not a list anymore(I'm not 100% sure on the error part).

Create automatically a certain numbers of Entrys - Tkinter/Python

I want to use Tkinter to write a code where the user can enter a number N. Depending on this number N the program automatically (i dont want to use any buttons) creates N-Entry rows.
My solution works partially:
from tkinter import *
root = Tk()
Label(root, text = "Enter Number of columns").grid(row = 0, column = 0)
N = IntVar()
e_N = Entry(root, textvariable = N).grid(row = 0, column = 1)
# Put trace callbacks on the Entry IntVar
def create_rows(name, index, mode):
rows = N.get()
for i in range(rows):
Entry(root).grid(row = i + 1, column = 0)
N.trace('w', create_rows)
# Setting the vars will trigger the trace
N.set(2)
mainloop()
When you run the code for the first time, it works fine. If you delete the number two and enter a new number, new lines are automatically created. However, an error message is displayed :
Exception in Tkinter callback
Traceback (most recent call last):
File "/home/anaconda3/lib/python3.7/tkinter/__init__.py", line 508, in get
return self._tk.getint(value)
_tkinter.TclError: expected integer but got ""
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/home/anaconda3/lib/python3.7/tkinter/__init__.py", line 1705, in __call__
return self.func(*args)
File "<ipython-input-97-2da00b5c0b50>", line 12, in create_rows
rows = N.get()
File "/home/anaconda3/lib/python3.7/tkinter/__init__.py", line 510, in get
return int(self._tk.getdouble(value))
_tkinter.TclError: expected floating-point number but got ""
This error is repeated as soon as the old number is deleted and a new one is entered.
Does someone know what is wrong and how to fix it?
Also as an extra: Only new lines can be created, i.e. if you first enter 5 and then 3, the last two lines are not deleted.
Edited code:
from tkinter import *
root = Tk()
Label(root, text = "Enter Number of columns").grid(row = 0, column = 0)
N = IntVar()
e_N = Entry(root, textvariable = N).grid(row = 0, column = 1)
# Put trace callbacks on the Entry IntVar
def create_rows(name, index, mode):
try:
rows = N.get()
except _tkinter.TclError:
""
for i in range(rows):
Entry(root).grid(row = i + 1, column = 0)
N.trace('w', create_rows)
# Setting the vars will trigger the trace
N.set(2)
mainloop()
Putting a trace on the value of N means it calls create_rows whenever it changes, including just after you deleted the original number, but before you type the new one. You could put N.get() inside a try/except, and only add new lines if the contents of the number entry are a valid integer. This will handle the deleted case when the value is an empty string, and also if someone types a non-number into the entry.

I can't output my variable as a label in Tkinter

I would like to put a variable in a Tkinter label that prints out the value of the variable 'x'.
I tried to follow the example from the Tkinter documentation but it still seems to give me a trace error 'in second_click lblx2.place(window)'
x = max(numbers)
y = min(numbers)
z = sum(numbers)
a = float(z / len(numbers))
var_x = StringVar()
var_x.set(x)
lblx2 = Label(window, textvariable=var_x, font=('Arial Bold', 15), bg='blue', fg='red')
lblx2.place(window)
I expect it to just place the number on my window like regular text but it does not print anything.
The full error:
Exception in Tkinter callback
Traceback (most recent call last):
File "REDACTED", line 1705, in __call__ return self.func(*args)
File "REDACTED", line 49, in <lambda> btn['command'] = (lambda: second_click())
File "REDACTED", line 102, in second_click lblx2.place(window)
File "REDACTED", line 2188, in place_configure
File "REDACTED", line 1320, in _options cnf = _cnfmerge(cnf)
File "REDACTED", line 104, in _cnfmerge for c in _flatten(cnfs):
TypeError: object of type 'Tk' has no len()
When I remove (window) from place, it stops giving the errors but still does not put the number on the window
I believe the error is that window is not a suitable parameter for a Label object. Instead, it is used to change the location of the element. You could either
turn it into this: lblx2.place(x=whatever,y=whatever) (note: replace whatever with the location)
or
Remove the whole line of code entirely as it's unnecessary
Don't see a problem. Please provide a full example, not just pieces of code one cannot simply run. Here is mine, that DOESN'T reproduce the issue, and it really doesn't get much simpler than this:
from tkinter import *
from random import random
x = random()
root = Tk()
var_x = StringVar()
var_x.set(x)
Label(root, textvariable=var_x).pack()
root.mainloop()

Class Variable Retrieval in Python

This is a GUI I’ve been writing for a script I already have working. What I’m struggling with here is retrieving the information in the textboxes.
Under the definition generate I am able to pop a name off of listx but I am unable to grab the local variable entry from any of the instances of the new_title_box class.
from Tkinter import *
import ttk
boxvar=""
folder=""
listx=[]
count = 1
myrow = 1
class new_title_box:
def __init__(self,name):
global myrow, count, listx
self.entry = StringVar()
self.name = name
self.name = ttk.Entry(mainframe,width=45,textvariable=self.entry)
self.name.grid(column=1,row=myrow+1,sticky=(N,W))
listx.append(name)
print(listx) ## For debugging to insure that it is working correctly, if it gives output it, this part works
myrow = myrow + 1
count=count+1
def make_new(*args):
new_title_box('box'+str(count))
def generate(*args):
global listx, boxvar
while len(listx) > 0:
boxvar=listx.pop(0)
print(boxvar) ## For debugging to insure that it is working correctly, if it gives output it, this part works
folder = boxvar.entry.get() ## Not working here
print(folder) ## For debugging to insure that it is working correctly, if it gives output it, this part works
root = Tk()
root.title("File Maker")
mainframe = ttk.Frame(root, padding = "50 50 50 50")
mainframe.grid(column = 0,row = 0,sticky = (N, W, E, S))
mainframe.columnconfigure(0,weight=1)
mainframe.columnconfigure(0,weight=1)
add_entry = ttk.Button(mainframe,width=20, text = "add entry", command=make_new)
add_entry.grid(column=2,row=2,sticky=(N,W))
add_entry = ttk.Button(mainframe,width=20, text = "make files", command=generate)
add_entry.grid(column=2,row=3,sticky=(N,W))
root.mainloop()
Here's the traceback I'm getting:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python33\lib\tkinter_init_.py", line 1442, in call
return self.func(*args)
File "C:\python\SampAqTkinter.py", line 28, in generate
folder = boxvar.entry ## Not working here
AttributeError: 'str' object has no attribute 'entry'
There are two things that need to be changed to fix the problem you describe:
In the new_title_box.__init__() method change: listx.append(name) to listx.append(self.name)
In the generate() function, change: folder = boxvar.entry.get() to folder = boxvar.get().
You are appending a string to listx, use self.name instead of the local string name

Categories

Resources