Inspired by this question, I would like to write my own resizing function for my root window. But I just noticed that my code shows some performance issues. If you resize it quickly you can see that the window doesn't finds its height prompt as I wish, it "stutters". (It's more like a swinging)
Does someone know why this happens? My best guess is that tkinter
event handler is too slow for it, or the math I did isn't the quickest way.
I did try update_idletasks()
on different locations and also several times. Another way that I have tried was to use the after
method but it made it worse.
Here is an example code:
import tkinter as tk
class FloatingWindow(tk.Tk):
def __init__(self):
super().__init__()
self.overrideredirect(True)
self.center()
self.label = tk.Label(self, text="Grab the upper-right corner to resize")
self.label.pack(side="top", fill="both", expand=True)
self.grip2 = tk.Label(self,bg='blue')
self.grip2.place(relx=1.0, rely=0, anchor="ne")
self.grip2.bind("<B1-Motion>",self.OnMotion)
def OnMotion(self, event):
abs_x = self.winfo_pointerx() - self.winfo_rootx()
abs_y = self.winfo_pointery() - self.winfo_rooty()
if abs_x >0:
x = self.winfo_rootx()
y = self.winfo_rooty()+abs_y
height = self.winfo_height()-abs_y
if height >0:
self.geometry("%dx%d+%d+%d" % (abs_x,height,
x,y))
def center(self):
width = 300
height = 300
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = (screen_width/2) - (width/2)
y_coordinate = (screen_height/2) - (height/2)
self.geometry("%dx%d+%d+%d" % (width, height,
x_coordinate, y_coordinate))
app=FloatingWindow()
app.mainloop()
Update
It appears that the performance issue is Microsoft related and a well known issue which drives most MS-Developer crazy.
Update 2
Since this issue seems MS-Windows related, I tried to find a MS specific solution and did a lot of research. I've tried to intercept messages like wm_pain
, wm_nccalcsize
and many more.
Somewhere on the way I thought, there is already an sizebox so it makes sense to make use of it. But it appears another issue with this solution.
A thin white stripe on the top edge. I took my quite a while till I found the answer its just the sizebox itself. Unfortunately, I haven't found a way to configure the sizebox via the win32 api or the Dwmapi.
TL;DR
The answer to this question is preferably a smooth resizing event with the blue and green Labels. But if you find a way to erase the thin white line and still have resizing ability, (just shrinking the window rect to the client rect does not work or you have just 1 pixel to resize) would be a solution too.
The updated code looks like this:
import tkinter as tk
import win32gui
import win32api
import win32con
class FloatingWindow(tk.Tk):
def __init__(self):
super().__init__()
#self.overrideredirect(True)
self.hWnd = int(self.wm_frame(), 16)
self.label = tk.Label(self, text="Grab one of the blue")
self.label.pack(side="top", fill="both", expand=True)
blues = {'se' : (1,1),'ne' : (1,0),'nw' : (0,0),'sw' : (0,1)}
grens = {'e' : (1,0.5), 'n' : (0.5,0), 'w' : (0,0.5), 's' : (0.5,1)}
for k,v in blues.items():
ref = tk.Label(self, bg='blue')
ref.place(relx=v[0],rely=v[1],anchor=k)
ref.bind("<B1-Motion>", lambda e, mode=k:self.OnMotion(e,mode))
for k,v in grens.items():
ref = tk.Label(self, bg='green')
ref.place(relx=v[0],rely=v[1],anchor=k)
ref.bind("<B1-Motion>", lambda e, mode=k:self.OnMotion(e,mode))
self.bind('<ButtonPress-1>', self.start_drag)
self.bind('<ButtonRelease-1>', self.stop_drag)
return
def stop_drag(self,event):
self.start_abs_x = None
self.start_abs_y = None
self.start_width = None
self.start_height= None
self.start_x = None
self.start_y = None
def start_drag(self,event):
self.update_idletasks()
self.start_abs_x = self.winfo_pointerx() - self.winfo_rootx()
self.start_abs_y = self.winfo_pointery() - self.winfo_rooty()
self.start_width = self.winfo_width()
self.start_height= self.winfo_height()
self.start_x = self.winfo_x()
self.start_y = self.winfo_y()
def OnMotion(self, event, mode):
self.update_idletasks()
abs_x = self.winfo_pointerx() - self.winfo_rootx()
abs_y = self.winfo_pointery() - self.winfo_rooty()
width = self.winfo_width()
height= self.winfo_height()
x = self.winfo_x()
y = self.winfo_y()
x_motion = self.start_abs_x - abs_x
y_motion = self.start_abs_y - abs_y
self.calc_x = x;self.calc_y=y;self.calc_w=width;
self.calc_h=self.start_height
if 'e' in mode:
self.calc_w = self.start_width-x_motion
if 's' in mode:
self.calc_h -= y_motion
if 'n' in mode:
self.calc_y = y-y_motion
self.calc_h = height+y_motion
if 'w' in mode:
self.calc_w = width+x_motion
self.calc_x = x-x_motion
self.geometry("%dx%d+%d+%d" % (self.calc_w,self.calc_h,
self.calc_x,self.calc_y))
def center(self):
width = 300
height = 300
screen_width = self.winfo_screenwidth()
screen_height = self.winfo_screenheight()
x_coordinate = (screen_width/2) - (width/2)
y_coordinate = (screen_height/2) - (height/2)
self.geometry("%dx%d+%d+%d" % (width, height,
x_coordinate, y_coordinate))
app=FloatingWindow()
app.update_idletasks()
hwnd = win32gui.GetParent(app.hWnd)
style= win32api.GetWindowLong(hwnd, win32con.GWL_STYLE)
style&= ~win32con.WS_CAPTION
#style&= ~win32con.WS_SIZEBOX
valid= win32api.SetWindowLong(hwnd, win32con.GWL_STYLE, style)
app.mainloop()
System Information:
Windows 10 Home; x64-base,Intel(R) Core(TM) i3-2120 @ 3.30GHz, 3300 MHz, 2Cores
with
Python 3.7.2 and tkinter 8.6