Unable to hide Tkinter window while I am setting its geometry - python

I have a root window and I want to create a Toplevel window positioned in a specific place on the screen (centralized over the root window). So far I have been unable to accomplish this without seeing the Toplevel window quickly build itself in its default location and then hop over to the position I want it. I would prefer not to see this because it's weird and jarring.
Is there a way to build, then reposition a window in Tkinter without seeing it reposition itself?
Here's some example code with a sleep thrown in to simulate a complex window being rendered:
from tkinter import *
import time
def centralize_over_root(window_to_centralize):
root_geometry = root.winfo_geometry()
root_width_height = root_geometry.split('+')[0]
root_width = root_width_height.split('x')[0]
root_height = root_width_height.split('x')[1]
root_x_y = root_geometry.split(f'{root_height}+')[1]
root_x = root_x_y.split('+')[0]
root_y = root_x_y.split('+')[1]
window_to_centralize.update()
time.sleep(0.5)
window_width = window_to_centralize.winfo_width()
window_height = window_to_centralize.winfo_height()
window_x = int(root_x) + round((int(root_width) - int(window_width)) / 2.0)
window_y = int(root_y) + round((int(root_height) - int(window_height)) / 2.0)
result = f'+{window_x}+{window_y}'
return result
def new_window():
new_window = Toplevel(root)
Label(new_window, text='Something').pack(padx=20, pady=20)
new_window.geometry(centralize_over_root(new_window))
root = Tk()
Button(root, text='Make new window', command=new_window).pack(padx=50, pady=50)
root.mainloop()

You can hide the window using withdraw() and show it after reposition using deiconify():
def new_window():
new_window = Toplevel(root)
new_window.withdraw() # hide the window
Label(new_window, text='Something').pack(padx=20, pady=20)
new_window.geometry(centralize_over_root(new_window))
new_window.deiconify() # show the window

I think the following will do what you want. There are comments showing where I made changes. The problem was the time.sleep() was interfering with the mainloop() and your window_to_centralize.update() which makes it appear before you're finished configuring it.
from tkinter import *
import time
def centralize_over_root(window_to_centralize):
root_geometry = root.winfo_geometry()
root_width_height = root_geometry.split('+')[0]
root_width = root_width_height.split('x')[0]
root_height = root_width_height.split('x')[1]
root_x_y = root_geometry.split(f'{root_height}+')[1]
root_x = root_x_y.split('+')[0]
root_y = root_x_y.split('+')[1]
# window_to_centralize.update() # Don't do this - will happen automatically.
# time.sleep(0.5)
root.after(500) # This pauses without interfering with mainloop.
window_width = window_to_centralize.winfo_width()
window_height = window_to_centralize.winfo_height()
window_x = int(root_x) + round((int(root_width) - int(window_width)) / 2.0)
window_y = int(root_y) + round((int(root_height) - int(window_height)) / 2.0)
result = f'+{window_x}+{window_y}'
return result
def new_window():
new_window = Toplevel(root)
Label(new_window, text='Something').pack(padx=20, pady=20)
new_window.geometry(centralize_over_root(new_window))
root = Tk()
Button(root, text='Make new window', command=new_window).pack(padx=50, pady=50)
root.mainloop()

Related

attempts to display the speedtest graph

Hello there i'm new to python and I'm trying to do speed testing using python getting data from (speedtest.net). I wrote the code, the speed shows, everything is fine, but now I need the graph to also be shown, as on the speedtest website itself. How to do it guys?
# Adding a library of window applications
from tkinter import *
# Adding a library of window applications
from speedtest import Speedtest
def test(): # Adding a function
download = Speedtest(secure=True).download()
upload = Speedtest(secure=True).upload()
download_speed = round(download / (10**6), 2)
upload_speed = round(upload / (10**6), 2)
download_label.config(text="Download Speed:\n" + str(download_speed) + "MbPs")
upload_label.config(text="upload_speed:\n" + str(upload_speed) + "MbPs")
root = Tk() # Adding a variable
root.title("speedTest") # Window name
root.geometry("300x400") # Window size
button = Button(root, text="Click to start", font=40, command=test) # Creating a button
button.pack(side=BOTTOM, pady=40) # Button display and location
download_label = Label(root, text="Download Speed:\n-", font=35) # Creating an icon
download_label.pack(pady=(50, 0))
upload_label = Label(root, text="upload_speed:\n-", font=35) # Creating an icon
upload_label.pack(pady=(10, 0))
root.mainloop() # Doesn't let you close the window until you close it yourself

