External window is empty with "tk.Toplevel" and "parent" - python

When I click on a label to open an external window (tertiary.py), the window opens normally but is empty inside. I don't get errors, but I don't see Tkinter widgets and miscellaneous items.
This is the secondary window where there is the label on which I click in order to open an external window. I add, for information purposes only, that this window is called secondary because it is displayed in a frame (with a vertical menu on the left) where the main window is called home = tk.Tk(). The secondary window opens and displays correctly in the frame.
Inside the secondary window there is the label thanks to which I want to open an external window (not in the frame) called tertiary
How can I solve?
secondary.py
import tkinter as tk
from tkinter import *
from tkinter import ttk
import other_options
from other_options import form_other_options
class Page2(tk.Frame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
self.configure(bg='white')
bar1=Frame(self, width=2200, height=35, background="#960000", highlightbackground="#b40909", highlightthickness=0) #grigio chiaro #b40909
bar1.place(x=0, y=0)
other_options = Label(self, text="Other Options", bg="#960000", foreground='white', font='Ubuntu 10')
other_options.place(x=1025, y=7)
other_options.bind("<Button-1>", lambda event: other_options.form_other_options(self))
tertiary.py
from tkinter import *
from tkinter import ttk
import tkinter as tk
def form_other_options(parent):
other_options = tk.Toplevel(parent)
other_options.title("Other options")
other_options.geometry("1000x800")
other_options.config(bg="white")
other_options.state("normal")
other_options.transient(parent)
class checkbox():
def __init__(self, master, **kw):
super().__init__(master, **kw)
labelframe1 = LabelFrame(self, text="checkbox", width=600,height=190, bg="white", foreground='#e10a0a')
labelframe1.place(x=10, y=13)
Checkbutton1 = IntVar()
Button1 = Checkbutton(self, text = "option1",
variable = Checkbutton1,
onvalue = 1,
offvalue = 0,
height = 2,
width = 10)
Button1.pack()
other_options.mainloop()

The first reason why the window is empty is that you never create an instance of the class checkbox, so the code that creates the widgets never runs.
So, the first step is to make sure you create an instance of that class:
other_options = tk.Toplevel(parent)
...
cb = checkbox(other_options)
When you do that, you will discover other bugs in your code. The first positional argument when creating a tkinter widget must be another widget. If you want the widget to be inside a Toplevel, the parent needs to be the toplevel or a descendant. You're using self which isn't a widget at all.
A simple fix is to use master instead of self, since you're passing the toplevel window as the master parameter:
labelframe1 = LabelFrame(master, ...)
Button1 = Checkbutton(master, ...)
You also need to remove the call to other_options.mainloop(). Unless there is a very compelling reason to do otherwise, an entire application should only call mainloop() exactly once.
There are other problems in the code that, strictly speaking, aren't related to the question being asked. For example, you're importing tkinter twice: once as import tkinter as tk and once as from tkinter import *. You only need to import tkinter once, and PEP8 recommends to use the first form:
import tkinter as tk
You'll need to add the prefix tk. to every place where you call a tkinter function. This will help make your code easier to understand and helps prevent pollution of the global namespace.
You also should use PEP8 naming standards. Specifically, class names should begin with an uppercase character. This will also make your code easier to understand.
There's also no reason why your checkbox class should be indented. You should move it out of the function.
Also, instead of checkbox not inheriting from anything, and then creating a LabelFrame, you should just inherit from LabelFrame. That way you can use your class more like a real widget, and would enable you to use self rather than master when creating the other widgets.
class Checkbox(tk.LabelFrame):
def __init__(self, master, **kw):
super().__init__(master, **kw)
...
button1 = tk.Checkbutton(self, ...)
...
There are probably other problems, but those are the most obvious.

Related

I get the error _tkinter.TclError: bad window path name ".!button" when I destroy the button

from tkinter import *
master=Tk()
class check:
def __init__(self,root):
self.root=root
self.b1=Button(root,text="Click me",command=self.undo)
self.b2=Button(root,text="Again",command=self.click)
def click(self):
self.b1.place(relx=0.5,rely=0.5)
def undo(self):
self.b1.destroy()
self.b2.place(relx=0.2,rely=0.2)
c=check(master)
c.click()
master.mainloop()
This is my code. I get _tkinter.TclError: bad window path name ".!button" error only when I use destroy method. But I want to delete previous button when another button appears.What should I do?
What are you doing? When you click the "Click me" button (and call the self.undo method, where the self.b1 button is destroyed) and then click the "Again" button (and call the self.click method, which tries to place already destroyed self.b1 button), you get the error, that the button does not exist. Of course, it doesn't because you have destroyed it.
It looks like you meant to hide the button. If you intended to do this, then you could just use .place_forget() method (there are also .pack_forget() and .grid_forget() methods for pack and grid window managers, respectively), that hides the widget, but not destroys it, and hence you would be able to restore it again when you need.
Here is your fixed code:
from tkinter import *
master = Tk()
class check:
def __init__(self, root):
self.root = root
self.b1 = Button(root, text="Click me", command=self.undo)
self.b2 = Button(root, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
c = check(master)
c.click()
master.mainloop()
I can also give you a piece of advice about the implementation:
1) You should write the code according to the PEP8 style; classes should be named in the CamelCase.
2) You should inherit your Tkinter app class(es) either from Tk (usage is shown below) Toplevel(the same as Tk, but use ONLY for child windows), Frame class (almost the same as for Tk, but you need to pack/grid/place that Frame in a window).
3) It's better to create the widgets in a separate function (it helps while developing complex and big apps).
4) It's recommended to write if __name__ == "__main__": condition before creating the window (if you do like this, you will be able to import this code from other modules, and the window won't open in that case).
Here is an example:
from tkinter import *
class Check(Tk):
def __init__(self):
super().__init__()
self.create_widgets()
self.click()
def create_widgets(self):
self.b1 = Button(self, text="Click me", command=self.undo)
self.b2 = Button(self, text="Again", command=self.click)
def click(self):
self.b2.place_forget()
self.b1.place(relx=0.5, rely=0.5)
def undo(self):
self.b1.place_forget()
self.b2.place(relx=0.2, rely=0.2)
if __name__ == "__main__":
Check().mainloop()
After you destroyed button b1 in the undo(self) function tkinter cannot access it anymore and will be confused when you try to place is somewhere in the click(self) function.
To make button b1 only disappear visually you could place it outside of the window instead of destroying it. To do so replace
self.b1.destroy()
with
self.b1.place(relx=-5, rely=0)
This will move the button b1 far to the left, where it cannot be seen.
When calling the click(self) function, the button will reappear, because it will be moved inside the window again.

