Coverage for Aux_Hud.py: 0%

249 statements  

« prev     ^ index     » next       coverage.py v7.6.3, created at 2024-10-14 11:07 +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 

18 

19######################################################################## 

20 

21### Aux_Hud.py 

22# 

23# Simple HUD display for FreePokerTools/fpdb HUD. 

24### 

25 

26# import L10n 

27# _ = L10n.get_translation() 

28 

29# Standard Library modules 

30import os 

31import logging 

32 

33from functools import partial 

34 

35from PyQt5.QtGui import QFont, QPixmap 

36from PyQt5.QtCore import Qt 

37from PyQt5.QtWidgets import QComboBox, QGridLayout, QHBoxLayout, QLabel, QPushButton, QSpinBox, QVBoxLayout, QWidget 

38 

39# FreePokerTools modules 

40import Aux_Base 

41import Stats 

42import Popup 

43import Configuration 

44 

45 

46# logging has been set up in fpdb.py or HUD_main.py, use their settings: 

47log = logging.getLogger("hud") 

48 

49 

50class Simple_HUD(Aux_Base.Aux_Seats): 

51 """A simple HUD class based on the Aux_Window interface.""" 

52 

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"])) 

73 

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. 

77 

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 

82 

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 

86 

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)] 

90 

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 ) 

101 

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) 

105 

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) 

109 

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 

114 

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() 

122 

123 def save_layout(self, *args): 

124 """Save new layout back to the aux element in the config file.""" 

125 

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 ) 

130 

131 

132class Simple_Stat_Window(Aux_Base.Seat_Window): 

133 """Simple window class for stat windows.""" 

134 

135 def __init__(self, aw=None, seat=None): 

136 super(Simple_Stat_Window, self).__init__(aw, seat) 

137 self.popup_count = 0 

138 

139 def button_release_right(self, event): # show pop up 

140 widget = self.childAt(event.pos()) 

141 

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{{}}") 

153 

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)] 

162 

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) 

174 

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) 

184 

185 

186class Simple_stat(object): 

187 """A simple class for displaying a single stat.""" 

188 

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 

200 

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])) 

207 

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 + "}") 

216 

217 

218class Simple_label(QLabel): 

219 pass 

220 

221 

222class Simple_table_mw(Aux_Base.Seat_Window): 

223 """Create a default table hud menu label""" 

224 

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. 

227 

228 # BTW: It might be better to do this with a different AW. 

229 

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 

235 

236 # self.connect("configure_event", self.configure_event_cb, "auxmenu") base class will deal with this 

237 

238 try: 

239 self.menu_label = hud.hud_params["label"] 

240 except Exception: 

241 self.menu_label = "fpdb menu" 

242 

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};") 

249 

250 self.setLayout(QVBoxLayout()) 

251 self.layout().setContentsMargins(0, 0, 0, 0) 

252 self.layout().addWidget(lab) 

253 

254 self.move(self.hud.table.x + self.aw.xshift, self.hud.table.y + self.aw.yshift) 

255 

256 def button_press_right(self, event): 

257 """Handle button clicks in the FPDB main menu event box.""" 

258 

259 if not self.menu_is_popped: 

260 self.menu_is_popped = True 

261 Simple_table_popup_menu(self) 

262 

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) 

266 

267 

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) 

276 

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() 

306 

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)) 

313 

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) 

330 

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) 

347 

348 self.set_spinners_active() 

349 

350 grid.addLayout(vbox1, 0, 0) 

351 grid.addLayout(vbox2, 0, 1) 

352 grid.addLayout(vbox3, 0, 2) 

353 

354 self.show() 

355 self.raise_() 

356 

357 def delete_event(self): 

358 self.parentwin.menu_is_popped = False 

359 self.destroy() 

360 

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() 

371 

372 def build_button(self, labeltext, cbkeyword): 

373 button = QPushButton(labeltext) 

374 button.clicked.connect(partial(self.callback, data=cbkeyword)) 

375 return button 

376 

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 

383 

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 

394 

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() 

398 

399 def change_spin_field_value(self, value, field): 

400 self.parentwin.hud.hud_params[field] = value 

401 

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)