App's icon is not show in taskbar because of Custom title bar?

I am creating an code editor in which I want to custom title bar to match my app theme and I have created an custom title bar but my app is not showing in taskbar
If any external libraries are for this, Please tell me
What libraries I have to learn to solve my problem please tell me
how to show app icon on taskbar, Actually I have no idea about it
if you can solve it
Please help me to solve my problem
this is my full code(not full code but short version of real one):-
from tkinter import*
def move(e):
xwin = root.winfo_x()
ywin = root.winfo_y()
startx = e.x_root
starty = e.y_root
ywin -= starty
xwin -= startx
def move_(e):
root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
startx = e.x_root
starty = e.y_root
frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
root.update_idletasks()
root.overrideredirect(False)
root.state("iconic")
def frame_map(event=None):
root.update_idletasks()
root.overrideredirect(True)
root.state("normal")
root.call()
def minimefunction(event=None):
global size
if size:
root.geometry(f"{screen_width}x{screen_height-40}+0+0")
minimsi.config(text=" \u2752 ")
size = False
else:
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
minimsi.config(text=" \u25a0 ")
size = True
def quitApp():
root.destroy()
def close_blink(event=None):
close_button.config(bg="red")
def close_blink1(event=None):
close_button.config(bg="gray19")
def minimsi_blink(event=None):
minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")
Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)
yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")
frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)
root.mainloop()
You can see the problem in this image:-
Disclaimer, this may not be the best approach to achieve OP`s goal, but I'm sticking to this example because:
There are several question with this example on StackOverflow
It shows a basic knowledge that is important for deeper digging.
The improvement of this code to the original is that the style applies again after you iconify it and bring that windows back up.
See the twitch of this example is how the taskbar keeps track of the application. There is a good article of Raymond Chen (Ms-Developer) where he quotes this:
“If you want to dynamically change a window’s style to one that
doesn’t support visible taskbar buttons, you must hide the window
first (by calling ShowWindow with SW_HIDE), change the window style,
and then show the window.”
And also points out a weak point of some programs and why they lose or get a blank taskbar icon:
Window is taskbar-eligible.
Window becomes visible ? taskbar button created.
Window goes taskbar-ineligible.
Window becomes hidden ? since the window is not taskbar-eligible at this point, the taskbar ignores it.
Anyway, the basic idea is the following:
Get the handle of the window. (Parent window of the root in this case for tkinter related reasons)
get the current style in a hexadecimal code
alter the hexadecimal code to your need with a bitwise operation
apply the altered style to the window
I added the following code at the beginning of your script:
from ctypes import windll
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
def set_appwindow():
global hasstyle
if not hasstyle:
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
root.withdraw()
root.after(100, lambda:root.wm_deiconify())
hasstyle=True
This code at the end of your script:
hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()
and added the line set_appwindow() in def frame_map(event=None):. Als I had to implement another additional two lines in def minieme1_(event=None): to update the hasstylevariable.
So the overall implementation of this approach was possible to have your window ready and withdrawn for described reason. An additional variable to avoid a infinite loop while you alter the style of your window that is either True while it is shown or False while it is iconyfied, alongside with your overrideredirect method.
Full Code
from tkinter import*
from ctypes import windll
GWL_EXSTYLE=-20
WS_EX_APPWINDOW=0x00040000
WS_EX_TOOLWINDOW=0x00000080
def set_appwindow():
global hasstyle
if not hasstyle:
hwnd = windll.user32.GetParent(root.winfo_id())
style = windll.user32.GetWindowLongW(hwnd, GWL_EXSTYLE)
style = style & ~WS_EX_TOOLWINDOW
style = style | WS_EX_APPWINDOW
res = windll.user32.SetWindowLongW(hwnd, GWL_EXSTYLE, style)
root.withdraw()
root.after(100, lambda:root.wm_deiconify())
hasstyle=True
def move(e):
xwin = root.winfo_x()
ywin = root.winfo_y()
startx = e.x_root
starty = e.y_root
ywin -= starty
xwin -= startx
def move_(e):
root.geometry(f"+{e.x_root + xwin}+{e.y_root + ywin}")
startx = e.x_root
starty = e.y_root
frame.bind("<B1-Motion>",move_)
def minieme1_(event=None):
global hasstyle
root.update_idletasks()
root.overrideredirect(False)
root.state("iconic")
hasstyle = False
def frame_map(event=None):
root.overrideredirect(True)
root.update_idletasks()
set_appwindow()
root.state("normal")
def minimefunction(event=None):
global size
if size:
root.geometry(f"{screen_width}x{screen_height-40}+0+0")
minimsi.config(text=" \u2752 ")
size = False
else:
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
minimsi.config(text=" \u25a0 ")
size = True
def quitApp():
root.destroy()
def close_blink(event=None):
close_button.config(bg="red")
def close_blink1(event=None):
close_button.config(bg="gray19")
def minimsi_blink(event=None):
minimsi.config(bg="gray29")
def minimsi_blink1(event=None):
minimsi.config(bg="gray19")
def minimsi1_blink(event=None):
minimsi1.config(bg="gray29")
def minimsi1_blink1(event=None):
minimsi1.config(bg="gray19")
root = Tk()
size = True
app_width = 600
app_height = 500
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
print(screen_width,screen_height)
x = (screen_width/2) - (app_width/2)
y = (screen_height/2) - (app_height/2)
root.geometry(f"{app_width}x{app_height}+{int(x)}+{int(y)}")
root.overrideredirect(True)
frame = Frame(root,bg="gray29")
Label(frame,text="My App",font="Consolas 15",bg="gray29",fg="white").pack(side=LEFT,padx=10)
close_button = Button(frame,text=" X ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=quitApp)
close_button.pack(side=RIGHT)
minimsi = Button(frame,text=" \u25a0 ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minimefunction)
minimsi.pack(side=RIGHT)
minimsi1 = Button(frame,text=" - ",font="Consolas 15",bg="gray19",fg="white",relief=GROOVE,borderwidth=0,command=minieme1_)
minimsi1.pack(side=RIGHT)
frame.pack(fill=X)
yscroll = Scrollbar(orient=VERTICAL)
yscroll.pack(side=RIGHT,fill=Y)
editor = Text(font="Consolas 15",bg="gray19",fg="white",insertbackground="white",borderwidth=0,yscrollcommand=yscroll.set)
yscroll.config(command=editor.yview)
editor.pack(expand=True,fill=BOTH)
root.config(bg="gray19")
frame.bind("<Button-1>",move)
frame.bind("<B1-Motion>",move)
# minimsi1.bind("<Button-1>",minieme1_)
frame.bind("<Map>",frame_map)
close_button.bind("<Enter>",close_blink)
close_button.bind("<Leave>",close_blink1)
minimsi.bind("<Enter>",minimsi_blink)
minimsi.bind("<Leave>",minimsi_blink1)
minimsi1.bind("<Enter>",minimsi1_blink)
minimsi1.bind("<Leave>",minimsi1_blink1)
hasstyle = False
root.update_idletasks()
root.withdraw()
set_appwindow()
root.mainloop()
Tested with python 3.10 and windows 11
You can use a hidden root window to let the system window manager to show an icon in the taskbar, and make the custom window as a transient child window of the hidden root window in order to simulate those iconify and deiconify effect.
You need to bind <Button> event on the custom window so to bring it to the front when it is clicked. Also need to bind <FocusIn> event on the hidden root window to move the focus to the custom window instead.
Below is the required changes:
...
def minieme1_(event=None):
# iconify hidden root window will iconify the custom window as well
hidden_root.iconify()
...
def quitApp():
# destroy the hidden root window will destroy the custom window as well
hidden_root.destroy()
...
def on_focus(event):
# bring custom window to front
root.lift()
# create a hidden root window
hidden_root = Tk()
hidden_root.attributes('-alpha', 0)
hidden_root.title('My App')
# you can use hidden_root.iconbitmap(...) or hidden_root.iconphoto(...) to set the icon image
# use Toplevel instead of Tk for the custom window
root = Toplevel(hidden_root)
# make it a transient child window of hidden root window
root.transient(hidden_root)
root.bind('<Button>', on_focus)
hidden_root.bind('<FocusIn>', on_focus)
...
#frame.bind("<Map>",frame_map) # it is not necessary and frame_map() is removed as well
...
The short answer is: you can't do this purely with Tkinter. You can set the window's icon via self.iconbitmap(path_to_icon), but in order to have a unique taskbar icon, you'll need to compile your app into a Windows executable.
See here
Edit: Unrelated, but as a matter of practice it's best to avoid star imports, e.g. from tkinter import * - it's much better to use something like import tkinter as tk and then prefix your Tkinter objects with tk. to avoid namespace pollution!

Way to avoid having multiple windows when using Messagebox (or alternative to it)?

I'm trying to write my first GUI based python program using Tkinter. I have created a base window which holds the menu bar with a couple of options.
One of the options is for a standard 'About' box. When I call the about section with
helpMainMenu.add_command(label="About", command=self.aboutProgram)
It opens the message box, but in a fresh window so there are now two windows showing on the taskbar.
Is there any way to stop it opening a new window and use the main one instead, or is there a better way to do it?
The full code is below
#! /usr/bin/python3
from tkinter import *
from tkinter import messagebox
import datetime
timeNow = datetime.datetime.now()
writeYear = 2020 # Enter the year you started writing the program
lineFeed = "\n"
programTitle = "Basic Menu"
programVersion = "Version 1.0.0"
programmerName = " Name (email#gmail.com)"
if timeNow.year > writeYear:
programAuthor = "©" + str(writeYear) + "-" + str(timeNow.year) + programmerName
else:
programAuthor = "©" + str(writeYear) + programmerName
aboutMessage = programTitle + lineFeed + programVersion + lineFeed + programAuthor
class Window(Frame):
def __init__(self, master=None):
Frame.__init__(self, master)
self.master = master
self.init_window()
def init_window(self):
self.master.title("{} ({})".format(programTitle, programVersion))
self.pack(fill=BOTH, expand=1)
menu = Menu(self.master)
self.master.config(menu=menu)
fileMainMenu = Menu(menu, tearoff=0) #Create the File menu container
fileMainMenu.add_command(label="Exit", command=self.programExit) # File menu option
menu.add_cascade(label="File", menu=fileMainMenu)
helpMainMenu = Menu(menu, tearoff=0) #Create the Help menu container
helpMainMenu.add_command(label="About", command=self.aboutProgram)
menu.add_cascade(label="Help", menu=helpMainMenu)
def programExit(self):
exitMsgBox = messagebox.askquestion ("Exit Application","Are you sure you want to exit the application",icon = "warning")
if exitMsgBox == "yes":
root.destroy()
exit()
def aboutProgram(self):
messagebox.showinfo("About","About the application", icon = "info")
root = Tk() # root window created. Here, that would be the only window, but
windowHeight = int(root.winfo_screenheight()/100*75) # Set the main window height to 75% of the screen height
windowWidth = int(root.winfo_screenwidth()/100*75) # Set the main window width to 75% of the screen width
screenWidth = int(root.winfo_screenwidth())
screenHeight = int(root.winfo_screenheight())
positionRight = int(root.winfo_screenwidth()/2 - windowWidth/2) # Get the screen width and divide by 2, then minus the result of 'windowWidth' divided by 2
positionDown = int(root.winfo_screenheight()/2 - windowHeight/2) # Get the screen height and divide by 2, then minus the result of 'windowHeight' divided by 2
root.geometry("{}x{}+{}+{}".format(windowWidth, windowHeight, positionRight, positionDown)) # Positions the window in the center of the page.
app = Window(root)
root.mainloop()
Python Version 3.7.3
tkinter.TkVersion 8.6
The simplest way would be to create a new frame for the "about" page, and then overlay it on top of the main window with place -- one of the few times when place is superior to grid and pack.
You should also do a "grab" on the frame so that all events are funneled to the frame and its children. With a grab, while the popup is visible you can't interact with the widgets on the main window.
Here's a quick example:
def aboutProgram(self):
# create the frame with a message and a button
# which destroys the window
aboutFrame = Frame(self.master, bd=2, relief="groove")
label = Label(aboutFrame, text="About the application...")
button = Button(aboutFrame, text="Ok", command=aboutFrame.destroy)
label.pack(side="top", padx=20, pady=20)
button.pack(side="bottom", pady=20)
# overlay the "about" page on top of the root window
aboutFrame.place(relx=.5, rely=.5, anchor="c")
# force all events to go to the popup
aboutFrame.grab_set()
If you want to completely hide the contents of the main window, you can change the place arguments to fill the window:
aboutFrame.place(x=0, y=0, anchor="nw", relwidth=1.0, relheight=1.0)

How to clear the turtle position, rotation and anyother thing made already. (Turtle is embedded inside a tkinter window)

I was trying to create a program inside tkinter that draws different patterns based on user input. I want a 'clear' button on my window that can clear everything on the window. I tried 'turtle.clear" and 'turtle.reset', they work but they open a new turtle window which I dont want and also that I am using tut = turtle.RawTurtle(). What can I do to try to fix this problem?
I use the following sample code:
from tkinter import *
from turtle import *
root = Tk()
tut = None
def reset_button():
tut.reset()
tut.hideturtle()
def draw_again():
tut.speed('fastest')
tut.color('blue', 'yellow')
tut.begin_fill()
while True:
tut.forward(200)
tut.left(170)
if abs(tut.pos()) < 1:
break
tut.end_fill()
button1 = Button(text = 'Reset', command = reset_button)
button1.pack()
button2 = Button(text = 'Draw', command = draw_again)
button2.pack()
canvas_Main = Canvas(root, bg='#ffffff', width = 500, height = 500)
canvas_Main.pack()
tut = RawTurtle(canvas_Main)
tut.speed('fastest')
tut.color('red', 'yellow')
tut.begin_fill()
while True:
tut.forward(200)
tut.left(170)
if abs(tut.pos()) < 1:
break
tut.end_fill()
root.mainloop()

Can't run Toplevel window

I am new to python and Tkinter and I need some help. I try to write a program which will show toplevel window with message on defined time. I introduce date, hour and text to program. Press "START" button and wait until toplevel window with message appear.
Program work when I do not use thread, but main window "freeze" until loop is done. Then new toplevel window appear with text.
What I would like to do is to get rid of "freezing" main window. My idea was to use thread for loop executing. But it does not work. When loop is finished in a thread it should call function which cause to Toplevel window appear. But it does not. Moreover program freeze.
I know that I should not use thread within tkinter mainloop but I can not figure out how in other way I can get rid of "freezing" main window.
thank you for all your answers.
Rafal
here is my program:
from Tkinter import *
import time
import calendar
import datetime
import thread
class Okienka(object):
def __init__(self, master):
self.rok = Label(master, text = "Podaj rok: ")
self.rok.grid(row = 0, sticky = E)
self.miesiac = Label(master, text = "Podaj miesiac w formacie XX: ")
self.miesiac.grid(row = 1, sticky = E)
self.dzien = Label(master, text = "Podaj dzien w formacie XX: ")
self.dzien.grid(row = 2, sticky = E)
self.godzina = Label(master, text = "Podaj godzine w formacie XX:XX: ")
self.godzina.grid(row = 3, sticky = E)
self.przyp = Label(master, text = "Tekst przypomnienia: ")
self.przyp.grid(columnspan = 2)
self.erok = Entry(master, width = 4)
self.erok.grid(row = 0 ,column = 1)
self.emiesiac = Entry(master, width = 2)
self.emiesiac.grid(row = 1 ,column = 1)
self.edzien = Entry(master, width = 2)
self.edzien.grid(row = 2 ,column = 1)
self.egodzina = Entry(master, width = 5)
self.egodzina.grid(row = 3 ,column = 1)
self.eprzyp = Text(master, width = 50, heigh = 10, font = ("Helvetica",10))
self.eprzyp.grid(columnspan = 2)
self.button1 = Button(master, text = "START", fg = "red", command = watek)
self.button1.grid(columnspan = 2)
def watek():
thread.start_new_thread(Czas,())
def Czas():
data = "{0}-{1}-{2} {3}".format(c.erok.get(), c.emiesiac.get(), c.edzien.get(), c.egodzina.get())
while True:
aktualny_czas = datetime.datetime.today()
czas_str = time.strftime(str(aktualny_czas))
czas_str = czas_str[:16]
print czas_str
if data == czas_str:
okienko()
break
def okienko():
komunikat = c.eprzyp.get("1.0","end-1c")
top = Toplevel()
top.title("Przypomnienie")
msg = Message(top, text = komunikat)
msg.pack()
root = Tk()
c = Okienka(root)
root.mainloop()
Destroying the root window in a Tkinter application actually means destroying (freezing) the whole application, not only the root window. The root window is the main
window for the application; it is the first to pop up and must be the last to
go. If I understand correctly, your application does not have an actual main
window: if a second window is opened from the initial window, closing the
initial window should not quit the application. Am I right?
If I am, the way to do that with Tkinter (or tcl/tk) is to create a fake root
window and hide it. This window will only be used to quit the application when
the last window is closed:

Categories

Resources