Coverage for Aux_Hud.py: 0%
249 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-27 18:50 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-27 18:50 +0000
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3"""Aux_Hud.py
5Simple HUD display for FreePokerTools/fpdb HUD.
6"""
8# import L10n
9# _ = L10n.get_translation()
10# Copyright 2011-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########################################################################
28# to do
30# Standard Library modules
31import os
32import logging
33# logging has been set up in fpdb.py or HUD_main.py, use their settings:
34log = logging.getLogger("hud")
35from functools import partial
37from PyQt5.QtGui import QCursor, QFont, QPixmap
38from PyQt5.QtCore import pyqtSignal, Qt
39from PyQt5.QtWidgets import (QComboBox, QGridLayout, QHBoxLayout,
40 QLabel, QPushButton, QSpinBox,
41 QVBoxLayout, QWidget)
43# FreePokerTools modules
44import Aux_Base
45import Stats
46import Popup
47import Configuration
50class Simple_HUD(Aux_Base.Aux_Seats):
51 """A simple HUD class based on the Aux_Window interface."""
53 def __init__(self, hud, config, aux_params):
54 # Save everything you need to know about the hud as attrs.
55 # That way a subclass doesn't have to grab them.
56 # Also, the subclass can override any of these attributes
57 super(Simple_HUD, self).__init__(hud, config, aux_params)
58 self.poker_game = self.hud.poker_game
59 self.site_params = self.hud.site_parameters
60 self.aux_params = aux_params
61 self.game_params = self.hud.supported_games_parameters["game_stat_set"]
62 self.max = self.hud.max
63 self.nrows = self.game_params.rows
64 self.ncols = self.game_params.cols
65 self.xpad = self.game_params.xpad
66 self.ypad = self.game_params.ypad
67 self.xshift = self.site_params['hud_menu_xshift']
68 self.yshift = self.site_params['hud_menu_yshift']
69 self.fgcolor = self.aux_params["fgcolor"]
70 self.bgcolor = self.aux_params["bgcolor"]
71 self.opacity = self.aux_params["opacity"]
72 self.font = QFont(self.aux_params["font"], int(self.aux_params["font_size"]))
74 # store these class definitions for use elsewhere
75 # this is needed to guarantee that the classes in _this_ module
76 # are called, and that some other overriding class is not used.
78 self.aw_class_window = Simple_Stat_Window
79 self.aw_class_stat = Simple_stat
80 self.aw_class_table_mw = Simple_table_mw
81 self.aw_class_label = Simple_label
83 # layout is handled by superclass!
84 # retrieve the contents of the stats. popup and tips elements
85 # for future use do this here so that subclasses don't have to bother
87 self.stats = [[None]*self.ncols for _ in range(self.nrows)]
88 self.popups = [[None]*self.ncols for _ in range(self.nrows)]
89 self.tips = [[None]*self.ncols for _ in range(self.nrows)]
91 for stat in self.game_params.stats:
92 self.stats[self.game_params.stats[stat].rowcol[0]][self.game_params.stats[stat].rowcol[1]] \
93 = self.game_params.stats[stat].stat_name
94 self.popups[self.game_params.stats[stat].rowcol[0]][self.game_params.stats[stat].rowcol[1]] \
95 = self.game_params.stats[stat].popup
96 self.tips[self.game_params.stats[stat].rowcol[0]][self.game_params.stats[stat].rowcol[1]] \
97 = self.game_params.stats[stat].tip
99 def create_contents(self, container, i):
100 # this is a call to whatever is in self.aw_class_window but it isn't obvious
101 container.create_contents(i)
103 def update_contents(self, container, i):
104 # this is a call to whatever is in self.aw_class_window but it isn't obvious
105 container.update_contents(i)
107 def create_common(self, x, y):
108 # invokes the simple_table_mw class (or similar)
109 self.table_mw = self.aw_class_table_mw(self.hud, aw=self)
110 return self.table_mw
112 def move_windows(self):
113 super(Simple_HUD, self).move_windows()
114 #
115 # tell our mw that an update is needed (normally on table move)
116 # custom code here, because we don't use the ['common'] element
117 # to control menu position
118 self.table_mw.move_windows()
120 def save_layout(self, *args):
121 """Save new layout back to the aux element in the config file."""
123 new_locs = {
124 self.adj[int(i)]: ((pos[0]), (pos[1]))
125 for i, pos in list(self.positions.items())
126 if i != 'common'
127 }
128 self.config.save_layout_set(self.hud.layout_set, self.hud.max,
129 new_locs, self.hud.table.width, self.hud.table.height)
132class Simple_Stat_Window(Aux_Base.Seat_Window):
133 """Simple window class for stat windows."""
135 def __init__(self, aw=None, seat=None):
136 super(Simple_Stat_Window, self).__init__(aw, seat)
137 self.popup_count = 0
139 def button_release_right(self, event): # show pop up
140 widget = self.childAt(event.pos())
142 if widget.stat_dict and self.popup_count == 0 and widget.aw_popup:
143 # do not popup on empty blocks or if one is already active
144 pu = Popup.popup_factory(
145 seat=widget.aw_seat,
146 stat_dict=widget.stat_dict,
147 win=self,
148 pop=self.aw.config.popup_windows[widget.aw_popup],
149 hand_instance=self.aw.hud.hand_instance,
150 config=self.aw.config)
151 pu.setStyleSheet(f"QWidget{{background:{self.aw.bgcolor};color:{self.aw.fgcolor};}}QToolTip{{}}")
153 def create_contents(self, i):
154 self.setStyleSheet(f"QWidget{{background:{self.aw.bgcolor};color:{self.aw.fgcolor};}}QToolTip{{}}")
155 self.grid = QGridLayout()
156 self.grid.setHorizontalSpacing(4)
157 self.grid.setVerticalSpacing(1)
158 self.grid.setContentsMargins(2, 2, 2, 2)
159 self.setLayout(self.grid)
160 self.stat_box = [[None]*self.aw.ncols for _ in range(self.aw.nrows)]
162 for r in range(self.aw.nrows):
163 for c in range(self.aw.ncols):
164 self.stat_box[r][c] = self.aw.aw_class_stat(self.aw.stats[r][c],
165 seat=self.seat,
166 popup=self.aw.popups[r][c],
167 game_stat_config=self.aw.hud.supported_games_parameters["game_stat_set"].stats[(r, c)],
168 aw=self.aw)
169 self.grid.addWidget(self.stat_box[r][c].widget, r, c)
170 self.stat_box[r][c].widget.setFont(self.aw.font)
172 def update_contents(self, i):
173 if i == "common":
174 return
175 player_id = self.aw.get_id_from_seat(i)
176 if player_id is None:
177 return
178 for r in range(self.aw.nrows):
179 for c in range(self.aw.ncols):
180 self.stat_box[r][c].update(player_id, self.aw.hud.stat_dict)
183class Simple_stat(object):
184 """A simple class for displaying a single stat."""
185 def __init__(self, stat, seat, popup, game_stat_config=None, aw=None):
186 self.stat = stat
187 self.lab = aw.aw_class_label("xxx") # xxx is used as initial value because longer labels don't shrink
188 self.lab.setAlignment(Qt.AlignCenter)
189 self.lab.aw_seat = aw.hud.layout.hh_seats[seat]
190 self.lab.aw_popup = popup
191 self.lab.stat_dict = None
192 self.widget = self.lab
193 self.stat_dict = None
194 self.hud = aw.hud
195 self.aux_params = aw.aux_params
197 def update(self, player_id, stat_dict):
198 self.stat_dict = stat_dict # So the Simple_stat obj always has a fresh stat_dict
199 self.lab.stat_dict = stat_dict
200 self.number = Stats.do_stat(stat_dict, player_id, self.stat, self.hud.hand_instance)
201 if self.number:
202 self.lab.setText(str(self.number[1]))
204 def set_color(self, fg=None, bg=None):
205 ss = f"QLabel{{font-family: {self.aux_params['font']};font-size: {self.aux_params['font_size']}pt;"
206 if fg:
207 ss += f"color: {fg};"
208 if bg:
209 ss += f"background: {bg};"
210 #print(f"Setting style sheet: {ss}")
211 self.lab.setStyleSheet(ss + "}")
214class Simple_label(QLabel):
215 pass
219class Simple_table_mw(Aux_Base.Seat_Window):
220 """Create a default table hud menu label"""
221# This is a recreation of the table main window from the default HUD
222# in the old Hud.py. This has the menu options from that hud.
224# BTW: It might be better to do this with a different AW.
226 def __init__(self, hud, aw = None):
227 super(Simple_table_mw, self).__init__(aw)
228 self.hud = hud
229 self.aw = aw
230 self.menu_is_popped = False
232 # self.connect("configure_event", self.configure_event_cb, "auxmenu") base class will deal with this
234 try:
235 self.menu_label = hud.hud_params['label']
236 except Exception:
237 self.menu_label = "fpdb menu"
239 lab = QLabel(self.menu_label)
240 logo = os.path.join(Configuration.GRAPHICS_PATH, 'tribal.jpg')
241 pixmap = QPixmap(logo)
242 pixmap = pixmap.scaled(45, 45)
243 lab.setPixmap(pixmap)
244 lab.setStyleSheet(f"background: {self.aw.bgcolor}; color: {self.aw.fgcolor};")
246 self.setLayout(QVBoxLayout())
247 self.layout().setContentsMargins(0, 0, 0, 0)
248 self.layout().addWidget(lab)
250 self.move(self.hud.table.x + self.aw.xshift, self.hud.table.y + self.aw.yshift)
252 def button_press_right(self, event):
253 """Handle button clicks in the FPDB main menu event box."""
255 if not self.menu_is_popped:
256 self.menu_is_popped = True
257 Simple_table_popup_menu(self)
259 def move_windows(self, *args):
260 # force menu to the offset position from table origin (do not use common setting)
261 self.move(self.hud.table.x + self.aw.xshift, self.hud.table.y + self.aw.yshift)
264class Simple_table_popup_menu(QWidget):
266 def __init__(self, parentwin):
268 super(Simple_table_popup_menu, self).__init__(None, Qt.Window | Qt.FramelessWindowHint)
269 self.parentwin = parentwin
270 self.move(self.parentwin.hud.table.x + self.parentwin.aw.xshift,
271 self.parentwin.hud.table.y + self.parentwin.aw.yshift)
272 self.setWindowTitle(self.parentwin.menu_label)
274# combobox statrange
275 stat_range_combo_dict = {
276 0: ('Since:' + " " + 'All Time', "A"),
277 1: ('Since:' + " " + 'Session', "S"),
278 2: ('Since:' + " " + 'n Days' + " - - >", "T"),
279 }
280# combobox seatsstyle
281 seats_style_combo_dict = {
282 0: ('Number of Seats:' + " " + 'Any Number', "A"),
283 1: ('Number of Seats:' + " " + 'Custom', "C"),
284 2: ('Number of Seats:' + " " + 'Exact', "E"),
285 }
286# combobox multiplier
287 multiplier_combo_dict = {
288 0: ('For This Blind Level Only', 1),
289 1: (' 0.5 to 2 * Current Blinds', 2),
290 2: (' 0.33 to 3 * Current Blinds', 3),
291 3: (' 0.1 to 10 * Current Blinds', 10),
292 4: ('All Levels', 10000),
293 }
294# ComboBox - set max seats
295 cb_max_dict = {0: ('Force layout'+'...', None)}
296 for pos, i in enumerate((sorted(self.parentwin.hud.layout_set.layout)), start=1):
297 cb_max_dict[pos] = (('%d-max' % i), i)
298 grid = QGridLayout()
299 self.setLayout(grid)
300 vbox1 = QVBoxLayout()
301 vbox2 = QVBoxLayout()
302 vbox3 = QVBoxLayout()
304 vbox1.addWidget(self.build_button('Restart This HUD', "kill"))
305 vbox1.addWidget(self.build_button('Save HUD Layout', "save"))
306 vbox1.addWidget(self.build_button('Stop this HUD', "blacklist"))
307 vbox1.addWidget(self.build_button('Close', "close"))
308 vbox1.addWidget(QLabel(''))
309 vbox1.addWidget(self.build_combo_and_set_active('new_max_seats', cb_max_dict))
311 vbox2.addWidget(QLabel('Show Player Stats for'))
312 vbox2.addWidget(self.build_combo_and_set_active('h_agg_bb_mult', multiplier_combo_dict))
313 vbox2.addWidget(self.build_combo_and_set_active('h_seats_style', seats_style_combo_dict))
314 hbox = QHBoxLayout()
315 hbox.addWidget(QLabel('Custom'))
316 self.h_nums_low_spinner = self.build_spinner('h_seats_cust_nums_low', 1, 9)
317 hbox.addWidget(self.h_nums_low_spinner)
318 hbox.addWidget(QLabel('To'))
319 self.h_nums_high_spinner = self.build_spinner('h_seats_cust_nums_high', 2, 10)
320 hbox.addWidget(self.h_nums_high_spinner)
321 vbox2.addLayout(hbox)
322 hbox = QHBoxLayout()
323 hbox.addWidget(self.build_combo_and_set_active('h_stat_range', stat_range_combo_dict))
324 self.h_hud_days_spinner = self.build_spinner('h_hud_days', 1, 9999)
325 hbox.addWidget(self.h_hud_days_spinner)
326 vbox2.addLayout(hbox)
328 vbox3.addWidget(QLabel('Show Opponent Stats for'))
329 vbox3.addWidget(self.build_combo_and_set_active('agg_bb_mult', multiplier_combo_dict))
330 vbox3.addWidget(self.build_combo_and_set_active('seats_style', seats_style_combo_dict))
331 hbox = QHBoxLayout()
332 hbox.addWidget(QLabel('Custom'))
333 self.nums_low_spinner = self.build_spinner('seats_cust_nums_low', 1, 9)
334 hbox.addWidget(self.nums_low_spinner)
335 hbox.addWidget(QLabel('To'))
336 self.nums_high_spinner = self.build_spinner('seats_cust_nums_high', 2, 10)
337 hbox.addWidget(self.nums_high_spinner)
338 vbox3.addLayout(hbox)
339 hbox = QHBoxLayout()
340 hbox.addWidget(self.build_combo_and_set_active('stat_range', stat_range_combo_dict))
341 self.hud_days_spinner = self.build_spinner('hud_days', 1, 9999)
342 hbox.addWidget(self.hud_days_spinner)
343 vbox3.addLayout(hbox)
345 self.set_spinners_active()
347 grid.addLayout(vbox1, 0, 0)
348 grid.addLayout(vbox2, 0, 1)
349 grid.addLayout(vbox3, 0, 2)
351 self.show()
352 self.raise_()
354 def delete_event(self):
355 self.parentwin.menu_is_popped = False
356 self.destroy()
358 def callback(self, checkState, data=None):
359 if data == "kill":
360 self.parentwin.hud.parent.kill_hud("kill", self.parentwin.hud.table.key)
361 if data == "blacklist":
362 self.parentwin.hud.parent.blacklist_hud("kill", self.parentwin.hud.table.key)
363 if data == "save":
364 # This calls the save_layout method of the Hud object. The Hud object
365 # then calls the save_layout method in each installed AW.
366 self.parentwin.hud.save_layout()
367 self.delete_event()
369 def build_button(self, labeltext, cbkeyword):
370 button = QPushButton(labeltext)
371 button.clicked.connect(partial(self.callback, data=cbkeyword))
372 return button
374 def build_spinner(self, field, low, high):
375 spinBox = QSpinBox()
376 spinBox.setRange(low, high)
377 spinBox.setValue(self.parentwin.hud.hud_params[field])
378 spinBox.valueChanged.connect(partial(self.change_spin_field_value, field=field))
379 return spinBox
381 def build_combo_and_set_active(self, field, combo_dict):
382 widget = QComboBox()
383 for pos in combo_dict:
384 widget.addItem(combo_dict[pos][0])
385 if combo_dict[pos][1] == self.parentwin.hud.hud_params[field]:
386 widget.setCurrentIndex(pos)
387 widget.currentIndexChanged[int].connect(partial(self.change_combo_field_value,
388 field=field, combo_dict=combo_dict))
389 return widget
391 def change_combo_field_value(self, sel, field, combo_dict):
392 self.parentwin.hud.hud_params[field] = combo_dict[sel][1]
393 self.set_spinners_active()
395 def change_spin_field_value(self, value, field):
396 self.parentwin.hud.hud_params[field] = value
398 def set_spinners_active(self):
399 if self.parentwin.hud.hud_params['h_stat_range'] == "T":
400 self.h_hud_days_spinner.setEnabled(True)
401 else:
402 self.h_hud_days_spinner.setEnabled(False)
403 if self.parentwin.hud.hud_params['stat_range'] == "T":
404 self.hud_days_spinner.setEnabled(True)
405 else:
406 self.hud_days_spinner.setEnabled(False)
407 if self.parentwin.hud.hud_params['h_seats_style'] == "C":
408 self.h_nums_low_spinner.setEnabled(True)
409 self.h_nums_high_spinner.setEnabled(True)
410 else:
411 self.h_nums_low_spinner.setEnabled(False)
412 self.h_nums_high_spinner.setEnabled(False)
413 if self.parentwin.hud.hud_params['seats_style'] == "C":
414 self.nums_low_spinner.setEnabled(True)
415 self.nums_high_spinner.setEnabled(True)
416 else:
417 self.nums_low_spinner.setEnabled(False)
418 self.nums_high_spinner.setEnabled(False)