Skip to content

Commit 765a5a4

Browse files
author
SupKittyMeow
committed
1.3.0 beta!1! rewritten using PySide instead of TKinter, for better performance and... SQUIARLECLES AND BLUR!1! anyway, right now, i got the basic code working (hopefully). Todo: settings, multi monitor support, testing on windows and linux
1 parent f9430ee commit 765a5a4

5 files changed

Lines changed: 468 additions & 268 deletions

File tree

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import sys
2+
import tkinter as tk
3+
import tkinter.ttk as ttk
4+
import time
5+
6+
BOUNCE_FACTOR = .88
7+
GRAVITY = 0.1
8+
FRICTION = 1.0125
9+
THROW_FACTOR = 200
10+
POP_FACTOR = 2
11+
12+
class Window:
13+
def __init__(self):
14+
self.root = tk.Tk()
15+
16+
self.root.overrideredirect(True)
17+
self.root.attributes('-topmost', True)
18+
19+
self.root.attributes('-alpha', 0.8)
20+
21+
self.screen_width = self.root.winfo_screenwidth()
22+
self.screen_height = self.root.winfo_screenheight()
23+
24+
self.hasBeenUnderMousePreviousFrame = False
25+
26+
self.root.geometry('{}x{}'.format(1, 1))
27+
self.root.resizable(False, False)
28+
29+
self.root.title("Wonky Window")
30+
31+
if sys.platform == "darwin": # macos
32+
self.root.createcommand(
33+
'tk::mac::ShowPreferences',
34+
self.on_settings
35+
)
36+
self.root['menu'] = tk.Menu(self.root)
37+
else:
38+
menubar = tk.Menu(self.root)
39+
settings_menu = tk.Menu(menubar, tearoff=0)
40+
settings_menu.add_command(label="Settings", command=self.on_settings,
41+
accelerator="Ctrl+,")
42+
menubar.add_cascade(label="Settings", menu=settings_menu)
43+
self.root['menu'] = menubar
44+
45+
self.windowX = self.root.winfo_pointerx() - 50
46+
self.windowY = self.root.winfo_pointery() - 50
47+
self.mouseDown = False
48+
49+
self.root.bind('<ButtonPress-1>', lambda event: self.mouse_down(True))
50+
self.root.bind('<ButtonRelease-1>', lambda event: self.mouse_down(False))
51+
self.root.bind('<Motion>', lambda event: self.motion())
52+
53+
self.root.bind('<Control-comma>', lambda event: self.on_settings())
54+
self.root.bind('<Command-comma>', lambda event: self.on_settings())
55+
56+
self.velX = 0
57+
self.velY = 0
58+
59+
self.offsetX = 0
60+
self.offsetY = 0
61+
62+
self.previousFramesX = [0, 0, 0, 0, 0, 0]
63+
self.previousFramesY = [0, 0, 0, 0, 0, 0]
64+
self.previousTimestamps = [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
65+
66+
self.state = "growing"
67+
self.animate_grow()
68+
self.root.mainloop()
69+
70+
def mouse_down(self, down):
71+
if hasattr(self, 'gravity') or hasattr(self, 'bounciness') or hasattr(self, 'friction'):
72+
widget_under_mouse = self.root.winfo_containing(self.root.winfo_pointerx(), self.root.winfo_pointery())
73+
if widget_under_mouse == self.gravity or widget_under_mouse == self.bounciness or widget_under_mouse == self.friction or widget_under_mouse == self.throw:
74+
self.hasBeenUnderMousePreviousFrame = True
75+
76+
if self.mouseDown == False and down == True:
77+
self.velX = 0
78+
self.velY = 0
79+
self.previousFramesX = [0, 0, 0, 0, 0, 0]
80+
self.previousFramesY = [0, 0, 0, 0, 0, 0]
81+
self.mouseDown = down
82+
self.offsetX = self.root.winfo_rootx() - self.root.winfo_pointerx()
83+
self.offsetY = self.root.winfo_rooty() - self.root.winfo_pointery()
84+
85+
if not down:
86+
distancesX = []
87+
distancesY = []
88+
89+
i = 0
90+
for frame in self.previousFramesX:
91+
if i == 0:
92+
i += 1
93+
continue
94+
95+
try:
96+
distancesX.append((self.previousFramesX[i] - self.previousFramesX[i-1]) / (self.previousTimestamps[i] - self.previousTimestamps[i-1]))
97+
except Exception:
98+
distancesX.append(0)
99+
i += 1
100+
i = 0
101+
for frame in self.previousFramesY:
102+
if i == 0:
103+
i += 1
104+
continue
105+
try:
106+
distancesY.append((self.previousFramesY[i] - self.previousFramesY[i-1]) / (self.previousTimestamps[i] - self.previousTimestamps[i-1]))
107+
except Exception:
108+
distancesY.append(0)
109+
i += 1
110+
111+
self.velX = sum(distancesX) / len(distancesX) / THROW_FACTOR
112+
self.velY = sum(distancesY) / len(distancesY) / THROW_FACTOR
113+
114+
def motion(self):
115+
if self.mouseDown == False:
116+
self.hasBeenUnderMousePreviousFrame = False
117+
if (self.mouseDown and not self.hasBeenUnderMousePreviousFrame):
118+
self.previousFramesX.append(self.root.winfo_pointerx())
119+
self.previousFramesY.append(self.root.winfo_pointery())
120+
self.previousTimestamps.append(time.time())
121+
self.previousFramesX.pop(0)
122+
self.previousFramesY.pop(0)
123+
self.previousTimestamps.pop(0)
124+
self.windowX = self.root.winfo_pointerx() + self.offsetX
125+
self.windowY = self.root.winfo_pointery() + self.offsetY
126+
self.check_window_collision()
127+
self.root.geometry('+{}+{}'.format(self.root.winfo_pointerx() + self.offsetX, self.root.winfo_pointery() + self.offsetY))
128+
129+
def on_gravity_change(self, value):
130+
global GRAVITY
131+
GRAVITY = float(value)
132+
133+
def on_bounciness_change(self, value):
134+
global BOUNCE_FACTOR
135+
BOUNCE_FACTOR = float(value)
136+
137+
def on_friction_change(self, value):
138+
global FRICTION
139+
FRICTION = float(value)
140+
141+
def on_throw_factor_change(self, value):
142+
global THROW_FACTOR
143+
THROW_FACTOR = 750 - float(value)
144+
145+
def on_pop_factor_change(self, value):
146+
global POP_FACTOR
147+
POP_FACTOR = float(value)
148+
149+
def show_sliders(self):
150+
self.hasBeenUnderMousePreviousFrame = False
151+
152+
self.gravity = ttk.Scale(self.root, from_=0, to=2, orient='horizontal', length=300, command=self.on_gravity_change)
153+
self.gravity.set(GRAVITY)
154+
self.gravity.pack(expand=True, pady=(25, 0))
155+
self.gravity_label = tk.Label(self.root, text="Gravity")
156+
self.gravity_label.pack(pady=(0, 50))
157+
158+
self.bounciness = ttk.Scale(self.root, from_=0, to=1, orient='horizontal', length=300, command=self.on_bounciness_change)
159+
self.bounciness.set(BOUNCE_FACTOR)
160+
self.bounciness.pack(expand=True)
161+
self.bounciness_label = tk.Label(self.root, text="Bounciness")
162+
self.bounciness_label.pack(pady=(0, 50))
163+
164+
self.friction = ttk.Scale(self.root, from_=1, to=1.2, orient='horizontal', length=300, command=self.on_friction_change)
165+
self.friction.set(FRICTION)
166+
self.friction.pack(expand=True)
167+
self.friction_label = tk.Label(self.root, text="Friction")
168+
self.friction_label.pack(pady=(0, 50))
169+
170+
self.throw = ttk.Scale(self.root, from_=25, to=700, orient='horizontal', length=300, command=self.on_throw_factor_change)
171+
self.throw.set(750 - THROW_FACTOR)
172+
self.throw.pack(expand=True)
173+
self.throw_label = tk.Label(self.root, text="Throw power")
174+
self.throw_label.pack(pady=(0, 25))
175+
176+
self.slide_in_effect()
177+
178+
def slide_in_effect(self, scale=0.0):
179+
if scale >= 300:
180+
self.gravity.configure(length=300)
181+
self.friction.configure(length=300)
182+
self.bounciness.configure(length=300)
183+
self.throw.configure(length=300)
184+
return
185+
self.gravity.configure(length=scale)
186+
self.bounciness.configure(length=scale)
187+
self.friction.configure(length=scale)
188+
self.throw.configure(length=scale)
189+
self.root.after(1, self.slide_in_effect, scale+2)
190+
191+
def hide_sliders(self, scale=300):
192+
if scale <= 0:
193+
self.gravity.destroy()
194+
self.gravity_label.destroy()
195+
self.bounciness.destroy()
196+
self.bounciness_label.destroy()
197+
self.friction.destroy()
198+
self.friction_label.destroy()
199+
self.throw.destroy()
200+
self.throw_label.destroy()
201+
return
202+
self.gravity.configure(length=scale)
203+
self.bounciness.configure(length=scale)
204+
self.friction.configure(length=scale)
205+
self.throw.configure(length=scale)
206+
207+
self.root.after(1, self.hide_sliders, scale-2)
208+
209+
def animate_settings_open(self, center_x, center_y, i=0):
210+
if i < 400:
211+
self.root.geometry('+{}+{}'.format(int(center_x - (int(100+i)) / 2), int(center_y - (int(100+i*.75) / 2))))
212+
self.root.geometry(f'{int(100+i)}x{int(100+i*.75)}')
213+
self.root.after(10, self.animate_settings_open, center_x, center_y, i+10)
214+
else:
215+
self.root.geometry(f'500x400')
216+
self.root.update()
217+
self.state = "settings"
218+
219+
def animate_settings_close(self, center_x, center_y, i=0):
220+
if i < 400:
221+
self.root.geometry('+{}+{}'.format(int(center_x - (int(500-i)) / 2), int(center_y - (int(400-i*.75) / 2))))
222+
self.root.geometry(f'{int(500-i)}x{int(400-i*.75)}')
223+
self.root.after(10, self.animate_settings_close, center_x, center_y, i+10)
224+
else:
225+
self.root.geometry(f'100x100')
226+
self.root.update()
227+
self.state = "loop"
228+
229+
def on_settings(self, i=1):
230+
self.mouse_down(False)
231+
self.velX = 0
232+
self.velY = 0
233+
if self.state == "loop":
234+
self.state = "opening settings"
235+
self.show_sliders()
236+
center_x = self.windowX + 50
237+
center_y = self.windowY + 50
238+
self.animate_settings_open(center_x=center_x, center_y=center_y)
239+
240+
elif self.state == "settings":
241+
self.state = "closing settings"
242+
self.hide_sliders()
243+
self.windowX = (self.root.winfo_rootx() + self.root.winfo_width() // 2) - 50
244+
self.windowY = (self.root.winfo_rooty() + self.root.winfo_height() // 2) - 50
245+
center_x = self.windowX + 50
246+
center_y = self.windowY + 50
247+
self.animate_settings_close(center_x=center_x, center_y=center_y)
248+
249+
def check_window_collision(self):
250+
if self.state == "loop":
251+
if not self.mouseDown:
252+
width = self.screen_width - self.root.winfo_width()
253+
height = self.screen_height - self.root.winfo_height()
254+
255+
if self.windowX < 0:
256+
distance = round(self.windowX - 0) / POP_FACTOR
257+
self.velX = (-self.velX * BOUNCE_FACTOR) + distance
258+
self.windowX = 0
259+
elif self.windowX > width:
260+
distance = round(self.windowX - width) / POP_FACTOR
261+
self.velX = (-self.velX * BOUNCE_FACTOR) + distance
262+
self.windowX = width
263+
264+
if self.windowY > height:
265+
distance = round(self.windowY - height) / POP_FACTOR
266+
self.velY = (-self.velY * BOUNCE_FACTOR) + distance
267+
self.windowY = height
268+
elif self.windowY < 38:
269+
distance = round(self.windowY - 38) / POP_FACTOR
270+
self.velY = (-self.velY * BOUNCE_FACTOR) + distance
271+
self.windowY = 38
272+
273+
def loop(self):
274+
if self.state == "loop":
275+
if not self.mouseDown:
276+
self.windowX += self.velX
277+
self.windowY += self.velY
278+
self.check_window_collision()
279+
self.root.geometry('+{}+{}'.format(int(self.windowX), int(self.windowY)))
280+
self.velY += GRAVITY
281+
self.velX /= FRICTION
282+
self.root.after(8, self.loop)
283+
284+
def animate_grow(self, i=1):
285+
if i <= 50:
286+
center_x = self.root.winfo_pointerx()
287+
center_y = self.root.winfo_pointery()
288+
self.root.geometry('+{}+{}'.format(int(center_x - i), int(center_y - i)))
289+
self.root.geometry(f'{i*2}x{i*2}')
290+
291+
self.root.after(10, self.animate_grow, i+1)
292+
else:
293+
self.windowX = self.root.winfo_x()
294+
self.windowY = self.root.winfo_y()
295+
self.state = "loop"
296+
self.loop()
297+
298+
window = Window()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pyinstaller==6.14.1

0 commit comments

Comments
 (0)