Method runs upon it being addressed as event handler - python

I'm learning Tkinter and I'm pretty new to programming.
I'm trying to create a window containing a frame, containing a button that deletes the frame containing the button. This is what I've written so far:
class Menu(Frame):
def __init__(self, master):
super(Menu, self).__init__(master)
self.pack()
self.create_bttn()
def create_bttn(self):
self.b1 = Button(self, text ="Instruktioner")
self.b1["command"] = self.instructions()
self.b1.pack()
def instructions(self):
self.pack_forget()
This code seems to create the frame and the button, then delete them again, without me calling the instructions method! I don't understand why and how to avoid this. I'd appreciate any help.

When you instantiate your Menu object, it calls to create_bttn() and this last method calls self.instructions(). You may want to modify some of the following lines:
self.b1["command"] = self.instructions()
or inside the instructions method
self.pack_forget()
Edit
Try replacing this
self.b1["command"] = self.instructions()
with
self.b1["command"] = self.instructions # without ()

Stop calling it yourself.
self.b1["command"] = self.instructions

Related

How do I use destroy within functions and classes - tkinter?

I've seen some usage of self.destroy() within classes but I couldn't get it working with what I wanted it to do.
I have the class resultsPage that shows results obtained on another page. I have made the displayResults(pageNo) function to show these when resultsPage is visible. The problem arises with the back and next buttons which are made to go between pages of results. All widgets are created on top of each other but I want to remove them all then create the new ones. I added self.destroy() to try and fix this but it didn't work.
I'm not sure if it's to do with the placement of where I'm defining my functions but I have had a play around with where they're defined and it hasn't changed the error message.
This is a simplified example of my code:
class resultsPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
def onShowFrame(self, event):
def displayResults(pageNo):
self.destroy()
#Create widgets related to pageNo
#Create back and next buttons e.g.
back = tk.Button(self, text="<=",
command=lambda: displayResults(pageNo - 1))
displayResults(1)
The error I get is: _tkinter.TclError: bad window path name ".!frame.!previewresultspage"
If it helps, I can post my full code but I thought I'd generalise it so it's more helpful to others.
You are deleting the widget in onShowFrame, and then immediately try to create a new widget with it as the parent. You can't use a deleted widget as the parent of another widget.
As pointed out, using self.destroy() in this case, will not work. To achieve the goal of deleting all widgets on the frame, you can use a loop (credit to #stovfl):
for widget in self.grid_slaves():
widget.destroy()

How do I create a save function for an application that has multiple classes?

I'm developing a program using Tkinter for my GUI. Right now I'm conceptualizing the entire project, so the code I will provide is very simplistic.
Essentially, I wanted to create a main window that has several widgets each written in their own classes. One of the widgets would be the navbar where "File" -> "Save As" would exist.
My issue with this is if I have navbar as a separate class that is instantiated in the master class, the save function written in the navbar would be unable to view the variables in the other classes.
I have thought of potentially two solutions for this, but I am not sure if either one is necessarily the right thing to do for best Software Engineering practices.
Potential solution 1: Create a separate thread that constantly waits for the user to click save. Once it clicks save it changes an event flag which causes the main class to call some save function to save all variables. My issue with this is it is a constant waste of resources. The thread will be wasting resources constantly waiting for the save button to be clicked.
Potential solution 2: Create the navbar in a separate class, but define the navbar functions in the main class that instantiates it. My issue with this is that it makes the main class colluded with extraneous functions that I would like to have defined elsewhere for better practices. Additionally, I am not entirely sure how I would do this but I am sure there is some way that it could be done if I spent time looking into it.
class Main:
def __init__(self, rt):
self.rt = rt
self.navbar = navbar.NavBar(rt)
self.rt.mainloop()
class NavBar(tkinter.Frame):
def __init__(self, master):
tkinter.Frame.__init__(self, master)
self.master = master
self.bg='red'
self.text = tkinter.Text(self, height=1, width=30)
self.text.insert(tkinter.END, "File")
self.text.pack()
self.grid(row=0, column=0)
if __name__ == "__main__":
root = tkinter.Tk()
root.title('Automation')
main = Main(root)
Here, Navbar would have a button named File which if you scrolled over would generate a list of other buttons, one of which being "Save As". If this Save As button is clicked, I would want to be able to save all variables belonging to Main.
TLDR: I want to figure out the best way for me to create a save file functionality, being able to save variables from different classes that are all instantiated under a main umbrella class.
Normally I will setup the Main class as the controller of all the Frames, and have the Main inherits from Tk directly. Below is a sample on how to access attributes from other classes using this approach:
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.navbar = NavBar(self)
self.another_frame = AnotherFrame(self)
self.navbar.grid(row=0, column=0)
self.another_frame.grid(row=1, column=0)
class NavBar(tk.Frame):
def __init__(self, master):
tk.Frame.__init__(self, master)
self.master = master
tk.Button(self,text="Button of first frame",command=self.print_other_frame).pack()
def print_other_frame(self):
print (self.master.another_frame.entry.get()) #access master attributes
class AnotherFrame(tk.Frame):
def __init__(self,master):
tk.Frame.__init__(self, master)
self.entry = tk.Entry(self)
self.entry.pack()
self.entry.insert(0,"Test from 2nd frame")
if __name__ == "__main__":
root = Main()
root.title('Automation')
root.mainloop()

Displaying only one frame at a time in tkinter

I've been struggling with this for a while. I think I'm missing some simple piece of information and I hope you guys can help clear this up for me.
I'm trying to get tkinter to display different frames which I will eventually place widgets inside of. Here's what I did:
I've made a class that is supposed to initialize the window and make all the different frames the program will run.
I've made a separate class for each frame(I'm intending to have variables associated with the different classes when the program is done), and assigned a variable that will start that class up and make it run it's init function
I ended the StartUp class by telling it to tkraise() the frame I want displayed, and that's where things stop working correctly.
I set each frame to a different color, so when you run this program you will see that they split the screen space up instead of one being raised to the top. What am I missing?
One last point, I am purposely trying to spell everything out in my program, I learn better that way. I left it so I have to type tkinter.blah-blah-blah in front of each tkinter command so I can recognize them easily, and I decided not to have my classes inherit Frame or Tk or anything. I'm trying to understand what I'm doing.
import tkinter
class StartUp:
def __init__(self):
self.root = tkinter.Tk()
self.root.geometry('300x300')
self.container = tkinter.Frame(master=self.root, bg='blue')
self.container.pack(side='top', fill='both', expand=True)
self.page1 = Page1(self)
self.page2 = Page2(self)
self.page1.main_frame.tkraise()
class Page1():
def __init__(self, parent):
self.main_frame = tkinter.Frame(master=parent.container, bg='green')
self.main_frame.pack(side='top', fill='both', expand=True)
class Page2():
def __init__(self, parent):
self.main_frame = tkinter.Frame(master=parent.container, bg='yellow')
self.main_frame.pack(side='top', fill='both', expand=True)
boot_up = StartUp()
boot_up.root.mainloop()
When you do pack(side='top', ...), top doesn't refer to the top of the containing widget, it refers to the top of any empty space in the containing widget. Page initially takes up all of the space, and then when you pack Page2, it goes below Page1 rather than being layered on top of it.
If you are using the strategy of raising one window above another, you need to either use grid or place to layer the widgets on top of each other. The layering is something pack simply can't do.
Your other choice is to call pack_forget on the current window before calling pack on the new windowl

