Passing function into tkinter object - python

I am creating a virtual keyboard using tkinter in python 3.4.
class Keyboard(Frame):
def __init__(self, root, callback):
Frame.__init__(self, root, callback)
self.pack()
callback is going to be a function invoked when user presses a key.
So I will run it like:
def press(key):
print(key)
root = Tk()
keyb = Keyboard(root, press)
keyb.grid()
root.mainloop()
However, this must not be the right way to pass the function press into Keyboard because I get an error "AttributeError: 'function' object has no attribute 'items'"
So how do I pass this function into Keyboard?

First of all, don't mix pack and grid. See http://effbot.org/tkinterbook/pack.htm
Second, the Frame class does not take a callback parameter, but in your code you called it with one:
Frame.__init__(self, root, callback)
See http://effbot.org/tkinterbook/frame.htm#reference for correct usage.
What you're looking for would be like this, where callback was removed completely from Keyboard:
def press(key):
print(key)
root = Tk()
keyb = Keyboard(root)
keyb.bind("<Key>", key)

Related

How to define Tkinter controller?

Code Background: trying to have a popup window call a function in another class.
I am having trouble defining controller and configuring my functions correctly. I get an error about controller not being defined before it passes to the popupWindow class which makes sense because I have not defined controller somewhere else but I don't know where to do that:
NameError: name 'controller' is not defined
I have studied these past answers for help but am still stuck:
Calling Tkinter frame controller from function rather then button command
Calling functions from a Tkinter Frame to another
Here is my simplified code (Note: I define the various variables in the values function in other functions within the class BoundingBox but I have not included those functions for clarity and to shorten the code):
I also don't understand how to make the get_page function work which is part of my problem with figuring out how and where to define controller.
import tkinter as tk
from tkinter import *
class BoundingBox(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
def get_page(self, page_class):
return self.frames[page_class]
def popup(self):
self.w=popupWindow(self.master,controller)
def values(self):
print(self.start_x, self.start_y, self.end_x, self.end_y, self.totwidth, self.totheight, self.value1, self.value2)
self.allcord.append([self.start_x, self.start_y, self.end_x, self.end_y, self.totwidth, self.totheight, self.value1, self.value2])
self.allrect.append(self.rect)
print (len(self.allcord))
class popupWindow(tk.Toplevel):
def __init__(self, master, controller):
super().__init__(master)
self.controller = controller
self.l1=Label(self,text="Breed")
self.l1.grid(row=0, column=0)
self.e1=Entry(self)
self.e1.grid(row=0, column=1)
self.l2=Label(self,text="Color")
self.l2.grid(row=1, column=0)
self.e2=Entry(self)
self.e2.grid(row=1, column=1)
self.b=Button(self,text='Save',command=self.cleanup)
self.b.grid(row=2, column=1)
def cleanup(self):
self.value1=self.e1.get()
self.value2=self.e2.get()
self.controller.values()
self.top.destroy()
if __name__ == "__main__":
draw = BoundingBox()
draw.mainloop()
In this specific case, controller simply refers to the main window. So, you simply need to pass self as the controller argument:
def popup(self):
self.w=popupWindow(self.master, self)
Notice how the cleanup method calls self.controller.values(). values is defined in BoundingBox, so it's clear that popupwindow was designed to have BoundingBox as the controller.

Tkinter escape binding not destroying

I am trying to learn tkinter and the idea that I have requires it to be in fullscreen. Before making it fullscreen, however I wanted to make sure I could close the window using escape. So through other questions similar to this one on Stack Overflow I have been trying to get it to destroy the tkinter window when I hit escape. To me this seems like it should work but I am getting an exception when I hit escape:
`Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Jake\AppData\Local\Programs\Python\Python36-
32\lib\tkinter\__init__.py", line 1699, in __call__
return self.func(*args)
TypeError: destroy() takes 1 positional argument but 2 were given`
This is confusing for me because I don't think I am calling any arguments at all let alone 2. I have added a quit button which calls the close method I made and that works but using escape doesn't seem to. I have supplied my code for clarity. I know this is similar to a lot of questions on here but I have tried everything and nothing seems to be working for me. Thanks in advance!
import tkinter
from tkinter import *
class Window(Frame):
def __init__(self, master = None):
Frame.__init__(self, master)
master.bind('<Escape>', master.destroy)
self.init_window()
def init_window(self):
self.pack(fill=BOTH, expand=1)
quitButton = Button(self, text="quit", command=self.close)
quitButton.place(x=0, y=0)
def close(self):
self.master.destroy()
def main():
root = Tk()
root.geometry('500x500')
app = Window(root)
root.mainloop()
main()
When you bind a function to an event, tkinter will always pass an event object to that function. The destroy function takes no arguments, which means you can't bind directly to it. You need to bind to a function that will accept the event object as an argument.
Since you already have a function, you can give it an optional named argument so that you can continue to call your function without the argument, but it can also be used in a binding:
class Window(Frame):
def __init__(self, master = None):
...
master.bind('<Escape>', self.close)
def close(self, event=None):
self.master.destroy()
You could also use an anonymous function:
master.bind('<Escape>', lambda event: master.destroy())
The following code works.
I used it in a class for creating a full screen app in a 800x480 touch screen for pi:
class FullScreenApp(object):
def __init__(self, master, **kwargs):
self.master=master
pad=3
self._geom='200x200+0+0'
master.geometry('{}x{}'.format(800,480))
master.bind('<Escape>', self.close)
def close(self, event=None):
self.master.destroy()

Error when creating a Toplevel widget in Python

I'm coding an application to control serial connected device, right now I'm stuck in a GUI error, here is the simplified code:
import Tkinter
class PaginaPrincipale(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
def connetti():
pagina_connessione=Tkinter.Toplevel()
pagina_connessione.title("Gestione connessione")
pagina_connessione.mainloop()
self.parent = parent
self.grid()
self.gestisci_connessione = Tkinter.Button(self, text="Connetti!", command=connetti)
self.gestisci_connessione.grid(row=0, column=0, sticky='EW')
if __name__ == "__main__":
applicazione = PaginaPrincipale(None)
applicazione.title = 'Pannello di controllo'
applicazione.mainloop()
When I run the program I get this error:TypeError: 'str' object is not callable
I'm new to Python2.7, I hope someone could tell me what I did wrong!
The widget has a method named title, which you can use to set the title. However, you are replacing this function with a string when you do this:
applicazione.title = 'Pannello di controllo'
Once you've done that, any subsequent attempt to call the function title will result in the error you get (ie: you can't "call" a string).
Instead, you need to call title as a function:
applicazione.title('Pannello di controllo')

TKInter checkbox variable is always 0

I'm using Python's TkInter module for a GUI. Below is a simple checkbox code.
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar, command=getCheckVal)
btnC.grid()
windowTime.mainloop()
This code works fine. Each time I tick the checkbox, I get 1, else 0.
However, when I run the same code in a function that is called from another TkInter command (when a button is pressed), it stops working. I always get 0 as the value.
class GUIMainClass:
def __init__(self):
'''Create the main window'''
self.window = Tk.Tk()
def askUser(self):
def getCheckVal():
print cbVar.get()
windowTime=Tk.Tk()
cbVar = Tk.IntVar()
btnC = Tk.Checkbutton(windowTime, text="Save", variable = cbVar,
command=getCheckVal)
btnC.grid()
windowTime.mainloop()
def cmdWindow(self):
frameShow=Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj=GUIMainClass()
GUIObj.cmdWindow()
This is very unusual. What could be going wrong?
EDIT: I've used 2 mainloops because I want a separate window (windowTime) to open up when I click "Show Plots" button. This new window should have the checkbox in it.
Your windowTime, cbVar, etc. variables are defined in the function's local scope. When askUser() completes execution, those values are thrown away. Prepend self. to them to save them as instance variables.
There should only be one mainloop() in your program, to run the main Tkinter root object. Try putting it as the very last line in the program. I recommend doing some reading on Effbot for how to set up a Tkinter application.
I'm not sure what all you're trying to do, but one problem is that the TK.IntVar called cbVar that you create in your askUser() method will be deleted when the function returns, so you need to attach it to something that will still exist after that happens. While you could make it a global variable, a better choice would be to make it an attribute of something more persistent and has a longer "lifespan".
Another likely issue is that generally there should only be one call to mainloop() in a single Tkinter application. It appears what you want to do is display what is commonly known as a Dialog Window, which Tkinter also supports. There's some standard ones built-in, plus some more generic classes to simplify creating custom ones. Here's some documentation I found which describes them in some detail. You may also find it helpful to look at their source code.
In Python 2 it's in the /Lib/lib-tk/tkSimpleDialog.py file and
in Python 3 the code's in a file named /Lib/tkinter/simpledialog.py.
Below is code that takes the latter approach and derives a custom dialog class named GUIButtonDialog from the generic one included the Tkinter library which is simply named Dialog.
try:
import Tkinter as Tk # Python 2
from tkSimpleDialog import Dialog
except ModuleNotFoundError:
import tkinter as Tk # Python 3
from tkinter.simpledialog import Dialog
class GUIButtonDialog(Dialog):
"""Custom one Button dialog box."""
def __init__(self, btnText, parent=None, title=None):
self.btnText = btnText
Dialog.__init__(self, parent, title)
def getCheckVal(self):
print(self.cbVar.get())
def body(self, master):
"""Create dialog body."""
self.cbVar = Tk.IntVar()
self.btnC = Tk.Checkbutton(master, text=self.btnText, variable=self.cbVar,
command=self.getCheckVal)
self.btnC.grid()
return self.btnC # Return the widget to get inital focus.
def buttonbox(self):
# Overridden to suppress default "OK" and "Cancel" buttons.
pass
class GUIMainClass:
def __init__(self):
"""Create the main window."""
self.window = Tk.Tk()
def askUser(self):
"""Display custom dialog window (until user closes it)."""
GUIButtonDialog("Save", parent=self.window)
def cmdWindow(self):
frameShow = Tk.Frame(self.window)
frameShow.grid()
btnSwitch = Tk.Button(frameShow, text='Show Plots', command=self.askUser)
btnSwitch.grid()
self.window.mainloop()
GUIObj = GUIMainClass()
GUIObj.cmdWindow()

