I would like to have a Tkinter window open at the bottom right of the screen or where ever the start bar is. Much like when you click on the battery icon on your laptp and the box pops up. My code currently hides it behind the start menu bar. I would essentially like it at the bottom right but sitting on top of the start menu bar. Also, not sure how to account for things if the start menu is not at the bottom.
My Code:
from Tkinter import *
def bottom_right(w=300, h=200):
# get screen width and height
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# calculate position x, y
x = (screen_width - w)
y = (screen_height-h)
root.geometry('%dx%d+%d+%d' % (w, h, x, y))
root = Tk()
bottom_right(500, 300)
root.mainloop()
You can use the win32api's .GetMonitorInfo() to find the 'working area' of the monitor, which is the area of the monitor without the taskbar. Then, seeing where the taskbar is, you can place the window in a corner of the working area. See this example:
import win32api
import Tkinter as tk
for monitor in win32api.EnumDisplayMonitors():
monitor_info = win32api.GetMonitorInfo(monitor[0])
if monitor_info['Flags'] == 1:
break
work_area = monitor_info['Work']
total_area = monitor_info['Monitor']
width = 300
height = 200
side = [i for i in range(4) if work_area[i]!=total_area[i]]
# Left
if side ==[0]:
x = str(work_area[0])
y = str(work_area[3]-height)
# Top
elif side == [1]:
x = str(work_area[2]-width)
y = str(work_area[1])
# Right
elif side == [2]:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
# Bottom
elif side == [3]:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
else:
x = str(work_area[2]-width)
y = str(work_area[3]-height)
geom = '{}x{}+{}+{}'.format(width, height, x, y)
root = tk.Tk()
root.configure(background='red')
root.geometry(geom)
root.overrideredirect(True)
root.mainloop()
Note that I've used overrideredirect to get rid of the frame of the window, since this messes with the placing a bit.
What you are calling the 'start menu bar' is usually called the taskbar, at least on Windows. root.iconify() minimizes the root window to the taskbar, wherever it happens to be, just as when one clicks [_] in the upper right of the window. Clicking on the icon de_iconifies it, just as with any other app.
root = tk.Tk()
root.iconify()
<build gui>
root.deiconify()
root.mainloop()
is a common pattern in a polished app when the part takes long enough to cause potentially visible construction activity. I believe putting the gui in a frame and packing the frame as the last step has the same effect (of hiding construction).
Related
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!
I am trying to make a program that lets me draw on a tkinter window using turtle. For some reason I cannot get the absolute mouse coordinates.
I have done root.winfo_pointerx() - root.winfo_rootx() (and vrootx).
I have also tried:
def mousePos(event):
x,y = event.x , event.y
return x,y
My code:
import turtle
import tkinter as tk
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500,height=500)
cv.focus_set()
cv.pack(side = tk.LEFT)
pen = turtle.RawTurtle(cv)
window = pen.getscreen()
def main():
window.setworldcoordinates(-500,-500,500,500)
window.bgcolor("white")
frame = tk.Frame(root)
frame.pack(side = tk.RIGHT,fill=tk.BOTH)
pointLabel = tk.Label(frame,text="Width")
pointLabel.pack()
def getPosition(event):
x = root.winfo_pointerx()-root.winfo_vrootx()
y = root.winfo_pointery()-root.winfo_vrooty()
pen.goto(x,y)
cv.bind("<Motion>", getPosition)
cv.pack
tk.mainloop()
pass
I want the cursor to be on top of the arrow, but instead it is always to the right and down. Also, when I move the mouse up, the arrow moves down, and vice versa.
Think hard about how you set the setworldcoordinate(). -500 - 500 means your world has 1,000 in size and window size is 500. Also, the mouse pointer offset from the window root - both absolute coordinates should be used. You mixed up the absolute coordinates - mouse pointer and vrootx which is in different scale so the distance of two makes no sense. Following code is probably closer to what you intended. Note that, I set the world coordinate to match the absolute coordinates of mouse pointer offset from the top/left corner of window.
import turtle
import tkinter as tk
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500,height=500)
cv.focus_set()
cv.pack(side = tk.LEFT)
pen = turtle.RawTurtle(cv)
window = pen.getscreen()
def main():
window.setworldcoordinates(0,500,500,0)
window.bgcolor("white")
frame = tk.Frame(root)
frame.pack(side = tk.RIGHT,fill=tk.BOTH)
pointLabel = tk.Label(frame,text="Width")
pointLabel.pack()
print(dir(root))
def getPosition(event):
x = root.winfo_pointerx()-root.winfo_rootx()
y = root.winfo_pointery()-root.winfo_rooty()
print(x, y)
pen.goto(x,y)
pass
cv.bind("<Motion>", getPosition)
cv.pack
tk.mainloop()
pass
if __name__ == "__main__":
main()
pass
You've got an issue working against you that isn't of your own making. The general rule is when in a turtle canvas, use turtle methods. But turtle doesn't have an inherent 'Motion' event type, so you were trying to use the raw Canvas one as a substitute. Thus the conflict.
An issue of your own making is that when you're inside a fast moving event handler, you need to disable the event hander as the first thing you do, reenabling on exit. Otherwise, events overlap and bad things happen. (Inadvertant recursions and other wierdness.)
I've rewritten your program below to work as I believe you intended. The fix is adding the missing turtle method so we can stay within the turtle domain:
import tkinter as tk
from turtle import RawTurtle, TurtleScreen
from functools import partial
def onscreenmove(self, fun, add=None): # method missing from turtle.py
if fun is None:
self.cv.unbind('<Motion>')
else:
def eventfun(event):
fun(self.cv.canvasx(event.x) / self.xscale, -self.cv.canvasy(event.y) / self.yscale)
self.cv.bind('<Motion>', eventfun, add)
def getPosition(x, y):
screen.onscreenmove(None) # disable events inside handler
pen.setheading(pen.towards(x, y))
pen.goto(x, y)
screen.onscreenmove(getPosition) # reenable handler on exit
root = tk.Tk()
root.title("Draw!")
cv = tk.Canvas(root, width=500, height=500)
cv.focus_set()
cv.pack(side=tk.LEFT)
screen = TurtleScreen(cv)
screen.onscreenmove = partial(onscreenmove, screen) # install missing method
pen = RawTurtle(screen)
frame = tk.Frame(root)
frame.pack(side=tk.RIGHT, fill=tk.BOTH)
tk.Label(frame, text="Width").pack()
screen.onscreenmove(getPosition)
screen.mainloop()
Mouse position for Tkinter:
import Tkinter as tk
root = tk.Tk()
def motion(event):
x, y = event.x, event.y
print('{}, {}'.format(x, y))
root.bind('<Motion>', motion)
root.mainloop()
Mouse position for turtle:
canvas = turtle.getcanvas()
x, y = canvas.winfo_pointerx(), canvas.winfo_pointery()
Hope this helps.
I am trying to make a ball go to one side of the screen turn around, then come back. Whenever I try running this program the tkinter window doesn't show up, but when i get rid of the sleep(0.5) part it shows up when the ball is already off the screen. Can someone tell my what I did wrong?
from tkinter import *
from time import sleep
window = Tk()
cWidth = 800
cHeight = 500
c = Canvas(window, width = cWidth, height = cHeight, bg='black')
c.pack()
x = 400
y = 250
ball = c.create_polygon(x, y, x, y+25, x+25, y+25, x+25,y, fill='yellow')
Ball_move = 10
for i in range(200):
c.move(ball, Ball_move, 0)
window.update
x += Ball_move
if x == cWidth:
Ball_move = -Ball_move
sleep(0.5)
window.mainloop()
In windows, Tkinter frame shows up only after you call mainloop(). In your case, the for loop might be blocking it. Keep the for loop in a function and then call that function using threads so that it won't block the main loop.
Everytime I create a window set and icon for the window manager and change some other property of the window (e.c. "resizable") it "flash" up. I suppose this is because the window manager is called again and it redos the window. Is there any way I can call maybe a "silent" change or something like that? I have not been very lucky in my research.
If you want to see for yourself, you are going to need an icon file. I uploaded the icon I am using here. If you are not comfortable with downloading that, you can also find suitable icon files in your system directory. Search "*.ico". Any 16x16 icon will do.
#! coding=utf-8
import tkinter as tk
import os
def CENTER_WINDOW(window,parent):
""" Verschiebt ein Fenster auf dem Bildschirm"""
# get window size
window.update_idletasks()
w = window.winfo_width()
h = window.winfo_height()
# get parent size
if parent=="screen":
p_x , p_y = window.winfo_screenwidth(), window.winfo_screenheight()
p_ox, p_oy = 0,0
else:
parent.update_idletasks()
p_x , p_y = parent.geometry().split("+")[0].split("x")
p_x , p_y = int(p_x) , int(p_y)
p_ox, p_oy = int(parent.geometry().split("+")[1]),int(parent.geometry().split("+")[2])
# calculate position x, y
x = p_x/2 + p_ox - w/2
y = p_y/2 + p_oy - h/2
window.geometry('%dx%d+%d+%d' % (w,h,x,y))
window.update_idletasks()
def top():
T = tk.Toplevel()
T.wm_iconbitmap( os.path.abspath("icon.ico"))
T.resizable(width=tk.FALSE, height=tk.FALSE)
CENTER_WINDOW(T,root)
root = tk.Tk()
root.geometry("100x50")
but = tk.Button(root,text="Toplevel",command=top)
CENTER_WINDOW(root,"screen")
but.pack()
root.mainloop()
In rough outline:
Call withdraw on the window immediately after creating it
Make your adjustments
Call deiconify on the window when you're ready to show it
I have a small annoying problem so I come to you to see if you can help to solve it.
I have the following code in Python2.7:
# main window
root = tk.Tk()
root.title('My application')
# create the objects in the main window
w = buildWidgetClass(root)
# size of the screen (monitor resolution)
screenWi = root.winfo_screenwidth()
screenHe = root.winfo_screenheight()
# now that widgets are created, find the widht and the height
root.update()
guiWi = root.winfo_width()
guiHe = root.winfo_height()
# position of the window
x = screenWi / 2 - guiWi / 2
y = screenHe / 2 - guiHe / 2
root.geometry("%dx%d+%d+%d" % (guiWi, guiHe, x, y))
So I create the main window (without any size) I insert widgets inside, then I define the size of the result, and place it in the middle of the screen.
The number of widget in the window may vary, so the resulting size!
So far, so good, it works ! My only problem is that the window first appear in the left top corner of the screen, then repositioned to the center. Not a big deal, but not really professional neither.
So my idea was to hide the main window during the widgets creation time and then make it appearing after having defined the geometry.
So after the first line, I added :
root.withdraw()
then at the end :
root.update()
root.deiconify()
but when the window reappear, it wasn't re-sized by the widget and have a size of 1x1 !!
I tried to replace root.withdraw() by root.iconify(), the window is correctly re-sized but, surprisingly, isn't deiconified at the end !!!
I'm a little bit lost on this ...
Using root.winfo_reqwidth instead of root.winfo_width may help in your case.
Finally with the input of kalgasnik, I have a working code
# main window
root = tk.Tk()
# hide the main window during time we insert widgets
root.withdraw()
root.title('My application')
# create the objects in the main window with .grid()
w = buildWidgetClass(root)
# size of the screen (monitor resolution)
screenWi = root.winfo_screenwidth()
screenHe = root.winfo_screenheight()
# now that widgets are created, find the width and the height
root.update()
# retrieve the requested size which is different as the current size
guiWi = root.winfo_reqwidth()
guiHe = root.winfo_reqheight()
# position of the window
x = (screenWi - guiWi) / 2
y = (screenHe - guiHe) / 2
root.geometry("%dx%d+%d+%d" % (guiWi, guiHe, x, y))
# restore the window
root.deiconify()
Thanks a lot to both of you for your time and your help