I want to create a class of Listbox object so that it can be used anywhere it is needed but it throws TypeError error, here is my code:
import tkinter as tk
class cls_list(tk.Listbox):
def __init__(self, master, callfrom):
tk.Listbox.__init__(self, master, callfrom)
callfrom.bind("<KeyRelease>", self.show_list)
def show_list(self, event):
x = callfrom.winfo_x()
y = callfrom.winfo_y()
h = callfrom.winfo_height()
self.place(x = x, y = y+h)
self.insert('end',*('Banana','Orange','Apple','Mango'))
if __name__ == '__main__':
root = tk.Tk()
ent = tk.Entry(root, font = (None, 20))
lst = cls_list(root, ent)
ent.pack()
root.mainloop()
Can someone correct me what I am doing wrong?
Here is complete Exception occurred:
File "/home/waheed/env_pinpoint/lib/python3.9/site-packages/test_listbox.py", line 5, in __init__
tk.Listbox.__init__(self, master, callfrom)
File "/usr/lib/python3.9/tkinter/__init__.py", line 3162, in __init__
Widget.__init__(self, master, 'listbox', cnf, kw)
File "/usr/lib/python3.9/tkinter/__init__.py", line 2566, in __init__
BaseWidget._setup(self, master, cnf)
File "/usr/lib/python3.9/tkinter/__init__.py", line 2537, in _setup
if 'name' in cnf:
File "/usr/lib/python3.9/tkinter/__init__.py", line 1652, in cget
return self.tk.call(self._w, 'cget', '-' + key)
TypeError: can only concatenate str (not "int") to str
This is what I have achieved so far.
import tkinter as tk
if __name__ == '__main__':
root = tk.Tk()
root.geometry('400x400')
ent = tk.Entry(root, font = (None, 20))
lst = tk.Listbox(root, font = (None, 20))
ent.pack()
ent.focus_set()
def hide_list(event):
index = int(lst.curselection()[0])
val = lst.get(index)
ent.delete(0,'end')
ent.insert(0,val)
lst.place_forget()
def show_list(event):
ent.update_idletasks()
x = ent.winfo_x()
y = ent.winfo_y()
h = ent.winfo_height()
lst.place(x = x, y = y+h)
items = ['Banana','Orange','Apple','Mango']
searchkey = ent.get()
lst.delete(0, "end")
for item in items:
if searchkey.lower() in item.lower():
lst.insert("end", item)
n = lst.size()
if n < 15:
lst.config(height = 0)
else:
lst.config(height = 15)
ent.bind("<KeyRelease>", show_list)
lst.bind("<<ListboxSelect>>", hide_list)
root.mainloop()
And now I want to make it an object (separate class) for using in multiple scripts.
I hope now I have explained my question properly.
The specific error you're asking about is caused because you're passing callfrom here: tk.Listbox.__init__(self, master, callfrom).
The listbox widget doesn't know what to do with callfrom, it expects the second positional argument to be a dictionary.
The proper way to initialize the class is without callfrom. You will need to save callfrom as an instance attribute if you need to use it outside of the __init__.
def __init__(self, master, callfrom):
tk.Listbox.__init__(self, master)
self.callfrom = callfrom
...
def show_list(self, event):
x = self.callfrom.winfo_x()
y = self.callfrom.winfo_y()
h = self.callfrom.winfo_height()
...
Related
Here is mi code from Python3.6 and Ubuntu 17.10
It is write with Atom and will be implemented on Jupyter
Notebook
from tkinter import *
from tkinter import ttk
Here start the error
class Aplicacion():
def __init__(self):
self.root = Tk()
self.root.geometry('300x200')
self.root.resizable(width = False, height = False)
self.root.configure(bg = 'red')
self.root.title('Cachonerismo')
self.nombre = StringVar()
self.respuesta = StringVar()
self.txt = ttk.Entry(self.root,textvariable = self.nombre)
self.txt.pack(side = TOP)
self.txt1 = ttk.Entry(self.root, textvariable = self.respuesta)
self.txt1.pack(side = BOTTOM)
self.btn = ttk.Button(self.root, text = 'Mostrar', command = self.saluda).pack(side = LEFT)
self.bcl = ttk.Button(self.root, text='Cerrar', command = self.root.destroy).pack(side = RIGHT)
self.root.mainloop()
def saluda(self):
self.a = self.nombre.get(self)
self.respuesta.set(a)
app = Aplicacion()
And here is the error i get
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.6/tkinter/__init__.py", line 1702, in __call__
return self.func(*args)
File "tkinterTest.py", line 23, in saluda
self.a = self.nombre.get(self)
TypeError: get() takes 1 positional argument but 2 were given
This is the chunk of code failing:
class StringVar(Variable):
"""Value holder for strings variables."""
_default = ""
def __init__(self, master=None, value=None, name=None):
"""Construct a string variable.
MASTER can be given as master widget.
VALUE is an optional value (defaults to "")
NAME is an optional Tcl name (defaults to PY_VARnum).
If NAME matches an existing variable and VALUE is omitted
then the existing value is retained.
"""
Variable.__init__(self, master, value, name)
def get(self):
"""Return value of variable as string."""
value = self._tk.globalgetvar(self._name)
if isinstance(value, str):
return value
return str(value)
You have to "call" the class to instantiate it:
self.nombre = StringVar()
self.respuesta = StringVar()
In my program snippet I create a python window with 2 fields and 3 buttons. The left two buttons should perform some action but instead an error is thrown:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib64/python3.4/tkinter/__init__.py", line 1538, in __call__
return self.func(*args)
File ".../GuiFile.py", line 11, in <lambda>
self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
File ".../ClassFile.py", line 11, in doButtonAction1
print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
AttributeError: 'NoneType' object has no attribute 'textField'
Why is dict item F[0] (created in line 9 of GuiFile.py) not recognized as Text() class with the attribute textField (defined in line 43 of GuiFile.py)?
MainProgramFile.py
#!/usr/bin/env python3
import sys
import ClassFile
import GuiFile as gf
if __name__== '__main__':
gf.StartGui().mainloop()
GuiFile.py
import sys
from tkinter import *
import ClassFile as cf
class StartGui(Frame):
F = {}
def __init__(self,parent=None):
Frame.__init__(self, parent)
self.F[0] = FieldTextClass().make_field(labeltext="Label of field 1", fieldtext="veld 1", fieldheight=90)
self.F[1] = FieldTextClass().make_field(labeltext="Label of field 2", fieldtext="veld 2")
self.F[2] = ButtonClass().make_button(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
self.F[3] = ButtonClass().make_button(stacked="left", buttontext= "Exchange button", buttoncommand = lambda: cf.mainButtons.doButtonSwitchValues(self))
self.F[4] = ButtonClass().make_button(stacked="right",buttontext= "Quit button",buttoncommand = lambda: cf.mainButtons.doButtonQuit(self))
self.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
#for i in range(self.F.__len__()): print(self.F[i].__class__,self.F[i].objectType)
class ButtonClass (Frame, Button):
objectType = "button"
def make_button(self, parent=None, stacked="horizontal", buttontext="Button", buttonwidth=120, buttonheight=32, buttoncommand=""):
self.buttonwidth=buttonwidth
self.buttonheight=buttonheight
self.buttontext=buttontext
self.buttoncommand=buttoncommand
if stacked=="vertical":
BUTTONSTACK = TOP
elif stacked=="right":
BUTTONSTACK = RIGHT
elif stacked=="horizontal" or stacked=="left":
BUTTONSTACK = LEFT
else:
BUTTONSTACK = LEFT
self.top = Frame(parent, height=self.buttonheight, width=self.buttonwidth)
self.top.pack_propagate(False)
self.top.pack(side=BUTTONSTACK)
button = Button(self.top, text=self.buttontext, command=self.buttoncommand,height=self.buttonheight, width=self.buttonwidth)
button.pack(fill=BOTH)
class FieldTextClass(Frame,Text,Label):
textField = None
objectType = "inputField"
def make_field(self, parent=None, labeltext="Empty", fieldtext="Empty", fieldwidth=600, fieldheight=20, labelwidth=120, labelheight=20):
self.fieldheight=fieldheight
self.fieldwidth=fieldwidth
self.fieldtext=fieldtext
self.labeltext=labeltext
self.labelheight=labelheight
self.labelwidth=labelwidth
self.top = Frame(parent)
#create the label, whith the text shifted left/top in a separate Frame
labelFrame = Frame(self.top, height = self.labelheight,width=self.labelwidth)
label = Label(labelFrame, text=self.labeltext, fg="black", anchor="nw")
label.pack(expand=True, fill=BOTH, anchor="nw", side=LEFT)
labelFrame.pack_propagate(False)
labelFrame.pack(side=LEFT, anchor="nw")
#create the text field, packed in a separate Frame
fieldFrame = Frame(self.top, height = self.fieldheight,width=self.fieldwidth)
self.textField = Text(fieldFrame, fg="black",bg="white")
self.textField.insert(INSERT,self.fieldtext)
self.textField.pack(expand=True, fill=BOTH, side=LEFT)
fieldFrame.pack_propagate(False)
fieldFrame.pack(side=LEFT)
self.top.pack(side=TOP)
ClassFile.py
import sys
from tkinter import *
import GuiFile as gf
class mainButtons():
def doButtonQuit(self):
print("Quitting test via ClassFile")
self.quit()
def doButtonAction1(self):
print(gf.StartGui.F[0].textField.get("1.0","end-1c"))
print(gf.StartGui.F[1].textField.get("1.0","end-1c"))
gf.StartGui.F[0].textField.delete("1.0","end")
gf.StartGui.F[0].textField.insert(INSERT, "New text")
def doButtonSwitchValues(self):
tmp0=gf.StartGui.F[0].textField.get("1.0","end-1c")
tmp1=gf.StartGui.F[1].textField.get("1.0","end-1c")
gf.StartGui.F[0].textField.delete("1.0","end")
gf.StartGui.F[0].textField.insert(INSERT, tmp1)
gf.StartGui.F[1].textField.delete("1.0","end")
gf.StartGui.F[1].textField.insert(INSERT, tmp0)
When you do ButtonClass().make_button() (or FieldTextClass.make_field()) , python will return the value of the function, not the instance of the class. The function returns None, so the dictionary elements are None.
The way you're using the custom classes is very strange. Instead of creating special functions, put that code in an __init__, and use the class like you would any other class.
For example:
class ButtonClass (Frame):
def __init__(self, parent=None, stacked="horizontal",
buttontext="Button", buttonwidth=120, buttonheight=32,
buttoncommand=""):
Frame.__init__(self, parent)
self.buttonwidth=buttonwidth
...
...
self.F[2] = ButtonClass(stacked="left",buttontext= "Action button", buttoncommand = lambda: cf.mainButtons.doButtonAction1(self))
Note: when doing it this way, you don't have to create a separate frame inside __init__ (ie: self.top), since self is itself already a Frame.
Here I need to crate a DropDown class & later add it to the root or main GUI. But Tkinter.StringVar() is throwing an error saying
`Traceback (most recent call last):
File "D:/Testing/Tiks/main2.py", line 64, in <module>
d = Droppy(application)
File "D:/Testing/Tiks/main2.py", line 45, in __init__
self.control_variable = Tkinter.StringVar()
File "C:\Python26\lib\lib-tk\Tkinter.py", line 251, in __init__
Variable.__init__(self, master, value, name)
File "C:\Python26\lib\lib-tk\Tkinter.py", line 182, in __init__
self._tk = master.tk
AttributeError: 'NoneType' object has no attribute 'tk'
Exception AttributeError: "StringVar instance has no attribute '_tk'" in <bound method StringVar.__del__ of <Tkinter.StringVar instance at 0x0236F7B0>> ignored`
My code is this
import Tkinter
class App(object):
def __init__(self):
self.root = Tkinter.Tk()
############################
############################
self.root.mainloop()
class Droppy(object):
def __init__(self, frame=None):
# if frame is None:
# raise Exception
self.frame = frame
self.control_variable = Tkinter.StringVar()
self.control_variable.set("Choose Options")
self.dropDown = None
def dropIt(self):
self.dropDown = Tkinter.OptionMenu(self.frame.root, self.control_variable, "Rules", "Processing", "Output",
"Actions")
self.dropDown.pack()
if __name__ == '__main__':
application = App()
# application = Droppy()
# application.dropIt()
d = Droppy(application)
d.dropIt()
Now I know that Tkinter.Tk() before Tkinter.StringVar() can solve this issue but I cannot put Tkinter.Tk() and Tkinter.StringVar()` in the same class. How can I avert this issue ? Can anyone help please
mainloop is endless loop and it is running till you close Tk window.
You create Droppy when App is already closed.
You have to create object before mainloop - for example inside App.__init__.
import Tkinter
class App(object):
def __init__(self):
self.root = Tkinter.Tk()
############################
d = Droppy(self)
d.dropIt()
############################
self.root.mainloop()
class Droppy(object):
def __init__(self, frame=None):
# if frame is None:
# raise Exception
self.frame = frame
self.control_variable = Tkinter.StringVar()
self.control_variable.set("Choose Options")
self.dropDown = None
def dropIt(self):
self.dropDown = Tkinter.OptionMenu(self.frame.root, self.control_variable,
"Rules", "Processing", "Output",
"Actions")
self.dropDown.pack()
if __name__ == '__main__':
application = App()
As an alternative you can call your mainloop() in the main:
if __name__ == '__main__':
application = App()
d = Droppy(application)
d.dropIt()
application.root.mainloop()
I am trying to change dashed lines into filled lines when you click on them. Instead, I get this error when clicking on a line:
Traceback (most recent call last):
File "/Users/dan/Documents/pyCatan/path_engine.py", line 106, in <module>
root.mainloop()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1017, in mainloop
self.tk.mainloop(n)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1414, in __call__
self.widget._report_exception()
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1175, in _report_exception
root = self._root()
AttributeError: Frame instance has no __call__ method
Common causes in similar questions were polluting variables with multiple uses, but I am not doing that here. Also, I have declared the method that is being called, which was the error in another, similar question.
Here is my noobie code:
from map_gen import MapGen
from gen_board import CatanApp
from Tkinter import *
class PathEngine(object):
'''Use the configuration options to show or hide certain attributes.'''
# show the edges explicitly
SHOW_EDGES = False
# show road intersections as nodes, explicitly
SHOW_NODES = True
# color the hexes to their resource color
COLOR_HEXES = False
CLICK_ADD_EDGES = True
# dimensions
HEIGHT = 600
WIDTH = 800
def __init__(self, root):
self._model = MapGen()
self._model.gen()
CatanApp.set_vertices(self._model.get_map())
self._model.prepare()
frame = Frame(root, height=PathEngine.HEIGHT, width=PathEngine.WIDTH)
frame.pack()
self._canvas = MapDrawer(frame)
self.render()
self._canvas.config(height=PathEngine.HEIGHT, width=PathEngine.WIDTH)
self._canvas.pack()
def render(self):
if PathEngine.SHOW_NODES:
for node in self._model.get_nodes():
self._canvas.draw_node(*node)
self.add_edges()
def add_edges(self):
for edge in self._model.get_roads():
if PathEngine.CLICK_ADD_EDGES:
self._canvas.draw_dashed_edge(edge[0][0], edge[0][1], edge[1][0], edge[1][1])
class MapDrawer(Canvas):
NODE_RADIUS = 20
def __init__(self, master):
Canvas.__init__(self, master)
self._root = master
def draw_dashed_edge(self, x1, y1, x2, y2, color=None):
if color is None:color = "black"
t = "road_%s_%s" % (str((x1, y1)), str((x2, y2)))
self.create_line(
x1,
y1,
x2,
y2,
fill=color,
dash=(1, 1),
width=3,
tags=("road", t)
)
f = lambda event: self.solidify_dashed_edge(t)
self.tag_bind(t, "<Button-1>", f)
def solidify_dashed_edge(self, tag):
self.itemconfigure(tag, dash=(0, 1))
def draw_node(self, x, y, color=None):
if color is None: color = "white"
self.create_oval(
x - MapDrawer.NODE_RADIUS / 2,
y - MapDrawer.NODE_RADIUS / 2,
x + MapDrawer.NODE_RADIUS / 2,
y + MapDrawer.NODE_RADIUS / 2,
fill=color,
outline="black"
)
if __name__ == "__main__":
root = Tk()
engine = PathEngine(root)
root.mainloop()
It looks like you've hit a name conflict with adding an attribute _root to your Canvas:
>>> import Tkinter as tk
>>> a = tk.Canvas()
>>> print a._root
<bound method Canvas._root of <Tkinter.Canvas instance at 0xee1c0>>
It's one of the hazards of working in python where there is no private data :-). Notice that _root is a method of Canvas objects. You override that method with an instance attribute:
class MapDrawer(Canvas):
NODE_RADIUS = 20
def __init__(self, master):
Canvas.__init__(self, master)
self._root = master
Where master is a Frame object.
I'm using Python's Tkinter to create a GUI for a project i'm working on.
When I try to run part of the code though, I get this error:
Traceback (most recent call last):
File "calculator.py", line 59, in <module>
app = Application()
File "calculator.py", line 28, in __init__
self.create_widgets()
File "calculator.py", line 45, in create_widgets
self.special_chars.create_button(char, self.add_char_event(special_characters[char]))
File "calculator.py", line 20, in create_button
self.button_list += Button(self, text = txt, command = fcn)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/
lib-tk/Tkinter.py", line 1206, in cget
TypeError: cannot concatenate 'str' and 'int' objects
The problem is that I can't find the file that the error message references; my python2.7/lib-tk folder only contains complied versions (.pyo and .pyc) of Tkinter.
Is there a way to figure out what's going wrong?
Here's the source of calculator.py
from Tkinter import *
from exp import full_eval
from maths import special_characters
class special_char_frame(LabelFrame):
def __init__(self, master = None, text = 'Special Characters'):
LabelFrame.__init__(self, master)
self.grid()
self.button_list = []
def create_button(self, txt, fcn):
self.button_list += Button(self, text = txt, command = fcn)
self.button_list[-1].grid(row = 0)
class Application(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
self.grid()
self.create_widgets()
def create_widgets(self):
## equation entry pane
self.text_entry = Entry(self, width = 100)
self.text_entry.grid(row = 0, column = 0)
self.text_entry.bind('<KeyPress-Return>', self.calculate)
## result pane
self.result = StringVar()
self.result_label = Label(self, textvariable = self.result, wraplength = 815, justify = LEFT)
self.result_label.grid(row = 1, column = 0, columnspan = 2, sticky = W)
self.result.set('')
## calculate button
self.calc_button = Button(self, text = 'Calculate', command = self.calculate)
self.calc_button.grid(row = 0, column = 1)
## special character button pane
self.special_chars = special_char_frame(self)
for char in special_characters:
self.special_chars.create_button(char, self.add_char_event(special_characters[char]))
self.special_chars.grid(column = 0, columnspan = 2, row = 2)
def calculate(self, event = None):
try:
self.result.set(full_eval(self.text_entry.get()))
except Exception as error:
raise
#self.result.set(str(error))
self.text_entry.select_range(0, END)
def add_char_event(self, char):
def add_char(self = self, event = None):
self.text_entry.insert(INSERT, char)
return add_char
app = Application()
app.master.title('Calculator')
app.mainloop()
full_eval is a function for evaluating mathematical expressions.
special_characters is a dict containing special characters and their explanations. For now it's just special_characters = {'imaginary unit' : u'\u2148'}
Ok, so I missed this the first time, but the issue is actually that you are trying to add a Button to a list:
self.button_list += Button(self, text = txt, command = fcn)
If you simply wrap the Button in brackets, the error goes away (which makes sense because you are supposed to be able to add two lists):
self.button_list += [Button(self, text = txt, command = fcn)]
ORIGINAL ATTEMPT
My guess:
special_characters is a dictionary. It has key-value mappings where the values are ints. Then, when used in self.text_entry.insert(INSERT, char), text_entry is trying to insert an int into a str and causing the above error. The simple solution: wrap char with str in add_char.
def add_char_event(self, char):
def add_char(self = self, event = None):
self.text_entry.insert(INSERT, str(char))
return add_char
Your other option is to wrap str around the special_characters lookup:
for char in special_characters:
self.special_chars.create_button(char,
self.add_char_event(str(special_characters[char])))