Coverage for Aux_Hud.py: 0%
249 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# Copyright 2011-2012, Ray E. Barker
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19########################################################################
21### Aux_Hud.py
22#
23# Simple HUD display for FreePokerTools/fpdb HUD.
24###
26# import L10n
27# _ = L10n.get_translation()
29# Standard Library modules
30import os
31import logging
33from functools import partial
35from PyQt5.QtGui import QFont, QPixmap
36from PyQt5.QtCore import Qt
37from PyQt5.QtWidgets import QComboBox, QGridLayout, QHBoxLayout, QLabel, QPushButton, QSpinBox, QVBoxLayout, QWidget
39# FreePokerTools modules
40import Aux_Base
41import Stats
42import Popup
43import Configuration
46# logging has been set up in fpdb.py or HUD_main.py, use their settings:
47log = logging.getLogger("hud")
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 )
95 self.popups[self.game_params.stats[stat].rowcol[0]][self.game_params.stats[stat].rowcol[1]] = (
96 self.game_params.stats[stat].popup
97 )
98 self.tips[self.game_params.stats[stat].rowcol[0]][self.game_params.stats[stat].rowcol[1]] = (
99 self.game_params.stats[stat].tip
100 )
102 def create_contents(self, container, i):
103 # this is a call to whatever is in self.aw_class_window but it isn't obvious
104 container.create_contents(i)
106 def update_contents(self, container, i):
107 # this is a call to whatever is in self.aw_class_window but it isn't obvious
108 container.update_contents(i)
110 def create_common(self, x, y):
111 # invokes the simple_table_mw class (or similar)
112 self.table_mw = self.aw_class_table_mw(self.hud, aw=self)
113 return self.table_mw
115 def move_windows(self):
116 super(Simple_HUD, self).move_windows()
117 #
118 # tell our mw that an update is needed (normally on table move)
119 # custom code here, because we don't use the ['common'] element
120 # to control menu position
121 self.table_mw.move_windows()
123 def save_layout(self, *args):
124 """Save new layout back to the aux element in the config file."""
126 new_locs = {self.adj[int(i)]: ((pos[0]), (pos[1])) for i, pos in list(self.positions.items()) if i != "common"}
127 self.config.save_layout_set(
128 self.hud.layout_set, self.hud.max, new_locs, self.hud.table.width, self.hud.table.height
129 )
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 )
152 pu.setStyleSheet(f"QWidget{{background:{self.aw.bgcolor};color:{self.aw.fgcolor};}}QToolTip{{}}")
154 def create_contents(self, i):
155 self.setStyleSheet(f"QWidget{{background:{self.aw.bgcolor};color:{self.aw.fgcolor};}}QToolTip{{}}")
156 self.grid = QGridLayout()
157 self.grid.setHorizontalSpacing(4)
158 self.grid.setVerticalSpacing(1)
159 self.grid.setContentsMargins(2, 2, 2, 2)
160 self.setLayout(self.grid)
161 self.stat_box = [[None] * self.aw.ncols for _ in range(self.aw.nrows)]
163 for r in range(self.aw.nrows):
164 for c in range(self.aw.ncols):
165 self.stat_box[r][c] = self.aw.aw_class_stat(
166 self.aw.stats[r][c],
167 seat=self.seat,
168 popup=self.aw.popups[r][c],
169 game_stat_config=self.aw.hud.supported_games_parameters["game_stat_set"].stats[(r, c)],
170 aw=self.aw,
171 )
172 self.grid.addWidget(self.stat_box[r][c].widget, r, c)
173 self.stat_box[r][c].widget.setFont(self.aw.font)
175 def update_contents(self, i):
176 if i == "common":
177 return
178 player_id = self.aw.get_id_from_seat(i)
179 if player_id is None:
180 return
181 for r in range(self.aw.nrows):
182 for c in range(self.aw.ncols):
183 self.stat_box[r][c].update(player_id, self.aw.hud.stat_dict)
186class Simple_stat(object):
187 """A simple class for displaying a single stat."""
189 def __init__(self, stat, seat, popup, game_stat_config=None, aw=None):
190 self.stat = stat
191 self.lab = aw.aw_class_label("xxx") # xxx is used as initial value because longer labels don't shrink
192 self.lab.setAlignment(Qt.AlignCenter)
193 self.lab.aw_seat = aw.hud.layout.hh_seats[seat]
194 self.lab.aw_popup = popup
195 self.lab.stat_dict = None
196 self.widget = self.lab
197 self.stat_dict = None
198 self.hud = aw.hud
199 self.aux_params = aw.aux_params
201 def update(self, player_id, stat_dict):
202 self.stat_dict = stat_dict # So the Simple_stat obj always has a fresh stat_dict
203 self.lab.stat_dict = stat_dict
204 self.number = Stats.do_stat(stat_dict, player_id, self.stat, self.hud.hand_instance)
205 if self.number:
206 self.lab.setText(str(self.number[1]))
208 def set_color(self, fg=None, bg=None):
209 ss = f"QLabel{{font-family: {self.aux_params['font']};font-size: {self.aux_params['font_size']}pt;"
210 if fg:
211 ss += f"color: {fg};"
212 if bg:
213 ss += f"background: {bg};"
214 # print(f"Setting style sheet: {ss}")
215 self.lab.setStyleSheet(ss + "}")
218class Simple_label(QLabel):
219 pass
222class Simple_table_mw(Aux_Base.Seat_Window):
223 """Create a default table hud menu label"""
225 # This is a recreation of the table main window from the default HUD
226 # in the old Hud.py. This has the menu options from that hud.
228 # BTW: It might be better to do this with a different AW.
230 def __init__(self, hud, aw=None):
231 super(Simple_table_mw, self).__init__(aw)
232 self.hud = hud
233 self.aw = aw
234 self.menu_is_popped = False
236 # self.connect("configure_event", self.configure_event_cb, "auxmenu") base class will deal with this
238 try:
239 self.menu_label = hud.hud_params["label"]
240 except Exception:
241 self.menu_label = "fpdb menu"
243 lab = QLabel(self.menu_label)
244 logo = os.path.join(Configuration.GRAPHICS_PATH, "tribal.jpg")
245 pixmap = QPixmap(logo)
246 pixmap = pixmap.scaled(45, 45)
247 lab.setPixmap(pixmap)
248 lab.setStyleSheet(f"background: {self.aw.bgcolor}; color: {self.aw.fgcolor};")
250 self.setLayout(QVBoxLayout())
251 self.layout().setContentsMargins(0, 0, 0, 0)
252 self.layout().addWidget(lab)
254 self.move(self.hud.table.x + self.aw.xshift, self.hud.table.y + self.aw.yshift)
256 def button_press_right(self, event):
257 """Handle button clicks in the FPDB main menu event box."""
259 if not self.menu_is_popped:
260 self.menu_is_popped = True
261 Simple_table_popup_menu(self)
263 def move_windows(self, *args):
264 # force menu to the offset position from table origin (do not use common setting)
265 self.move(self.hud.table.x + self.aw.xshift, self.hud.table.y + self.aw.yshift)
268class Simple_table_popup_menu(QWidget):
269 def __init__(self, parentwin):
270 super(Simple_table_popup_menu, self).__init__(None, Qt.Window | Qt.FramelessWindowHint)
271 self.parentwin = parentwin
272 self.move(
273 self.parentwin.hud.table.x + self.parentwin.aw.xshift, self.parentwin.hud.table.y + self.parentwin.aw.yshift
274 )
275 self.setWindowTitle(self.parentwin.menu_label)
277 # combobox statrange
278 stat_range_combo_dict = {
279 0: ("Since:" + " " + "All Time", "A"),
280 1: ("Since:" + " " + "Session", "S"),
281 2: ("Since:" + " " + "n Days" + " - - >", "T"),
282 }
283 # combobox seatsstyle
284 seats_style_combo_dict = {
285 0: ("Number of Seats:" + " " + "Any Number", "A"),
286 1: ("Number of Seats:" + " " + "Custom", "C"),
287 2: ("Number of Seats:" + " " + "Exact", "E"),
288 }
289 # combobox multiplier
290 multiplier_combo_dict = {
291 0: ("For This Blind Level Only", 1),
292 1: (" 0.5 to 2 * Current Blinds", 2),
293 2: (" 0.33 to 3 * Current Blinds", 3),
294 3: (" 0.1 to 10 * Current Blinds", 10),
295 4: ("All Levels", 10000),
296 }
297 # ComboBox - set max seats
298 cb_max_dict = {0: ("Force layout" + "...", None)}
299 for pos, i in enumerate((sorted(self.parentwin.hud.layout_set.layout)), start=1):
300 cb_max_dict[pos] = (("%d-max" % i), i)
301 grid = QGridLayout()
302 self.setLayout(grid)
303 vbox1 = QVBoxLayout()
304 vbox2 = QVBoxLayout()
305 vbox3 = QVBoxLayout()
307 vbox1.addWidget(self.build_button("Restart This HUD", "kill"))
308 vbox1.addWidget(self.build_button("Save HUD Layout", "save"))
309 vbox1.addWidget(self.build_button("Stop this HUD", "blacklist"))
310 vbox1.addWidget(self.build_button("Close", "close"))
311 vbox1.addWidget(QLabel(""))
312 vbox1.addWidget(self.build_combo_and_set_active("new_max_seats", cb_max_dict))
314 vbox2.addWidget(QLabel("Show Player Stats for"))
315 vbox2.addWidget(self.build_combo_and_set_active("h_agg_bb_mult", multiplier_combo_dict))
316 vbox2.addWidget(self.build_combo_and_set_active("h_seats_style", seats_style_combo_dict))
317 hbox = QHBoxLayout()
318 hbox.addWidget(QLabel("Custom"))
319 self.h_nums_low_spinner = self.build_spinner("h_seats_cust_nums_low", 1, 9)
320 hbox.addWidget(self.h_nums_low_spinner)
321 hbox.addWidget(QLabel("To"))
322 self.h_nums_high_spinner = self.build_spinner("h_seats_cust_nums_high", 2, 10)
323 hbox.addWidget(self.h_nums_high_spinner)
324 vbox2.addLayout(hbox)
325 hbox = QHBoxLayout()
326 hbox.addWidget(self.build_combo_and_set_active("h_stat_range", stat_range_combo_dict))
327 self.h_hud_days_spinner = self.build_spinner("h_hud_days", 1, 9999)
328 hbox.addWidget(self.h_hud_days_spinner)
329 vbox2.addLayout(hbox)
331 vbox3.addWidget(QLabel("Show Opponent Stats for"))
332 vbox3.addWidget(self.build_combo_and_set_active("agg_bb_mult", multiplier_combo_dict))
333 vbox3.addWidget(self.build_combo_and_set_active("seats_style", seats_style_combo_dict))
334 hbox = QHBoxLayout()
335 hbox.addWidget(QLabel("Custom"))
336 self.nums_low_spinner = self.build_spinner("seats_cust_nums_low", 1, 9)
337 hbox.addWidget(self.nums_low_spinner)
338 hbox.addWidget(QLabel("To"))
339 self.nums_high_spinner = self.build_spinner("seats_cust_nums_high", 2, 10)
340 hbox.addWidget(self.nums_high_spinner)
341 vbox3.addLayout(hbox)
342 hbox = QHBoxLayout()
343 hbox.addWidget(self.build_combo_and_set_active("stat_range", stat_range_combo_dict))
344 self.hud_days_spinner = self.build_spinner("hud_days", 1, 9999)
345 hbox.addWidget(self.hud_days_spinner)
346 vbox3.addLayout(hbox)
348 self.set_spinners_active()
350 grid.addLayout(vbox1, 0, 0)
351 grid.addLayout(vbox2, 0, 1)
352 grid.addLayout(vbox3, 0, 2)
354 self.show()
355 self.raise_()
357 def delete_event(self):
358 self.parentwin.menu_is_popped = False
359 self.destroy()
361 def callback(self, checkState, data=None):
362 if data == "kill":
363 self.parentwin.hud.parent.kill_hud("kill", self.parentwin.hud.table.key)
364 if data == "blacklist":
365 self.parentwin.hud.parent.blacklist_hud("kill", self.parentwin.hud.table.key)
366 if data == "save":
367 # This calls the save_layout method of the Hud object. The Hud object
368 # then calls the save_layout method in each installed AW.
369 self.parentwin.hud.save_layout()
370 self.delete_event()
372 def build_button(self, labeltext, cbkeyword):
373 button = QPushButton(labeltext)
374 button.clicked.connect(partial(self.callback, data=cbkeyword))
375 return button
377 def build_spinner(self, field, low, high):
378 spinBox = QSpinBox()
379 spinBox.setRange(low, high)
380 spinBox.setValue(self.parentwin.hud.hud_params[field])
381 spinBox.valueChanged.connect(partial(self.change_spin_field_value, field=field))
382 return spinBox
384 def build_combo_and_set_active(self, field, combo_dict):
385 widget = QComboBox()
386 for pos in combo_dict:
387 widget.addItem(combo_dict[pos][0])
388 if combo_dict[pos][1] == self.parentwin.hud.hud_params[field]:
389 widget.setCurrentIndex(pos)
390 widget.currentIndexChanged[int].connect(
391 partial(self.change_combo_field_value, field=field, combo_dict=combo_dict)
392 )
393 return widget
395 def change_combo_field_value(self, sel, field, combo_dict):
396 self.parentwin.hud.hud_params[field] = combo_dict[sel][1]
397 self.set_spinners_active()
399 def change_spin_field_value(self, value, field):
400 self.parentwin.hud.hud_params[field] = value
402 def set_spinners_active(self):
403 if self.parentwin.hud.hud_params["h_stat_range"] == "T":
404 self.h_hud_days_spinner.setEnabled(True)
405 else:
406 self.h_hud_days_spinner.setEnabled(False)
407 if self.parentwin.hud.hud_params["stat_range"] == "T":
408 self.hud_days_spinner.setEnabled(True)
409 else:
410 self.hud_days_spinner.setEnabled(False)
411 if self.parentwin.hud.hud_params["h_seats_style"] == "C":
412 self.h_nums_low_spinner.setEnabled(True)
413 self.h_nums_high_spinner.setEnabled(True)
414 else:
415 self.h_nums_low_spinner.setEnabled(False)
416 self.h_nums_high_spinner.setEnabled(False)
417 if self.parentwin.hud.hud_params["seats_style"] == "C":
418 self.nums_low_spinner.setEnabled(True)
419 self.nums_high_spinner.setEnabled(True)
420 else:
421 self.nums_low_spinner.setEnabled(False)
422 self.nums_high_spinner.setEnabled(False)