I'm trying to figure out how to get the height of a tkInter window title bar but can't seem to find any info on how it's done.
I have tried using root.geometry() and it seems that root.geometry() only returns the size of the window content and not the total size of the window with title bar and border sizes. I have seen other people say that you need to ask the OS for those things. I was hoping to avoid this because it will make it harder to make the code platform independent. There must be a way to do this without going to the OS for this. Anyone know what it is I must do to get this info?
My system:
OS: Linux
KDE Plasma: 5.16.4
KDE Frameworks: 5.61.0
import tkinter
root = tkinter.Tk()
root.geometry("250x250+100+100")
root.update_idletasks()
print('root.winfo_x() = ', root.winfo_x())
print('root.winfo_y() = ', root.winfo_y())
print('root.geometry() = ', root.geometry())
root.mainloop()
Test code results:
root.winfo_x() = 100
root.winfo_y() = 100
root.geometry() = 250x250+100+100
The height of the window when measured with a screen ruler app is:
x=102, y=286
Title bar(default) is a system setting.As far as I know,it depends on many factors.(System zoom ratio,different OS,DPI awareness and so on).
In windows,change the zoom ratio will get different value of height.
About the question:
tkinter will be recognized a old software in windows,you need to set DPI awareness to make it have the system normal height(fit the system zoom ratio if your system zoom ratio is not 100%).
Normally,the height of system title bar are same:but some exception(I am not really know about winapi),different DPI awareness will show you the different height of title bar:
The same:
To make them same and get the normal height of title bar:
import tkinter
import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(2)
print(ctypes.windll.user32.GetSystemMetrics(4))
root = tkinter.Tk()
root.mainloop()
Result:
Refer:
MSDN doc:GetSystemMetrics,DPI awareness(value of DPI awareness).
I have figured this out finally. In order to get the height of the title bar I first added a frame to the window and set the window geometry('1x1'). I also had to use update_idletasks() to update tkinter internal data. After doing this I got the correct height. Seems strange to have to do it this way but it worked. Below is the code I used to get the height. Tested and works in Windows and Linux. If anyone knows of a more correct way to do this I would sure like to know. Also if anyone is using apple please let me know if the code below works.
UPDATE: I have updated the code and it's been tested in (Linux, Windows and MacOS) to give the correct title bar height. I think this is the best way to do this because you don't need to make OS dependent system calls.But I don't know whether it could work for any zoom level.
Update:now could work for any zoom level.(Test passed in windows 10,windows 7)
import tkinter as tk
from sys import platform
class App(tk.Tk):
def __init__(self):
super().__init__()
tk.Frame(self).update_idletasks()
self.geometry('350x200+100+100')
self.update_idletasks()
offset_y = 0
if platform in ('win32', 'darwin'):
import ctypes
try: # >= win 8.1
ctypes.windll.shcore.SetProcessDpiAwareness(2)
except: # win 8.0 or less
ctypes.windll.user32.SetProcessDPIAware()
offset_y = int(self.geometry().rsplit('+', 1)[-1])
bar_height = self.winfo_rooty() - offset_y
print(f'Height: {bar_height}\nPlatform: {platform}')
# self.destroy()
def main():
app = App()
app.mainloop()
if __name__ == '__main__':
main()
Simpler way is just testing how self.winfo_y() changes after setting geometry first time and then correct for this change.
Related
python 3.10, 3.11, windows 10
short:
transparency is affecting title bar when it shouldn't, simple code with example below, move the window to the middle of the screen, maximise and restore to reproduce the behaviour
long:
I know it looks so simple, but please bear with me. This thing is driving me nuts. I'm not sure what I could be doing wrong, it looks like a bug, maybe?
Maximising (or restoring to normal state after maximise) breaks the title bar. Title bar is not registering mouse clicks, I can't close the window or resize it because click goes through. It acts as it if it was transparent. In the main app I am using another hidden window with -alpha to grab mouse events on transparent canvas. Both windows are bound together and act as one. Having second window behind this one also doesn't help. Both windows are affected and unclickable (well, -alpha part of the window is clickable, but not the title bar).
I made a very short code to reproduce this behaviour. You can try it with frame instead of a canvas, or a button. Result is the same. Curious thing I have also discovered when using #000000 for colour - it breaks the window in even weirder way. Not sure what to make of it. #000001 works the same as yellow (or blue, red, etc.).
example:
from tkinter import Canvas, Tk
root = Tk()
root.attributes('-transparentcolor', 'yellow', '-topmost', True)
root.geometry("600x600")
x = Canvas(root, bg='yellow')
x.pack(expand=1, fill='both')
root.mainloop()
If I try different width and height values, sometimes I can grab only half of the title bar or close the window, but not minimise. It seems to depend on where the window is on the screen and the size of the object. Feels like the transparent part of the object is extending through the title bar making it unresponsive. I tried separating the title bar from the rest of the window with frames or shapes but it doesn't help (sometimes it works, but is dependent on size and location of the window - you may get lucky and not notice the behaviour)
x = Canvas(root, bg='yellow', height=600, width=600)
x.pack()
The best solution I've come up with so far:
def refresh(self):
self.state('iconic')
if self.state() == 'iconic':
self.state('normal')
self.focus_force() # needed for Entry widget
This function assigned to the button which is minimising the window to the taskbar and then returning it to normal state immediately. Obviously this is far from elegant, because the user have to perform an unnecessary action. I could use overrideredirect and hopefully recreate resize and close functionality of the window but it seems like an overkill for rather simple app.
Not sure what else to say. It's late, bye
edit:
trying this now and it somehow works, but sometimes the window blinks uncomfortably.
from tkinter import Canvas, Tk
class Window(Tk):
def __init__(self):
super().__init__()
self.bind("<Map>", self.refresh)
self.canv = Canvas(self)
self.canv.pack(expand=1, fill='both')
def refresh(self, event):
if self.state() == 'normal':
self.attributes('-transparentcolor', 'yellow', '-topmost', True)
self.canv.configure(bg='yellow')
elif self.state() == 'zoomed':
self.attributes('-transparentcolor', 'blue', '-topmost', True)
self.canv.configure(bg='blue')
if __name__ == '__main__':
w = Window()
w.mainloop()
I need to know dimensions (exactly: height) of the screen below the current Gtk.Window.The most frequently recommended method:
window = Gtk.Window()
screen = window.get_screen()
h = screen.height()
does the job, but gives me DeprecationWarning: Gdk.Screen.height is deprecated, and is likely to stop working sooner or later. I wouldn't like to add any new dependencies, so this cool cheat sheet doesn't help.
My code is expected to work on Linux w/ multi-headed setups, and this must be the height of the current screen.
More detailed background: the program I'm working on is a wallpaper manager. I want the (floating) window to use all the possible space vertically, and leave some space on both sides, to watch the background change.
Having no reasonable solution, I decided to follow this answer.
def check_height_and_start(window):
w, h = window.get_size()
window.destroy()
if common.sway or common.env['wm'] == "i3":
h = h * 0.95
app = GUI(h)
def main():
(...)
w = Gtk.Window() # actually I derived from Gtk.Window to make the window transparent
if common.sway or common.env['wm'] == "i3":
w.fullscreen() # .maximize() doesn't work as expected on sway
else:
w.maximize()
w.present()
GLib.timeout_add(delay_ms, check_height_and_start, w)
Gtk.main()
This way the window height does not include panels, if any. Unfortunately it doesn't work on tiling window window managers, so I had to use fullscreen() instead of maximize(), and decrease height arbitrarily.
The delay_ms optimal value varies with the hardware speed and WM in use, which is another inconvenience.
Is there a 'standard' way of ensuring that the displayed window is wide enough to display the window title?
import tkinter as tk
root = tk.Tk()
root_f = tk.Frame(root)
root.title('Long Window Title Containing Much Text')
text_f = tk.Frame(root_f)
text_l = tk.Label(text_f, text='Short text')
root_f.grid()
text_f.grid()
text_l.grid()
root.geometry('+{}+{}'.format(100, 100))
root.mainloop()
root.quit()
When I use the winfo method to get the width of the root (or text) frames they give the size of the text not the size of a window wide enough to display the whole window title.
I know it has to be something simple, but I can't see it.
Thanks
No, there is no standard way. Tkinter has no way of knowing how long the title is on the titlebar. The OS / window manager has completely control of that portion of the window and doesn't expose any platform-independent way of getting at that information.
You would have to know not only the font used by the OS for window titles, but also whether there were any other decorations (eg: buttons, images, etc) that appear in the title area.
If you're fine with making assumptions that the font is the same as the default tkinter font, you can use tkinter's ability to measure the length of a string in a given font with the measure method of a font object.
Every result I've gotten from searching this question up has to do with changing the border color for Tkinter widget's WITHIN the main app window, or changing the background color and stuff.
What I'm looking to do is actually change the windows border color itself. Which I have marked in this picture here
I read some stuff concerning 'Styles' but it didn't seem to be what I was looking for.
Can anyone help point me to a tutorial for changing this, or explain how it can be done?
Or if there is a question that asked exactly the same question I was asking that is answered point me to it.
As I see you are using windows.
This color is set by the theme you are currently using. It is the same for every window.
So I cross out the possibility of only using the Tkinter module for this.
Tkinter is responsible for what is in the window but the window manager decides about the border. For example in Ubuntu the window would look totally different.
I guess, you would need some windows specific calls for that.
You can remove the border with root.overrideredirect(1) if I remember correctly.
PS: put "windows" into the tags of this question.
Hi I was looking for this answer too, found it after like 80 minutes searching, Only work, to activate DWMWA_USE_IMMERSIVE_DARK_MODE
found it here: Can I change the title bar in Tkinter?
I didnt found dwmwindowattribute in dwmwindowattribute that affect Top bar color sadly :(.
For border colour:
It should be possible to call DWMWA_BORDER_COLOR, but honestly I dont know how, there is some article calling it in C++ here:
change-the-color-of-the-title-bar-caption-of-a-win32-application
Tried this but doesnt work:
set_window_attribute(hwnd, 22, '0x000000FF', 4)
Here is working code for pure black top:
import tkinter as tk
import ctypes as ct
def dark_title_bar(window):
"""
MORE INFO:
https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute
"""
window.update()
set_window_attribute = ct.windll.dwmapi.DwmSetWindowAttribute
get_parent = ct.windll.user32.GetParent
hwnd = get_parent(window.winfo_id())
value = 2
value = ct.c_int(value)
set_window_attribute(hwnd, 20, ct.byref(value),
4)
root = tk.Tk()
root.title("Crystaly Ball")
root.geometry("1400x900")
root.configure(background="#222246")
dark_title_bar(root)
root.mainloop()
You can get the usable screen size (screen minus task bar, regardless of where it sits) like this:
import Tkinter as tk
root = tk.Tk()
root.state('zoomed')
root.update()
usable_width = root.winfo_width()
usable_height = root.winfo_height()
Is there a way to do it that is not visible to the user? In Tkinter, 'withdrawn' (hidden) and 'zoomed' are mutually exclusive states.
You can get the total screen size by adding:
total_width = root.winfo_screenwidth()
total_height = root.winfo_screenheight()
So far I've been unable to find a way to do this. Any ideas?
'zoomed' did not work for me in Linux Mint, but I finally found something that did and I think it is more portable. This can even be called while the window is withdrawn so that the user does not see the changes.
w, h = root.maxsize()
You can call:
root.attributes("-alpha", 0)
after creating the window. This will make it invisible, and then you can perform size calculations in the same way you already are.