Move window styling and timers to separate script files and/ or class
This commit aims to clean up the significant mess that was the timer code and amassing of functions. In order to better clean up the main GUI code these functions have been moved to their own respective script files. More specifically any future commits that contain window styling aspects will be added to the styles.py script and any timer related code will be moved into timers.py. The timer code has also been abstracted a little more, however, we are still restricted by the API, one solution would be to modify warpy and better standardize the returns from the three open world json objects. Currently there is explicit handler functions within the class that can set this information, however, it may be more reasonable to return smarter json objects that have the current state attached.
This commit is contained in:
134
src/overlay.py
134
src/overlay.py
@@ -4,13 +4,8 @@ from warpy import warpy
|
|||||||
import win32gui
|
import win32gui
|
||||||
import win32api
|
import win32api
|
||||||
import win32con
|
import win32con
|
||||||
import math
|
from styles import enable_clickthrough
|
||||||
from datetime import datetime
|
from timers import Timer
|
||||||
from ctypes import windll
|
|
||||||
|
|
||||||
GWL_EXSTYLE = -20
|
|
||||||
WS_EX_APPWINDOW = 0x00040000
|
|
||||||
WS_EX_TOOLWINDOW = 0x00000080
|
|
||||||
|
|
||||||
X_POS_WIN_OFFSET = 8
|
X_POS_WIN_OFFSET = 8
|
||||||
Y_POS_WIN_OFFSET = 31
|
Y_POS_WIN_OFFSET = 31
|
||||||
@@ -23,32 +18,7 @@ root.wm_attributes("-disabled", True) # Disable interactions with the window
|
|||||||
root.wm_attributes("-transparent", '#2e3440') # Used to make window transparent
|
root.wm_attributes("-transparent", '#2e3440') # Used to make window transparent
|
||||||
root.overrideredirect(True) # Remove the title bar
|
root.overrideredirect(True) # Remove the title bar
|
||||||
|
|
||||||
|
root.after(1, lambda: enable_clickthrough(root)) # Make overlay click through.
|
||||||
def enable_clickthrough(_root):
|
|
||||||
"""
|
|
||||||
Sets the app window to be click through.
|
|
||||||
:param _root: Tkinter parent
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
_hwnd = windll.user32.GetParent(_root.winfo_id())
|
|
||||||
style = windll.user32.GetWindowLongPtrW(_hwnd, GWL_EXSTYLE)
|
|
||||||
style |= win32con.WS_EX_TRANSPARENT | win32con.WS_EX_LAYERED
|
|
||||||
win32gui.SetWindowLong(_hwnd, win32con.GWL_EXSTYLE, style)
|
|
||||||
|
|
||||||
|
|
||||||
def disable_clickthrough(_root):
|
|
||||||
"""
|
|
||||||
TODO: Properly implement this so it can undo click-through transparency
|
|
||||||
:param _root: Tkinter parent
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
_hwnd = windll.user32.GetParent(_root.winfo_id())
|
|
||||||
style = windll.user32.GetWindowLongPtrW(_hwnd, GWL_EXSTYLE)
|
|
||||||
style |= win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED
|
|
||||||
win32gui.SetWindowLong(_hwnd, win32con.GWL_EXSTYLE, style)
|
|
||||||
|
|
||||||
|
|
||||||
root.after(10, lambda: enable_clickthrough(root)) # Make overlay click through.
|
|
||||||
# Define the root's geometry based on the geometry of specified application.
|
# Define the root's geometry based on the geometry of specified application.
|
||||||
# List of Warframe class names:
|
# List of Warframe class names:
|
||||||
# - WarframePublicEvolutionGfxD3D12
|
# - WarframePublicEvolutionGfxD3D12
|
||||||
@@ -95,76 +65,54 @@ warframe = warpy.Worldstate("pc", loop=loop)
|
|||||||
timers.grid(row=0, column=0)
|
timers.grid(row=0, column=0)
|
||||||
|
|
||||||
tk.Label(timers, text="Cetus", fg="#FFFFFF").grid(row=0, column=0)
|
tk.Label(timers, text="Cetus", fg="#FFFFFF").grid(row=0, column=0)
|
||||||
tk.Label(timers, text="Deimos", fg="#FFFFFF").grid(row=0, column=2)
|
tk.Label(timers, text="Deimos", fg="#FFFFFF").grid(row=0, column=3)
|
||||||
tk.Label(timers, text="Vallis", fg="#FFFFFF").grid(row=0, column=4)
|
tk.Label(timers, text="Vallis", fg="#FFFFFF").grid(row=0, column=6)
|
||||||
|
|
||||||
cetus_timer_svar = tk.StringVar(timers)
|
# String state indicators
|
||||||
vallis_timer_svar = tk.StringVar(timers)
|
cetus_timer = Timer(tk.StringVar(timers), tk.StringVar(timers), root, loop)
|
||||||
cambion_timer_svar = tk.StringVar(timers)
|
vallis_timer = Timer(tk.StringVar(timers), tk.StringVar(timers), root, loop)
|
||||||
|
cambion_timer = Timer(tk.StringVar(timers), tk.StringVar(timers), root, loop)
|
||||||
|
|
||||||
cetus_timer_label = tk.Label(timers, textvariable=cetus_timer_svar)
|
cetus_state_label = tk.Label(timers, textvariable=cetus_timer.state)
|
||||||
vallis_timer_label = tk.Label(timers, textvariable=vallis_timer_svar)
|
vallis_state_label = tk.Label(timers, textvariable=vallis_timer.state)
|
||||||
cambion_timer_label = tk.Label(timers, textvariable=cambion_timer_svar)
|
cambion_state_label = tk.Label(timers, textvariable=cambion_timer.state)
|
||||||
|
|
||||||
cetus_timer_label.grid(row=0, column=1)
|
cetus_state_label.grid(row=0, column=1)
|
||||||
cambion_timer_label.grid(row=0, column=3)
|
cambion_state_label.grid(row=0, column=4)
|
||||||
vallis_timer_label.grid(row=0, column=5)
|
vallis_state_label.grid(row=0, column=7)
|
||||||
|
|
||||||
|
cetus_timer_label = tk.Label(timers, textvariable=cetus_timer.timer)
|
||||||
|
vallis_timer_label = tk.Label(timers, textvariable=vallis_timer.timer)
|
||||||
|
cambion_timer_label = tk.Label(timers, textvariable=cambion_timer.timer)
|
||||||
|
|
||||||
|
cetus_timer_label.grid(row=0, column=2)
|
||||||
|
cambion_timer_label.grid(row=0, column=5)
|
||||||
|
vallis_timer_label.grid(row=0, column=8)
|
||||||
|
|
||||||
|
|
||||||
# Asynchronous functions
|
# Asynchronous functions
|
||||||
|
async def cetus_state_widget():
|
||||||
|
json = await warframe.cetus_status()
|
||||||
|
next_attempt, remaining_time = cetus_timer.set_state(json, cetus_timer.cetus_handler)
|
||||||
|
cetus_timer.update_timer(remaining_time)
|
||||||
|
root.after(next_attempt, lambda: loop.run_until_complete(cetus_state_widget()))
|
||||||
|
|
||||||
|
|
||||||
# TODO: See if there is a way to decorate these methods and reduce some duplicate code
|
async def vallis_state_widget():
|
||||||
# Timers
|
json = await warframe.vallis_status()
|
||||||
# Following methods are using an additional 120000 ms (2m) to attempt to avoid polling the API before expiration
|
next_attempt, remaining_time = vallis_timer.set_state(json, vallis_timer.vallis_handler)
|
||||||
# update which would result in a negative timedelta
|
vallis_timer.update_timer(remaining_time)
|
||||||
async def get_cetus_cycle():
|
root.after(next_attempt, lambda: loop.run_until_complete(vallis_state_widget()))
|
||||||
cetus_status = await warframe.cetus_status()
|
|
||||||
expiration = datetime.strptime(cetus_status["expiry"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
||||||
if cetus_status["isDay"]:
|
|
||||||
cetus_timer_svar.set("Day")
|
|
||||||
else:
|
|
||||||
cetus_timer_svar.set("Night")
|
|
||||||
remaining_time = expiration - datetime.utcnow()
|
|
||||||
if remaining_time.total_seconds() < 0:
|
|
||||||
retry = 120000
|
|
||||||
else:
|
|
||||||
retry = remaining_time.total_seconds() * 1000.0 + 120000
|
|
||||||
print("Next Cetus cycle retry in: " + str(remaining_time))
|
|
||||||
root.after(math.ceil(retry), lambda: loop.run_until_complete(get_cetus_cycle()))
|
|
||||||
|
|
||||||
|
|
||||||
async def get_vallis_cycle():
|
async def cambion_state_widget():
|
||||||
vallis_status = await warframe.vallis_status()
|
json = await warframe.cambion_status()
|
||||||
expiration = datetime.strptime(vallis_status["expiry"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
next_attempt, remaining_time = cambion_timer.set_state(json, cambion_timer.cambion_handler)
|
||||||
if vallis_status["isWarm"]:
|
cambion_timer.update_timer(remaining_time)
|
||||||
vallis_timer_svar.set("Warm")
|
root.after(next_attempt, lambda: loop.run_until_complete(cambion_state_widget()))
|
||||||
else:
|
|
||||||
vallis_timer_svar.set("Cold")
|
|
||||||
remaining_time = expiration - datetime.utcnow()
|
|
||||||
if remaining_time.total_seconds() < 0:
|
|
||||||
retry = 120000
|
|
||||||
else:
|
|
||||||
retry = remaining_time.total_seconds() * 1000.0 + 120000
|
|
||||||
print("Next Vallis cycle retry in: " + str(remaining_time))
|
|
||||||
root.after(math.ceil(retry), lambda: loop.run_until_complete(get_vallis_cycle()))
|
|
||||||
|
|
||||||
|
loop.run_until_complete(cetus_state_widget())
|
||||||
async def get_cambion_cycle():
|
loop.run_until_complete(vallis_state_widget())
|
||||||
cambion_status = await warframe.cambion_status()
|
loop.run_until_complete(cambion_state_widget())
|
||||||
expiration = datetime.strptime(cambion_status["expiry"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
|
||||||
cambion_timer_svar.set(cambion_status["active"].capitalize())
|
|
||||||
remaining_time = expiration - datetime.utcnow()
|
|
||||||
if remaining_time.total_seconds() < 0:
|
|
||||||
retry = 120000
|
|
||||||
else:
|
|
||||||
retry = remaining_time.total_seconds() * 1000.0 + 120000
|
|
||||||
print("Next Cambion cycle retry in: " + str(remaining_time))
|
|
||||||
root.after(math.ceil(retry), lambda: loop.run_until_complete(get_cambion_cycle()))
|
|
||||||
|
|
||||||
|
|
||||||
loop.run_until_complete(get_cetus_cycle())
|
|
||||||
loop.run_until_complete(get_vallis_cycle())
|
|
||||||
loop.run_until_complete(get_cambion_cycle())
|
|
||||||
|
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|||||||
31
src/styles.py
Normal file
31
src/styles.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
import win32gui
|
||||||
|
import win32con
|
||||||
|
from ctypes import windll
|
||||||
|
|
||||||
|
GWL_EXSTYLE = -20
|
||||||
|
WS_EX_APPWINDOW = 0x00040000
|
||||||
|
WS_EX_TOOLWINDOW = 0x00000080
|
||||||
|
|
||||||
|
|
||||||
|
def enable_clickthrough(_root):
|
||||||
|
"""
|
||||||
|
Sets the app window to be click through.
|
||||||
|
:param _root: Tkinter parent
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
_hwnd = windll.user32.GetParent(_root.winfo_id())
|
||||||
|
style = windll.user32.GetWindowLongPtrW(_hwnd, GWL_EXSTYLE)
|
||||||
|
style |= win32con.WS_EX_TRANSPARENT | win32con.WS_EX_LAYERED
|
||||||
|
win32gui.SetWindowLong(_hwnd, win32con.GWL_EXSTYLE, style)
|
||||||
|
|
||||||
|
|
||||||
|
def disable_clickthrough(_root):
|
||||||
|
"""
|
||||||
|
TODO: Properly implement this so it can undo click-through transparency
|
||||||
|
:param _root: Tkinter parent
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
_hwnd = windll.user32.GetParent(_root.winfo_id())
|
||||||
|
style = windll.user32.GetWindowLongPtrW(_hwnd, GWL_EXSTYLE)
|
||||||
|
style |= win32con.WS_EX_COMPOSITED | win32con.WS_EX_LAYERED
|
||||||
|
win32gui.SetWindowLong(_hwnd, win32con.GWL_EXSTYLE, style)
|
||||||
54
src/timers.py
Normal file
54
src/timers.py
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import math
|
||||||
|
import styles
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
|
||||||
|
def _create_timer_values(json):
|
||||||
|
expiration = datetime.strptime(json["expiry"], "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||||
|
remaining_time = expiration - datetime.utcnow()
|
||||||
|
remaining_seconds = remaining_time.total_seconds()
|
||||||
|
return expiration, remaining_time, remaining_seconds
|
||||||
|
|
||||||
|
|
||||||
|
class Timer:
|
||||||
|
def __init__(self, timer, state, root, loop):
|
||||||
|
self.timer = timer
|
||||||
|
self.state = state
|
||||||
|
self._root = root
|
||||||
|
self._loop = loop
|
||||||
|
|
||||||
|
def set_state(self, json, state_handler):
|
||||||
|
expiration, remaining_time, remaining_seconds = _create_timer_values(json)
|
||||||
|
if remaining_seconds < 0:
|
||||||
|
self.timer.set(str('00:00:00'))
|
||||||
|
retry = 120000
|
||||||
|
else:
|
||||||
|
self.timer.set(str(remaining_time))
|
||||||
|
retry = remaining_seconds * 1000.0 + 120000
|
||||||
|
state_handler(json)
|
||||||
|
return round(retry), remaining_time
|
||||||
|
|
||||||
|
# Do these belong here?
|
||||||
|
def cetus_handler(self, json):
|
||||||
|
self.state.set(json['state'].capitalize())
|
||||||
|
|
||||||
|
def vallis_handler(self, json):
|
||||||
|
if json["isWarm"]:
|
||||||
|
self.state.set("Warm")
|
||||||
|
else:
|
||||||
|
self.state.set("Cold")
|
||||||
|
|
||||||
|
def cambion_handler(self, json):
|
||||||
|
self.state.set(json['active'].capitalize())
|
||||||
|
# End ?
|
||||||
|
|
||||||
|
def update_timer(self, remaining_time):
|
||||||
|
remaining_time -= timedelta(seconds=1)
|
||||||
|
seconds = remaining_time.total_seconds()
|
||||||
|
if seconds <= 0:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self.timer.set(str(remaining_time).split(".")[0])
|
||||||
|
self._root.after(1000, lambda: self.update_timer(remaining_time))
|
||||||
|
self._root.after(1000, lambda: styles.enable_clickthrough(self._root)) # Required to maintain transparency
|
||||||
|
|
||||||
Reference in New Issue
Block a user