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