Skip to content
Merged

Dev #25

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
4796606
ci: actually add windows builds to github release
truenicoco Feb 12, 2026
6293dfc
fix(hu): position inversion
truenicoco Feb 12, 2026
689fed5
fix: do not show board when action is closed preflop
truenicoco Feb 15, 2026
d2cbcee
feat: new look for tables
truenicoco Feb 15, 2026
501ffd0
feat: new look for card backs
truenicoco Feb 15, 2026
fb614da
imprv: visibility of stack size
truenicoco Feb 15, 2026
09be2cc
imprv: new button look
truenicoco Feb 15, 2026
a12321a
imprv: animate stacks to center of pot text item
truenicoco Feb 15, 2026
1976d40
fix: hide bet item when animating directly from stack to pot
truenicoco Feb 15, 2026
519b880
imprv: make active bets yellow
truenicoco Feb 17, 2026
5457f29
refactor: use QDialog for full screen
truenicoco Feb 17, 2026
f9ff613
fix: hide turn and river when everybody folds before river
truenicoco Feb 22, 2026
88d7214
imprv: do not automatically enter full screen on replay
truenicoco Feb 22, 2026
052a77b
chore: update .gitignore
truenicoco Mar 3, 2026
9aac8e3
fix: save background color for next run
truenicoco Mar 3, 2026
1742125
imprv: rename UTG→LJ 6-handed
truenicoco Mar 3, 2026
ebf7a3c
imprv: show known hands as soon as action closes
truenicoco Mar 4, 2026
f59e897
fix: handle bogus value for bg/webcam in config file
truenicoco Mar 4, 2026
2c3cfe4
imprv: indent HH files for readability/editability
truenicoco Mar 4, 2026
5596b02
style(ruff): apply autofixes
truenicoco Mar 8, 2026
8a5e0f1
test: add some HHs for manual testing
truenicoco Mar 8, 2026
08b8a12
fix: number of actions to showdown
truenicoco Mar 8, 2026
6845e6b
fix: allow locale decimals separator in stack dialog
truenicoco Mar 9, 2026
c465b1f
fix: sounds when revealing cards and on showdown
truenicoco Mar 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:
- uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_PREFIX }}-windows
path: pyinstaller/dist
path: pyinstaller/dist/windows/hh-creator

release:
needs: [build-linux, build-windows]
Expand All @@ -69,8 +69,12 @@ jobs:
- name: Check what artifacts have been downloaded
run: |
ls -la
ls -la ${{ env.ARTIFACT_PREFIX }}*
ls -la ${{ env.ARTIFACT_PREFIX }}
- uses: vimtor/action-zip@v1.2
with:
files: ${{ env.ARTIFACT_PREFIX }}-windows
dest: ${{ env.ARTIFACT_PREFIX }}-windows.zip
- name: Github release
uses: softprops/action-gh-release@v2
with:
files: ${{ env.ARTIFACT_PREFIX }}*
files: ${{ env.ARTIFACT_PREFIX }}-windows.zip
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ artifacts/
.venv/
requirements.txt
.python-version
junk
.gram
.zed
2 changes: 1 addition & 1 deletion hh_creator/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from hh_creator.util import init_sounds


def main():
def main() -> None:
parser = ArgumentParser()

parser.add_argument(
Expand Down
27 changes: 19 additions & 8 deletions hh_creator/animations.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ class Animations:
animations = []

@classmethod
def add_callback(cls, callback):
def add_callback(cls, callback) -> None:
if not cls.animations:
raise ValueError
cls.animations[-1].finished.connect(callback)

@classmethod
def add(cls, animation):
def add(cls, animation) -> None:
cls.animations.append(animation)

@classmethod
def start(cls):
def start(cls) -> None:
group = QtCore.QParallelAnimationGroup()
for i, a in enumerate(cls.animations):
if i == 0:
Expand All @@ -31,7 +31,7 @@ def start(cls):
group.start(group.DeleteWhenStopped)

@classmethod
def reset(cls):
def reset(cls) -> None:
cls.animations = []

@classmethod
Expand All @@ -42,17 +42,22 @@ def text(
duration: int,
scene,
content=None,
callbacks=tuple(),
callbacks=(),
target_font=False,
):
target_item_center: bool = False,
font_kwargs: dict | None = None,
) -> None:
# print(f"Animating {source} to {target}")
if content is None:
content = source.content

