Tkinter - Button created on wrong screen - python

I am trying to create multiple windows using tkinter , but i am having no success so far ... When i create a child window and put a button on it , the button is created in the parent window!
from tkinter import *
class Login_screen(Frame):
def __init__(self,master):
Frame.__init__(self, master)
self.grid()
self.button1 = Button(text = "Open",command = lambda: self.open_login())
self.button1.grid()
def open_login(self):
self.root2 = Toplevel()
self.root2.geometry("400x200")
self.app2 = Main_screen(self.root2)
class Main_screen(Frame):
def __init__(self,master):
Frame.__init__(self,master)
self.grid()
self.button = Button(text = "Close",command = lambda: self.close_windows())
self.button.grid()
def close_windows(self):
self.grid_forget()
root = Tk()
root.geometry("800x600")
app = Login_screen(root)
root.mainloop()

You need to supply the Button() with the master argument:
self.button = Button(master = self, text = "Close",command = lambda: self.close_windows())
master is the first arg to a widget so it can also be done via: Button(self, text=...)
This is good practice and you should get in the habit of always explicitly providing master, otherwise Tk defaults this arg to None and will place it on the root window.

Related

tkinter pack_forget vs Window default center position

I have an app with multiple windows. I use pack_forget to eliminate the login window and invoke the main window. However this main window loses the default centered position of tkinter. The window is created at position (0 , 0).
Is there any simple way to make this main window be created in the default centered position?
example code, 3 files ->
main.py
#!/usr/bin/env python3
from tkinter import *
from frm_login import Wlogin
class Mainframe(Tk):
def __init__(self):
Tk.__init__(self)
self.frame = Wlogin(self)
self.frame.pack()
def change(self, frame):
self.frame.pack_forget() # delete currrent frame
self.frame = frame(self)
self.frame.pack() # make new frame
if __name__== '__main__':
app = Mainframe()
app.mainloop()
frm_login.py
from tkinter import *
from frm_default import Wmain
class Func(Frame):
def check(self, event=None):
if self.pwd.get() == '1':
self.master.change(Wmain)
else:
self.status.config(text='wrong password')
class Wlogin(Func):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
master.title('Enter password')
master.geometry('300x200')
self.status = Label(self, fg='red')
self.status.pack()
self.lbl = Label(self, text='Enter password')
self.lbl.pack()
self.pwd = Entry(self, show='*')
self.pwd.insert(-1, '1')
self.pwd.pack()
self.pwd.focus()
self.pwd.bind('<Return>', self.check)
self.pwd.bind('<KP_Enter>', self.check)
self.btn = Button(self, text='Done', command=self.check)
self.btn.pack()
self.btn = Button(self, text='Cancel', command=self.quit)
self.btn.pack()
frm_default.py
from tkinter import *
class Wmain(Frame):
def __init__(self, master=None, **kwargs):
Frame.__init__(self, master, **kwargs)
master.title('Main application')
master.geometry('600x400')
There is nothing about your forget / repack code that makes this unique. You can use the same commands you would otherwise. So either define the position yourself:
master.geometry('600x400+300+400')
Or use tk PlaceWindow function:
master.eval('tk::PlaceWindow . center')
Or calculate the position from the window size and monitor size:
master.geometry("600x400")
master.update_idletasks()
x = (master.winfo_screenwidth() - master.winfo_reqwidth()) // 2
y = (master.winfo_screenheight() - master.winfo_reqheight()) // 2
master.geometry(f"+{x}+{y}")
FWIW, my experience tells me that setting the window size yourself instead of letting tkinter calculate it will lead to bugs down the road.

How to create a button on a new widget using grid() method in tkinter?

import tkinter as tk
class Main:
def __init__(self, parent):
self.parent = parent
self.button = tk.Button(text="Build", command=self.new_window)
self.button.grid(row=1, column=0)
def new_window(self):
self.window = tk.Tk()
self.app = Graph(self.window)
self.window.mainloop()
class Graph:
def __init__(self, parent):
self.parent = parent
self.new_button = tk.Button(text="text")
self.new_button.grid(in_=self.parent)
def main():
root = tk.Tk()
app = Main(root)
root.mainloop()
if __name__ == "__main__":
main()
Here's my code and I'm trying to create a button on a new widget using grid() + in_, but there is a problem - button dont creates on a new widget instead of this it creates on the main one.
In tkinter widgets are by default assigned to root window, Tk unless passed a parent widget as the first positional argument, which is omitted in:
self.new_button = tk.Button(text="Destroy", command=self.destroy)
You should replace it with:
self.new_button = tk.Button(parent, text="Destroy", command=self.destroy)

import a python file that create a window when main window button clicks

