How does creating two instances of Tk work with one mainloop? - python

How does adding master = Tk() into the __init__ of a subclass of tkinter.Frame
produce two windows (app and app2) when only app.mainloop() is called?
from tkinter import Frame,Button,Tk
class Application(Frame):
def say_hi(self):
print('Hello world?!')
def close(self):
self.master.destroy()
def createWidgets(self):
self.quit_b = Button(self, width=12, text='Quit', bg='tan',
command=self.close)
self.quit_b.grid(row=0, column=0, padx=8, pady=8)
self.hello_b = Button(self, width=12, text='Hello',
command=self.say_hi)
self.hello_b.grid(row=0, column=1, padx=8, pady=8)
def __init__(self):
master = Tk() # <------------------------ ! see here !
Frame.__init__(self, master)
self.grid()
self.createWidgets()
app = Application()
app.master.title('Hello world!')
app2 = Application()
app2.master.title('Hello world! 2')
app.mainloop()

You cannot create two instances of the class Tk, and it's somewhat unusual to instantiate it within the __init__ of another class. Your code should work, but I've never seen it done that way.
You need to create an instance of Tk before creating any other widgets. Since your main app is a subclass of Frame, you're partially creating the instance of a Frame before initializing Tkinter which is simply not the way it should be done. It might work, but the behavior is undefined.
Instead, it's generally better to create your application as a subclass of Tk:
from Tkinter import tk
class Application(tk.Tk):
...
app = Application(...)
app.mainloop()
OR, create an instance of Tk at the global scope, and pass it as an argument to your other widgets:
from Tkinter import tk
class Application(tkFrame):
...
root = tk.Tk()
myframe = Application(root)
root.mainloop()
If you need more than one window, create additional windows with the Toplevel class.

Related

Calling filedialog from inside a class Tkinter

I am trying to write an app in tkinter python that allows you to browse and choose a folder. I am using a class for each frame in my app, for example: BrowseFolders frame. In one of my frames I have a button that calls a command that opens filedialog.askdirectory. However, everytime I push the button the app seems to freeze and I have no other option other than closing it.
This is my class (notice the self.selectFolder_button):
class BrowseFolders(tk.Frame):
def __init__(self, container):
super().__init__(container, bg='#232228')
self.path = ''
# big title
pyglet.font.add_file('ARCENA.ttf')
self.Mp3Scanner_label = tk.Label(self, text="MP3 Scanner", font=('AR CENA', 70),
background='#232228', foreground='#E3EEF9')
self.slogan_label = tk.Label(self, text='Upload your files to MongoDB', font=('AR CENA', 30),
background='#232228', foreground='#E3EEF9')
self.selectFolder_button = tk.Button(self, text="Select Folder", font=('AR CENA', 15),
background='grey', command=self.select_folder)
self.pack()
def select_folder(self):
self.path = filedialog.askdirectory(initialdir='/', title="Select Folder")
def pack(self):
self.Mp3Scanner_label.place(relx=0.5, rely=0.35, anchor='center')
self.slogan_label.place(relx=0.5, rely=0.55, anchor='center')
self.selectFolder_button.place(relx=0.5, rely=0.85, relwidth=0.9, relheight=0.08, anchor='center')
super().pack(expand=True, fill='both')
class App(tk.Tk):
def __init__(self):
super().__init__()
self.title('MP3 Scanner')
self.geometry('600x450')
if __name__ == "__main__":
app = App()
BrowseFolders(app)
app.mainloop()
I've already tried using lambda, changing my select_folder() method to be static, writing the method outside my class. Anything I do where the button somehow calls this function freezes my app. The only case where this does work is when my frame isn't a class.
Thank you very much!
The problem really was the font, I removed pyglet.font.add_file('ARCENA.ttf') and now its working!

How use event in other file?

