【3日目】Fletでソリティアサンプルアプリ作成 ~UI構築の実践編②~

この記事は広告を含みます。

kunio-ud-zatta.hatenablog.com

本日は、上記のリンクの続きになります。

では、さっそく、、、写経の続きを。

Solitaire setup

Create card deck

solitaire.pyにSuite とRankを追加

class Suite:
    def __init__(self, suite_name, suite_color):
        self.name = suite_name
        self.color = suite_color

class Rank:
    def __init__(self, card_name, card_value):
        self.name = card_name
        self.value = card_value

Cardクラスにsuiteとrankを追加

class Card(ft.GestureDetector):
    def __init__(self, solitaire, suite, rank):
        super().__init__()
        self.mouse_cursor=ft.MouseCursor.MOVE
        self.drag_interval=5
        self.on_pan_start=self.start_drag
        self.on_pan_update=self.drag
        self.on_pan_end=self.drop
        self.suite=suite
        self.rank=rank
        self.face_up=False
        self.top=None
        self.left=None
        self.solitaire = solitaire
        self.slot = None
        self.content=ft.Container(
            width=CARD_WIDTH,
            height=CARD_HEIGTH,
            border_radius = ft.border_radius.all(6),
            content=ft.Image(src="/images/card_back.png"))

card_back.pngGitHubからダウンロードしました。

github.com

数字とマーク設定ですね。

def create_card_deck(self):
    suites = [
        Suite("hearts", "RED"),
        Suite("diamonds", "RED"),
        Suite("clubs", "BLACK"),
        Suite("spades", "BLACK"),
    ]
    ranks = [
        Rank("Ace", 1),
        Rank("2", 2),
        Rank("3", 3),
        Rank("4", 4),
        Rank("5", 5),
        Rank("6", 6),
        Rank("7", 7),
        Rank("8", 8),
        Rank("9", 9),
        Rank("10", 10),
        Rank("Jack", 11),
        Rank("Queen", 12),
        Rank("King", 13),
    ]

    self.cards = []

    for suite in suites:
        for rank in ranks:
            self.cards.append(Card(solitaire=self, suite=suite, rank=rank))

スロットの作成

本家の公式ページから引用。

レイアウトはこのようです。

def create_slots(self):
    
    self.stock = Slot(top=0, left=0, border=ft.border.all(1))
    self.waste = Slot(top=0, left=100, border=None)

    self.foundations = []
    x = 300
    for i in range(4):
        self.foundations.append(Slot(top=0, left=x, border=ft.border.all(1, "outline")))
        x += 100

    self.tableau = []
    x = 0
    for i in range(7):
        self.tableau.append(Slot(top=150, left=x, border=None))
        x += 100

    self.controls.append(self.stock)
    self.controls.append(self.waste)
    self.controls.extend(self.foundations)
    self.controls.extend(self.tableau)
    self.update()

ディール

上部にrandomをimportを忘れずに。。。

def deal_cards(self):
    random.shuffle(self.cards)
    self.controls.extend(self.cards)
    
    # deal to tableau
    first_slot = 0
    remaining_cards = self.cards
    
    while first_slot < len(self.tableau):
        for slot in self.tableau[first_slot:]:
            top_card = remaining_cards[0]
            top_card.place(slot)
            remaining_cards.remove(top_card)
        first_slot +=1

    # place remaining cards to stock pile
    for card in remaining_cards:
        card.place(self.stock)
    
    self.update()

Slotクラスのコンストラクタでのborderを書き忘れてて、このあとエラーでつまずきました。。。

こんなかんじですね。

カードクラスのplaceメソッドを修正

def place(self, slot):
    """Place draggable pile to the slot"""
    if slot in self.solitaire.tableau:
        self.top = slot.top + len(slot.pile) * CARD_OFFSET
    else:
        self.top = slot.top
    self.left = slot.left

CARD_OFFSETが定数ですね。ここも公式とは違うのでひっかかりました。。。

次はドロップ機能

def drop(self, e: ft.DragEndEvent):
    for slot in self.solitaire.tableau:
        if (
            abs(self.top - (slot.top + len(slot.pile) * CARD_OFFSET)) < DROP_PROXIMITY
        and abs(self.left - slot.left) < DROP_PROXIMITY
        ):
            self.place(slot)
            self.solitaire.update()
            return

    for slot in self.solitaire.foundations:
        if (
            abs(self.top - slot.top) < DROP_PROXIMITY
        and abs(self.left - slot.left) < DROP_PROXIMITY
        ):
            self.place(slot)
            self.solitaire.update()
            return
        
    self.bounce_back()

カードクラスに

    def turn_face_up(self):
        """Reveals card"""
        self.face_up = True
        self.content.content.src = f"/images/{self.rank.name}_{self.suite.name}.svg"
        self.solitaire.update()

ソリティアクラスに以下を追加

        for slot in self.tableau:
            slot.get_top_card().turn_face_up()
        
        self.update()

実行!!!

今日はそれっぽくなったので、ここまでにします。