How can I add an input that is selected by a click

thanks a lot @jinnyzor u r the best!

1 Like

each card should have their own id? a div for each card group (Aces … AKs … etc?)

This inspired me and I started playing around:

import dash
from dash import html, Input, Output, callback_context
import numpy as np
import dash_bootstrap_components as dbc

# raw input
cards = ['2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As',
         '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah',
         '2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d', 'Td', 'Jd', 'Qd', 'Kd', 'Ad',
         '2c', '3c', '4c', '5c', '6c', '7c', '8c', '9c', 'Tc', 'Jc', 'Qc', 'Kc', 'Ac']

# convert to array
card_array = np.asarray(cards)

# reshape into desired layout
card_array = card_array.reshape((4, 13))

rows, _ = card_array.shape

content = []
for r in range(rows):
    row = card_array[r, :]
    row_content = []
    for c in row:
        row_content.append(
            dbc.Col(
                dbc.Card(
                    children=html.Button(c, id=c),
                )
            )
        )
    dbc_rows = dbc.Row(
        id=f'row{r}',
        children=row_content
    )
    content.append(dbc_rows)

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div(
    [
        dbc.Container(
            id='container',
            children=content,
            fluid=True
        ),
        html.Br(),
        html.Div(id='output')
    ]
)


@app.callback(
    Output('output', 'children'),
    [
        Input(card_name, 'n_clicks')
        for card_name in cards
    ],
    prevent_initial_call=True
)
def update(*args):
    trigger = callback_context.triggered_id
    return f'you clicked button {trigger}'


if __name__ == '__main__':
    app.run(debug=True)

I used dbc.Cards so you could give the cards the ID instead of the buttons if needed. Or you could change the dbc.Cards to something different. Maybe that’s not what you were after, in the end I still do not fully understand your question :sweat_smile:

thanks, this is perfect, I just started this new proyect, and didn’t know from where to start. This is a good beginning

Here is what would happen if you followed my suggestion:

Coded (used Aimped for initial):

import dash
from dash import html, Input, Output, callback_context
import numpy as np
import dash_bootstrap_components as dbc

# raw input
cards = ['2s', '3s', '4s', '5s', '6s', '7s', '8s', '9s', 'Ts', 'Js', 'Qs', 'Ks', 'As',
         '2h', '3h', '4h', '5h', '6h', '7h', '8h', '9h', 'Th', 'Jh', 'Qh', 'Kh', 'Ah',
         '2d', '3d', '4d', '5d', '6d', '7d', '8d', '9d', 'Td', 'Jd', 'Qd', 'Kd', 'Ad',
         '2c', '3c', '4c', '5c', '6c', '7c', '8c', '9c', 'Tc', 'Jc', 'Qc', 'Kc', 'Ac']

app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

app.layout = html.Div([html.Div(i, id=i, n_clicks=0, className='myCustomCard') for i in cards] +
                      [html.Div(id='output')], id='myCardHolder')


@app.callback(
    Output('output', 'children'),
    [
        Input(card_name, 'n_clicks')
        for card_name in cards
    ],
    prevent_initial_call=True
)
def update(*args):
    trigger = callback_context.triggered_id
    return f'you clicked button {trigger}'


if __name__ == '__main__':
    app.run(debug=True)
1 Like

haha - this is a lot of fun! I was trying to think of an example for pattern matching callback for the Dash Example Index and this topic inspired me.

By using pattern matching callbacks, you can avoid having 52 inputs in the callback.

I’ll probably turn this into some type of simple game for the Example Index, but in the meantime, here’s my version:

card_game

import random
from dash import Dash, html, Input, Output, ALL, ctx
import dash_bootstrap_components as dbc
from dash_iconify import DashIconify

app = Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP])