I'm prepared two files. First with GUI class. Second file is a script where I want to use a GUI, Databases and Template classes. A Databases and Template classes works. I check it in other scripts.
Questions:
How can I use event(KeyRelase) form GUI class in script?
First:
from tkinter import *
from tkinter.font import Font
from tkinter.ttk import Separator
class GUI:
def __init__(self, master):
self.ent_InventoryNumber = Entry(self.frm_FirstColumn, font=fontStyle, width=35, borderwidth=2, justify=CENTER)
self.ent_InventoryNumber.insert(END, "Wprowadź TUTAJ numer inwentarzowy")
self.ent_InventoryNumber.grid(row=2, column=0, columnspan=2, padx=5, pady=20, ipadx=2, ipady=2)
self.ent_InventoryNumber.bind("<KeyRelease>", lambda x: self.searchChamber())
def searchChamber(self):
return self.ent_InventoryNumber.get()
Second file with script:
import Databases as Db
import Template
from GUI import GUI
def test(a):
print(a)
window = Tk()
myGUI = GUI(window)
window.mainloop()
# test(myGUI.searchChamber()) ???
bind works correctly in you code. Problem is that bind can't get value which you use with return - it can't assing it to any variable and you have to use this value directly in searchChamber or assign to some variable to use it later.
Other problem: when you close window then tkinter destroy all widgets and you have to keep value from Entry in some variable
def searchChamber(self):
self.result = self.ent_InventoryNumber.get()
and later get this variable
test(GUI.result)
In this example I use print() to see if bind executes function after every pressed key. I also use class variable self.result to keep value and use it after closing window.
GUI.py
import tkinter as tk
class GUI:
def __init__(self, master):
self.result = '' # default value as start
self.ent_InventoryNumber = tk.Entry(master)
self.ent_InventoryNumber.insert('end', "Wprowadź TUTAJ numer inwentarzowy")
self.ent_InventoryNumber.grid(row=2, column=0)
self.ent_InventoryNumber.bind("<KeyRelease>", self.searchChamber)
def searchChamber(self, event=None):
self.result = self.ent_InventoryNumber.get()
print('[DEBUG] searchChamber:', self.result)
main.py
import tkinter as tk
from GUI import GUI
window = tk.Tk()
myGUI = GUI(window)
window.mainloop()
print('result:', myGUI.result)

How to open a new window in tkinter python 3.7 having same functions as that of original window?

I am working on code in tkinter python v3.7 where I want to open a new window which has same functionalities like original window. How can I do that?
While searching for solution I came across function naming Toplevel which creates new tkinter window. But this new window is completely new, It doesn't have functionalities(Button, geometry size in my case) which were provided in original one.
from tkinter import *
class TextPad:
def new_window(self):
top = Toplevel()
def __init__(self, master):
master.title('Text Pad')
master.geometry('400x400')
self.button = Button(master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()
My original window has geometry size of '400x400' and It has 'button', I want to open a new window having this functionalities.
I assume you want two (or more) windows at the same time.
If you want identical window then use again TextPad()
but this time use Toplevel() instead of Tk()
def new_window(self):
top = TextPad(Toplevel())
and don't run second mainloop()
If you want replace first window by new window - to have alwasy only one window - then you would have to destroy old window and then create new one using TextPad() with Tk() and mainloop()
But it would need to use self.master instead of master to have access to master in method new_window
from tkinter import *
class TextPad:
def new_window(self):
self.master.destroy()
root = Tk()
t = TextPad(root)
root.mainloop()
def __init__(self, master):
self.master = master
self.master.title('Text Pad')
self.master.geometry('400x400')
self.button = Button(self.master, text='Press',
command=self.new_window)
self.button.pack()
root = Tk()
t = TextPad(root)
root.mainloop()

What does frame.__init__ do? - tkinter

I am watching Sentdex's Tkinter tutorial and there are some related problems that arise:
Basic Class Questions that I'm just new to: How come Frame follows after the declaration of the Window class (sorry for a basic class question)? How is self.pack working without specifying what to pack?
What does frame.__init__ contribute to this code?
The code is this:
from tkinter import *
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master, bg='LightBlue')
self.master = master
self.init_window()
def init_window(self):
self.master.title("GUI")
self.master.geometry('400x400')
self.pack(fill=BOTH, expand=1)
quitButton = Button(self, text='Quit')
quitButton.place(x=0, y=0)
root = Tk()
app = Window(root)
root.mainloop()
Thanks in advance!
The class Window inherits from tk.Frame, this is what (Frame) after Window means.
In such a situation, Window is also a tk.Frame, hence when calling pack() on self, it is in essence packing itself.
It is likely less confusing to avoid star imports:
import tkinter as tk
class Window(tk.Frame): # <-- avoid star imports
def __init__(self, master=None):
self.master = master
super().__init__(master, bg='LightBlue') # <-- use super instead of hardcoding the parent class
self.init_window()
def init_window(self):
self.master.title("GUI")
self.master.geometry('400x400')
self.pack(fill=tk.BOTH, expand=True)
self.quit_button = tk.Button(self, text='Quit',
command=self.master.destroy)
self.quit_button.pack()
if __name__ == '__main__':
root = tk.Tk()
app = Window(root)
root.mainloop()
Frame is in brackets so that the class Window can inherit the methods of the tkinter.Frame class.
The Frame.init function initialises the class as a tkinter Frame.
The self.pack() line packs the Frame into self.master, which was created a couple of lines before.
Have a look at some basic classes and build up from there.
I did this tutorial a little while ago and found that I had to spend a little time having a look at classes first. I used some youtube videos I think, good luck!

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