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.
Related
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()
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
I'm new to TKinter. I need to change the text of a button and its state when its clicked, then do some actions, and finally change again its text and state.
The problem is the changes only apply once the function has ended, skipping the first change of state and text. It never changes the Buttons text to "loading" and the button is never disabled.
Here is the code for the problem i'm experiencing:
#!/usr/bin/env python
import tkinter as tk
import time
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master = master
self.pack(fill=tk.BOTH, expand=1)
self.create_widgets()
def create_widgets(self):
self.master.title("CW POS")
cierre = tk.Button(
self.master,
command=self.re_imprimir_ultimo_cierre)
cierre["text"] = "foo"
cierre.pack(fill=tk.BOTH, expand=1)
self._cierre = cierre
salir = tk.Button(self.master, text="quit", command=self.salir)
salir.pack(fill=tk.BOTH, expand=1)
def salir(self):
exit()
def re_imprimir_ultimo_cierre(self):
self._cierre["text"] = "Loading..."
self._cierre["state"] = tk.DISABLED
# TODO: magic
time.sleep(2)
self._cierre["text"] = "foo"
self._cierre["state"] = tk.NORMAL
root = tk.Tk()
root.geometry("240x180")
root.resizable(False, False)
app = Application(root)
root.mainloop()
How do I make the button show text="loading" and state=DISABLED, while the button is doing my calculations?
There is a pretty quick fix to this problem, you just need to update the button, once you change it's text to "Loading" (self._cierre["text"] = "Loading...")
def re_imprimir_ultimo_cierre(self):
self._cierre["text"] = "Loading..."
self._cierre["state"] = tk.DISABLED
self._cierre.update() # This is the line I added
# TODO: magic
time.sleep(2)
self._cierre["text"] = "foo"
self._cierre["state"] = tk.NORMAL
This just simply updates the buttons state after you change the text and state.
From what I understand this is because a button will run all the code within its command, before updating anything on the screen, so you essentially have to force the button to update itself within its command.
Hope this helps :)
I want a button in my window to open a new window and close the previous one. Is it possible to have one button do both of these? I've tried in the following code, but it hasn't worked, just told me that window is not defined:
import tkinter
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = window2).pack()
window.mainloop()
def window2():
window.destroy() #This is where the error is
menu = tkinter.Tk()
etc, etc, etc
window1()
First, you need to return the window object from the first function:
def window1():
window = tkinter.Tk()
tkinter.Button(window, text = "Next", command = lambda: window2(window)).pack()
window.mainloop()
return window
Then, you need to pass the window as an argument to your function:
def window2(window):
window.destroy()
menu = tkinter.Tk()
And then call window1 with:
window = window1()
and click the button to destroy it and do the rest
This is an example using Toplevels, which is usually a better choice than creating, destroying, re-creating Tk() instances. The unique Toplevel ID is passed to the close_it function using partial(). You would, of course, combine them or have the close function call the open function.
try:
import Tkinter as tk ## Python 2.x
except ImportError:
import tkinter as tk ## Python 3.x
from functools import partial
class OpenToplevels():
""" open and close additional Toplevels with a button
"""
def __init__(self):
self.root = tk.Tk()
self.button_ctr=0
but=tk.Button(self.root, text="Open a Toplevel",
command=self.open_another)
but.grid(row=0, column=0)
tk.Button(self.root, text="Exit Tkinter", bg="red",
command=self.root.quit).grid(row=1, column=0, sticky="we")
self.root.mainloop()
def close_it(self, id):
id.destroy()
def open_another(self):
self.button_ctr += 1
id = tk.Toplevel(self.root)
id.title("Toplevel #%d" % (self.button_ctr))
tk.Button(id, text="Close Toplevel #%d" % (self.button_ctr),
command=partial(self.close_it, id),
bg="orange", width=20).grid(row=1, column=0)
Ot=OpenToplevels()
Yes. Is possible. But you'll need to def that:
def window1:
blablabla
blablabla
def window2:
window2.destroy() <-- Here where the error was
How you noticed, put your name of window what you want Destroy and it will work!
using Python3
You could use a "global" such as:
root = Tk()
root.title('This is the root window')
def window_create():
global window_one
window_one = Tk()
window_one.title('This is window 1')
Then, from any function (or elsewhere) when you want to destroy window_one, do:
def window_destroyer():
window_one.destroy()
You could call your window_destroyer function from a button anywhere such as root which the example shows:
kill_window_btn = Button(root, text="Destroy", command=window_destroyer).pack()
Of course, follow your own naming conventions. :)
It seems to me, just 'global window_one' would solve it.
Is there a way to tell Tkinter that I want some widget to always remain focused? I've created a minimal example that can be run to show my issue , here's an example window with small toplevel windows also overlayed:
Now if I click the upper title tk, the main window comes into focus and suddenly the small windows are behind the main window
I want to treat these smaller windows as if they are always in focus until the user specifically closes them. Of course this is a minimal example that is an idea behind a small subsection of my large application , is there any easy setting I can use for the toplevel that guarantees it will always remain in focus regardless of other windows? Here's the actual code that can be run to replicate this:
from Tkinter import *
class PropertyDialog(Toplevel):
def __init__(self, root, string):
Toplevel.__init__(self)
self.wm_overrideredirect(1)
self.root = root
self.\
geometry('+%d+%d' %
(root.winfo_pointerx(),
root.winfo_pointery()))
try:
self.tk.call('::Tk::unsupported::MacWindowStyle',
'style', self._w,
'help', 'noActivates')
except TclError:
pass
window_frame = Frame(self)
window_frame.pack(side=TOP, fill=BOTH, expand=True)
exit_frame = Frame(window_frame, background='#ffffe0')
exit_frame.pack(side=TOP, fill=X, expand=True)
button = Button(exit_frame, text='x', width=3, command=self.free,
background='#ffffe0', highlightthickness=0, relief=FLAT)
button.pack(side=RIGHT)
text_frame = Frame(window_frame)
text_frame.pack(side=TOP, fill=BOTH, expand=True)
label = Label(text_frame, text=string, justify=LEFT,
background='#ffffe0',
font=('tahoma', '8', 'normal'))
label.pack(ipadx=1)
def free(self):
self.destroy() # first we destroy this one
for val,widget in enumerate(dialogs): # go through the dialogs list
if widget is self: # when we find this widget
dialogs.pop(val) # pop it out
break # and stop searching
if dialogs: # if there are any dialogs left:
for widget in dialogs: # go through each widget
widget.lift(aboveThis=self.root) # and lift it above the root
def bind():
"""
toggle property window creation mode
"""
root.bind('<ButtonPress-1>', create)
def create(event):
"""
Create actual window upon mouse click
"""
dialogs.append(PropertyDialog(root, 'help me'))
root = Tk()
dialogs = []
root.geometry('%dx%d' % (300,400))
Button(root, text='create', command=bind).pack()
root.mainloop()
change this:
if dialogs: # if there are any dialogs left:
for widget in dialogs: # go through each widget
widget.lift(aboveThis=self.root) # and lift it above the root
to this:
if dialogs: # if there are any dialogs left:
for widget in dialogs: # go through each widget
widget.lift() # and lift it above the root
the widgets will stay above the main window.
EDIT:
Sorry that only half worked... the widows will stay above sometimes with that code
:-X
It was keeping the widgets on top until you closed one of them.... this code does keep the widgets on top
it uses the self.attributes("-topmost", True) when you spawn the windows.
Sorry again.
from Tkinter import *
class PropertyDialog(Toplevel):
def __init__(self, root, string):
Toplevel.__init__(self)
self.wm_overrideredirect(1)
self.root = root
self.\
geometry('+%d+%d' %
(root.winfo_pointerx(),
root.winfo_pointery()))
try:
self.tk.call('::Tk::unsupported::MacWindowStyle',
'style', self._w,
'help', 'noActivates')
except TclError:
pass
window_frame = Frame(self)
window_frame.pack(side=TOP, fill=BOTH, expand=True)
exit_frame = Frame(window_frame, background='#ffffe0')
exit_frame.pack(side=TOP, fill=X, expand=True)
button = Button(exit_frame, text='x', width=3, command=self.free,
background='#ffffe0', highlightthickness=0, relief=FLAT)
button.pack(side=RIGHT)
text_frame = Frame(window_frame)
text_frame.pack(side=TOP, fill=BOTH, expand=True)
label = Label(text_frame, text=string, justify=LEFT,
background='#ffffe0',
font=('tahoma', '8', 'normal'))
label.pack(ipadx=1)
self.attributes("-topmost", True)
def free(self):
self.destroy() # first we destroy this one
def bind():
"""
toggle property window creation mode
"""
root.bind('<ButtonPress-1>', create)
def create(event):
"""
Create actual window upon mouse click
"""
dialogs.append(PropertyDialog(root, 'help me'))
root = Tk()
dialogs = []
root.geometry('%dx%d' % (300,400))
Button(root, text='create', command=bind).pack()
root.mainloop()
I recommend moving away from Toplevel widgets, since those are separate windows and you're suppressing their window-like behavior. This version makes PropertyDialog inherit from Frame instead of Toplevel, using the place() geometry manager. When you click the main window, it first checks whether the widget clicked was the main window or a popup window to prevent a new popup from appearing when you close an existing one. Changed areas are marked with #CHANGED#.
from Tkinter import *
class PropertyDialog(Frame): #CHANGED#
def __init__(self, root, string, event): #CHANGED#
Frame.__init__(self) #CHANGED#
self.root = root
try:
self.tk.call('::Tk::unsupported::MacWindowStyle',
'style', self._w,
'help', 'noActivates')
except TclError:
pass
exit_frame = Frame(self, background='#ffffe0') #CHANGED#
exit_frame.pack(side=TOP, fill=X, expand=True)
button = Button(exit_frame, text='x', width=3, command=self.free,
background='#ffffe0', highlightthickness=0, relief=FLAT)
button.pack(side=RIGHT)
text_frame = Frame(self) #CHANGED#
text_frame.pack(side=TOP, fill=BOTH, expand=True)
label = Label(text_frame, text=string, justify=LEFT,
background='#ffffe0',
font=('tahoma', '8', 'normal'))
label.pack(ipadx=1)
self.place(x=event.x, y=event.y, anchor=NW) #CHANGED#
def free(self):
self.destroy()
# other things you want to do - if there's nothing else,
# just bind the close button to self.destroy
def bind():
"""
toggle property window creation mode
"""
root.bind('<ButtonPress-1>', create)
def create(event):
"""
Create actual window upon mouse click
"""
if event.widget is root: #CHANGED#
dialogs.append(PropertyDialog(root, 'help me', event))
root = Tk()
dialogs = []
root.geometry('%dx%d' % (300,400))
Button(root, text='create', command=bind).pack()
root.mainloop()