TomSchimansky / CustomTkinter

A modern and customizable python UI-library based on Tkinter
MIT License
11.52k stars 1.08k forks source link

Center Window (get screensize independent of scaling) #1707

Open matypoker opened 1 year ago

matypoker commented 1 year ago

there is a way to init the window or toplevel in the center of the screen? no matter the scale? using eval method and (width//2, height//2) does not work. I don't found a similar question using customtkinter on google.

matypoker commented 1 year ago

i could use a tk window, but antialiasing is awful

Akascape commented 1 year ago

@matypoker Try this method:

import customtkinter as ctk

root = ctk.CTk()

width = 500
height = 500
spawn_x = int((root.winfo_screenwidth()-width)/2) 
spawn_y = int((root.winfo_screenheight()-height)/2)

root.geometry(f"{width}x{height}+{spawn_x}+{spawn_y}")

root.mainloop()
matypoker commented 1 year ago

@matypoker Try this method:

import customtkinter as ctk

root = ctk.CTk()

width = 500
height = 500
spawn_x = int((root.winfo_screenwidth()-width)/2) 
spawn_y = int((root.winfo_screenheight()-height)/2)

root.geometry(f"{width}x{height}+{spawn_x}+{spawn_y}")

root.mainloop()

nop, don't work test

Akascape commented 1 year ago

@matypoker but it is working on my system (windows11). It can be an environment issue.

matypoker commented 1 year ago

mmm, do you think its windows or vscode?

matypoker commented 1 year ago

when I use a TK window all methods work fine.

Akascape commented 1 year ago

@matypoker Try to run in the native compiler (that comes with python) instead of VSCode.

avalon60 commented 1 year ago

It appears centrally on my screen (Pycharm on Linux Mint): image

matypoker commented 1 year ago

test

well, i make it run with python terminal and the problem still... :_(

matypoker commented 1 year ago

I want to clarify that I took drastic measures and formatted the PC. The last image I sent is after the formatting, and I only installed Python and VS Code. It's Windows 11

matypoker commented 1 year ago

I found the problem, it's the Windows scaling. I reduced the scaling to 100% and it works fine. When I disable the automatic scaling and go back to my Windows scaling, the image quality is terrible. Just as the CTk documentation says.

matypoker commented 1 year ago

"winfo_screenwidth" returns the screen values with scaling applied, not the actual values. For example, if the resolution of my screen is 3840x2160 with a scaling of 175%, the value returned by "winfo_screenwidth" and "winfo_screenheight" would be 2194x1234. Is there any way to access the actual resolution without scaling?

Akascape commented 1 year ago

@matypoker in that case we can also adjust the scalling for the CTk window.

matypoker commented 1 year ago

I'm not sure how to implement customtkinter.set_widget_scaling(float_value) and customtkinter.set_window_scaling(float_value). It has to be done for every scale automatically.

What I'm saying is that each PC will have a different scale, and the window has to start centered regardless of the scale.

Akascape commented 1 year ago

@matypoker Maybe disabling the scaling can work.

import ctypes
ctypes.windll.shcore.SetProcessDpiAwareness(0)
ghost commented 1 year ago

For future questions, I would suggest creating questions in Discussions (specifically the Q&A section).

Maybe this would work? :

  1. Get the user's current UI scale (not sure how this would be accomplished)
  2. Disable the scaling as @Akascape said here
  3. Center the window with the solution @Akascape provided here
  4. Finally re-enable UI scaling with .set_widget_scaling() and .set_window_scaling()

You may also have to re-enable the system UI scaling (basically the reverse of ctypes.windll.shcore.SetProcessDpiAwareness(0)).

matypoker commented 1 year ago

Disable scaling and center the window, but scaling cannot be reactivated once the window is created. I suppose scaling can be reactivated with:

customtkinter.set_widget_scaling(float_value) # widget dimensions and text size customtkinter.set_window_scaling(float_value) # window geometry dimensions

However, I don't quite understand the values I should assign to float_value, or how to obtain the scaling and pass it to the function.

ghost commented 1 year ago

However, I don't quite understand the values I should assign to float_value, or how to obtain > the scaling and pass it to the function.

I can help you with this @matypoker:

0.8 = 80% of UI scale (smaller) 1.0 = 100% of UI scale (original) 1.1 = 110% of UI scale (bigger)

TomSchimansky commented 1 year ago

I will overwrite winfo_screenwidth and winfo_screenheight soon to return the unscaled values so that the approach at the beginning will work independent of the Windows scaling.

brurpo commented 1 year ago

you can get the proper resolution like so:

import ctypes

scaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100

root = customtkinter.CTk()

w, h = int(root.winfo_screenwidth()*scaleFactor), int(root.winfo_screenheight()*scaleFactor)

My problem is that root.attributes("-fullscreen", True) does not go fullscreen, probably because ctk gets the wrong resolution because of the scaling.

ghost commented 1 year ago

Maybe disable scaling effects on ctk, make the window full screen, then enable scaling?

matypoker commented 1 year ago

Maybe disable scaling effects on ctk, make the window full screen, then enable scaling?

Once the window instance is created. Enabling or disabling scaling not has effect

brurpo commented 1 year ago

Hey, I managed to get into proper full screen, and exiting, by doing so:

root.state('zoomed')
root.attributes("-fullscreen", True)

root.state('normal')
root.attributes("-fullscreen", False)

ctk was able to enter proper full screen when the window was maximized, but only "maximized" to 1/4 of the screen when not. (scaling 200%)

matypoker commented 1 year ago

What you can do is disable scaling, obtain the Windows scaling value using "customtkinter.ScalingTracker.get_window_dpi_scaling", and then manually apply scaling to all widgets, including font sizes. Start the window with the alpha attribute set to 0, and the window in its normal state without modifications. Then, using an "after" method, change the state to "fullscreen" and set the alpha back to 1. The delay for the "after" method can be as short as 25 milliseconds because, in my experience, if you start the window with altered states, it doesn't work correctly. However, if you do it after initialization, it works fine. Since the "alpha" is set to 0, the change won't be visible.

ghost commented 1 year ago

While @matypoker's approach works, using the after function does take up a little bit of processing/CPU power. Not sure if it has any performance effects, but I don't think it is ideal.

matypoker commented 1 year ago

I didn't know, I use the after method for almost everything, and I never had performance issues.

LiTao54826 commented 8 months ago

This will work properly

import customtkinter
import ctypes
scaleFactor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100

class App(customtkinter.CTk):
    def __init__(self, width, height):
        super().__init__()
        self.title("CTk example")
        screenwidth = self.winfo_screenwidth()
        screenheight = self.winfo_screenheight()
        geometry = '%dx%d+%d+%d' % (width, height, int((screenwidth - width) / 2*scaleFactor), int((screenheight - height) / 2*scaleFactor))
        self.geometry(geometry)
        self.button = customtkinter.CTkButton(self, command=self.button_click)
        self.button.grid(row=0, column=0, padx=20, pady=10)

    # add methods to app
    def button_click(self):
        print("button click")

app = App(500,200)
app.mainloop()