mirror of
https://github.com/rangermix/TwitchDropsMiner.git
synced 2026-05-26 07:08:04 +00:00
Implement a dynamic tray icon status picture
This commit is contained in:
@@ -19,7 +19,7 @@ script:
|
|||||||
|
|
||||||
# Package the app.
|
# Package the app.
|
||||||
- mkdir -p "$TARGET_APPDIR"/usr/{src,share/icons/hicolor/128x128/apps}
|
- mkdir -p "$TARGET_APPDIR"/usr/{src,share/icons/hicolor/128x128/apps}
|
||||||
- cp -r "$SOURCE_DIR/../lang" "$SOURCE_DIR/../pickaxe.ico" "$SOURCE_DIR"/../*.py "$TARGET_APPDIR/usr/src"
|
- cp -r "$SOURCE_DIR/../lang" "$SOURCE_DIR/../icons" "$SOURCE_DIR"/../*.py "$TARGET_APPDIR/usr/src"
|
||||||
- cp "$SOURCE_DIR/pickaxe.png" "$TARGET_APPDIR/usr/share/icons/hicolor/128x128/apps/io.github.devilxd.twitchdropsminer.png"
|
- cp "$SOURCE_DIR/pickaxe.png" "$TARGET_APPDIR/usr/share/icons/hicolor/128x128/apps/io.github.devilxd.twitchdropsminer.png"
|
||||||
|
|
||||||
# Install requirements.
|
# Install requirements.
|
||||||
|
|||||||
@@ -17,7 +17,12 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
# (source_path, dest_path, required)
|
# (source_path, dest_path, required)
|
||||||
to_add: list[tuple[Path, str, bool]] = [
|
to_add: list[tuple[Path, str, bool]] = [
|
||||||
(Path("pickaxe.ico"), '.', True), # icon file
|
# icon files
|
||||||
|
(Path("icons/pickaxe.ico"), "./icons", True),
|
||||||
|
(Path("icons/active.ico"), "./icons", True),
|
||||||
|
(Path("icons/idle.ico"), "./icons", True),
|
||||||
|
(Path("icons/error.ico"), "./icons", True),
|
||||||
|
(Path("icons/maint.ico"), "./icons", True),
|
||||||
# SeleniumWire HTTPS/SSL cert file and key
|
# SeleniumWire HTTPS/SSL cert file and key
|
||||||
(Path(SITE_PACKAGES_PATH, "seleniumwire/ca.crt"), "./seleniumwire", False),
|
(Path(SITE_PACKAGES_PATH, "seleniumwire/ca.crt"), "./seleniumwire", False),
|
||||||
(Path(SITE_PACKAGES_PATH, "seleniumwire/ca.key"), "./seleniumwire", False),
|
(Path(SITE_PACKAGES_PATH, "seleniumwire/ca.key"), "./seleniumwire", False),
|
||||||
@@ -99,10 +104,10 @@ exe = EXE(
|
|||||||
console=False,
|
console=False,
|
||||||
upx_exclude=[],
|
upx_exclude=[],
|
||||||
target_arch=None,
|
target_arch=None,
|
||||||
icon="pickaxe.ico",
|
|
||||||
runtime_tmpdir=None,
|
runtime_tmpdir=None,
|
||||||
codesign_identity=None,
|
codesign_identity=None,
|
||||||
entitlements_file=None,
|
entitlements_file=None,
|
||||||
|
icon="icons/pickaxe.ico",
|
||||||
bootloader_ignore_signals=False,
|
bootloader_ignore_signals=False,
|
||||||
disable_windowed_traceback=False,
|
disable_windowed_traceback=False,
|
||||||
name="Twitch Drops Miner (by DevilXD)",
|
name="Twitch Drops Miner (by DevilXD)",
|
||||||
|
|||||||
25
gui.py
25
gui.py
@@ -1044,13 +1044,21 @@ class TrayIcon:
|
|||||||
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
||||||
self._manager = manager
|
self._manager = manager
|
||||||
self.icon: pystray.Icon | None = None
|
self.icon: pystray.Icon | None = None
|
||||||
self.icon_image = Image_module.open(resource_path("pickaxe.ico"))
|
self._icon_images: dict[str, Image_module.Image] = {
|
||||||
|
"pickaxe": Image_module.open(resource_path("icons/pickaxe.ico")),
|
||||||
|
"active": Image_module.open(resource_path("icons/active.ico")),
|
||||||
|
"idle": Image_module.open(resource_path("icons/idle.ico")),
|
||||||
|
"error": Image_module.open(resource_path("icons/error.ico")),
|
||||||
|
"maint": Image_module.open(resource_path("icons/maint.ico")),
|
||||||
|
}
|
||||||
|
self._icon_state: str = "pickaxe"
|
||||||
self._button = ttk.Button(master, command=self.minimize, text=_("gui", "tray", "minimize"))
|
self._button = ttk.Button(master, command=self.minimize, text=_("gui", "tray", "minimize"))
|
||||||
self._button.grid(column=0, row=0, sticky="ne")
|
self._button.grid(column=0, row=0, sticky="ne")
|
||||||
|
|
||||||
def __del__(self) -> None:
|
def __del__(self) -> None:
|
||||||
self.stop()
|
self.stop()
|
||||||
self.icon_image.close()
|
for icon_image in self._icon_images.values():
|
||||||
|
icon_image.close()
|
||||||
|
|
||||||
def _shorten(self, text: str, by_len: int, min_len: int) -> str:
|
def _shorten(self, text: str, by_len: int, min_len: int) -> str:
|
||||||
if (text_len := len(text)) <= min_len + 3 or by_len <= 0:
|
if (text_len := len(text)) <= min_len + 3 or by_len <= 0:
|
||||||
@@ -1096,7 +1104,9 @@ class TrayIcon:
|
|||||||
pystray.Menu.SEPARATOR,
|
pystray.Menu.SEPARATOR,
|
||||||
pystray.MenuItem(_("gui", "tray", "quit"), bridge(self.quit)),
|
pystray.MenuItem(_("gui", "tray", "quit"), bridge(self.quit)),
|
||||||
)
|
)
|
||||||
self.icon = pystray.Icon("twitch_miner", self.icon_image, self.get_title(drop), menu)
|
self.icon = pystray.Icon(
|
||||||
|
"twitch_miner", self._icon_images[self._icon_state], self.get_title(drop), menu
|
||||||
|
)
|
||||||
# self.icon.run_detached()
|
# self.icon.run_detached()
|
||||||
loop.run_in_executor(None, self.icon.run)
|
loop.run_in_executor(None, self.icon.run)
|
||||||
|
|
||||||
@@ -1142,6 +1152,13 @@ class TrayIcon:
|
|||||||
if self.icon is not None:
|
if self.icon is not None:
|
||||||
self.icon.title = self.get_title(drop)
|
self.icon.title = self.get_title(drop)
|
||||||
|
|
||||||
|
def change_icon(self, state: str):
|
||||||
|
if state not in self._icon_images:
|
||||||
|
raise ValueError("Invalid icon state")
|
||||||
|
self._icon_state = state
|
||||||
|
if self.icon is not None:
|
||||||
|
self.icon.icon = self._icon_images[state]
|
||||||
|
|
||||||
|
|
||||||
class Notebook:
|
class Notebook:
|
||||||
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
def __init__(self, manager: GUIManager, master: ttk.Widget):
|
||||||
@@ -1937,7 +1954,7 @@ class GUIManager:
|
|||||||
# withdraw immediately to prevent the window from flashing
|
# withdraw immediately to prevent the window from flashing
|
||||||
self._root.withdraw()
|
self._root.withdraw()
|
||||||
# root.resizable(False, True)
|
# root.resizable(False, True)
|
||||||
set_root_icon(root, resource_path("pickaxe.ico"))
|
set_root_icon(root, resource_path("icons/pickaxe.ico"))
|
||||||
root.title(WINDOW_TITLE) # window title
|
root.title(WINDOW_TITLE) # window title
|
||||||
root.bind_all("<KeyPress-Escape>", self.unfocus) # pressing ESC unfocuses selection
|
root.bind_all("<KeyPress-Escape>", self.unfocus) # pressing ESC unfocuses selection
|
||||||
# Image cache for displaying images
|
# Image cache for displaying images
|
||||||
|
|||||||
BIN
icons/active.ico
Normal file
BIN
icons/active.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
icons/error.ico
Normal file
BIN
icons/error.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
icons/idle.ico
Normal file
BIN
icons/idle.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
BIN
icons/maint.ico
Normal file
BIN
icons/maint.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
3
main.py
3
main.py
@@ -87,7 +87,7 @@ if __name__ == "__main__":
|
|||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
root.overrideredirect(True)
|
root.overrideredirect(True)
|
||||||
root.withdraw()
|
root.withdraw()
|
||||||
set_root_icon(root, resource_path("pickaxe.ico"))
|
set_root_icon(root, resource_path("icons/pickaxe.ico"))
|
||||||
root.update()
|
root.update()
|
||||||
parser = Parser(
|
parser = Parser(
|
||||||
SELF_PATH.name,
|
SELF_PATH.name,
|
||||||
@@ -168,6 +168,7 @@ if __name__ == "__main__":
|
|||||||
await client.shutdown()
|
await client.shutdown()
|
||||||
if not client.gui.close_requested:
|
if not client.gui.close_requested:
|
||||||
# user didn't request the closure
|
# user didn't request the closure
|
||||||
|
client.gui.tray.change_icon("error")
|
||||||
client.print(_("status", "terminated"))
|
client.print(_("status", "terminated"))
|
||||||
client.gui.status.update(_("gui", "status", "terminated"))
|
client.gui.status.update(_("gui", "status", "terminated"))
|
||||||
# notify the user about the closure
|
# notify the user about the closure
|
||||||
|
|||||||
@@ -608,11 +608,13 @@ class Twitch:
|
|||||||
if self.settings.dump:
|
if self.settings.dump:
|
||||||
self.gui.close()
|
self.gui.close()
|
||||||
continue
|
continue
|
||||||
|
self.gui.tray.change_icon("idle")
|
||||||
self.gui.status.update(_("gui", "status", "idle"))
|
self.gui.status.update(_("gui", "status", "idle"))
|
||||||
self.stop_watching()
|
self.stop_watching()
|
||||||
# clear the flag and wait until it's set again
|
# clear the flag and wait until it's set again
|
||||||
self._state_change.clear()
|
self._state_change.clear()
|
||||||
elif self._state is State.INVENTORY_FETCH:
|
elif self._state is State.INVENTORY_FETCH:
|
||||||
|
self.gui.tray.change_icon("maint")
|
||||||
# ensure the websocket is running
|
# ensure the websocket is running
|
||||||
await self.websocket.start()
|
await self.websocket.start()
|
||||||
await self.fetch_inventory()
|
await self.fetch_inventory()
|
||||||
@@ -835,6 +837,7 @@ class Twitch:
|
|||||||
self.change_state(State.IDLE)
|
self.change_state(State.IDLE)
|
||||||
del new_watching, selected_channel, watching_channel
|
del new_watching, selected_channel, watching_channel
|
||||||
elif self._state is State.EXIT:
|
elif self._state is State.EXIT:
|
||||||
|
self.gui.tray.change_icon("pickaxe")
|
||||||
self.gui.status.update(_("gui", "status", "exiting"))
|
self.gui.status.update(_("gui", "status", "exiting"))
|
||||||
# we've been requested to exit the application
|
# we've been requested to exit the application
|
||||||
break
|
break
|
||||||
@@ -982,6 +985,7 @@ class Twitch:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def watch(self, channel: Channel, *, update_status: bool = True):
|
def watch(self, channel: Channel, *, update_status: bool = True):
|
||||||
|
self.gui.tray.change_icon("active")
|
||||||
self.gui.channels.set_watching(channel)
|
self.gui.channels.set_watching(channel)
|
||||||
self.watching_channel.set(channel)
|
self.watching_channel.set(channel)
|
||||||
if update_status:
|
if update_status:
|
||||||
|
|||||||
Reference in New Issue
Block a user