Enable button after closing a second window in python (Tkinter) - python

I want to re-enable a button in other window, the idea is that in my principal window press a button and start the second window, in this moment this button will be disable, and once the second window will be close, the button will need to be enable again.
I am trying to use root.protocol("WN_DELET_WINDOW",self.on_closing) but when I closed that window nothing happens. Actually I put a print sentence trying to identify if the program enter on this part, but the result is the same Nothing happens.
class SerialTkWindow():
def __init__(self,lock) -> None:
lock.configure(state='disable')
root =Tk()
root.geometry('300x100')
root.title("Reset HU ?")
root.resizable(0,0)
#region Label
welcomeLbl = Label(root,text="Select COM Port",fg="black",bg=root["bg"],font="Helvetica 12")
welcomeLbl.pack()
#endregion
root.protocol("WN_DELET_WINDOW",self.on_closing)
root.mainloop()
def on_closing(self, lock):
print("Regresamos")
lock.configure(state=NORMAL)
lock is refered in SerialTkWindow(self.sendCanBtn) and self.sendCanBtn is refered in self.sendCanBtn = Button(frame3,text="Send CAN Msgs",command=self.CANMsgBtn)

Instead of using root.protocol("WN_DELETE_WINDOW",self.on_closing), you can call on_closing after the mainloop returns.
from tkinter import *
class SerialTkWindow:
def __init__(self, lock) -> None:
self.lock = lock
self.lock.configure(state='disable')
self.root = Tk()
self.root.geometry('300x100')
self.root.title("Reset HU ?")
self.root.resizable(0, 0)
# region Label
self.welcomeLbl = Label(self.root, text="Select COM Port", fg="black", bg=root["bg"], font="Helvetica 12")
self.welcomeLbl.pack()
# endregion
def on_closing(self):
print("Regresamos")
self.lock.configure(state=NORMAL)
root = Tk()
def open_serial_tk():
win = SerialTkWindow(winbutton)
while True:
try:
win.root.update()
win.root.update_idletasks()
except TclError:
win.on_closing()
break
winbutton = Button(root, text="open", command=open_serial_tk)
winbutton.pack()
root.mainloop()

Related

Copying from Messagebox with Tkinter

I've written a password generator with Tkinter and have set a messagebox that pops-up when the data for the website is already in the database.
Is there an option that allows me to copy the text from the pop-up? Because these passwords are really long. Or do I need to go into the file where it is saved to copy it?
messagebox.showinfo(title=website, message=f" Email: {email}\nPassword: {password}")
Try something like this:
import tkinter as tk
class Popup:
def __init__(self, title:str="Popup", message:str="", master=None):
if master is None:
# If the caller didn't give us a master, use the default one instead
master = tk._get_default_root()
# Create a toplevel widget
self.root = tk.Toplevel(master)
# A min size so the window doesn't start to look too bad
self.root.minsize(200, 40)
# Stop the user from resizing the window
self.root.resizable(False, False)
# If the user presses the `X` in the titlebar of the window call
# self.destroy()
self.root.protocol("WM_DELETE_WINDOW", self.destroy)
# Set the title of the popup window
self.root.title(title)
# Calculate the needed width/height
width = max(map(len, message.split("\n")))
height = message.count("\n") + 1
# Create the text widget
self.text = tk.Text(self.root, bg="#f0f0ed", height=height,
width=width, highlightthickness=0, bd=0,
selectbackground="orange")
# Add the text to the widget
self.text.insert("end", message)
# Make sure the user can't edit the message
self.text.config(state="disabled")
self.text.pack()
# Create the "Ok" button
self.button = tk.Button(self.root, text="Ok", command=self.destroy)
self.button.pack()
# Please note that you can add an icon/image here. I don't want to
# download an image right now.
...
# Make sure the user isn't able to spawn new popups while this is
# still alive
self.root.grab_set()
# Stop code execution in the function that called us
self.root.mainloop()
def destroy(self) -> None:
# Stop the `.mainloop()` that's inside this class
self.root.quit()
# Destroy the window
self.root.destroy()
def show_popup():
print("Starting popup")
Popup(title="title", message="Message on 1 line", master=root)
print("Ended popup")
print("Starting popup")
Popup(title="title", message="Message\nOn 2 lines", master=root)
print("Ended popup")
root = tk.Tk()
root.geometry("300x300")
button = tk.Button(root, text="Click me", command=show_popup)
button.pack()
root.mainloop()
It's just a simple class that behaves a lot like messagebox.showinfo. You can add an icon if you want. Please note that some of the functionality is missing but it should work with your code.
For more info on the functions that I used please read the docs. Here are the unofficial ones.

