Implement exclude argument
This commit is contained in:
1 parent
fdc9dabd49
commit
83b8de11a1
4 files changed
+81
-32
No files matched your search
Vendored
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
"type": "python",
|
"type": "python",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "main.py",
|
"program": "main.py",
|
||||||
"args": ["--wrong"],
|
"args": ["--excludes", "1", "2", "3"],
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": false
|
"justMyCode": false
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -262,6 +262,7 @@ class GameSelector:
|
|||||||
self._list.pack(fill="both", expand=True)
|
self._list.pack(fill="both", expand=True)
|
||||||
self._selection: Optional[str] = self._manager._twitch.options.game
|
self._selection: Optional[str] = self._manager._twitch.options.game
|
||||||
self._games: OrderedDict[str, Game] = OrderedDict()
|
self._games: OrderedDict[str, Game] = OrderedDict()
|
||||||
|
self._excluded: Set[int] = set()
|
||||||
self._list.bind("<<ListboxSelect>>", self._on_select)
|
self._list.bind("<<ListboxSelect>>", self._on_select)
|
||||||
|
|
||||||
def set_games(self, games: Iterable[Game]):
|
def set_games(self, games: Iterable[Game]):
|
||||||
@@ -270,30 +271,42 @@ class GameSelector:
|
|||||||
self._list.delete(0, "end")
|
self._list.delete(0, "end")
|
||||||
self._list.insert("end", *self._games.keys())
|
self._list.insert("end", *self._games.keys())
|
||||||
self._list.config(width=0) # autoadjust listbox width
|
self._list.config(width=0) # autoadjust listbox width
|
||||||
if self._selection is not None:
|
# process excluded games and relink the selection
|
||||||
selected_index: Optional[int] = next(
|
self._excluded.clear()
|
||||||
(
|
selected_index: Optional[int] = None
|
||||||
i
|
exclude = self._manager._twitch.options.exclude
|
||||||
for i, str_game in enumerate(self._games.keys())
|
for i, game_name in enumerate(self._list.get(0, "end")):
|
||||||
if str_game == self._selection
|
if game_name in exclude:
|
||||||
),
|
self._excluded.add(i)
|
||||||
None,
|
self._list.itemconfig(i, foreground="gray60")
|
||||||
)
|
elif game_name == self._selection:
|
||||||
|
selected_index = i
|
||||||
|
self._list.selection_clear(0, "end")
|
||||||
if selected_index is not None:
|
if selected_index is not None:
|
||||||
# reselect the currently selected item
|
# reselect the currently selected item
|
||||||
self._list.selection_clear(0, "end")
|
|
||||||
self._list.selection_set(selected_index)
|
self._list.selection_set(selected_index)
|
||||||
else:
|
elif self._selection is not None:
|
||||||
# the game we've had selected isn't there anymore - clear selection
|
# the game we've had selected isn't there anymore - clear selection
|
||||||
self._selection = None
|
self._selection = None
|
||||||
|
|
||||||
def _on_select(self, event):
|
def _on_select(self, event):
|
||||||
current = self._list.curselection()
|
current: Tuple[int, ...] = self._list.curselection()
|
||||||
if not current:
|
if not current:
|
||||||
# can happen when the user clicks on an empty list
|
# can happen when the user clicks on an empty list
|
||||||
new_selection = None
|
return
|
||||||
|
idx: int = current[0]
|
||||||
|
if idx in self._excluded:
|
||||||
|
# user clicked on an excluded game - reselect the previous one if possible
|
||||||
|
self._list.selection_clear(0, "end")
|
||||||
|
if self._selection is not None:
|
||||||
|
for i, game_name in enumerate(self._list.get(0, "end")):
|
||||||
|
if game_name == self._selection:
|
||||||
|
self._list.selection_set(i)
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
new_selection = self._list.get(current[0])
|
self._selection = None
|
||||||
|
return
|
||||||
|
new_selection: str = self._list.get(idx)
|
||||||
if new_selection != self._selection:
|
if new_selection != self._selection:
|
||||||
self._selection = new_selection
|
self._selection = new_selection
|
||||||
self._manager._twitch.change_state(State.GAME_SELECT)
|
self._manager._twitch.change_state(State.GAME_SELECT)
|
||||||
@@ -303,15 +316,15 @@ class GameSelector:
|
|||||||
return None
|
return None
|
||||||
return self._games[self._selection]
|
return self._games[self._selection]
|
||||||
|
|
||||||
def set_first(self) -> Game:
|
def set_first(self) -> Optional[Game]:
|
||||||
# select and return the first game from the list
|
# select and return the first non-excluded game from the list
|
||||||
self._list.selection_clear(0, "end")
|
self._list.selection_clear(0, "end")
|
||||||
self._list.selection_set(0)
|
for i, game_name in enumerate(self._list.get(0, "end")):
|
||||||
for game_name, first_game in self._games.items():
|
if i not in self._excluded:
|
||||||
# just one iteration to get, set and return the first game
|
|
||||||
self._selection = game_name
|
self._selection = game_name
|
||||||
return first_game
|
self._list.selection_set(i)
|
||||||
raise RuntimeError("No games to select from")
|
return self._games[game_name]
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
class _BaseVars(TypedDict):
|
class _BaseVars(TypedDict):
|
||||||
@@ -1028,7 +1041,9 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
async def main(exit_event: asyncio.Event):
|
async def main(exit_event: asyncio.Event):
|
||||||
# Initialize GUI debug
|
# Initialize GUI debug
|
||||||
mock = SimpleNamespace(options=SimpleNamespace(game=None, tray=False), channels={})
|
mock = SimpleNamespace(
|
||||||
|
options=SimpleNamespace(game=None, tray=False, exclude={"Lit Game"}), channels={}
|
||||||
|
)
|
||||||
mock.change_state = lambda state: mock.gui.print(f"State change: {state.value}")
|
mock.change_state = lambda state: mock.gui.print(f"State change: {state.value}")
|
||||||
mock.state_change = lambda state: partial(mock.change_state, state)
|
mock.state_change = lambda state: partial(mock.change_state, state)
|
||||||
gui = GUIManager(mock) # type: ignore
|
gui = GUIManager(mock) # type: ignore
|
||||||
@@ -1041,19 +1056,21 @@ if __name__ == "__main__":
|
|||||||
gui.login.update("Login required", None)
|
gui.login.update("Login required", None)
|
||||||
# Game selector
|
# Game selector
|
||||||
gui.games.set_games([
|
gui.games.set_games([
|
||||||
|
create_game(420690, "Lit Game"),
|
||||||
create_game(123456, "Best Game"),
|
create_game(123456, "Best Game"),
|
||||||
# create_game(654321, "My Game Very Long Name"),
|
create_game(654321, "My Game Very Long Name"),
|
||||||
])
|
])
|
||||||
# Channel list
|
# Channel list
|
||||||
gui.channels.display(
|
gui.channels.display(
|
||||||
create_channel(
|
create_channel(
|
||||||
name="Thomus", status=0, game=None, drops=False, viewers=0, points=0, priority=True
|
name="Thomus", status=0, game=None, drops=False, viewers=0, points=0, priority=True
|
||||||
)
|
),
|
||||||
|
add=True,
|
||||||
)
|
)
|
||||||
channel = create_channel(
|
channel = create_channel(
|
||||||
name="Traitus", status=1, game=None, drops=False, viewers=0, points=0, priority=True
|
name="Traitus", status=1, game=None, drops=False, viewers=0, points=0, priority=True
|
||||||
)
|
)
|
||||||
gui.channels.display(channel)
|
gui.channels.display(channel, add=True,)
|
||||||
gui.channels.set_watching(channel)
|
gui.channels.set_watching(channel)
|
||||||
gui.channels.display(
|
gui.channels.display(
|
||||||
create_channel(
|
create_channel(
|
||||||
@@ -1064,7 +1081,8 @@ if __name__ == "__main__":
|
|||||||
viewers=42,
|
viewers=42,
|
||||||
points=1234567,
|
points=1234567,
|
||||||
priority=False,
|
priority=False,
|
||||||
)
|
),
|
||||||
|
add=True,
|
||||||
)
|
)
|
||||||
gui.channels.display(
|
gui.channels.display(
|
||||||
create_channel(
|
create_channel(
|
||||||
@@ -1075,7 +1093,8 @@ if __name__ == "__main__":
|
|||||||
viewers=69,
|
viewers=69,
|
||||||
points=1234567,
|
points=1234567,
|
||||||
priority=False,
|
priority=False,
|
||||||
)
|
),
|
||||||
|
add=True,
|
||||||
)
|
)
|
||||||
gui._root.update()
|
gui._root.update()
|
||||||
gui.channels.get_selection()
|
gui.channels.get_selection()
|
||||||
|
|||||||
@@ -9,10 +9,12 @@ import logging
|
|||||||
import argparse
|
import argparse
|
||||||
import traceback
|
import traceback
|
||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
|
from copy import copy
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tkinter import messagebox
|
from tkinter import messagebox
|
||||||
from typing import Optional, NoReturn
|
from typing import Optional, Union, Set, NoReturn, Generic
|
||||||
|
|
||||||
|
from utils import _T
|
||||||
from twitch import Twitch
|
from twitch import Twitch
|
||||||
from version import __version__
|
from version import __version__
|
||||||
from exceptions import CaptchaRequired
|
from exceptions import CaptchaRequired
|
||||||
@@ -41,12 +43,40 @@ class Parser(argparse.ArgumentParser):
|
|||||||
messagebox.showerror("Argument Parser Error", self._message.getvalue())
|
messagebox.showerror("Argument Parser Error", self._message.getvalue())
|
||||||
|
|
||||||
|
|
||||||
|
class SetCollectAction(argparse.Action, Generic[_T]):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
option_strings,
|
||||||
|
dest,
|
||||||
|
*,
|
||||||
|
nargs: Optional[Union[int, str]] = None,
|
||||||
|
const: Optional[_T] = None,
|
||||||
|
default: Optional[Set[_T]] = None,
|
||||||
|
**kwargs,
|
||||||
|
) -> None:
|
||||||
|
if nargs is not None and nargs in ('?', '*') or isinstance(nargs, int) and nargs <= 0:
|
||||||
|
raise ValueError("'nargs' has to be '+' or an integer greater than zero")
|
||||||
|
if default is None:
|
||||||
|
default = set()
|
||||||
|
elif not isinstance(default, set):
|
||||||
|
raise TypeError("'default' has to be of 'set' type")
|
||||||
|
super().__init__(option_strings, dest, nargs, const, default, **kwargs)
|
||||||
|
|
||||||
|
def __call__(self, parser, namespace, values, option_string=None):
|
||||||
|
items: Set[_T] = getattr(namespace, self.dest, self.default)
|
||||||
|
items = copy(items)
|
||||||
|
for value in values:
|
||||||
|
items.add(value)
|
||||||
|
setattr(namespace, self.dest, items)
|
||||||
|
|
||||||
|
|
||||||
class ParsedArgs(argparse.Namespace):
|
class ParsedArgs(argparse.Namespace):
|
||||||
_verbose: int
|
_verbose: int
|
||||||
_debug_ws: bool
|
_debug_ws: bool
|
||||||
_debug_gql: bool
|
_debug_gql: bool
|
||||||
log: bool
|
log: bool
|
||||||
tray: bool
|
tray: bool
|
||||||
|
exclude: Set[str]
|
||||||
no_run_check: bool
|
no_run_check: bool
|
||||||
game: Optional[str]
|
game: Optional[str]
|
||||||
|
|
||||||
@@ -90,9 +120,10 @@ parser = Parser(
|
|||||||
)
|
)
|
||||||
parser.add_argument("--version", action="version", version=f"v{__version__}")
|
parser.add_argument("--version", action="version", version=f"v{__version__}")
|
||||||
parser.add_argument("-v", dest="_verbose", action="count", default=0)
|
parser.add_argument("-v", dest="_verbose", action="count", default=0)
|
||||||
parser.add_argument("-g", "--game", default=None)
|
|
||||||
parser.add_argument("--tray", action="store_true")
|
parser.add_argument("--tray", action="store_true")
|
||||||
parser.add_argument("--log", action="store_true")
|
parser.add_argument("--log", action="store_true")
|
||||||
|
parser.add_argument("-g", "--game", default=None)
|
||||||
|
parser.add_argument("--exclude", action=SetCollectAction, nargs='+', metavar="GAME")
|
||||||
# undocumented debug args
|
# undocumented debug args
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--no-run-check", dest="no_run_check", action="store_true", help=argparse.SUPPRESS
|
"--no-run-check", dest="no_run_check", action="store_true", help=argparse.SUPPRESS
|
||||||
|
|||||||
@@ -180,7 +180,6 @@ class Twitch:
|
|||||||
WebsocketTopic("User", "CommunityPoints", self._user_id, self.process_points),
|
WebsocketTopic("User", "CommunityPoints", self._user_id, self.process_points),
|
||||||
])
|
])
|
||||||
first_select: bool = True
|
first_select: bool = True
|
||||||
games: List[Game] = []
|
|
||||||
full_cleanup: bool = False
|
full_cleanup: bool = False
|
||||||
channels: Final[OrderedDict[int, Channel]] = self.channels
|
channels: Final[OrderedDict[int, Channel]] = self.channels
|
||||||
self.change_state(State.INVENTORY_FETCH)
|
self.change_state(State.INVENTORY_FETCH)
|
||||||
@@ -193,7 +192,7 @@ class Twitch:
|
|||||||
self.change_state(State.GAMES_UPDATE)
|
self.change_state(State.GAMES_UPDATE)
|
||||||
elif self._state is State.GAMES_UPDATE:
|
elif self._state is State.GAMES_UPDATE:
|
||||||
# Figure out which games to watch, and claim the drops we can
|
# Figure out which games to watch, and claim the drops we can
|
||||||
games.clear()
|
games: List[Game] = []
|
||||||
for game, campaigns in self.inventory.items():
|
for game, campaigns in self.inventory.items():
|
||||||
add_game = False
|
add_game = False
|
||||||
for campaign in campaigns:
|
for campaign in campaigns:
|
||||||
|
|||||||
Reference in new issue
Block a user