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.
Related
So I heard there was a method in which you could make a separate tk.Toplevel class I have been attempting to use it but I dont know exactly whats wrong I really need help
here is the code as of now I will keep trying to make adjustments but its not easy
import tkinter as tk
from tkinter import ttk
class App(tk.Tk):
def __init__(self, title: str):
super().__init__()
self.title(title)
self.label = ttk.Label(self, text="Hello, World!")
self.label.pack(side=tk.TOP)
self.button = ttk.Button(self, text="Button", command=self.Alter)
self.button.pack(side=tk.RIGHT)
class ThemeWindow(tk.Toplevel):
def __init__(self, parent):
super().__init__(parent)
self.title("NEW")
self.parent = parent
self.label = tk.Label(parent, text="Hello, World!")
self.label.pack(side=tk.TOP)
self.button = tk.Button(parent, text="Button", command=parent.Alter)
self.button.pack(side=tk.RIGHT)
I have edited and used examples given in a course I am taking but clearly I am misreading.
The key part is AttributeError: '_tkinter.tkapp' object has no attribute 'Alter'' - that's telling you that your App class (which inherits from Tk) has no attribute called Alter.
The problem is caused by your App class, and more specifically by self.button = ttk.Button(self, text="Button", command=self.Alter)
You're telling self.button to call the function self.Alter as its callback function (the command parameter), but you haven't defined a function called Alter within the App class.
You need to do something like this:
class App(tk.Tk):
def __init__(self, title: str):
super().__init__()
self.title(title)
self.label = ttk.Label(self, text="Hello, World!")
self.label.pack(side=tk.TOP)
self.button = ttk.Button(
self,
text="Button",
command=self.alter # don't use uppercase in function names!
)
self.button.pack(side=tk.RIGHT)
def alter(self): # define a function 'alter' within this class
print('Hello') # do whatever you need this function to do here
Now when button is pressed, it will be able to call alter, which (in this case) should print "Hello"
Likewise, your ThemeWindow class should be able to call this via parent.alter
Again, note that I've changed the name from "Alter" to "alter" to adhere to Python naming conventions.
In my program, I am creating a window from my root tkinter window, and hiding the root using the .withdraw() function. When I try to show the root window again by calling the root class, it does not show and my program exits. Here's a rough outline of my code describing the problem:
class MainGUI:
def __init__(self, master):
self.master = master
#....Create and .grid() all GUI Widgets....
# Button for switching to other window
button = Button(text="CLICKME", command=lambda: self.other_window())
# Call and define show function at the end of __init__
self.show()
def show(self):
self.master.update()
self.master.deiconify()
# Create other window and withdraw self on button click
def other_window(self):
OtherGUI(self.master)
self.master.withdraw()
class OtherGUI:
def __init__(self, master):
# Function for returning to main window, calls MainGUI class
# to create window and withdraws self.
def main_window():
MainGUI(self.master)
self.master.withdraw()
master = self.master = Toplevel(master)
#....Create and .grid() all GUI Widgets....
# Button for switching back to main window
button = Button(text="CLICKME", command=lambda: self.main_window())
Using print functions in the MainGUI, I was able to see that when trying to switch back to the main window, show() is actually called, and the entire class does appear to be entered.
This puzzles me as I've only really learn how to do this from other forum posts, and using root.update() and .deiconify() seemed to be the solution for most people, however I have no idea why this isn't working.
Does anyone have an idea as to where I'm going wrong here?
The example you presented will not work for several reason.
#really you should build your gui as an inherited class as it makes things much easier to manage in tkinter.
class MainGUI:
def __init__(self, master):
self.master = master
button = Button(text="CLICKME", command=lambda: self.other_window())
# no need for lambda expressions here.
# missing geometry layout... grid(), pack() or place()
self.show()
# self.show does nothing here because your show method is improperly indented.
# your other_window method is also not properly indented.
def show(self):
self.master.update()
self.master.deiconify()
def other_window(self):
OtherGUI(self.master)
self.master.withdraw()
class OtherGUI:
def __init__(self, master):
# this function should be its own method.
def main_window():
MainGUI(self.master)
self.master.withdraw()
master = self.master = Toplevel(master)
# this is not how you should be defining master.
button = Button(text="CLICKME", command=lambda: self.main_window())
# missing geometry layout... grid(), pack() or place()
# your button command is using a lambda to call a class method but your define it as a function instead.
Here is a simpler version of what you are attempting that will work:
import tkinter as tk
class MainGUI(tk.Tk):
def __init__(self):
super().__init__()
tk.Button(self, text="Open Toplevel", command=self.open_toplevel_window).pack()
def open_toplevel_window(self):
OtherGUI(self)
self.withdraw()
class OtherGUI(tk.Toplevel):
def __init__(self, master):
super().__init__()
tk.Button(self, text="Close top and deiconify main", command=self.main_window).pack()
def main_window(self):
self.master.deiconify()
self.destroy()
MainGUI().mainloop()
As you can see here when you inherit from the tkinter classes that control the main window and toplevel windows it becomes easier to manage them and less code to perform a task.
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()
Python - Events, Frames and Tkinter
Here I have my GameApplication class which creates and runs the tkinter application. I would like to have it so that I can call the same function from every frame and using that function find out what frame was clicked. I'm only just diving into python now so excuse me if this seems simple.
Thanks for the help in advance.
# Import needed classes.
from tkinter import *
# Create the GameApplication class to run the GUI
class GameApplication(Frame):
def __init__(self, master=None):
super().__init__(master)
self.grid()
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.position_1 = Frame(width=100, height=100, bg="#CC0000")
# This is the Frame I want to get information about.
self.position_1.bind("<Button-1>", self.callback)
self.position_1.grid(column=0, row=0)
self.position_2 = Frame(width=100, height=100, bg="#00CC00")
# And this one too.
self.position_2.bind("<Button-1>", self.callback)
self.position_2.grid(column=1, row=0)
def callback(self, event):
print("?")
if __name__ == "__main__":
root = Tk()
app = GameApplication(master=root)
root.mainloop()
Take a look at this . It's a small project I wrote in python . I have a function that checks the status of each call . I call that function after each function has been executed.
class Messengers:
def __init__(self):
self.box = tkMessageBox
def successer(self):
self.box.showinfo("ImageR Success", "Done YO! Go run a test :)")
def failure(self):
self.box.showerror('ImageR Failure', 'Yo you broke me!')
The code is here https://github.com/jaytarang92/imager . I use subprocess.check_ouput to make sure the call was done correctly.
I've programmed using tkinter before, but usually did a long procedural GUI class that implemented other non GUI classes I've created. This time I wanted to do it using more OOP making it more modular.
I ran into a problem, I've searched for answers and haven't found any, which usually means it's either really easy or I'm really wrong. I created an inherited classes from tk.LabelFrame and created GUI widgets in them. I also have methods to manipulate the widgets in the classes but I can't figure out how to execute a function in another inherited class, partly because I can't figure out how to correctly instantiate an object from the other class (which have tkinter ('parent') objects as parameters).
Would I do this by overloading constructors? I've seen something about #classmethods and *args, **kwargs but haven't acted on them as I'm not sure if that's the right route either. There's some debate about the best/correct way to implement an overloaded constructor in python. I'm stumped as to what is the most apropos for what I'm trying to accomplish...
Thanks
#python 2.7 on win7
import Tkinter as tk
class Testing(tk.LabelFrame):
buttonwidth = 10
def __init__(self, parent):
self.parent=parent
#results = Results(???) #<-- Don't know how to instantiate Results object
tk.LabelFrame.__init__(self, self.parent,
text="Test Operations",
padx=10,
pady=10,
)
self.taskButton = tk.Button(
self,
text="Do A Task",
width=self.buttonWidth,
command=self.doATask,
)
self.taskButton.pack()
def doATask(self):
#want to execute function in Results.getResult() but don't know how
#results.getResults() #<--what I want to do
print("place holder")
class Results(tk.LabelFrame):
def __init__(self, parent):
self.parent = parent
tk.LabelFrame.__init__(self, self.parent, text="Visual Results")
self.resultLbl = tk.Label(self, text="Result")
self.resultLbl.pack()
def getResult(self):
self.resultLbl.configure(bg='yellow')
class Application(tk.Frame):
def __init__(self, parent):
self.parent = parent
tk.Frame.__init__(self, self.parent)
self.Testing = Testing(self.parent)
self.Results = Results(self.parent)
self.Testing.pack(fill=tk.X)
self.Results.pack(fill=tk.X)
if __name__ == "__main__":
root = tk.Tk()
root.title("Modular GUI App")
Application(root).pack()
root.mainloop()
I'd recommend sticking to instance variables, which are created for each individual object, unlike class variables which are shared among all of a class's instantiations - just prepend those variable names with self. (e.g. self.results). Also, stick to naming conventions so you don't have a Testing class and a Testing object of that class.
You instantiate objects according to their __init__. The Results class has an __init__ defined as def __init__(self, parent):, so it needs a parent. If you want it to have the same parent as the Testing object that created it, simply do results = Results(parent). However, you don't want to do this (see below).
A problem that I encountered after making the above change was that the Application class instantiated its own Results object, and that was what was actually being displayed, not the one created by the Testing object. Refer back to that object instead of creating a new one. Pass the Application object to each of these classes so they can refer to each other. Now, having said that, it's generally better to have each class know as little about other classes as possible, so that making a change in one class doesn't require any changes in other classes.
The following code will make the label yellow when you click the button.
import Tkinter as tk
class Testing(tk.LabelFrame):
def __init__(self, parent, main):
self.buttonWidth = 10
self.parent=parent
self.main = main # save the instantiating class
tk.LabelFrame.__init__(self, self.parent,
text="Test Operations",
padx=10,
pady=10
)
self.taskButton = tk.Button(
self,
text="Do A Task",
width=self.buttonWidth,
command=self.doATask,
)
self.taskButton.pack()
def doATask(self):
#want to execute function in Results.getResult() but don't know how
self.main.results.getResult() #<--what you can do
class Results(tk.LabelFrame):
def __init__(self, parent, main):
self.parent = parent
self.main = main # save the instantiating class
tk.LabelFrame.__init__(self, self.parent, text="Visual Results")
self.resultLbl = tk.Label(self, text="Result")
self.resultLbl.pack()
def getResult(self):
self.resultLbl.config(bg='yellow')
class Application(tk.Frame):
def __init__(self, parent):
self.parent = parent
tk.Frame.__init__(self, self.parent)
self.testing = Testing(self.parent, self)
self.results = Results(self.parent, self)
self.testing.pack(fill=tk.X)
self.results.pack(fill=tk.X)
if __name__ == "__main__":
root = tk.Tk()
root.title("Modular GUI App")
Application(root).pack()
root.mainloop()
This worked for me
Results(None,None).getResult()
goodluck!