How can I prevent my main window from running with a Toplevel window in python and Tkinter?

I am trying to stop the main window from running until a button has been pressed on a separate Toplevel window.
Example:
from tkinter import *
let_user_through = False
window = Tk()
def activate_main_window():
global let_user_through
let_user_through = True
frame = Toplevel()
b = Button(frame, text="Enter", command=activate_main_window).pack()
if let_user_through == True:
lbl = Label(window, text="Hello")
#bunch of code
#bunch of code
window.mainloop()
In this example, in the main window there is a label that reads: "Hello".
But I don't want people to be able to see it if they haven't pressed the button on the frame
Once the user has pressed the button, the frame will destroy itself and the main window will continue executing a bunch of code.
I'm a beginner to tkinter so i'm not sure if the answer is obvious or not. Thanks!
You can use frame.wait_window() to wait until frame is destroyed. Also you need to call frame.destroy() inside activate_main_window().
from tkinter import *
let_user_through = False
window = Tk()
def activate_main_window():
global let_user_through
let_user_through = True
frame.destroy() # need to destroy frame
# wait for root window becomes visible
# otherwise "frame" may be open behind root window
window.wait_visibility()
frame = Toplevel()
Button(frame, text="Enter", command=activate_main_window).pack()
frame.grab_set() # capture keyboard/mouse events
frame.wait_window() # wait for "frame" to be destroyed
if let_user_through:
Label(window, text="Hello").pack()
#bunch of code
#bunch of code
# should it be within the above for loop?
window.mainloop()
A small change to your code using window.withdraw and window.deiconify works for me. #acw1668 correctly pointed out an error in my original code, so here is the fix.
Your main window is invisible until user presses button.
import tkinter as tk
let_user_through = False
window = tk.Tk()
window.withdraw()
def activate_main_window():
global let_user_through
let_user_through = True
frame.destroy() # need to destroy frame
frame = tk.Toplevel()
tk.Button(frame, text="Enter", command=activate_main_window).pack()
frame.wait_window() # wait for "frame" to be destroyed
if let_user_through:
tk.Label(window, text="Hello").pack()
window.update()
window.deiconify()
#bunch of code
#bunch of code
window.mainloop()
I've created a class that removes the need for let_user_through and sets up code for any next steps.
import tkinter as tk
class invisible:
def __init__( self ):
self.window = tk.Tk()
self.window.withdraw() # make window invisible
self.frame = tk.Toplevel()
tk.Button(
self.frame, text = "Enter", command = self.activate_main_window ).pack( fill='both' )
self.frame.wait_window( ) # wait for "frame"
self.button = tk.Button( self.window, text = "Hello", command = self.remove_next )
self.button.pack( fill = 'both')
self.window.update()
self.window.deiconify() # make window visible
def activate_main_window( self ):
self.frame.destroy() # need to destroy frame
def remove_next( self ):
self.button.destroy()
tk.Label( self.window, text = "Bunch of codeA" ).pack( fill = 'both' )
tk.Label( self.window, text = "Bunch of codeB" ).pack( fill = 'both' )
tk.Label( self.window, text = "Bunch of codeC" ).pack( fill = 'both' )
# continue code initialization
if __name__ == '__main__':
make = invisible()
tk.mainloop()

Value from new window, button, and script to main script/window python+tkinter

I have a main script. When you push the button (in tkinter) you open a class with a new window and a new button.
When you click the new button (in the new window and different file) the text in the main window should be updated.
I have the following:
Main script
from tkinter import *
from kandit import Kandit
root=Tk()
def hoop():
s=Kandit()
label.configure(text=s)
button=Button(root, text="ok", command=hoop)
button.grid(row=0,column=0)
label=Label(root, text="nog niet dus")
label.grid(row=1, column=0)
Sub-script
class Kandit:
def __init__(self):
self.Master=Toplevel()
self.Button=Button(self.Master, text="check", command=self.Return())
self.Button.grid(row=0,column=0)
self.Master.mainloop()
def Return(self):
self.Keuze="nothing"
return self.Keuze #, self.Master.destroy()
except from the destroy it works until the moment I press the "check" button.
Than nothing happens.
Try this:
import tkinter as tk
class Kandit:
def __init__(self):
# Set the default value for keuze:
self.keuze = None
self.master = tk.Toplevel()
# If the user presses the "X" in the window toolbar call `_return`
self.master.protocol("WM_DELETE_WINDOW", self.destroy)
# When the button is pressed call `_return`
self.button = tk.Button(self.master, text="check", command=self._return)
self.button.grid(row=0, column=0)
# Start the mainloop. Later we will stop the mainloop
# Note it waits until the button is pressed/window closed
self.master.mainloop()
# Here we can garantee that `_return` was called
# and `self.keuze` has set to the value we need.
def _return(self):
# Set the result in a variable
self.keuze = "nothing"
self.destroy()
def destroy(self):
# Stop the mainloop so that the program can continue
self.master.quit()
# Remove the window from the screen
self.master.destroy()
def hoop():
# Create the window and wait until the button is pressed/window closed
new_window = Kandit()
# Get the result from the class
new_text = new_window.keuze
#if
# Set the label with the result
label.configure(text=new_text)
root = tk.Tk()
button = tk.Button(root, text="ok", command=hoop)
button.grid(row=0, column=0)
label = tk.Label(root, text="nog niet dus")
label.grid(row=1, column=0)
root.mainloop()
The problem in your case is that you can't return values from the __init__ method. This is why you have you save the result to a variable and retrieve it later

