Remove Game Selector;

Implement multi-game mining based on the priority list
This commit is contained in:
DevilXD committed 2022-03-30 14:15:26 +02:00
1 parent b94bc1e02e
commit 5af4ee79c3
9 files changed
+164 -261

No files matched your search

+29 -137
View File
@@ -6,7 +6,7 @@ import tkinter as tk
from math import log10, ceil
from functools import partial
from tkinter.font import Font
from collections import abc, namedtuple, OrderedDict
from collections import abc, namedtuple
from tkinter import Tk, ttk, StringVar, DoubleVar, IntVar
from typing import Any, TypedDict, NoReturn, TYPE_CHECKING
@@ -341,121 +341,6 @@ class LoginForm:
self._var.set(f"{status}\n{user_str}")
class GameSelector:
def __init__(self, manager: GUIManager, master: ttk.Widget):
self._manager = manager
self._settings = manager._twitch.settings
self._var = StringVar(master)
frame = ttk.LabelFrame(master, text="Game Selector", padding=(4, 0, 4, 4))
frame.grid(column=1, row=1, sticky="nsew", padx=2)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
self._list = PaddedListbox(
frame,
height=5,
padding=(1, 0),
activestyle="none",
selectmode="single",
highlightthickness=0,
exportselection=False,
)
self._list.grid(column=0, row=0, sticky="nsew")
self._selection: str | None = None
self._games: OrderedDict[str, tuple[int, Game]] = OrderedDict()
self._excluded_indices: set[int] = set()
self._list.bind("<<ListboxSelect>>", self._on_select)
def set_games(self, games: abc.Iterable[Game]):
self._games.clear()
self._games.update((str(game), (i, game)) for i, game in enumerate(games))
self._list.delete(0, "end")
self._list.insert("end", *self._games.keys())
self._list.config(width=0) # autoadjust listbox width
# gray-out excluded games and relink the selection
self._excluded_indices.clear()
self._exclude_sync()
selected_index: int | None = None
if self._selection is not None and self._selection in self._games:
selected_index = self._games[self._selection][0]
self._list.selection_clear(0, "end")
if selected_index is not None:
# reselect the currently selected item
self._list.selection_set(selected_index)
elif self._selection is not None:
# the game we've had selected isn't there anymore - clear selection
self._selection = None
def _exclude_update(self, game_name: str, *, index: int | None = None) -> None:
# called from the settings tab or _update below
if index is None:
try:
index = self._games[game_name][0]
except KeyError:
# user added an excluded game that isn't on the list - just return
return
if (
game_name in self._settings.exclude
or self._settings.priority_only
and game_name not in self._settings.priority
):
if index not in self._excluded_indices:
self._excluded_indices.add(index)
self._list.itemconfig(index, foreground="gray60")
elif index in self._excluded_indices:
self._excluded_indices.discard(index)
self._list.itemconfig(index, foreground='')
def _exclude_sync(self) -> None:
# called when priority_only state changes
for game_name, (i, _) in self._games.items():
self._exclude_update(game_name, index=i)
def _on_select(self, event: tk.Event[PaddedListbox]) -> None:
current: tuple[int, ...] = self._list.curselection()
if not current:
# can happen when the user clicks on an empty list
return
idx: int = current[0]
if idx in self._excluded_indices:
# 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:
self._selection = None
return
new_selection: str = self._list.get(idx)
if new_selection != self._selection:
self._selection = new_selection
self._manager._twitch.change_state(State.GAME_SELECT)
def get_next(self) -> Game | None:
if self._selection is not None:
return self._games[self._selection][1]
# select and return the first prioritized game from the list
self._list.selection_clear(0, "end")
for game_name in self._settings.priority:
if game_name in self._games:
# it's a prioritized game - easy choice
idx, game = self._games[game_name]
self._selection = game_name
self._list.selection_set(idx)
return game
# if priority_only is enabled, we end here
if self._settings.priority_only:
return None
# with priorities out of the way, select and return the first non-excluded game instead
for i, game_name in enumerate(self._list.get(0, "end")):
if i not in self._excluded_indices:
self._selection = game_name
self._list.selection_set(i)
return self._games[game_name][1]
return None
class _BaseVars(TypedDict):
progress: DoubleVar
percentage: StringVar
@@ -476,28 +361,28 @@ class _ProgressVars(TypedDict):
class CampaignProgress:
BAR_LENGTH = 240
BAR_LENGTH = 420
def __init__(self, manager: GUIManager, master: ttk.Widget):
self._manager = manager
self._vars: _ProgressVars = {
"campaign": {
"name": StringVar(master), # campaign name
"name": StringVar(master, "..."), # campaign name
"progress": DoubleVar(master), # controls the progress bar
"percentage": StringVar(master), # percentage display string
"remaining": StringVar(master), # time remaining string
"percentage": StringVar(master, "-%"), # percentage display string
"remaining": StringVar(master), # time remaining string, filled via _update_time
},
"drop": {
"rewards": StringVar(master), # drop rewards
"rewards": StringVar(master, "..."), # drop rewards
"progress": DoubleVar(master), # as above
"percentage": StringVar(master), # as above
"percentage": StringVar(master, "-%"), # as above
"remaining": StringVar(master), # as above
},
}
self._frame = frame = ttk.LabelFrame(
master, text="Campaign Progress", padding=(4, 0, 4, 4)
)
frame.grid(column=0, row=1, sticky="nsew", padx=2)
frame.grid(column=0, row=1, columnspan=2, sticky="nsew", padx=2)
frame.columnconfigure(0, weight=2)
frame.columnconfigure(1, weight=1)
ttk.Label(frame, text="Campaign:").grid(column=0, row=0, columnspan=2)
@@ -1100,8 +985,8 @@ class SettingsPanel:
AUTOSTART_NAME: str = "TwitchDropsMiner"
def __init__(self, manager: GUIManager, master: ttk.Widget):
self._twitch = manager._twitch
self._settings = manager._twitch.settings
self._game_selector = manager.games
self._vars: _SettingsVars = {
"tray": IntVar(master, self._settings.autostart_tray),
"autostart": IntVar(master, self._settings.autostart),
@@ -1199,7 +1084,16 @@ class SettingsPanel:
ttk.Button(
exclude_frame, text="❌", command=self.exclude_delete, width=2, style="Large.TButton"
).grid(column=0, row=2, columnspan=2, sticky="ew")
self.priority_only() # update exclude section state
# Reload button
reload_frame = ttk.Frame(center_frame)
reload_frame.grid(column=0, row=1, columnspan=3, pady=4)
ttk.Label(
reload_frame,
text="Most changes require a reload to take an immediate effect: "
).grid(column=0, row=0)
ttk.Button(
reload_frame, text="Reload", command=self._twitch.state_change(State.INVENTORY_FETCH)
).grid(column=1, row=0)
def clear_selection(self) -> None:
self._priority_list.selection_clear(0, "end")
@@ -1226,6 +1120,13 @@ class SettingsPanel:
self._exclude_entry.config(values=games_list)
self._priority_entry.config(values=games_list)
def priorities(self) -> dict[str, int]:
# NOTE: we shift the indexes so that 0 can be used as the default one
size = self._priority_list.size()
return {
game_name: i - size for i, game_name in enumerate(self._priority_list.get(0, "end"))
}
def priority_add(self) -> None:
game_name: str = self._priority_entry.get()
if not game_name:
@@ -1240,7 +1141,6 @@ class SettingsPanel:
self._priority_list.insert("end", game_name)
self._priority_list.see("end")
self._settings.priority.append(game_name)
self._game_selector._exclude_update(game_name)
else:
# already there, set the selection on it
self._priority_list.selection_set(existing_idx)
@@ -1272,15 +1172,11 @@ class SettingsPanel:
idx: int | None = self._priority_idx()
if idx is None:
return
# NOTE: get the item before it's removed from the list
item: str = self._priority_list.get(idx)
self._priority_list.delete(idx)
del self._settings.priority[idx]
self._game_selector._exclude_update(item)
def priority_only(self) -> None:
self._settings.priority_only = bool(self._vars["priority_only"].get())
self._game_selector._exclude_sync()
def exclude_add(self) -> None:
game_name: str = self._exclude_entry.get()
@@ -1288,9 +1184,9 @@ class SettingsPanel:
# prevent adding empty strings
return
self._exclude_entry.clear()
e = self._settings.exclude
if game_name not in e:
e.add(game_name)
exclude = self._settings.exclude
if game_name not in exclude:
exclude.add(game_name)
# insert it alphabetically
for i, item in enumerate(self._exclude_list.get(0, "end")):
if game_name < item:
@@ -1300,7 +1196,6 @@ class SettingsPanel:
else:
self._exclude_list.insert("end", game_name)
self._exclude_list.see("end")
self._game_selector._exclude_update(game_name)
else:
# it was already there, select it
for i, item in enumerate(self._exclude_list.get(0, "end")):
@@ -1322,7 +1217,6 @@ class SettingsPanel:
if item in self._settings.exclude:
self._settings.exclude.discard(item)
self._exclude_list.delete(idx)
self._game_selector._exclude_update(item)
class GUIManager:
@@ -1388,7 +1282,6 @@ class GUIManager:
self.websockets = WebsocketStatus(self, main_frame)
self.login = LoginForm(self, main_frame)
self.progress = CampaignProgress(self, main_frame)
self.games = GameSelector(self, main_frame)
self.output = ConsoleOutput(self, main_frame)
self.channels = ChannelList(self, main_frame)
# Inventory tab
@@ -1498,7 +1391,6 @@ class GUIManager:
self._cache.save()
def set_games(self, games: abc.Iterable[Game]) -> None:
self.games.set_games(games)
self.settings.set_games(games)
def display_drop(