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:
雲華
2021-05-04 14:52:18 -04:00
parent 45691dc8ad
commit f1d4fb2d1c
3 changed files with 126 additions and 93 deletions

View File

@@ -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
View 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
View 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