I am creating 2 window in my program and i am using two class, since the code is complex, i separate it in 2 different python file. After i imported the second window file, how can i make sure it open without having this error which show in this picture
The original result should look like this after the new window button clicked:
Coding for Main Window:
from tkinter import *
import classGUIProgram
class Window(Tk):
def __init__(self, parent):
Tk.__init__(self, parent)
self.parent = parent
self.initialize()
def initialize(self):
self.geometry("600x400+30+30")
self.wButton = Button(self, text='newWindow', command = self.OnButtonClick)
self.wButton.pack()
def OnButtonClick(classGUIProgram):
classGUIProgram.top = Toplevel()
master = Tk()
b = classGUIProgram.HappyButton(master)
master.mainloop()
if __name__ == "__main__":
window = Window(None)
window.title("title")
window.mainloop()
Coding for Second Window:
from tkinter import *
class HappyButton:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(frame, text="Print message", command=self.printMessage)
self.printButton.pack(side=LEFT)
self.quitButton = Button(frame, text="Quit", command= quit)
self.quitButton.pack(side=LEFT)
self.downloadHistoryCB=Checkbutton(frame, text="Download History")
self.downloadHistoryCB.pack(side=LEFT)
def printMessage(self):
print("Wow this actually worked!")
master = Tk()
b = HappyButton(master)
master.mainloop()
You're creating extra Tk windows. Here is an example of using Toplevel widgets and another file.
mainWindow.py
import tkinter as tk
import secondWindow as sW
class MainWindow(tk.Tk):
def __init__(self):
super().__init__()
self.title("Main Window")
self.geometry("600x400+30+30")
tk.Button(self, text = "New Window", command = self.new_window).pack()
tk.Button(self, text = "Close Window", command = self.close).pack()
self._second_window = None
def new_window(self):
# This prevents multiple clicks opening multiple windows
if self._second_window is not None:
return
self._second_window = sW.SubWindow(self)
def close(self):
# Destory the 2nd window and reset the value to None
if self._second_window is not None:
self._second_window.destroy()
self._second_window = None
if __name__ == '__main__':
window = MainWindow()
window.mainloop()
secondWindow.py
import tkinter as tk
class SubWindow(tk.Toplevel):
def __init__(self, master):
super().__init__(master)
self.title("Sub Window")
self.geometry("400x300+30+30")
# Change what happens when you click the X button
# This is done so changes also reflect in the main window class
self.protocol('WM_DELETE_WINDOW', master.close)
tk.Button(self, text = "Print", command = self.printMessage).pack()
def printMessage(self):
print("Wow this actually worked!")
When using another file be sure to not have any global code you don't want running. Your classes don't have to inherit from Tk and Toplevel, this is just an example. But you need to ensure you only ever have one instance of Tk otherwise you get the behaviour you encountered

Is it possible to keep the same window for every class in python's tkinter?

I am trying to create a program in tkinter which allows me to open an initial window then to keep it throughout all classes used. For example, if I was to create a button in a window then when I click this button, it would exuecute a method that destroys the widget, and then executes a new class that builds a new screen within the same window, such as text opposed to a button.
from tkinter import *
class Window1:
def __init__(self, master):
self.master = master
self.label = Button(self.master, text = "Example", command = self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
## Code to execute next class
class Window2:
def __init__(self, master):
self.master = master
self.label = Label(self.master, text = "Example")
self.label.pack()
def main():
root = Tk()
run = Window1(root)
root.mainloop()
if __name__ == '__main__':
main()
I understand this is less practical, but I am curious. Cheers.
Tk() creates main window and variable root gives you access to this window. You can use root as argument for Window2 and you will have access to main window inside Window2
from tkinter import *
class Window1:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Button(self.master, text="Example", command=self.load_new)
self.label.pack()
def load_new(self):
self.label.destroy()
# use `root` with another class
self.another = Window2(self.master)
class Window2:
def __init__(self, master):
# keep `root` in `self.master`
self.master = master
self.label = Label(self.master, text="Example")
self.label.pack()
root = Tk()
run = Window1(root)
root.mainloop()
--
Probably nobody use another class to create Label in place of Button ;)
--
EDIT: In this example using names Window1 and Windows2 is misleading because there is only one window and two classes which use this window. I would rather use names FirstOwner, SecondOwner
Everything is implemented in one Tk class and in this case there always is only one window.
from tkinter import *
from tkinter import ttk
class MainWindow():
def __init__(self, mainWidget):
self.main_frame = ttk.Frame(mainWidget, width=300, height=150, padding=(0, 0, 0, 0))
self.main_frame.grid(row=0, column=0)
self.some_kind_of_controler = 0
self.main_gui()
def main_gui(self):
root.title('My Window')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=0, column=0)
self.main_label_2 = ttk.Label(self.main_frame, text='Object_2')
self.main_label_2.grid(row=1, column=0)
self.main_label_3 = ttk.Label(self.main_frame, text='Object_3')
self.main_label_3.grid(row=2, column=0)
self.setings_button = ttk.Button(self.main_frame, text='Setings')
self.setings_button.grid(row=0, column=1)
self.setings_button.bind('<Button-1>', self.setings_gui)
self.gui_elements = [self.main_label_1,
self.main_label_2,
self.main_label_3,
self.setings_button]
def setings_gui(self, event):
self.gui_elements_remove(self.gui_elements)
root.title('Setings')
self.main_label_1 = ttk.Label(self.main_frame, text='Object_1')
self.main_label_1.grid(row=2, column=0)
self.main_menu_button = ttk.Button(self.main_frame, text='Main menu')
self.main_menu_button.grid(row=0, column=1)
self.main_menu_button.bind('<Button-1>', self.back_to_main)
self.some_kind_of_controler = 1
self.gui_elements = [self.main_label_1,
self.main_menu_button]
def back_to_main(self, event):
if self.some_kind_of_controler == 1:
self.gui_elements_remove(self.gui_elements)
else:
pass
self.main_gui()
def gui_elements_remove(self, elements):
for element in elements:
element.destroy()
def main():
global root
root = Tk()
root.geometry('300x150+50+50')
window = MainWindow(root)
root.mainloop()
if __name__ == '__main__':
main()