SUITS = ["spades", "clubs", "diamonds", "hearts"]
RANKS = ["ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "jack", "queen", "king"]


def create_deck(shuffle=False):
    """Create a  deck of 52 cards"""
    deck = [(s, r) for s in SUITS for r in RANKS]
    if shuffle:
        random.shuffle(deck)
    return deck


def make_card(card):
    """format the card as a button"""
    suit, rank = card
    color = "black" if suit in ["spades", "clubs"] else "red"
    card_icon = DashIconify(
        icon=f"game-icons:card-{rank}-{suit}", color=color, height=60
    )
    return dbc.Button(
        [card_icon], color="white", className="p-0", id={"index": f"{suit}-{rank}"}
    )


def deck_layout(deck):
    """make card layout"""
    return [dbc.Row(dbc.Col([make_card(c) for c in deck if s in c])) for s in SUITS]


deck = create_deck()

app.layout = dbc.Container(
    [
        html.H1("Select a Card"),
        html.Div(deck_layout(deck)),
        html.H2("You Selected:"),
        html.Div(id="card-selected"),
    ]
)


@app.callback(
    Output("card-selected", "children"),
    Input({"index": ALL}, "n_clicks"),
    prevent_initial_call=True,
)
def selected(_):
    selected_card = ctx.triggered_id.index
    suit, _, rank = selected_card.partition("-")
    color = "black" if suit in ["spades", "clubs"] else "red"
    return DashIconify(icon=f"game-icons:card-{rank}-{suit}", color=color, height=60)

if __name__ == "__main__":
    app.run_server(debug=True)


3 Likes

really cool!

you R all the best!

how do I change to 4 colors?

color = “black” if suit is [“spades”] or “green” if suit is [“clubs”] or “blue” if suit is [“diamonds”]else “red”

(correctly)

You do like on-liners, don’t you? :rofl:

I’ll have to take a look into pattern matching callbacks, looks interesting.

So neat. Wow. Thanks Ann. It’s amazing how much can be done with so few lines of code.

Yeah, I’ll admit that one was over the top :sweat_smile:


I’d probably add a dict:

SUIT_COLORS={"spades": "black", "clubs":"green", "diamonds":"blue", "hearts": "red" }

Then instead of

color = "black" if suit in ["spades", "clubs"] else "red"

it would be:

color = SUIT_COLORS[suit]

Thanks you all for all the help,

I’ve only been programing for less than 2 months in hours … and in this time I managed to pull out an app (I invite you to the web www.pokerutils.com … but still need a week of work in there to add users, so dont be hard on me please, but managed to create for poker) and some trading bots … without doing a single course of python, or programming … I know python is easier … but finding very interesting how much a do without knowing a *** … next step is to pull out a poker solver (thats vanguard for 8 years ago, doc papers etc) … give me some time and some answers.

thanks to all, you are really helpfull & sorry for asking too much, but If a baby doesn’t cry …

Is there any way I can select by 1 click, instead of n_clicks?

I whant to have various cards selected, first click, the first card, second click the second one … is this possible?

So you mean that you would like to show more than one card selected? If so, you could take a look at this example.

It shows how to add or remove sereral elements (in this case a card with a figure), but it would be similar.

yes, but need different outputs, to work on the cards (solve ev … numerical stuff) just whant to get different outputs for each card) … I need to have between 2 and 7 cards choosed … how can I make that possible?

hi, does this work for getting different outputs? or are they the same outputs?

What do you mean by “different”?

Please have a read here:

This sounds like what you are looking for

Simple Example with MATCH

Like ALL, MATCH will fire the callback when any of the component’s properties change. However, instead of passing all of the values into the callback, MATCH will pass just a single value into the callback. Instead of updating a single output, it will update the dynamic output that is “matched” with.

So, I turned this into a game:

  • Deal the cards
  • Play them from the active hand (outlined in red)

css

#player2 {
    position: absolute;
    top: 0px;
    display: flex;
    flex-direction: row;
    justify-content: center;
}

#player1 {
    position: absolute;
    left: 0px;
    top: 0px;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
    justify-content: center;
}

#player3 {
    position: absolute;
    right: 0px;
    top: 0px;
    display: flex;
    flex-direction: column;
    align-items: center;
}

#player0 {
    position: absolute;
    bottom: 0px;
    display: flex;
    flex-direction: row;
    justify-content: center;
}

#cards-played {
    display: flex;
    align-items: center;
    height: 100vh;
}

#cardActivity {
    position: absolute;
    top: 50%;
    left: 50%;
}

.disabled button.playingCard {
    cursor: not-allowed;
    pointer-events: none;
}

.active {
    border: 2pt solid red;
}

app.py

import random
from dash import Dash, html, Input, Output, ALL, ctx, State, dcc
from dash.exceptions import PreventUpdate
import dash_bootstrap_components as dbc
from dash_iconify import DashIconify

app = Dash(__name__, external_stylesheets=[dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP])