Self Made Tkinter Popup Menu Python

I was writing a GUI library in Python based on tkinter and I was designing and building all the widgets, but I have come to the PopUp menus.
Due that tkinter picks system menus and this can't be customized, I write the following code to make a frame where I can put my customized buttons in and works as a popup.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.place(x=0)
def function1():
print('function1 activated')
# create a menu
f = Frame(root,width=80,height=60,background='green')
b2 = Button(f,text='function',command=function1)
b2.pack()
def open_popup(event):
try:
f.place(x=event.x, y=event.y)
root.after(1)
f.focus_set()
w.bind_all("<Button-1>",close_popup)
except:
print("Can't open popup menu")
def close_popup(event):
try:
f.place_forget()
root.after(1)
w.unbind_all("<Button-1>")
except:
print("Can't close popup menu")
w.bind("<Button-3>", open_popup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
Everything works well, if I clicked with the mouse right-button the popup menu appears, and if I clicked on every other part the popup menu dissapears.
The problem is that, due to bind_all when I press the button of my popup menu, function1 doesn't run and the event handler closes the popup. I have tried with only bind but this time, function1 runs and the event handler doesn't activates.
Is there anyway I can do that?
Thanks
I would do this using a tracking variable.
We can first assign None to f as a way to check if f is currently set up.
If f is not None then we create frame and button. Then when the function is activated we can run function and destroy the frame the button was in. This also destroys the button and then we set f back to None for out next use.
Take a look at the below reworked example.
Let me know if you have any questions.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.place(x=0)
f = None # Tracking F to see if it is None or not.
def function1():
global f
print('function1 activated')
# add this to the end of the function to destroy the frame and reset f
if f != None:
f.destroy()
f = None
def open_popup(event):
global f
# if f is None then create frame and button else don't
if f == None:
f = Frame(root,width=80,height=60,background='green')
f.place(x=event.x, y=event.y)
b2 = Button(f,text='function',command=function1)
b2.pack()
else:
print("Can't open popup menu")
w.bind("<Button-3>", open_popup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
I found a way to do this without modifying so much the code, the idea of the tracking variable was good but doesn't solve all the problems, and this code does.
from tkinter import *
root = Tk()
w = Label(root, text="Right-click to display menu", width=40, height=20)
w.pack()
def function1():
print('function1 activated')
try:
f.place_forget()
except:
pass
# create a menu
f = Frame(root,width=80,height=60,background='green')
b2 = Button(f,text='function',command=function1)
b2.place(x=0,y=5)
def open_popup(event):
try:
f.place(x=event.x, y=event.y)
root.after(1)
f.focus_set()
except:
pass
def close_popup(event):
try:
f.place_forget()
root.after(1)
w.unbind_all("<Button-1>")
except:
pass
def enable_depopup(event):
w.bind_all("<Button-1>",close_popup)
def disable_depopup(event):
w.unbind_all("<Button-1>")
w.bind("<Button-3>", open_popup)
w.bind("<Motion>", enable_depopup)
f.bind("<Motion>", disable_depopup)
b = Button(root, text="Quit", command=root.destroy)
b.pack()
root.mainloop()
In this way, everytime I move the mouse over the parent window, the <Button-1> of the mouse is binded to close the popup menu.
And doing a trick, that is place the button of the menu a few pixels down, this let the mouse pass through the popup frame to reach the button and disable the <Button-1> binding letting me click the button.
The function of the button activate the place_forget method of the frame, so everything works correctly.

Python - Tkinter exit button on a child window and how it affects a toggle button state

I'm a newbie trying to learn Python with the Raspberry Pi. I've been writing some code to try to make a simple emulator for the piFace add on board.
There are a few issues with it and I'm learning as I work my way through them.
My code opens a window and shows a toggle button which toggles an LED image on/off. I also added a button that opens a child window. The child window has two buttons. One is a on/off toggle button that toggles an LED image on/off, the other is an Exit button.
My problem is that when the LED is ‘on’ if I use the Exit button the child window closes, as it should. But if I re-open the child window and use the toggle button to turn the LED on, nothing happens. If I press the toggle button again the LED then comes on.
I kind of understand what the problem is. Because I close the child window when the LED is ‘on’ the toggle button state is still in the ON state. And, when I re-open the window and click the toggle button I'm just setting the toggle button state to OFF.
I'm not sure how to address the problem. Should I look at closing the window a different and probably correct way? Should I look at a way of presetting the state of the toggle switch each time the child window is open? Should I try something completely different? Should I stop altogether? :-)
I hope that makes some sense.
Thanks for any help.
Here's my code....
# Idle 10_01_2014_GUI label image toggle
from time import sleep
from Tkinter import *
import Tkinter as tk
import threading
class App:
def __init__(self, master):
self.master=master
frame = Frame(master)
frame.pack()
Label(frame, text='Turn LED ON').grid(row=0, column=0)
Label(frame, text='Turn LED OFF').grid(row=0, column=1)
self.button0 = Button(frame, text='LED 0 OFF', command=self.convert0)
self.button0.grid(row=2, column=0)
self.LED0 = Label(frame, image=logo2)
self.LED0.grid(row=2, column=1)
self.buttonnewwindow = Button(frame, text='Knight Rider TEST', command=self.new_window)
self.buttonnewwindow.grid(row=10, column=0)
self.button8 = Button(frame, text='Exit', command=quit)
self.button8.grid(row=11, column=0)
def convert0(self, tog=[0]):
tog[0] = not tog[0]
if tog[0]:
print('LED 0 ON')
self.button0.config(text='LED 0 ON')
self.LED0.config(image = logo)
self.LED0.grid(row=2, column=1)
else:
print('LED 0 OFF')
self.button0.config(text='LED 0 OFF')
self.LED0.config(image = logo2)
self.LED0.grid(row=2, column=1)
def new_window(self):
print('New Window')
self.newWindow = tk.Toplevel(self.master)
self.app = App2(self.newWindow)
self.newWindow.grab_set() # I added this line to stop opening multiple new windows
class App2:
def __init__(self, master):
self.signal = False
print('self.signal', self.signal)
self.master=master # I added this line to make the exit button work
frame = Frame(master)
frame.pack()
Label(frame, text='Turn LED ON').grid(row=0, column=0)
Label(frame, text='Turn LED OFF').grid(row=0, column=1)
self.button0 = Button(frame, text='Knight Rider OFF', command=self.convert0)
self.button0.grid(row=2, column=0)
self.LED0 = Label(frame, image=logo2)
self.LED0.grid(row=2, column=1)
self.button9 = Button(frame, text='Exit', command=self.close_window)
self.button9.grid(row=3, column=0)
def convert0(self, tog=[0]):
tog[0] = not tog[0]
if tog[0]:
print('Knight Rider ON')
self.button0.config(text='Knight Rider ON')
self.signal = True
print('self.signal', self.signal)
print('tog[0]', tog[0])
self.LED0.config(image = logo)
else:
print('Knight Rider OFF')
self.button0.config(text='Knight Rider OFF')
self.signal = False
print('self.signal', self.signal)
print('tog[0]', tog[0])
self.LED0.config(image = logo2)
def close_window(self):
print('Knight Rider OFF')
print('self.signal', self.signal)
self.button0.config(text='Knight Rider OFF')
self.LED0.config(image = logo2)
self.signal = False
print('self.signal', self.signal)
sleep(.5)
print('Close Child window')
self.master.destroy() # I added this line to make the exit button work
root = Tk()
logo2 = PhotoImage(file="c:\\Users\\joebloggs\\Downloads\\led-off.gif")
logo = PhotoImage(file="c:\\Users\\joebloggs\\Downloads\\led-on.gif")
root.wm_title('LED on & off program')
app = App(root)
root.mainloop()
The roots of the problem come from the fact that you start the child window assuming that
self.signal = False. I you have a way of detecting the state of the LED, put it here and the problem will solve itself, i.e. self.signal = get_led_state().
Now, if getting the genuine LED state is not possible, then you would need to store that state somewhere else, so that it is preserved between opening and closing the child window. One way would be simply putting the signal field into the App class. But I would go as follows: creating a State class, which will hold the state of the LED for me, e.g.:
class State:
LED_ON = true
You will then control the State.LED_ON field instead of self.signal to get or set the LED state. Note, that it is not thread-safe, but I believe you don't have to worry about that right now :)

Categories