Python Tkinter class structure practice

#game class
import Tkinter as tk
class Game(tk.Canvas):
def __init__(self, master):
canvas = tk.Canvas(master)
canvas.pack()
button = tk.Button(canvas, text='Quit', command=self.quit_game)
button.pack()
def quit_game(self):
root.destroy()#Should i put something else here?
root = tk.Tk()
game = Game(root)
root.mainloop()
Is it good practice, or, in other words, is there a problem with inheriting from canvas directly instead of frame, if for example I am not going to add any widgets except the canvas?
Another question I have is regarding the root.destroy(). I don't understand why I can't say master.destroy() or something to that effect.
There is nothing wrong with inheriting from Canvas or any other Tkinter widget.
re master.destroy() vs root.destroy(): you can call it however you want. You simply need a reference to the root window. If you call it root, to destroy it you would call root.destroy().
In general you should avoid the use of global variables. Given that you're passing in the root widget to your class, you can save a reference and use that instead:
class Game(tk.Canvas):
def __init__(self, master):
self.master = master
...
def quit_game(self):
self.master.destroy()

How does class Application(frame): works?

I have a question. I thought that a class can be based on an object or previously define class. When I change it into class Application(object): it doesn't work. Can you tell me why it didn't work and why did the code below works or why did class Application(Frame) works? Frame is not a previously define object and not object. Sorry for my English. Here is my code:
# Lazy Buttons
# Demonstrates using a class with tkinter
from tkinter import *
class Application(Frame): #
""" A GUI application with three buttons. """
def __init__(self, master):
super(Application, self).__init__(master)
self.grid()
self.create_widgets()
def create_widgets(self):
""" Create three buttons that do nothing. """
# create the first Button
self.bttn1 = Button(self, text= "I do nothing.")
self.bttn1.grid()
# create second button
self.bttn2 = Button(self)
self.bttn2.grid()
self.bttn2.configure(text="Me too!")
# create third button
self.bttn3 = Button(self)
self.bttn3.grid()
self.bttn3["text"] = "Same here!"
# main
root= Tk()
root.title("Lazy Buttons")
root.geometry("200x85")
app = Application(root)
root.mainloop()
Frame is a previously defined class. It's part of tkinter, which you imported on your first line.
Your Application class extends Frame, which means that it gets methods from Frame and can do everything that a Tk Frame can do, like show widgets. If you don't extend Frame, and only extend object instead, this will not work.
It might be clearer to replace...
from tkinter import *
with...
import tkinter as tk
and fix your references to Tk's classes (they would become tk.Button, tk.Frame and tk.Tk).

Inheriting from Frame or not in a Tkinter application

