Add match, turn and history views with a dark theme

- Top bar switching between Deck, Match, Turn and History views
- Match view: life, player counters, commanders, emblems, zone counts,
  turn timer and timeouts, land drops used/allowed, play/draw and Bo3
  draw counts, with the deck view below
- Turn view: per-player and total stats per turn with history, plus
  whole-game totals and land drops
- History: every finished game (colours, winner, who was on the play);
  click to reopen it in the other views; imports Player-prev.log
- Customize menu to show/hide items and an Arrange dialog to reorder
  deck sections and turn stats, remembered across sessions
- Static dark theme with mint accent, rounded corners, larger text
- Commanders tracked separately from emblems, with location and tax
- Quieter, non-sticky warning for cards lost to MTGA log gaps
- Correct Scryfall images for tokens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aeth3randClaude Opus 5.5 committed 2026-10-16 11:18:44 -04:00
1 parent 7dc2ce0da8
commit 5b3c8c8e63
22 files changed
+2599 -193

No files matched your search

+14 -4
View File
@@ -47,7 +47,9 @@ class ImageCache(QObject):
self.evict()
def path_for(self, grp_id: int) -> Path:
return self.dir / f"{grp_id}.jpg"
# Tokens get their own names: early versions fetched wrong images for them.
prefix = "t" if self.cards.get(grp_id).is_token else ""
return self.dir / f"{prefix}{grp_id}.jpg"
def pixmap(self, grp_id: int) -> QPixmap | None:
"""Return the image if cached; otherwise start a download and return None."""
@@ -89,8 +91,16 @@ class ImageCache(QObject):
def _candidate_urls(self, grp_id: int) -> list[str]:
q = "format=image&version=normal"
urls = [f"{SCRYFALL}/cards/arena/{grp_id}?{q}"]
info = self.cards.get(grp_id)
if info.is_token:
# MTGA token ids/numbers don't line up with Scryfall's cards; tokens live in "t<set>".
name = urllib.parse.quote(info.name)
urls = []
if info.set_code:
urls.append(f"{SCRYFALL}/cards/named?exact={name}&set=t{urllib.parse.quote(info.set_code.lower())}&{q}")
urls.append(f"{SCRYFALL}/cards/search?q={urllib.parse.quote(f'!"{info.name}" is:token')}&format=image&version=normal")
return urls
urls = [f"{SCRYFALL}/cards/arena/{grp_id}?{q}"]
if info.set_code and info.collector_number:
set_code = urllib.parse.quote(info.set_code.lower())
cn = urllib.parse.quote(info.collector_number)
@@ -153,8 +163,8 @@ class ImageCache(QObject):
except OSError:
continue
total -= size
if p.stem.isdigit():
self._pixmaps.pop(int(p.stem), None)
if p.stem.lstrip("t").isdigit():
self._pixmaps.pop(int(p.stem.lstrip("t")), None)
if total <= self.max_bytes * 0.9:
break
self.size_changed.emit(total)