Returning a value after calling a function with a button in Tkinter

from Tkinter import *
from tkFileDialog import askopenfilename
from PIL import Image
def main():
filename = askopenfilename(filetypes=[("Jpeg","*.jpg")])
return filename
root = Tk()
button = Button(root,text="Open",command=main)
button.pack()
root.title("Image Manipulation Program")
root.mainloop()
I am kind of a newbie at programming in general, but I am trying to make an imaging program through the Tkinter GUI library. What I need to be able to do in the code above is return the string that is stored in filename so it is in the global scope of the program and I am able to use it. The problem is I don't know how to do this when calling the function with a button. I cannot find the answer to this problem on any website so I would appreciate anybody's help with this problem.
If you use the class based approach to Tk applications, instead of returning values from event handlers, you can assign them to instance variables. This the best approach, as function-based GUI applications don't scale well precisely because the need to place stuff at module scope.
from Tkinter import *
class Application(Frame):
def main(self):
self.filename = askopenfilename(filetypes=[("Jpeg","*.jpg")])
def createWidgets(self):
self.button = Button(root,text="Open",command=self.main)
self.button.pack()
def __init__(self, master=None):
Frame.__init__(self, master)
self.filename = None
self.pack()
self.createWidgets()
root = Tk()
root.title("Image Manipulation Program")
app = Application(master=root)
app.mainloop()
Generally, it is bad practice to use global variables to pass information around your program. However, if you really must do this, use a mutable data type (such as a list or a dict) as your global variable and change its contents from your callback function, main.
returned_values = {} # Create an empty dict.
def main():
returned_values['filename'] = askopenfilename(filetypes=[("Jpeg","*.jpg")])
# returned_values['filename'] may now be accessed in the global scope.
If you intend to do this frequently, consider implementing your own class to pass information around.

Python: removing a TKinter frame

I want to remove a frame from my interface when a specific button is clicked.
This is the invoked callback function
def removeMyself(self):
del self
However, it doesn't remove itself. I'm probably just deleting the object in python without updating the interface ?
thanks
Update
self.itemFrame = tk.Frame(parent)
self.itemFrame.pack(expand=False, side=tk.TOP)
removeB = tk.Button(self.itemFrame, text="Remove", width=10, command=self.removeIsosurface)
def removeIsosurface(self):
self.itemFrame.Destroy()
Error message:
AttributeError: Frame instance has no attribute 'Destroy'
To remove, call either frm.pack_forget() or frm.grid_forget() depending on whether the frame was packed or grided.
Then call frm.destroy() if you aren't going to use it again, or hold onto the reference and repack or regrid when you want to show it again.
del does not delete anything. del something just removes something from the local scope. And although if something was the only reference to an object, it may allow the object it to be garbage collected in the future, don't even think of using del to delete objects!!! And since self is just a normal variables, del self does nothing, except of course stopping the rest of the method from accessing the instance (so at the end of the method, it's actually like pass).
The exact way to remove a widget from the GUI depends on what geometry manager you use. If you used .grid(), you can use .grid_forget(). Note that this still doesn't destroy the widget - quite the contrary, you can go on and .grid() it again! - but that doesn't make any difference.
Let's say you're making a class. You have to do a couple of things special here:
The frame you want to destroy has to be an instance variable
You have to write a callback (which you did)
So, here's how a basic prototype would look.
from Tkinter import Tk, Frame, Button, Label
class GUI:
def __init__(self, root):
self.root = root # root is a passed Tk object
self.button = Button(self.root, text="Push me", command=self.removethis)
self.button.pack()
self.frame = Frame(self.root)
self.frame.pack()
self.label = Label(self.frame, text="I'll be destroyed soon!")
self.label.pack()
def removethis(self):
self.frame.destroy()
root = Tk()
window = GUI(root)
root.mainloop()
Happy hunting!
wont this help : self.destroy()
chk this out : PY cookbook the last para

Categories