Coverage for Mucked.py: 0%
218 statements
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
« prev ^ index » next coverage.py v7.6.3, created at 2024-10-15 19:33 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""Mucked.py
5Mucked cards display for FreePokerTools HUD.
6"""
8from __future__ import print_function
9from __future__ import division
10# Copyright 2008-2012, Ray E. Barker
11#
12# This program is free software; you can redistribute it and/or modify
13# it under the terms of the GNU General Public License as published by
14# the Free Software Foundation; either version 2 of the License, or
15# (at your option) any later version.
16#
17# This program is distributed in the hope that it will be useful,
18# but WITHOUT ANY WARRANTY; without even the implied warranty of
19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20# GNU General Public License for more details.
21#
22# You should have received a copy of the GNU General Public License
23# along with this program; if not, write to the Free Software
24# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26########################################################################
29from past.utils import old_div
30# import L10n
31# _ = L10n.init_translation()
33# Standard Library modules
34import logging
37from PyQt5.QtCore import QObject
38from PyQt5.QtGui import QPainter, QPixmap, QStandardItem, QStandardItemModel
39from PyQt5.QtWidgets import QGridLayout, QLabel, QTableView, QVBoxLayout, QWidget
41# FreePokerTools modules
42import Card
43import Aux_Base
44# Utility routine to get the number of valid cards in the card tuple
46# logging has been set up in fpdb.py or HUD_main.py, use their settings:
47log = logging.getLogger("hud")
50def valid_cards(ct):
51 return sum(c != 0 for c in ct)
54class Stud_mucked(Aux_Base.Aux_Window):
55 def __init__(self, hud, config, params):
56 self.hud = hud # hud object that this aux window supports
57 self.config = config # configuration object for this aux window to use
58 self.params = params # hash aux params from config
60 try:
61 site_params = self.config.get_site_parameters(self.hud.site)
62 self.hero = site_params["screen_name"]
63 except Exception:
64 self.hero = ""
66 self.mucked_list = Stud_list(self, params, config, self.hero)
67 self.mucked_cards = Stud_cards(self, params, config)
68 self.mucked_list.mucked_cards = self.mucked_cards
70 def create(self):
71 self.container = QWidget()
72 self.vbox = QVBoxLayout()
73 self.container.setLayout(self.vbox)
74 self.container.setWindowTitle(self.hud.table.name)
76 self.mucked_list.create(self.vbox)
77 self.mucked_cards.create(self.vbox)
78 self.container.show()
80 def update_data(self, new_hand_id, db_connection):
81 # uncomment next line when action is available in the db
82 # self.mucked_cards.update_data(new_hand_id, db_connection)
83 self.mucked_list.update_data(new_hand_id, db_connection)
85 def update_gui(self, new_hand_id):
86 self.mucked_cards.update_gui(new_hand_id)
87 self.mucked_list.update_gui(new_hand_id)
90class Stud_list(object):
91 def __init__(self, parent, params, config, hero):
92 self.parent = parent
93 self.params = params
94 self.config = config
95 self.hero = hero
97 def create(self, container):
98 self.container = container
99 self.treeview = QTableView()
100 self.liststore = QStandardItemModel(0, 4, self.treeview)
101 self.treeview.setModel(self.liststore)
102 self.liststore.setHorizontalHeaderLabels(["HandID", "Cards", "Net", "Winner"])
103 self.treeview.verticalHeader().hide()
104 self.container.addWidget(self.treeview)
106 def update_data(self, new_hand_id, db_connection):
107 """Updates the data needed for the list box."""
109 # db_connection = Database.Database(self.config, 'fpdb', '')
110 self.winners = db_connection.get_winners_from_hand(new_hand_id)
111 pot = 0
112 winners = ""
113 for player in list(self.winners.keys()):
114 pot = pot + int(self.winners[player])
115 if winners != "":
116 winners = f"{winners}, "
117 winners = winners + player
118 pot_dec = "%.2f" % (old_div(float(pot), 100))
120 hero_cards = self.get_hero_cards(self.parent.hero)
121 self.info_row = ((new_hand_id, hero_cards, pot_dec, winners),)
123 def get_hero_cards(self, hero):
124 """Formats the hero cards for inclusion in the table."""
125 if hero == "":
126 return "xxxxxx"
127 return next(
128 (
129 Card.valueSuitFromCard(self.parent.hud.cards[stat["seat"]][0])
130 + Card.valueSuitFromCard(self.parent.hud.cards[stat["seat"]][1])
131 + Card.valueSuitFromCard(self.parent.hud.cards[stat["seat"]][2])
132 for stat in list(self.parent.hud.stat_dict.values())
133 if stat["screen_name"] == hero
134 ),
135 "xxxxxx",
136 )
138 def update_gui(self, new_hand_id):
139 self.liststore.appendRow(list(map(QStandardItem, self.info_row[0])))
140 self.treeview.resizeColumnsToContents()
141 self.treeview.horizontalHeader().setStretchLastSection(True)
144class Stud_cards(object):
145 def __init__(self, parent, params, config):
146 self.parent = parent
147 self.params = params
148 self.config = config
150 self.card_images = self.parent.hud.parent.deck.get_all_card_images()
151 self.grid_contents = {}
152 self.eb = {}
154 self.rows = 8
155 self.cols = 7
157 def create(self, container):
158 self.container = container
159 self.grid = QGridLayout()
161 for r in range(self.rows):
162 for c in range(self.cols):
163 # Start by creating a box of nothing but card backs
164 self.eb[(c, r)] = QLabel()
165 self.eb[(c, r)].setPixmap(self.card_images[0])
167 # set up the contents for the cells
168 for r in range(self.rows):
169 self.grid_contents[(0, r)] = QLabel("%d" % (r + 1))
170 self.grid_contents[(1, r)] = QLabel("player %d" % (r + 1))
171 self.grid_contents[(4, r)] = QLabel("-")
172 self.grid_contents[(9, r)] = QLabel("-")
173 self.grid_contents[(2, r)] = self.eb[(0, r)]
174 self.grid_contents[(3, r)] = self.eb[(1, r)]
175 self.grid_contents[(5, r)] = self.eb[(2, r)]
176 self.grid_contents[(6, r)] = self.eb[(3, r)]
177 self.grid_contents[(7, r)] = self.eb[(4, r)]
178 self.grid_contents[(8, r)] = self.eb[(5, r)]
179 self.grid_contents[(10, r)] = self.eb[(6, r)]
181 # add the cell contents to the table
182 for c in range(self.cols + 4):
183 for r in range(self.rows):
184 self.grid.addWidget(self.grid_contents[(c, r)], r, c)
186 self.container.addLayout(self.grid)
188 def update_data(self, new_hand_id, db_connection):
189 self.tips = []
190 action = db_connection.get_action_from_hand(new_hand_id)
191 print(action)
192 for street in action:
193 temp = ""
194 for act in street:
195 temp = temp + act[0] + " " + act[1] + "s "
196 if act[2] > 0:
197 if act[2] % 100 > 0:
198 temp = temp + "%4.2f\n" % (old_div(float(act[2]), 100))
199 else:
200 temp = temp + "%d\n" % (old_div(act[2], 100))
201 else:
202 temp = temp + "\n"
203 self.tips.append(temp)
205 def update_gui(self, new_hand_id):
206 self.clear()
207 for c, cards in list(self.parent.hud.cards.items()):
208 if c == "common":
209 continue
210 self.grid_contents[(1, c - 1)].setText(self.get_screen_name(c))
211 for i in (
212 (0, cards[0]),
213 (1, cards[1]),
214 (2, cards[2]),
215 (3, cards[3]),
216 (4, cards[4]),
217 (5, cards[5]),
218 (6, cards[6]),
219 ):
220 if i[1] != 0:
221 # Pixmaps are stored in dict with rank+suit keys
222 (_rank, _suit) = Card.valueSuitFromCard(i[1])
223 _rank = Card.card_map[_rank]
224 self.eb[(i[0], c - 1)].setPixmap(self.card_images[_suit][_rank])
225 # action in tools tips for later streets
226 # round_to_col = (0, 3, 4, 5, 6)
227 # for round in range(1, len(self.tips)):
228 # for r in range(0, self.rows):
229 # self.eb[(round_to_col[round], r)].set_tooltip_text(self.tips[round])
231 def get_screen_name(self, seat_no):
232 """Gets and returns the screen name from stat_dict, given seat number."""
233 return next(
234 (
235 self.parent.hud.stat_dict[k]["screen_name"]
236 for k in list(self.parent.hud.stat_dict.keys())
237 if self.parent.hud.stat_dict[k]["seat"] == seat_no
238 ),
239 "No Name",
240 )
242 def clear(self):
243 for r in range(0, self.rows):
244 self.grid_contents[(1, r)].setText(" ")
245 for c in range(0, 7):
246 # Start by creating a box of nothing but card backs
247 self.eb[(c, r)].setPixmap(self.card_images[0])
250class Flop_Mucked(Aux_Base.Aux_Seats, QObject):
251 """Aux_Window class for displaying mucked cards for flop games."""
253 def __init__(self, hud, config, params):
254 super(Flop_Mucked, self).__init__(hud, config, params)
255 QObject.__init__(self)
256 self.card_images = self.hud.parent.deck.get_all_card_images()
257 self.card_height = self.hud.parent.hud_params["card_ht"]
258 self.card_width = self.hud.parent.hud_params["card_wd"]
259 self.uses_timer = True # this Aux_seats object uses a timer to control hiding
261 def create_common(self, x, y):
262 """Create the window for the board cards and do the initial population."""
263 w = self.aw_class_window(self, "common")
264 self.positions["common"] = self.create_scale_position(x, y)
265 w.move(self.positions["common"][0] + self.hud.table.x, self.positions["common"][1] + self.hud.table.y)
266 if "opacity" in self.params:
267 w.setWindowOpacity(float(self.params["opacity"]))
268 return w
270 def create_contents(self, container, i):
271 """Create the widgets for showing the contents of the Aux_seats window."""
272 container.seen_cards = QLabel()
273 container.seen_cards.setPixmap(self.card_images[0])
274 container.setLayout(QVBoxLayout())
275 container.layout().setContentsMargins(0, 0, 0, 0)
276 container.layout().addWidget(container.seen_cards)
278 # NOTE: self.hud.cards is a dictionary of:
279 # { seat_num: (card, card, [...]) }
280 #
281 # Thus the individual hands (cards for seat) are tuples
282 def update_contents(self, container, i):
283 hist_seat = self.hud.layout.hh_seats[i] if type(i) is int else i
284 if hist_seat not in self.hud.cards:
285 return
287 cards = self.hud.cards[hist_seat]
288 # Here we want to know how many cards the given seat showed;
289 # board is considered a seat, and has the id 'common'
290 # 'cards' on the other hand is a tuple. The format is:
291 # (card_num, card_num, ...)
292 n_cards = valid_cards(cards)
293 if n_cards > 1:
294 # scratch is a working pixmap, used to assemble the image
295 scratch = QPixmap(int(self.card_width) * n_cards, int(self.card_height))
296 painter = QPainter(scratch)
297 x = 0 # x coord where the next card starts in scratch
298 for card in cards:
299 # concatenate each card image to scratch
300 # flop game never(?) has unknown cards.
301 # FIXME: if "show one and fold" ever becomes an option,
302 # this needs to be changed
303 if card is None or card == 0:
304 break
306 # This gives us the card symbol again
307 (_rank, _suit) = Card.valueSuitFromCard(card)
308 _rank = Card.card_map[_rank]
309 px = self.card_images[_suit][_rank]
310 painter.drawPixmap(x, 0, px)
311 x += px.width()
313 painter.end()
314 if container is not None:
315 container.seen_cards.setPixmap(scratch)
316 container.resize(1, 1)
317 container.move(
318 self.positions[i][0] + self.hud.table.x, self.positions[i][1] + self.hud.table.y
319 ) # here is where I move back
320 container.show()
322 self.displayed = True
323 if i != "common" and self.get_id_from_seat(i) is not None:
324 self.m_windows[i].setToolTip(self.hud.stat_dict[self.get_id_from_seat(i)]["screen_name"])
326 def save_layout(self, *args):
327 """Save new common position back to the layout element in the config file."""
328 new_locs = {i: ((pos[0]), (pos[1])) for i, pos in list(self.positions.items()) if i == "common"}
329 self.config.save_layout_set(self.hud.layout_set, self.hud.max, new_locs, width=None, height=None)
331 def update_gui(self, new_hand_id):
332 """Prepare and show the mucked cards."""
333 if self.displayed:
334 self.hide()
335 # See how many players showed a hand. Skip if only 1 shows (= hero)
336 n_sd = self.count_seats_with_cards(self.hud.cards)
337 if n_sd < 2:
338 return
340 super(Flop_Mucked, self).update_gui(new_hand_id)
342 if self.displayed and float(self.params["timeout"]) > 0:
343 self.timer_on = True
344 self.startTimer(int(1000 * float(self.params["timeout"])))
346 def timerEvent(self, event):
347 self.killTimer(event.timerId())
348 if self.timer_on:
349 self.hide()
351 def button_press_cb(self, widget, event, i, *args):
352 """Handle button clicks in the event boxes."""
354 if event.button == 2: # middle button event, hold display (do not timeout)
355 if self.timer_on:
356 self.timer_on = False
357 else:
358 self.timer_on = False
359 self.hide()
360 elif event.button == 1 and i == "common": # left button event (move)
361 # firstly, cancel timer, otherwise block becomes locked if move event
362 # is happening when timer eventually times-out
363 if self.timer_on:
364 self.timer_on = False
365 # only allow move on "common" element - seat block positions are
366 # determined by aux_hud, not mucked card display
367 window = widget.get_parent()
368 window.begin_move_drag(event.button, int(event.x_root), int(event.y_root), event.time)
370 def expose_all(self):
371 for i in self.hud.cards:
372 self.m_windows[i].show()
373 self.m_windows[i].move(self.positions[i][0] + self.hud.table.x, self.positions[i][1] + self.hud.table.y)
374 self.displayed = True