Close main window after opening a new one

I found this example of code here on stackoverflow and I would like to make the first window close when a new one is opened.
So what I would like is when a new window is opened, the main one should be closed automatically.
#!/usr/bin/env python
import Tkinter as tk
from Tkinter import *
class windowclass():
def __init__(self,master):
self.master = master
self.frame = tk.Frame(master)
self.lbl = Label(master , text = "Label")
self.lbl.pack()
self.btn = Button(master , text = "Button" , command = self.command )
self.btn.pack()
self.frame.pack()
def command(self):
print 'Button is pressed!'
self.newWindow = tk.Toplevel(self.master)
self.app = windowclass1(self.newWindow)
class windowclass1():
def __init__(self , master):
self.master = master
self.frame = tk.Frame(master)
master.title("a")
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25 , command = self.close_window)
self.quitButton.pack()
self.frame.pack()
def close_window(self):
self.master.destroy()
root = Tk()
root.title("window")
root.geometry("350x50")
cls = windowclass(root)
root.mainloop()
You would withdraw the main window, but you have no way to close the program after the button click in the Toplevel, when the main window is still open but doesn't show Also pick one or the other of (but don't use both)
import Tkinter as tk
from Tkinter import *
This opens a 2nd Toplevel which allows you to exit the program
import Tkinter as tk
class windowclass():
def __init__(self,master):
self.master = master
##self.frame = tk.Frame(master) not used
self.lbl = tk.Label(master , text = "Label")
self.lbl.pack()
self.btn = tk.Button(master , text = "Button" , command = self.command )
self.btn.pack()
##self.frame.pack() not used
def command(self):
print 'Button is pressed!'
self.master.withdraw()
toplevel=tk.Toplevel(self.master)
tk.Button(toplevel, text="Exit the program",
command=self.master.quit).pack()
self.newWindow = tk.Toplevel(self.master)
self.app = windowclass1(self.newWindow)
class windowclass1():
def __init__(self , master):
""" note that "master" here refers to the TopLevel
"""
self.master = master
self.frame = tk.Frame(master)
master.title("a")
self.quitButton = tk.Button(self.frame,
text = 'Quit this TopLevel',
width = 25 , command = self.close_window)
self.quitButton.pack()
self.frame.pack()
def close_window(self):
self.master.destroy() ## closes this TopLevel only
root = tk.Tk()
root.title("window")
root.geometry("350x50")
cls = windowclass(root)
root.mainloop()
In your code:
self.newWindow = tk.Toplevel(self.master)
You are not creating a new window independent completely from your root (or master) but rather a child of the Toplevel (master in your case), of course this new child toplevel will act independent of the master until the master gets detroyed where the child toplevel will be destroyed as well,
To make it completely seperate, create a new instance of the Tk object and have it close the windowclass window (destroy its object):
self.newWindow = Tk()
you have two options here:
1 - Either you need to specify in the windowclass1.close_window(), that you want to destroy the cls object when you create the windowclass1() object, this way:
def close_window(self):
cls.master.destroy()
2 - Which is the preferred one for generality, is to destroy the cls after you create windowclass1 object in the windowclass.command() method, like this:
def command(self):
print 'Button is pressed!'
self.newWindow = Tk()
self.app = windowclass1(self.newWindow)
self.master.destroy()
and make the quitButton in the __init__() of windowclass1 like this:
self.quitButton = tk.Button(self.frame, text = 'Quit', width = 25, command = self.master.quit)
to quit completely your program

Categories