How to get the height of a tkinter window title bar
Asked Answered
N

4

9

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
Newby answered 1/9, 2019 at 0:32 Comment(8)
I can't find anything in that post that gets me the true width and height of a window. I need the width and height of window decorations ie (title bar, border widths) and it's content. Once I have the outer size of the window I can calculate the rest.Newby
Possible duplicate of Python tk window get x, y, geometry/coordinates without top of window. Verfied, returns (0, 22) which ist the height of the titelbar. Edit your question and show your attemp.Poltroon
My results (101, 122), so it may depend from the used OS Window Manager. I use Linux LXDE.Poltroon
I have tested the code above on Linux KDE 5.61.0 and on Windows 10, both give the same results as noted above.Newby
My bad, sould be 'frame.winfo_geometry(), frame.winfo_rootx(), frame.winfo_rooty()`Poltroon
frame.winfo_geometry() gives me 250x250+0+0 the same thing!Newby
Such things are controlled by your system's window manager, and while tkinter provides a littlebit interaction with it, like icon or title, unfortunately, geometry isn't a case. For example, on window system you can use GetSystemMetrics(SM_CYCAPTION) for your task. Another direction could be detecting a geometry of both client and non-client area of your window and do math.Laureen
So, in bitter end, "other people" are right! But, after all, under the hood of tkinter a window decoration is platform dependent. Thus, you or someone else will add these dependencies one way or another. Don't run away from it.Laureen
N
3

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()
Newby answered 29/4, 2020 at 1:56 Comment(6)
Are you sure it is the height of the title bar?It seems it is the distance between widget's upper edge and screen upper edge....If you use self.geometry('1x1+100+1000'), see the result,it will be Height=1031 in my PC.Mittel
And I gotta to say,the method to get the height of title bar should be different in the different OS.In windows,you could use winapi to get it.Mittel
@jizhihaoSAMA, I have corrected the code above to include the window offset so now it works the way it should. This should also work no matter what zoom level you use or OS. I prefer not to use OS dependent system calls. Thank you for your response I will stick to using what I have unless there is some reason not to, it works fine for me!.Newby
Yes, your idea is also correct if there is 100% zoom level,you could just set DPI awareness just for windows,you could use some code to judge the platform.That's also could work on both linux and windows.Mittel
I have tested the code on macOS and it gives the right titlebar height.Carolus
@ j_4321, Thank you for taking the time to test the code for me, much appreciated.Newby
M
8

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: enter image description here The same: enter image description here

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: enter image description here enter image description here

Refer: MSDN doc:GetSystemMetrics,DPI awareness(value of DPI awareness).

Mittel answered 29/4, 2020 at 8:13 Comment(0)
N
3

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()
Newby answered 29/4, 2020 at 1:56 Comment(6)
Are you sure it is the height of the title bar?It seems it is the distance between widget's upper edge and screen upper edge....If you use self.geometry('1x1+100+1000'), see the result,it will be Height=1031 in my PC.Mittel
And I gotta to say,the method to get the height of title bar should be different in the different OS.In windows,you could use winapi to get it.Mittel
@jizhihaoSAMA, I have corrected the code above to include the window offset so now it works the way it should. This should also work no matter what zoom level you use or OS. I prefer not to use OS dependent system calls. Thank you for your response I will stick to using what I have unless there is some reason not to, it works fine for me!.Newby
Yes, your idea is also correct if there is 100% zoom level,you could just set DPI awareness just for windows,you could use some code to judge the platform.That's also could work on both linux and windows.Mittel
I have tested the code on macOS and it gives the right titlebar height.Carolus
@ j_4321, Thank you for taking the time to test the code for me, much appreciated.Newby
F
0

Simpler way is just testing how self.winfo_y() changes after setting geometry first time and then correct for this change.

Footnote answered 27/3, 2021 at 14:14 Comment(0)
W
0

Get the difference between the rooty and the y coordinate as such:

title_bar_height = root.winfo_rooty() - root.winfo_y()
outer_window_height = root.winfo_height() + title_bar_height
Weld answered 14/3 at 18:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.