Add MTGA Assistant: native Linux deck and game tracker
PySide6 app that follows MTG Arena's Player.log under Steam/Proton and shows my remaining library with draw odds, my zones, and everything revealed about the opponent (board, graveyard, exile, known hand/library, seen this match). - Auto-detects the Steam library, Proton prefix log and MTGA install - Card names/costs/types from MTGA's local SQLite card database - Tracks card identity across zone changes, shuffles and Bo3 games - Scryfall card images with a lazy, size-capped LRU disk cache - Live log following with restart detection, plus --replay mode - Flags log gaps where MTGA omits oversized game-state messages Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
734336168c
commit
7dc2ce0da8
21 files changed
+2736
No files matched your search
@@ -0,0 +1,80 @@
|
||||
"""Large image of the hovered card, with a text fallback while it downloads."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import QSize, Qt
|
||||
from PySide6.QtGui import QPixmap, QResizeEvent
|
||||
from PySide6.QtWidgets import QLabel, QSizePolicy, QVBoxLayout, QWidget
|
||||
|
||||
from ..carddb import CardDB
|
||||
from ..images import ImageCache
|
||||
|
||||
|
||||
class CardPreview(QWidget):
|
||||
def __init__(self, cards: CardDB, images: ImageCache, parent=None):
|
||||
super().__init__(parent)
|
||||
self.cards = cards
|
||||
self.images = images
|
||||
self.grp_id: int | None = None
|
||||
self._pixmap: QPixmap | None = None
|
||||
|
||||
self.image = QLabel()
|
||||
self.image.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.image.setSizePolicy(QSizePolicy.Policy.Ignored, QSizePolicy.Policy.Ignored)
|
||||
self.image.setMinimumSize(200, 280)
|
||||
self.caption = QLabel()
|
||||
self.caption.setWordWrap(True)
|
||||
self.caption.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
lay = QVBoxLayout(self)
|
||||
lay.setContentsMargins(4, 4, 4, 4)
|
||||
lay.addWidget(self.image, 1)
|
||||
lay.addWidget(self.caption)
|
||||
|
||||
images.image_ready.connect(self._on_image_ready)
|
||||
self._show_placeholder("Hover a card to preview it")
|
||||
|
||||
def show_card(self, grp_id: int) -> None:
|
||||
if grp_id == self.grp_id:
|
||||
return
|
||||
self.grp_id = grp_id
|
||||
info = self.cards.get(grp_id)
|
||||
details = [info.type_line]
|
||||
if info.power or info.toughness:
|
||||
details.append(f"{info.power}/{info.toughness}")
|
||||
if info.set_code:
|
||||
details.append(f"{info.set_code} #{info.collector_number}")
|
||||
self.caption.setText(f"<b>{info.name}</b><br>{' · '.join(d for d in details if d)}")
|
||||
pm = self.images.pixmap(grp_id)
|
||||
if pm is not None:
|
||||
self._pixmap = pm
|
||||
self._rescale()
|
||||
elif self.images.is_failed(grp_id):
|
||||
self._show_placeholder("No image available")
|
||||
else:
|
||||
self._show_placeholder("Loading image…")
|
||||
|
||||
def _show_placeholder(self, text: str) -> None:
|
||||
self._pixmap = None
|
||||
self.image.setPixmap(QPixmap())
|
||||
self.image.setText(text)
|
||||
|
||||
def _on_image_ready(self, grp_id: int) -> None:
|
||||
if grp_id == self.grp_id:
|
||||
self.grp_id = None
|
||||
self.show_card(grp_id)
|
||||
|
||||
def _rescale(self) -> None:
|
||||
if self._pixmap is None:
|
||||
return
|
||||
self.image.setPixmap(
|
||||
self._pixmap.scaled(self.image.size(), Qt.AspectRatioMode.KeepAspectRatio,
|
||||
Qt.TransformationMode.SmoothTransformation)
|
||||
)
|
||||
|
||||
def resizeEvent(self, event: QResizeEvent) -> None:
|
||||
super().resizeEvent(event)
|
||||
self._rescale()
|
||||
|
||||
def sizeHint(self) -> QSize:
|
||||
return QSize(320, 600)
|
||||
Reference in new issue
Block a user