Coverage for Aux_Hud.py: 0%
250 statements
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-18 00:10 +0000
« prev ^ index » next coverage.py v7.6.7, created at 2024-11-18 00:10 +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 + " - HUD configuration")
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)
353 grid.addWidget(QLabel(f"Stat set: {self.parentwin.aw.game_params.name}"), 1, 0)
355 self.show()
356 self.raise_()
358 def delete_event(self):
359 self.parentwin.menu_is_popped = False
360 self.destroy()
362 def callback(self, checkState, data=None):
363 if data == "kill":
364 self.parentwin.hud.parent.kill_hud("kill", self.parentwin.hud.table.key)
365 if data == "blacklist":
366 self.parentwin.hud.parent.blacklist_hud("kill", self.parentwin.hud.table.key)
367 if data == "save":
368 # This calls the save_layout method of the Hud object. The Hud object
369 # then calls the save_layout method in each installed AW.
370 self.parentwin.hud.save_layout()
371 self.delete_event()
373 def build_button(self, labeltext, cbkeyword):
374 button = QPushButton(labeltext)
375 button.clicked.connect(partial(self.callback, data=cbkeyword))
376 return button
378 def build_spinner(self, field, low, high):
379 spinBox = QSpinBox()
380 spinBox.setRange(low, high)
381 spinBox.setValue(self.parentwin.hud.hud_params[field])
382 spinBox.valueChanged.connect(partial(self.change_spin_field_value, field=field))
383 return spinBox
385 def build_combo_and_set_active(self, field, combo_dict):
386 widget = QComboBox()
387 for pos in combo_dict:
388 widget.addItem(combo_dict[pos][0])
389 if combo_dict[pos][1] == self.parentwin.hud.hud_params[field]:
390 widget.setCurrentIndex(pos)
391 widget.currentIndexChanged[int].connect(
392 partial(self.change_combo_field_value, field=field, combo_dict=combo_dict)
393 )
394 return widget
396 def change_combo_field_value(self, sel, field, combo_dict):
397 self.parentwin.hud.hud_params[field] = combo_dict[sel][1]
398 self.set_spinners_active()
400 def change_spin_field_value(self, value, field):
401 self.parentwin.hud.hud_params[field] = value
403 def set_spinners_active(self):
404 if self.parentwin.hud.hud_params["h_stat_range"] == "T":
405 self.h_hud_days_spinner.setEnabled(True)
406 else:
407 self.h_hud_days_spinner.setEnabled(False)
408 if self.parentwin.hud.hud_params["stat_range"] == "T":
409 self.hud_days_spinner.setEnabled(True)
410 else:
411 self.hud_days_spinner.setEnabled(False)
412 if self.parentwin.hud.hud_params["h_seats_style"] == "C":
413 self.h_nums_low_spinner.setEnabled(True)
414 self.h_nums_high_spinner.setEnabled(True)
415 else:
416 self.h_nums_low_spinner.setEnabled(False)
417 self.h_nums_high_spinner.setEnabled(False)
418 if self.parentwin.hud.hud_params["seats_style"] == "C":
419 self.nums_low_spinner.setEnabled(True)
420 self.nums_high_spinner.setEnabled(True)
421 else:
422 self.nums_low_spinner.setEnabled(False)
423 self.nums_high_spinner.setEnabled(False)