if font_kwargs is None:
font_kwargs = {}

if target_font:
font_kwargs = target.font_kwargs()
font_kwargs = target.font_kwargs() | font_kwargs
else:
font_kwargs = source.font_kwargs()
font_kwargs = source.font_kwargs() | font_kwargs

item_to_animate = TextItem(
hide_if_empty=source.hide_if_empty,
Expand All @@ -69,6 +74,12 @@ def text(
else:
target_pos = target.get_pos_if_content(content)

if target_item_center:
rect = target.boundingRect()
target_pos = QtCore.QPointF(
target_pos.x() + rect.width() / 2, target_pos.y()
)

animation = QtCore.QPropertyAnimation(scene)
animation.setTargetObject(item_to_animate)
animation.setPropertyName(b"pos")
Expand Down
75 changes: 47 additions & 28 deletions hh_creator/card.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import contextlib
import itertools
import logging
from dataclasses import dataclass
from functools import total_ordering
from pathlib import Path
from typing import TYPE_CHECKING, List, Union
from typing import TYPE_CHECKING

from deuces import Card as DeucesCard
from deuces import Evaluator
Expand Down Expand Up @@ -62,15 +63,15 @@ def one_letter_format(self):


class CardLook(QtWidgets.QGraphicsItemGroup):
instances = []
instances: list["CardLook"] = []

def __init__(
self,
scale_factor=config.config["look"].getfloat("player_card_scale"),
crop_bottom=False,
*a,
**kw,
):
) -> None:
root = Path("cards_cut") if crop_bottom else Path("cards")
super().__init__(*a, **kw)
self.scale_factor = scale_factor
Expand All @@ -85,60 +86,80 @@ def __init__(
self.addToGroup(k)
k.setVisible(False)
k.setScale(scale_factor)
self.back = Image.get(root / "back-red")
self.back.setScale(scale_factor)
self.crop_bottom = crop_bottom
variant = ("back-" + config.config["look"]["card-back"]) or "back-red"
self.back = Image.get(root / variant)
self._crop_back_pixmap()
self._apply_back_scale_factor()
self.scale_factor = scale_factor
self.addToGroup(self.back)
self.instances.append(self)

def _update_look(self):
def _update_look(self) -> None:
if self._rank is None or self._suit is None:
self.back.setVisible(True)
return
self.back.setVisible(False)
for (rank, suit), item in self.faces.items():
item.setVisible((rank, suit) == (self._rank, self._suit))

def _apply_back_scale_factor(self) -> None:
mult = 1 if isinstance(self.back, Qt.QGraphicsSvgItem) else 0.5
self.back.setScale(self.scale_factor * mult)

def _crop_back_pixmap(self) -> None:
if isinstance(self.back, Qt.QGraphicsPixmapItem) and self.crop_bottom:
pixmap = self.back.pixmap()
cropped = pixmap.copy(0, 0, pixmap.width(), int(0.62 * pixmap.height()))
self.back.setPixmap(cropped)

def boundingRect(self):
rect_f = super().boundingRect()
scaled = Qt.QRectF(*(x * self.scale_factor for x in rect_f.getCoords()))
return scaled

@classmethod
def change_back(cls, color):
def change_back(cls, color: str) -> None:
config.config["look"]["card-back"] = color
config.save_config()
for i in cls.instances:
visible = i.back.isVisible()
i.back.deleteLater()
pos = i.back.scenePos()
i.back = Image.get(Path("cards") / f"back-{color}")
i.back.setScale(i.scale_factor)
scene = i.back.scene()
if scene is not None:
scene.removeItem(i.back)
if isinstance(i, Qt.QGraphicsSvgItem):
i.back.deleteLater()
i.back = Image.get(
Path("cards" + ("_cut" if i.crop_bottom else "")) / f"back-{color}"
)
i._crop_back_pixmap()
i._apply_back_scale_factor()
i.back.setPos(pos)
i.back.setVisible(visible)
i.addToGroup(i.back)

def hide_face(self):
def hide_face(self) -> None:
self.back.setVisible(True)

def discover(self):
def discover(self) -> None:
if self._rank is not None and self._suit is not None:
self.back.setVisible(False)


class CardItem(CardLook):
def __init__(self, *a, **kw):
def __init__(self, *a, **kw) -> None:
super().__init__(*a, **kw)
self._suit: Union[Suit, None] = None
self._rank: Union[Rank, None] = None
self._suit: Suit | None = None
self._rank: Rank | None = None
self._update_look()

@property
def suit(self):
return self._suit

@suit.setter
def suit(self, suit):
def suit(self, suit) -> None:
self._suit = suit
self._update_look()

Expand All @@ -147,23 +168,21 @@ def rank(self):
return self._rank

@rank.setter
def rank(self, rank):
def rank(self, rank) -> None:
self._rank = rank
self._update_look()

def wheelEvent(self, event: QtWidgets.QGraphicsSceneWheelEvent):
def wheelEvent(self, event: QtWidgets.QGraphicsSceneWheelEvent) -> None:
log.debug("Wheel on card")
mw = self.scene().parent()
if mw.state != mw.State.ACTIONS:
return
if self.rank is not None:
attr = "next" if event.delta() > 0 else "prev"
try:
with contextlib.suppress(ValueError):
self.rank = getattr(self.rank, attr)()
except ValueError:
pass

def mousePressEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent):
def mousePressEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent) -> None:
mw = self.scene().parent()
if mw.state != mw.State.ACTIONS:
return
Expand All @@ -183,7 +202,7 @@ def mousePressEvent(self, event: QtWidgets.QGraphicsSceneMouseEvent):
else:
self.suit = None