Attribute error in opening file dialog in python GUI using tkinter

I am starting to learn Python and the tkinter package and I am writing a program to load a text file on the GUI window. To open the file browser, I installed the button and its necessary function as shown in the below code. The program runs but when I click on the "browse" button, I am getting an attribute error saying : "'assign_1' object has no attribute 'var_filename'". It would be great if anyone could help me with this.
from tkinter import *
from tkinter import messagebox
from tkinter import simpledialog
from tkinter import filedialog
from math import *
from numpy import *
import string
root = Tk()
def close_window_callback(root):
if messagebox.askokcancel("Quit", "Do you really wish to quit?"):
root.destroy()
class assign_1:
def __init__(self,master):
self.master = master
frame = Frame(master)
frame.pack()
self.canvas = Canvas(master,width=1000,height=1000, bg="yellow")
self.button_browse = Button(frame, text="Browse",
command=self.browse_file)
self.button_browse.pack()
self.button_load = Button(frame, text="Load")
self.button_load.pack(side = LEFT)
self.canvas.pack(expand=YES, fill=BOTH)
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
root.protocol("WM_DELETE_WINDOW", lambda root_window=root: close_window_callback(root_window))
assign_1(root)
root.mainloop()
Although, as Rinzler pointed out, your indentation is wrong in the code you posted, that would lead to another error (AttributeError: assign_1 instance has no attribute 'browse_file'). So I'm guessing the indentation in the code you actually use is correct.
The problem is that you try to use self.var_filename.set(...) without having defined what self.var_filename is. If you want it to be a StringVar, which seems to be the case since you use set and get, you have to initialize it. To do this you should put self.var_filename = StringVar(master) in the class' __init__ function. A small example demonstrating this:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.var_filename = StringVar(master)
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.var_filename.set(filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")]))
filename = self.var_filename.get()
print(filename)
assign_1(root)
root.mainloop()
However, from the looks of it, in your case there is no need to use a tkinter StringVar, just use a normal string variable:
root = Tk()
class assign_1:
def __init__(self, master):
self.master = master
self.button_browse = Button(master, text="Browse", command=self.browse_file)
self.button_browse.pack()
def browse_file(self):
self.filename = filedialog.askopenfilename(filetypes=[("allfiles","*"),("pythonfiles","*.txt")])
print(self.filename)
assign_1(root)
root.mainloop()
The indentation is wrong. The function browse_file you wanted to define as method of the class assign_1 (use capitalise letters to declare name of classes) is a global function as you defined it.
You have also not defined self.var_filename anywhere, so it will then give you the error:
AttributeError: 'assign_1' object has no attribute 'var_filename'
Under the function close_window_callback, you have also wrong indentation.

Categories