kivy: KeyError: (3385,) in widget.py - python

When trying to exectue following code (edited version from the kivy-designer, stands under the MIT license):
def __init__(self, **kwargs):
self._buttons = {}
super(PlaygroundSizeView, self).__init__(**kwargs)
for title, values in self.default_sizes:
grid = StackLayout(orientation="lr-tb", size_hint=(1, None))
def sort_sizes(item):
return item[1][1] * item[1][0]
values = sorted(values, key=sort_sizes, reverse=True)
for name, size in values:
btn = ToggleButton(text='', markup=True, size_hint=(0.25, 0.25))
btntext = ('%s\n[color=777777][size=%d]%dx%d[/size][/color]' %
(name, btn.font_size * 0.8, size[0], size[1]))
btn.text = btntext
btn.bind(on_press=partial(self.set_size, size))
grid.add_widget(btn)
self._buttons[name] = btn
item = AccordionItem(title=title)
_sv = ScrollView(do_scroll_x=False)
_sv.add_widget(grid)
item.add_widget(_sv)
self.accordion.add_widget(item)
self.accordion.select(self.accordion.children[-1])
self.update_buttons()
I get following Error 7 times in a row with different numbers before the program stops:
Exception ignored in: functools.partial(, 3385)
Traceback (most recent call last):
File "E:\Programme(x86)\Python\Kivy Virtual Environment\lib\site-packages\kivy\uix\widget.py", line 239, in _widget_destructor
KeyError: (3385,)
It only appeared after I edited the code and added ScrollView at #1. I think it might be that python is trying to garbage collect ScrollView but is somehow unable to.
Linue 239 of widget.py is the del line of following function:
def _widget_destructor(uid, r):
# Internal method called when a widget is deleted from memory. the only
# thing we remember about it is its uid. Clear all the associated callbacks
# created in kv language.
del _widget_destructors[uid]
Builder.unbind_widget(uid)
Thanks a lot in advance!

That line #1 has a typo in add_widget.
Are you sure that add_widget returns the parent widget?
I suggest you make a ScrollView first, then add the grid, then add the item to accordion.

Related

error when changing a Tkinter button command method

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)

"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.

wx python 3.0.2 classic 'CheckListBox' object has no attribute 'GetItemHeight'

Investigating porting my wx 2.8 python app to wx 3.0.2 classic (it's about time) and run into this hurdle. Apparently the GetItemHeight is not part of CheckListBox class anymore:
bash\basher\patcher_dialog.py:519: wxPyDeprecationWarning: Accessing deprecated property.
mouseItem = (event.m_y/self.gPatchers.GetItemHeight() +
Traceback (most recent call last):
File "bash\basher\patcher_dialog.py", line 519, in OnMouse
mouseItem = (event.m_y/self.gPatchers.GetItemHeight() +
AttributeError: 'CheckListBox' object has no attribute 'GetItemHeight'
People were using them to get the item hovered upon in a mouse event handler (gPatchers below is a wx.CheckListBox):
def OnMouse(self,event):
if event.Moving():
mouseItem = (event.m_y/self.gPatchers.GetItemHeight() +
self.gPatchers.GetScrollPos(wx.VERTICAL))
if mouseItem != self.mouseItem:
self.mouseItem = mouseItem
self.MouseEnteredItem(mouseItem)
elif event.Leaving():
self.gTipText.SetLabel(self.defaultTipText)
self.mouseItem = -1
event.Skip()
So how do I achieve this in wx python 3.0.2 ?
EDIT: cross posted in the wx-users mailing list: https://groups.google.com/forum/#!topic/wxpython-users/mMYr-51sE4s
You can simply use HitTest from the underlying class ListBox to find which item is at the given x/y coordinate. Item is the int index of list element. The documentation is for wxPython phoenix, but works identical under wxPython MSW classic 3.0.2.
# lb is a wx.(Check)ListBox instance
lb.Bind(wx.EVT_MOTION, self.OnMouse)
def OnMouse(self, evt):
obj = evt.GetEventObject()
x, y = evt.GetPosition()
if isinstance(obj, wx.ListBox):
item = obj.HitTest(wx.Point(x, y))
# do something with item index information
Hi you can try the following trick to do this.
I couldn't try this with your code but it is also the way you can find the height of CheckListBox
self.gPatchers.GetClientRect()[3]
It will return the height in pixel.
GetClientRect() method will return tuple of 4 value (x,y,width,height)

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