mirror of
https://github.com/rangermix/TwitchDropsMiner.git
synced 2026-05-31 01:19:36 +00:00
Implement tray icon title displaying progress
This commit is contained in:
72
gui.py
72
gui.py
@@ -327,7 +327,6 @@ class _BaseVars(TypedDict):
|
||||
progress: DoubleVar
|
||||
percentage: StringVar
|
||||
remaining: StringVar
|
||||
minutes: int
|
||||
|
||||
|
||||
class _CampaignVars(_BaseVars):
|
||||
@@ -347,20 +346,19 @@ class CampaignProgress:
|
||||
BAR_LENGTH = 240
|
||||
|
||||
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
||||
self._manager = manager
|
||||
self._vars: _ProgressVars = {
|
||||
"campaign": {
|
||||
"name": StringVar(), # campaign name
|
||||
"progress": DoubleVar(), # controls the progress bar
|
||||
"percentage": StringVar(), # percentage display string
|
||||
"remaining": StringVar(), # time remaining string
|
||||
"minutes": 0, # remaining minutes
|
||||
},
|
||||
"drop": {
|
||||
"rewards": StringVar(), # drop rewards
|
||||
"progress": DoubleVar(), # as above
|
||||
"percentage": StringVar(), # as above
|
||||
"remaining": StringVar(), # as above
|
||||
"minutes": 0, # as above
|
||||
},
|
||||
}
|
||||
self._frame = frame = ttk.LabelFrame(
|
||||
@@ -400,6 +398,7 @@ class CampaignProgress:
|
||||
maximum=1,
|
||||
variable=self._vars["drop"]["progress"],
|
||||
).grid(column=0, row=10, columnspan=2)
|
||||
self._drop: Optional[TimedDrop] = None
|
||||
self._timer_task: Optional[asyncio.Task[None]] = None
|
||||
self._update_time(0)
|
||||
|
||||
@@ -411,12 +410,19 @@ class CampaignProgress:
|
||||
return (hours, minutes)
|
||||
|
||||
def _update_time(self, seconds: int):
|
||||
drop = self._drop
|
||||
if drop is not None:
|
||||
drop_minutes = drop.remaining_minutes
|
||||
campaign_minutes = drop.campaign.remaining_minutes
|
||||
else:
|
||||
drop_minutes = 0
|
||||
campaign_minutes = 0
|
||||
drop_vars: _DropVars = self._vars["drop"]
|
||||
campaign_vars: _CampaignVars = self._vars["campaign"]
|
||||
dseconds = seconds % 60
|
||||
hours, minutes = self._divmod(drop_vars["minutes"], seconds)
|
||||
hours, minutes = self._divmod(drop_minutes, seconds)
|
||||
drop_vars["remaining"].set(f"{hours:>2}:{minutes:02}:{dseconds:02} remaining")
|
||||
hours, minutes = self._divmod(campaign_vars["minutes"], seconds)
|
||||
hours, minutes = self._divmod(campaign_minutes, seconds)
|
||||
campaign_vars["remaining"].set(f"{hours:>2}:{minutes:02}:{dseconds:02} remaining")
|
||||
|
||||
async def _timer_loop(self):
|
||||
@@ -430,7 +436,7 @@ class CampaignProgress:
|
||||
|
||||
def start_timer(self):
|
||||
if self._timer_task is None:
|
||||
if self._vars["drop"]["minutes"] <= 0:
|
||||
if self._drop is None or self._drop.remaining_minutes <= 0:
|
||||
# if we're starting the timer at 0 drop minutes,
|
||||
# all we need is a single instant time update setting seconds to 60,
|
||||
# to avoid substracting a minute from campaign minutes
|
||||
@@ -444,6 +450,12 @@ class CampaignProgress:
|
||||
self._timer_task = None
|
||||
|
||||
def display(self, drop: TimedDrop, *, countdown: bool = True):
|
||||
self._drop = drop
|
||||
# drop update
|
||||
vars_drop = self._vars["drop"]
|
||||
vars_drop["rewards"].set(drop.rewards_text())
|
||||
vars_drop["progress"].set(drop.progress)
|
||||
vars_drop["percentage"].set(f"{drop.progress:6.1%}")
|
||||
# campaign update
|
||||
campaign = drop.campaign
|
||||
vars_campaign = self._vars["campaign"]
|
||||
@@ -452,13 +464,9 @@ class CampaignProgress:
|
||||
vars_campaign["percentage"].set(
|
||||
f"{campaign.progress:6.1%} ({campaign.claimed_drops}/{campaign.total_drops})"
|
||||
)
|
||||
vars_campaign["minutes"] = campaign.remaining_minutes
|
||||
# drop update
|
||||
vars_drop = self._vars["drop"]
|
||||
vars_drop["rewards"].set(drop.rewards_text())
|
||||
vars_drop["progress"].set(drop.progress)
|
||||
vars_drop["percentage"].set(f"{drop.progress:6.1%}")
|
||||
vars_drop["minutes"] = drop.remaining_minutes
|
||||
# tray
|
||||
tray = self._manager.tray
|
||||
tray.display_progress(drop)
|
||||
if countdown:
|
||||
# reschedule our seconds update timer
|
||||
self.stop_timer()
|
||||
@@ -683,18 +691,30 @@ class ChannelList:
|
||||
|
||||
|
||||
class TrayIcon:
|
||||
TITLE = "Twitch Drops Miner"
|
||||
|
||||
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
||||
self._manager = manager
|
||||
self._icon: Optional[pystray.Icon] = None
|
||||
self.icon: Optional[pystray.Icon] = None
|
||||
self._button = ttk.Button(master, command=self.minimize, text="Minimize to Tray")
|
||||
self._button.grid(column=0, row=0, sticky="e")
|
||||
|
||||
def is_tray(self) -> bool:
|
||||
return self._icon is not None
|
||||
return self.icon is not None
|
||||
|
||||
def get_title(self, drop: Optional[TimedDrop]) -> str:
|
||||
if drop is None:
|
||||
return self.TITLE
|
||||
return (
|
||||
f"{self.TITLE}\n"
|
||||
f"{drop.rewards_text()}: {drop.progress:.1%} "
|
||||
f"({drop.campaign.claimed_drops}/{drop.campaign.total_drops})"
|
||||
)
|
||||
|
||||
def start(self):
|
||||
if self._icon is None:
|
||||
if self.icon is None:
|
||||
loop = self._manager._twitch._loop
|
||||
drop = self._manager.progress._drop
|
||||
|
||||
# we need this because tray icon lives in a separate thread
|
||||
def bridge(func):
|
||||
@@ -705,18 +725,18 @@ class TrayIcon:
|
||||
pystray.Menu.SEPARATOR,
|
||||
pystray.MenuItem("Quit", bridge(self.quit)),
|
||||
)
|
||||
self._icon = pystray.Icon(
|
||||
self.icon = pystray.Icon(
|
||||
"twitch_miner",
|
||||
ICOImage(resource_path("pickaxe.ico")),
|
||||
"Twitch Drops Miner",
|
||||
self.get_title(drop),
|
||||
menu,
|
||||
)
|
||||
self._icon.run_detached()
|
||||
self.icon.run_detached()
|
||||
|
||||
def stop(self):
|
||||
if self._icon is not None:
|
||||
self._icon.stop()
|
||||
self._icon = None
|
||||
if self.icon is not None:
|
||||
self.icon.stop()
|
||||
self.icon = None
|
||||
|
||||
def quit(self):
|
||||
self.stop()
|
||||
@@ -733,8 +753,8 @@ class TrayIcon:
|
||||
self._manager._root.deiconify()
|
||||
|
||||
def notify(self, message: str, title: Optional[str] = None, duration: float = 10):
|
||||
if self._icon is not None:
|
||||
icon = self._icon
|
||||
if self.icon is not None:
|
||||
icon = self.icon
|
||||
|
||||
async def notifier():
|
||||
icon.notify(message, title)
|
||||
@@ -743,6 +763,10 @@ class TrayIcon:
|
||||
|
||||
asyncio.create_task(notifier())
|
||||
|
||||
def display_progress(self, drop: TimedDrop):
|
||||
if self.icon is not None:
|
||||
self.icon.title = self.get_title(drop)
|
||||
|
||||
|
||||
class GUIManager:
|
||||
def __init__(self, twitch: Twitch):
|
||||
|
||||
Reference in New Issue
Block a user