why does importing a tk variable from main break this code? - python-3.x

I'm trying to organize my code into two separate modules - main, and new_window
main passes a tk object to new_window to populate it with a label and a button.
Pressing the button is supposed to close the window.
This is main:
import tkinter as tk
from new_window import *
#I create a tk object called "main_menu"
main_menu = tk.Tk()
#The tk object is passed to a class in another module to have its properties applied
main_menu_obj = MainMenu(main_menu)
#the main loop function is called as required by tk
main_menu.mainloop()
This is new_window:
import tkinter as tk
#to close the window, the tk variable created in main must is imported
from main import main_menu
class MainMenu:
#when this module receives the tk object from main, it adds a label and a button
def __init__(self, root):
print('main menu')
root.title('Main Menu')
Label_1 = tk.Label(root, text='Main Menu')
Button_1 = tk.Button(root, text='close',command=self.close)
Label_1.pack()
Button_1.pack()
#when the button is pressed, the tk object "main_menu" is destroyed
def close(self):
print('closing window')
main.main_menu.destroy()
When I run this, I receive: NameError: name 'MainMenu' is not defined. But I just defined this class in new_window. What mistake am I making? If I comment out "from main import main_menu" and comment out "main.main_menu.destroy()" it works great and prints "closing window" when I press the close button. (but obviously it doesn't close the window then).

It is circular import issue, i.e. A imports B and B imports A.
Actually you don't need to import main inside new_window because you have already passed the reference of the root window to MainMenu class. Therefore you can use this reference to close the root window.
Updated new_window.py:
import tkinter as tk
# don't need to import main here
class MainMenu:
#when this module receives the tk object from main, it adds a label and a button
def __init__(self, root):
# save argument root
self.root = root
print('main menu')
root.title('Main Menu')
Label_1 = tk.Label(root, text='Main Menu')
Button_1 = tk.Button(root, text='close',command=self.close)
Label_1.pack()
Button_1.pack()
#when the button is pressed, the tk object "main_menu" is destroyed
def close(self):
print('closing window')
self.root.destroy()

Related

How to open two modules in one window, Python Tkinter

I have a question: i am tossing the code from 2ch files, i have already lost ideas. Calling fileA.py opens a window for me with two buttons exit and start. Exit works but when I click start I need to open the second window fileB.pt. (I want both windows to open in one window) Seemingly works only problem I have is it doesn't open "window on window" but "docks underneath" and I have the effect of two windows open :/. Please help, thank you in advance:) Python 3.10
fileA.py
import tkinter as tk
from GUI.module.scale_of_img import get_scale
class FirstPage:
def __init__(self, root):
self.root = root
def get_settings(self):
# Window settings
self.root.title('....')
self.root.resizable(False, False)
self.root.geometry("1038x900")
if __name__ == '__main__':
first = FirstPage(tk.Tk())
first.get_run_first_page()
fileB.py
import tkinter as tk
"importy..."
''' The second side of the application '''
class SecondPage:
def __init__(self, root=None):
self.root = root
self.my_canvas = tk.Canvas(self.root, width=1038, height=678)
self.my_canvas.pack(fill="both", expand=True)
if __name__ == '__main__':
second = SecondPage(tk.Tk())
second.get_run()
in order to put two "windows" in the same "window" you need to put all items inside a Frame, which is basically a container than you can simply pack when you want everything to show and unpack when you want everything in it to disapear.
all items in the first window will be children of a frame and all items in the second window will be children of another frame, and to switch you just need to call pack_forget() on one and pack() on another.
for the first file
class FirstPage:
def __init__(self, root):
self.root = root
self.frame = tk.Frame(root)
self.frame.pack(expand=True)
def get_picture(self):
# all items inside this window must be children of self.frame
self.my_canvas = tk.Canvas(self.frame, width=1038, height=500)
...
def get_second_page(self):
from GUI.module.second_page import SecondPage
self.frame.pack_forget() # to hide first page
# self.frame.destroy() # if you are never brining it back
SecondPage(self.root).get_run()
and for the second file
class SecondPage:
def __init__(self, root=None):
self.root = root
self.frame = tk.Frame(root) # new frame
self.frame.pack(expand=True)
self.my_canvas = tk.Canvas(self.frame, width=1038, height=678)
self.my_canvas.pack(fill="both", expand=True)
def get_button(self):
# Add buttons
# all here should be children of self.frame now
button1 = tk.Button(self.frame, text="...", )
...
you could destroy the first frame when you switch over to save some resources if you don't intend to return to it ever again, but the difference in memory is negligible.
assuming what you want is another Tk window to open, you shouldn't give it the same root, instead use an instance of Toplevel
from tkinter import Toplevel
# class definition here
def get_second_page(self):
from GUI.module.second_page import SecondPage
SecondPage(Toplevel(self.root)).get_run()
passing the Toplevel as a child of self.root is necessary, but note that the two windows have different roots.
Edit: turns out this wasn't what the OP ment by "window on window" -_-, but it am keeping it here for other readers.

How to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu

I'm trying to enter a file name into your graphics window and then read that file and display the file in a graphics window using a file menu. When I press new another window opens to ask for a name and displays it in the main window, but I can't get the new window to open and do the rest. I have the TopLevel to open a new window, but I get NameError: name 'TopLevel' is not defined and can't continue from there. What can I do to make it work?
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
from tkinter import filedialog as fd
class Example(Frame):
def __init__(self, parent):
Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = TopLevel()
Label(self, text='Enter Your Name').grid(row=0)
e1 = Entry(self)
e1.grid(row=0, column=1)
def main():
root = Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()
if __name__ == '__main__':
main()
The problem is simply that you're not defining or importing anything named Toplevel. The way you're importing tkinter makes this an easy problem to have.
My recommendation is to remove these statements:
from tkinter import Tk, Frame, Menu
from tkinter.ttk import *
... and replace them with these:
import tkinter as tk
from tkinter import ttk
From then on, you have access to almost everything in the tkinter and ttk packages. You simply need to add a tk. or ttk. prefix to everything you use from those packages.
This keeps global namespace pollution at a minimum (ie: you only add two names to the global namespace), and makes your code more self-documenting.
class Example(tk.Frame):
def __init__(self, parent):
ttk.Frame.__init__(self, parent)
self.parent = parent
self.initUI()
def initUI(self):
self.parent.title("Simple menu")
menubar = tk.Menu(self.parent)
self.parent.config(menu=menubar)
fileMenu = tk.Menu(menubar)
fileMenu.add_command(label="New", command=self.onNew)
menubar.add_cascade(label="File", menu=fileMenu)
def onNew(self):
print("do New")
top = tk.TopLevel()
ttk.Label(self, text='Enter Your Name').grid(row=0)
e1 = ttk.Entry(self)
e1.grid(row=0, column=1)
def main():
root = tk.Tk()
root.geometry("250x150+300+300")
app = Example(root)
root.mainloop()

Connecting two files or classes in Python

I have two pages(each having a class) that that needs to be connected, page1(file1.py) and page2(file2.py).
I want to open page2 with a button that will be on page1. As mentioned the codes for each page are in separate files, file1 and file2.
I used import file2 but as soon as I run the program it opens and runs file2.py which I dont want. I want to open page2 via button when the user needs to open it.
Any suggestions???
Regards
Khisrow
Just put the second page inside a class, import that inside a function and call that. Then give the function as the command to a Button.
Like this:
Module_one:
import tkinter as tk
def test_func():
import Module_two
Module_two.TestClass()
root = tk.Tk()
b = tk.Button(root, text="Click", command=lambda: test_func())
b.pack()
root.mainloop()
Module_two:
class TestClass:
def __init__(self):
import tkinter as tk
root = tk.Toplevel()
lbl = tk.Label(root, text="Test Label")
lbl.pack()

how do i stop the tkinter window being not interactable while a "computation heavy" function is running

i have a tkinter window that does has some widgeds that the user can interact with and when a function that takes a little bit of time to run the whole window cant be interacted with.and i want to change that
i heard you can solve this problem by implementing multiprocessing to the function but i dont really understand how to implement it.
my code is something like this :
import tkinter as tk
from tkinter import *
def functionthattakessometime():
while True:
print("haa")
root=tk.Tk()
b=Button(root,text="print haha",command=functionthattakessometime)
a=Button(root,text="do nothing")
b.pack()
root.mainloop()
You cant press the "do nothing" window after you pressed the "print haha" button and i want to change that
so you can press the "do nothing" button even after you pressed the "print haha" button.
The threading library is what you want here.
Here is a simple example of how it would work with your code.
import tkinter as tk
import threading
root = tk.Tk()
allow_print = True
def function_that_takes_sometime():
while allow_print:
print("haha")
def start_thread():
global allow_print
allow_print = True
thread = threading.Thread(target=function_that_takes_sometime)
thread.start()
def stop_thread():
global allow_print
allow_print = False
tk.Button(root, text="print haha", command=start_thread).pack()
tk.Button(root, text="Stop print", command=stop_thread).pack()
root.mainloop()
That said I would change a few things.
First I would remove from tkinter import * as you should never import tkinter twice and it is better to just use import tkinter as tk because this prevents us from overwriting any methods on accident.
Second I would build this in a class so we can avoid global variables.
Here is an OOP version:
import tkinter as tk
import threading
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.allow_print = True
tk.Button(self, text="print haha", command=self.start_thread).pack()
tk.Button(self, text="Stop print", command=self.stop_thread).pack()
def function_that_takes_sometime(self):
while self.allow_print:
print("haha")
def start_thread(self):
self.allow_print = True
thread = threading.Thread(target=self.function_that_takes_sometime)
thread.start()
def stop_thread(self):
self.allow_print = False
Main().mainloop()
And to simplify this further because often threading is overkill we can use the after() method to manage the loop and not affect the mainloop.
import tkinter as tk
class Main(tk.Tk):
def __init__(self):
super().__init__()
self.allow_print = True
tk.Button(self, text="print haha", command=self.function_that_takes_sometime).pack()
tk.Button(self, text="Stop print", command=self.stop_print).pack()
def function_that_takes_sometime(self):
if self.allow_print:
print("haha")
self.after(1000, self.function_that_takes_sometime)
def stop_print(self):
self.allow_print = False
Main().mainloop()

How to run a function as soon as GUI started?

I need to run a check function as soon as the tkinter GUI is available. I tried with the following code, but the Messagebox which comes up is unresponsive and I can't press the OK button.
import tkinter.messagebox as mbox
import tkinter
from tkinter import ttk
class MQ(ttk.Frame):
def __init__(self, parent, *args, **kwargs):
ttk.Frame.__init__(self, parent, *args, **kwargs)
self.root = parent
self.init_gui()
if mycheck=True:
mbox.showinfo("Title","message")
...
...
if __name__ == '__main__':
root = tkinter.Tk()
MQ(root)
root.mainloop()
You can use after_idle to run something as soon as the GUI starts up, or you can use after to have it run after a brief amount of time. The two have slightly different behaviors with respect to whether the code runs before or after the root window is displayed (which might be platform dependent; I'm not sure)
import tkinter as tk
from tkinter import messagebox
def say_hello(root, message):
tk.messagebox.showinfo("Info", message)
root = tk.Tk()
root.after(1, say_hello, root, "Hello, world")
root.mainloop()

Resources