Calling functions without closing the master window - Python Tkinter - python-3.x

I've tried to create a software. In this software there is a menu widget with function button that open functions e widgets. But, I've noticed, to keep going on script, it's necessary to close the master window (menu).
I've created an example to you understand my problem.
from tkinter import *
#Create Fuction that open new fuctions and widget
def test():
#Open a new widget
def fun_test_1():
top_level = Tk()
def test1():
top_level1 = Toplevel()
top_level1.title('new1')
top_level1.mainloop()
Button(top_level, text='test1',command=test1).pack()
top_level.mainloop()
fun_test_1()
#Before, open the second widget
def fun_test_2():
print('def fun_test_2(): works!')
top_level = Tk()
def test1():
top_level1 = Toplevel()
top_level1.title('new1')
top_level1.mainloop()
Button(top_level, text='Button', command=test1).pack()
top_level.mainloop()
fun_test_2()
root = Tk()
root.title('MASTER')
Button(root, text='Button',command=test).pack()
root.mainloop()
So, I need that fun_test_2() be called without close the root widget
And all functions i've tried to change Tk() to Toplevel() and Toplevel() to Tk().

The problem is you calling mainloop more than once, and for creating more than one instance of Tk. When you call mainloop, it won't return until that window has been destroyed. That's a fundamental aspect of how tkinter works.
The solution is to not create more than one instance of Tk and to not call mainloop more than once. If you need multiple windows, create instances of Toplevel. And again, only call mainloop once in total, not once per window.

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.

multiple commands for a tkinter button

I have been trying to execute 2 commands in 1 button. I have read that using lambda can solve the problem. But my situation is a little different. On button press, one command is to destroy the existing GUI, and the second command, I want is to open another SERVER GUI. Below is my existing button functionality.
exit_button = Button(topFrame, text='Quit', command=window.destroy)
How can I open another GUI using same button?
Thank you for any help.
Edit:-
I have now created the below function:
def close_func():
os.kill(os.getpid(), signal.SIGINT)
GUI_Interface()
window.destroy()
server_socket.close()
GUI_Interface is a function that I need to call after closing the existing .py file. If I put GUI_Interface as the first command for close_func(), then it really does not go back to the 2nd step and never closes the existing.py file.
And if I place GUI_Interface in the end, it just closes the existing one and nevr opens the function of the new .py file
Three options at least:
using or (with lambda if arguments):
from tkinter import Tk, Button
root = Tk()
Button(root, text='Press', command=lambda: print('hello') or root.destroy() or print('hi')).pack()
root.mainloop()
important to use or because and didn't work (don't know why or how this works at all)
or use function definitions:
from tkinter import Tk, Button
def func():
print('hello')
root.destroy()
print('hi')
root = Tk()
Button(root, text='Press', command=func).pack()
root.mainloop()
or lists with lambda:
from tkinter import Tk, Button
def func():
print('hello')
root.destroy()
print('hi')
root = Tk()
Button(root, text='Press', command=lambda: [print('hello'), root.destroy()]).pack()
root.mainloop()

How does Tkinter keep track of its widgets when used with classes?

from tkinter import *
class SampleClass:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.printButton = Button(master, text = "PrintButton", command=self.printMessage)
self.printButton.pack(side = LEFT)
def printMessage(self):
print("Hulk Smash!")
root = Tk()
samp = SampleClass(root)
root.mainloop()
The Tkinter root is passed to the class as a reference only once. So, when the root changes (pressing a button, or entering some text using entry widgets), the state of root is changed. How does the class samp know that the root has changed? I understand that the root.mainloop() method makes calls to the root in a loop but the class samp seems to have no idea of the changing reference. What am I missing here?
Tkinter is a thin wrapper around a Tcl interpreter which has loaded the "tk" package. When you create a widget (eg: Frame(master)), this creates an object in the Tcl interpreter. It is the Tcl interpreter that keeps hold of the reference to the master widget, and it is the Tcl interpreter that responds to changes.

tkinter Button does not open new window

I’m experiencing problems with tkinter.
I have code that is meant to open a new window on button press, but the window does not open.
Here’s my code:
Main Module
#!/usr/bin/python
#encoding: latin-1
import tkinter
import ce
#window config
window = tkinter.Tk() #create window
window.title("BBDOassist") #set title
window.geometry("750x500") #set size
…
# buttons
button_ce = tkinter.Button(window, text="CE Evaluation", command="ce.run()")
button_ce.pack()
window.mainloop() #draw the window and start
’CE’ Module
#!/usr/bin/python
#encoding: latin-1
import tkinter
…
def run():
#window config
window = tkinter.Tk() #create window
window.title("BBDOassist - CE Evaluation") #set title
window.geometry("750x500") #set size
…
window.mainloop() #draw the window and start
You have at least two problems
First, you must give the command attribute a reference to a function. You are passing it a string. A string is useless. You need to change your button definition to this:
button_ce = tkinter.Button(window, text="CE Evaluation", command=ce.run)
Second, if you want to create additional windows then you need to create instances of Toplevel rather than Tk. A tkinter program needs exactly one instance of Tk, and you need to call mainloop exactly once.
Change run to look like this (and remove the call to mainloop inside run):
def run():
#window config
window = tkinter.Toplevel()
...

Python: Attribute error when trying to access thread owned variable

I create a simple window with tkinter and start it as thread to keep the main program running next to the window. This is a shortened version of it:
import tkinter as tk
import threading
class mainWindow(threading.Thread):
def __init__(self, winWidth=500, winHeight=300):
threading.Thread.__init__(self)
self.winWidth = winWidth
self.winHeight = winHeight
# Save all drawn objects, to move or delete them later
self.bricks = []
self.start() #start thread
def run(self):
# parent object for all windows
self.master = tk.Tk()
self.master.protocol("WM_DELETE_WINDOW", self.callback)
self.show()
def callback(self):
self.master.quit()
# Initialize everything important
def show(self, tileSize=10):
# create main window
self.w = tk.Canvas(
self.master,
width=self.winWidth,
height=self.winHeight,
background="white")
self.w.pack()
# draw brick
color = "gray49"
posX = 200
posY = 100
self.bricks.append(self.w.create_rectangle(posX, posY, posX+20, posY+20, fill=color))
tk.mainloop()
def move_brick(self, x,y):
self.w.move(self.brick, x, y)
mainWindow = mainWindow()
mainWindow.move_brick(100,100)
When I run the shown code the window opens correctly, but when I try to move the rectangle with move_brick(...) I get this error:
AttributeError: 'mainWindow' object has no attribute 'w'
Why can't the object find my Canvas w?
You probably have a race condition, which is common for threaded applications. The main thread is likely calling move_brick before the worker thread has a chance to create the widget.
You can probably see this happening if you add print statements right before and after creating the widget, and in your move_brick function.
Even if you fix this, this code likely won't work because all tkinter code needs to run in a single thread. Creating the GUI in one thread and calling move_brick in another thread is not the right way to use tkinter.

Resources