I've seen two basic ways of setting up a tkinter program. Is there any reason to prefer one to the other?
from Tkinter import *
class Application():
def __init__(self, root, title):
self.root = root
self.root.title(title)
self.label = Label(self.root, text='Hello')
self.label.grid(row=0, column=0)
root = Tk()
app = Application(root, 'Sample App')
root.mainloop()
and
from Tkinter import *
class Application(Frame):
def __init__(self, title, master=None):
Frame.__init__(self, master)
self.grid()
self.master.title(title)
self.label = Label(self, text='Hello')
self.label.grid(row=0, column=0)
app = Application('Sample App')
app.mainloop()
The option I prefer* is to inherit from the class Tk. I think it is the more reasonable choice since the window is, in effect, your application. Inheriting from Frame doesn't make any more sense to me then inheriting from Button or Canvas or Label. Since you can only have a single root, it makes sense that that is what you inherit from.
I also think it makes the code more readable if you do the import as import Tkinter as tk rather than from Tkinter import *. All of your calls then explicitly mention the tk module. I don't recommend this for all modules, but to me it makes sense with Tkinter.
For example:
import Tkinter as tk
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.label = tk.Label(text="Hello, world")
self.label.pack(padx=10, pady=10)
app = SampleApp()
app.mainloop()
* Note: since originally writing this answer I have changed my position. I now prefer to inherit from Frame rather than Tk. There's no real advantage one way or the other, it's more of a philosophical choice than anything else. Regardless, I believe that whether you inherit from Frame or Tk, I think either choice is better than the first example in the code that inherits from nothing.
The one slight advantage inheriting from Frame has over Tk is in the case where you want your application to support multiple identical windows. In that case, inheriting from Frame lets you create the first window as a child of root, and additional windows as children of instances of Toplevel. However, I've seen very few programs that ever have a need to do this.
For more information about how I think Tkinter programs should be structured, see my answer to the question Python Tkinter program structure.
A Frame is usually used as a geometry master for other widgets.
Since an application usually has numerous widgets, you'll often want to contain them all in a Frame, or at least use the Frame to add some borderwidth, padding, or other nicety.
Many example snippets you might find on the web do not use a Frame because
they just want to demonstrate some feature in the shortest amount of code.
So, use a Frame if you need it, otherwise, do not.
Edit: I think the best way to organize a GUI is given in this Tkinter tutorial:
simpleApp.py:
import Tkinter as tk
class SimpleApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.label = tk.Label(frame, text=title)
self.label.pack(padx=10,pady=10)
if __name__=='__main__':
root = tk.Tk()
app = SimpleApp(root,title='Hello, world')
root.mainloop()
This is mainly like your first example in that SimpleApp inherits from object, not Frame. I think this is better than subclassing Frame since we are not overriding any Frame methods. I prefer to think of SimpleApp as having a Frame rather than being a Frame.
Having SimpleApp subclass object does have a significant advantage over subclassing tk.Tk, however: it makes it easy to embed SimpleApp in a larger app:
import simpleApp
import Tkinter as tk
class BigApp(object):
def __init__(self, master, **kwargs):
title=kwargs.pop('title')
frame=tk.Frame(master, **kwargs)
frame.pack()
self.simple = simpleApp.SimpleApp(frame,title=title)
frame.pack(padx=10, pady=10)
self.simple2 = simpleApp.SimpleApp(frame,title=title)
frame.pack()
if __name__=='__main__':
root = tk.Tk()
app = BigApp(root,title='Hello, world')
root.mainloop()
Thus, simpleApp.py can be a stand-alone script as well as an importable module.
If you try this with SimpleApp inheriting from tk.Tk, you end up with extra undesired windows.
There can be an advantage to setting your top level object to inherit from Tk instead of Frame. The advantage arises when you have dynamic element to your GUI, e.g. a Label whose contents you want to set with a textvariable=foo instead of text= 'Label text'.
In this case, it is very helpful to use the Tkinter.DoubleVar, Tkinter.IntVar, and Tkinter.StringVar objects to hold the data, since the GUI will automatically update whenever these objects are set. However, to use these objects, you must specify their master as the root Tkinter.Tk() instance running. This is easier if you explicitly make your main object be a subclass of Tkinter.Tk, then have that generate frames and widgets, so you can pass along the Tk instance and set up your variables properly.
Here is a short example program to illustrate the idea.
import Tkinter as tk
class Tkclass(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
app=Application(self)
app.master.title("Animal to Meat")
app.mainloop()
class Application(tk.Frame):
def __init__(self, parent):
tk.Frame.__init__(self, parent)
self.grid(sticky=tk.N+tk.S+tk.E+tk.W)
self.meatvar = tk.StringVar(master=parent)
self.meatvar.set("Meat?")
self.createWidgets()
def createWidgets(self):
top=self.winfo_toplevel()
top.rowconfigure(0, weight=1)
top.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
self.columnconfigure(1, weight=1)
self.columnconfigure(2, weight=1)
self.columnconfigure(3, weight=1)
self.cowButton = tk.Button(self, text='Cow', command=self.setBeef)
self.cowButton.grid(row=0,column=0)
self.pigButton = tk.Button(self, text='Pig',command=self.setPork)
self.pigButton.grid(row=0,column=1)
self.meatLabel = tk.Label(self)
self.meatLabel.configure(textvariable=self.meatvar)
self.meatLabel.grid(row=0,column=2)
self.quit = tk.Button(self, text='Quit',command=self.QuitApp)
self.quit.grid(row=0, column=3)
def setBeef(self):
self.meatvar.set("Beef")
def setPork(self):
self.meatvar.set("Pork")
def QuitApp(self):
top=self.winfo_toplevel()
top.quit()
main = Tkclass()

Using Tkinter in python to edit the title bar

I am trying to add a custom title to a window but I am having troubles with it. I know my code isn't right but when I run it, it creates 2 windows instead, one with just the title tk and another bigger window with "Simple Prog". How do I make it so that the tk window has the title "Simple Prog" instead of having a new additional window. I dont think I'm suppose to have the Tk() part because when i have that in my complete code, there's an error
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.root = Tk()
self.root.title("Simple Prog")
If you don't create a root window, Tkinter will create one for you when you try to create any other widget. Thus, in your __init__, because you haven't yet created a root window when you initialize the frame, Tkinter will create one for you. Then, you call make_widgets which creates a second root window. That is why you are seeing two windows.
A well-written Tkinter program should always explicitly create a root window before creating any other widgets.
When you modify your code to explicitly create the root window, you'll end up with one window with the expected title.
Example:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,parent=None):
Frame.__init__(self,parent)
self.parent = parent
self.pack()
self.make_widgets()
def make_widgets(self):
# don't assume that self.parent is a root window.
# instead, call `winfo_toplevel to get the root window
self.winfo_toplevel().title("Simple Prog")
# this adds something to the frame, otherwise the default
# size of the window will be very small
label = Entry(self)
label.pack(side="top", fill="x")
root = Tk()
abc = ABC(root)
root.mainloop()
Also note the use of self.make_widgets() rather than ABC.make_widgets(self). While both end up doing the same thing, the former is the proper way to call the function.
Here it is nice and simple.
root = tkinter.Tk()
root.title('My Title')
root is the window you create and root.title() sets the title of that window.
Try something like:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.pack()
root = Tk()
app = ABC(master=root)
app.master.title("Simple Prog")
app.mainloop()
root.destroy()
Now you should have a frame with a title, then afterwards you can add windows for
different widgets if you like.
One point that must be stressed out is:
The .title() method must go before the .mainloop()
Example:
from tkinter import *
# Instantiating/Creating the object
main_menu = Tk()
# Set title
main_menu.title("Hello World")
# Infinite loop
main_menu.mainloop()
Otherwise, this error might occur:
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/tkinter/__init__.py", line 2217, in wm_title
return self.tk.call('wm', 'title', self._w, string)
_tkinter.TclError: can't invoke "wm" command: application has been destroyed
And the title won't show up on the top frame.
Example of python GUI
Here is an example:
from tkinter import *;
screen = Tk();
screen.geometry("370x420"); //size of screen
Change the name of window
screen.title('Title Name')
Run it:
screen.mainloop();
I found this works:
window = Tk()
window.title('Window')
Maybe this helps?
Easy method:
root = Tk()
root.title('Hello World')
Having just done this myself you can do it this way:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.parent = parent
self.pack()
ABC.make_widgets(self)
def make_widgets(self):
self.parent.title("Simple Prog")
You will see the title change, and you won't get two windows. I've left my parent as master as in the Tkinter reference stuff in the python library documentation.
For anybody who runs into the issue of having two windows open and runs across this question, here is how I stumbled upon a solution:
The reason the code in this question is producing two windows is because
Frame.__init__(self, parent)
is being run before
self.root = Tk()
The simple fix is to run Tk() before running Frame.__init__():
self.root = Tk()
Frame.__init__(self, parent)
Why that is the case, I'm not entirely sure.
self.parent is a reference to the actual window, so self.root.title should be self.parent.title, and self.root shouldn't exist.
widget.winfo_toplevel().title("My_Title")
changes the title of either Tk or Toplevel instance that the widget is a child of.
I found a solution that should help you:
from tkinter import Tk, Button, Frame, Entry, END
class ABC(Frame):
def __init__(self,master=None):
super().__init__(master)
self.pack()
self.master.title("Simple Prog")
self.make_widgets()
def make_widgets(self):
pass
root = Tk()
app = ABC(master=root)
app.mainloop()
Found at: docs.python.org

Categories

Resources