SUITS = ["spades", "clubs", "diamonds", "hearts"]
RANKS = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "jack", "queen", "king", "ace"]
players = ['player1', 'player2', 'player3', 'player0']
values = {'ace':14, 'king':13, 'queen':12, 'jack':11, '10':10,
          '9':9, '8':8, '7':7, '6':6, '5':5, '4':4, '3':3, '2':2,
          'spades':60, 'clubs':20, 'diamonds':30, 'hearts':40}



def create_deck(shuffle=False):
    """Create a  deck of 52 cards"""
    deck = [(s, r) for s in SUITS for r in RANKS]
    if shuffle:
        random.shuffle(deck)
    return deck

def shuffle_deck(deck):
    newDeck = []
    for suit in deck:
        for card in suit:
            newDeck.append(card)
    random.shuffle(newDeck)
    return newDeck

def sort_key(button):
    return values[button.id['index'].split('-')[0]] + values[str(button.id['index'].split('-')[1])]

def make_card(card):
    """format the card as a button"""
    suit, rank = card
    color = "black" if suit in ["spades", "clubs"] else "red"
    card_icon = DashIconify(
        icon=f"game-icons:card-{rank}-{suit}", color=color, height=45
    )
    return dbc.Button(
        [card_icon], color="white", className="playingCard", id={"index": f"{suit}-{rank}"}
    )


def make_deck(deck):
    """make card layout"""
    return [[make_card(c) for c in deck if s in c] for s in SUITS]

app.layout = dbc.Container(
    [
        html.Div([],id='cards-played'),
        dcc.Store(id='countChildren', data=0),
        html.Button('Deal Cards', n_clicks=0, id='cardActivity'),
        html.Div(id="card-selected"),
    ] + [html.Div(id=i) for i in players],
)


@app.callback(
    Output("cards-played", "children"),
    Input('cardActivity', 'n_clicks'),
    Input({"index": ALL}, "n_clicks"),
    State('cards-played','children'),
    prevent_initial_call=True,
)
def selected(n1, _, played):
    selected_card = ctx.triggered_id
    if selected_card:
        if selected_card == 'cardActivity':
            return []
    if ctx.triggered[0]['value']:
        selected_card = ctx.triggered_id.index
        suit, _, rank = selected_card.partition("-")
        color = "black" if suit in ["spades", "clubs"] else "red"
        if len(played) > 3:
            played = []
        return played + [DashIconify(icon=f"game-icons:card-{rank}-{suit}", color=color, height=60)]
    return played

@app.callback(
    [Output(i, 'children') for i in players],
    Input('cardActivity', 'n_clicks'),
    Input({"index": ALL}, "n_clicks"),
    [State(i, 'children') for i in players],
    prevent_initial_call=True,
)
def dealCards(n1, i, *args):
    selected_card = ctx.triggered_id
    if selected_card == 'cardActivity':
        deck = create_deck(True)
        madeDeck = make_deck(deck)
        shuffledDeck = shuffle_deck(madeDeck)
        dealt = [[]for i in players]
        x = 0
        for card in shuffledDeck:
            dealt[x].append(card)
            x += 1
            if x > 3:
                x = 0
        for hand in dealt:
            hand = hand.sort(key=sort_key)
    else:
        dealt = args
        for hand in dealt:
            for card in hand:
                if card['props']['id'] == selected_card:
                    hand.remove(card)
    return dealt

@app.callback(
    [Output(i, 'className') for i in players],
    Output('countChildren', 'data'),
    Input('cardActivity', 'n_clicks'),
    Input("cards-played", "children"),
    State('countChildren', 'data'),
    [State(i, 'className') for i in players],
    prevent_initial_call=True,
)
def setActive(n1, i, data, *args):
    selected_card = ctx.triggered_id
    active = list(args)
    if selected_card:
        countChildren = [data]
        if selected_card == 'cardActivity':
            act = random.randint(0,3)
            active = ['disabled']*len(list(args))
            active[act] = 'active'
            countChildren = [0]
        elif len(ctx.triggered[0]['value']) != data:
            countChildren = [len(ctx.triggered[0]['value'])]
            for i in range(len(active)):
                if active[i] == 'active':
                    active[i] = 'disabled'
                    if i >= len(active)-1:
                        active[0] = 'active'
                    else:
                        active[i+1] = 'active'
                    break
        return active + countChildren



if __name__ == "__main__":
    app.run_server(debug=True)
1 Like