def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent):
def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent) -> None:
mw = self.scene().parent()
if mw.state != mw.State.ACTIONS:
return
Expand All @@ -207,7 +226,7 @@ def contextMenuEvent(self, event: QtWidgets.QGraphicsSceneContextMenuEvent):
menu.addActions(actions)
menu.exec(event.screenPos())

def deuces_format(self):
def deuces_format(self) -> str | None:
try:
return f"{self.rank.one_letter_format()}{self.suit.one_letter_format()}"
except AttributeError:
Expand All @@ -217,7 +236,7 @@ def to_deuces(self):
return DeucesCard.new(self.deuces_format())

@classmethod
def reset(cls):
def reset(cls) -> None:
for c in cls.instances:
c.rank = None
c.suit = None
Expand All @@ -226,8 +245,8 @@ def reset(cls):
@total_ordering
@dataclass
class Hand:
cards: List[CardItem]
board: List[CardItem]
cards: list[CardItem]
board: list[CardItem]

def __gt__(self, other: "Hand"):
return self.deuces_score() < other.deuces_score()
Expand All @@ -241,7 +260,7 @@ def deuces_score(self):
)


def get_winners(player_items: List["PlayerItemGroup"], board: List["CardItem"]):
def get_winners(player_items: list["PlayerItemGroup"], board: list["CardItem"]):
scores = []
for player in player_items:
if player.n_cards == 2:
Expand Down
4 changes: 2 additions & 2 deletions hh_creator/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from pathlib import Path


def save_config(window_geometry=None, window_state=None):
def save_config(window_geometry=None, window_state=None) -> None:
log.info(f"Writing config to {config_filename}")
with config_filename.open("w", encoding="utf-8") as fp:
config.write(fp)
Expand All @@ -16,7 +16,7 @@ def save_config(window_geometry=None, window_state=None):
fp.write(bytes(window_state))


def restore_defaults():
def restore_defaults() -> None:
config = ConfigParser(interpolation=ExtendedInterpolation())
config.read(RESOURCE_PATH / "default.ini", encoding="utf-8")
with config_filename.open("w", encoding="utf-8") as fp:
Expand Down
Loading