Coverage for GuiRingPlayerStats.py: 0%

398 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 

4# Copyright 2008-2011 Steffen Schaumburg 

5# This program is free software: you can redistribute it and/or modify 

6# it under the terms of the GNU Affero General Public License as published by 

7# the Free Software Foundation, version 3 of the License. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU General Public License for more details. 

13# 

14# You should have received a copy of the GNU Affero General Public License 

15# along with this program. If not, see <http://www.gnu.org/licenses/>. 

16# In the "official" distribution you can find the license in agpl-3.0.txt. 

17 

18from __future__ import print_function 

19from __future__ import division 

20 

21 

22from past.utils import old_div 

23# import L10n 

24# _ = L10n.get_translation() 

25 

26from time import time 

27 

28from PyQt5.QtCore import Qt 

29from PyQt5.QtGui import QStandardItem, QStandardItemModel 

30from PyQt5.QtWidgets import ( 

31 QCheckBox, 

32 QDialog, 

33 QDialogButtonBox, 

34 QFrame, 

35 QGridLayout, 

36 QHBoxLayout, 

37 QLabel, 

38 QScrollArea, 

39 QSpinBox, 

40 QSplitter, 

41 QTableView, 

42 QVBoxLayout, 

43 QWidget, 

44) 

45 

46import Card 

47import Database 

48import Filters 

49# import Charset 

50 

51colalias, colheading, colshowsumm, colshowposn, colformat, coltype, colxalign = 0, 1, 2, 3, 4, 5, 6 

52ranks = { 

53 "x": 0, 

54 "2": 2, 

55 "3": 3, 

56 "4": 4, 

57 "5": 5, 

58 "6": 6, 

59 "7": 7, 

60 "8": 8, 

61 "9": 9, 

62 "T": 10, 

63 "J": 11, 

64 "Q": 12, 

65 "K": 13, 

66 "A": 14, 

67} 

68fast_names = {"PokerStars": "Zoom", "Bovada": "Zone", "PacificPoker": "Snap", "Winamax": "Go Fast"} 

69onlinehelp = { 

70 "Game": ("Type of Game"), 

71 "Hand": ("Hole Cards"), 

72 "Posn": ("Position"), 

73 "Name": ("Player Name"), 

74 "Hds": ("Number of hands seen"), 

75 "Seats": ("Number of Seats"), 

76 "VPIP": ("Voluntarily put in preflop/3rd street %"), 

77 "PFR": ("Preflop/3rd street raise %"), 

78 "PF3": ("% 3 bet preflop/3rd street"), 

79 "PF4": ("% 4 bet preflop/3rd street"), 

80 "PFF3": ("% fold to 3 bet preflop/3rd street"), 

81 "PFF4": ("% fold to 4 bet preflop/3rd street"), 

82 "AggFac": ("Aggression factor") + ("\n"), 

83 "AggFreq": ("Post-flop aggression frequency"), 

84 "ContBet": ("% continuation bet"), 

85 "RFI": ("% Raise First In / % Raise when first to bet"), 

86 "Steals": ("% steal attempted"), 

87 "CARpre": ("% called a raise preflop"), 

88 "Saw_F": ("Flop/4th street seen %"), 

89 "SawSD": ("Saw Showdown / River"), 

90 "WtSDwsF": ("% went to showdown when seen flop/4th street"), 

91 "W$wsF": ("% won money when seen flop/4th street"), 

92 "W$SD": ("% won some money at showdown"), 

93 "FlAFq": ("Aggression frequency flop/4th street"), 

94 "TuAFq": ("Aggression frequency turn/5th street"), 

95 "RvAFq": ("Aggression frequency river/6th street"), 

96 #'PoFAFq':('Total % agression'), TODO 

97 "Net($)": ("Total Profit"), 

98 "bb/100": ("Big blinds won per 100 hands"), 

99 "Rake($)": ("Amount of rake paid"), 

100 "bbxr/100": ("Big blinds won per 100 hands when excluding rake"), 

101 "Variance": ("Measure of uncertainty"), 

102 "Std. Dev.": ("Measure of uncertainty"), 

103} 

104 

105 

106class GuiRingPlayerStats(QSplitter): 

107 def __init__(self, config, querylist, mainwin, debug=True): 

108 QSplitter.__init__(self, None) 

109 self.debug = debug 

110 self.conf = config 

111 self.main_window = mainwin 

112 self.sql = querylist 

113 

114 self.liststore = [] # gtk.ListStore[] stores the contents of the grids 

115 self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids 

116 

117 self.MYSQL_INNODB = 2 

118 self.PGSQL = 3 

119 self.SQLITE = 4 

120 

121 # create new db connection to avoid conflicts with other threads 

122 self.db = Database.Database(self.conf, sql=self.sql) 

123 self.cursor = self.db.cursor 

124 

125 settings = {} 

126 settings.update(self.conf.get_db_parameters()) 

127 settings.update(self.conf.get_import_parameters()) 

128 settings.update(self.conf.get_default_paths()) 

129 

130 # text used on screen stored here so that it can be configured 

131 self.filterText = {"handhead": ("Hand Breakdown for all levels listed above")} 

132 

133 filters_display = { 

134 "Heroes": True, 

135 "Sites": True, 

136 "Games": True, 

137 "Currencies": True, 

138 "Limits": True, 

139 "LimitSep": True, 

140 "LimitType": True, 

141 "Type": True, 

142 "UseType": "ring", 

143 "Seats": True, 

144 "SeatSep": True, 

145 "Dates": True, 

146 "Groups": True, 

147 "GroupsAll": True, 

148 "Button1": True, 

149 "Button2": True, 

150 } 

151 

152 self.filters = Filters.Filters(self.db, display=filters_display) 

153 self.filters.registerButton1Name(("Filters")) 

154 self.filters.registerButton1Callback(self.showDetailFilter) 

155 self.filters.registerButton2Name(("Refresh Stats")) 

156 self.filters.registerButton2Callback(self.refreshStats) 

157 

158 scroll = QScrollArea() 

159 scroll.setWidget(self.filters) 

160 

161 # ToDo: store in config 

162 # ToDo: create popup to adjust column config 

163 # columns to display, keys match column name returned by sql, values in tuple are: 

164 # is column displayed(summary then position), column heading, xalignment, formatting, celltype 

165 self.columns = self.conf.get_gui_cash_stat_params() 

166 

167 # Detail filters: This holds the data used in the popup window, extra values are 

168 # added at the end of these lists during processing 

169 # sql test, screen description, min, max 

170 self.handtests = [ # already in filter class : ['h.seats', 'Number of Players', 2, 10] 

171 ["gt.maxSeats", "Size of Table", 2, 10], 

172 ["h.playersVpi", "Players who VPI", 0, 10], 

173 ["h.playersAtStreet1", "Players at Flop", 0, 10], 

174 ["h.playersAtStreet2", "Players at Turn", 0, 10], 

175 ["h.playersAtStreet3", "Players at River", 0, 10], 

176 ["h.playersAtStreet4", "Players at Street7", 0, 10], 

177 ["h.playersAtShowdown", "Players at Showdown", 0, 10], 

178 ["h.street0Raises", "Bets to See Flop", 0, 5], 

179 ["h.street1Raises", "Bets to See Turn", 0, 5], 

180 ["h.street2Raises", "Bets to See River", 0, 5], 

181 ["h.street3Raises", "Bets to See Street7", 0, 5], 

182 ["h.street4Raises", "Bets to See Showdown", 0, 5], 

183 ] 

184 

185 self.cardstests = [ 

186 [Card.DATABASE_FILTERS["pair"], ("Pocket pairs")], 

187 [Card.DATABASE_FILTERS["suited"], ("Suited")], 

188 [Card.DATABASE_FILTERS["suited_connectors"], ("Suited connectors")], 

189 [Card.DATABASE_FILTERS["offsuit"], ("Offsuit")], 

190 [Card.DATABASE_FILTERS["offsuit_connectors"], ("Offsuit connectors")], 

191 ] 

192 self.stats_frame = None 

193 self.stats_vbox = None 

194 self.detailFilters = [] # the data used to enhance the sql select 

195 self.cardsFilters = [] 

196 

197 self.stats_frame = QFrame() 

198 self.stats_frame.setLayout(QVBoxLayout()) 

199 

200 self.stats_vbox = QSplitter(Qt.Vertical) 

201 self.stats_frame.layout().addWidget(self.stats_vbox) 

202 

203 self.addWidget(scroll) 

204 self.addWidget(self.stats_frame) 

205 self.setStretchFactor(0, 0) 

206 self.setStretchFactor(1, 1) 

207 

208 # Make sure Hand column is not displayed. 

209 hand_column = next((x for x in self.columns if x[0] == "hand")) 

210 hand_column[colshowsumm] = hand_column[colshowposn] = False 

211 

212 # If rfi and steal both on for summaries, turn rfi off. 

213 rfi_column = next((x for x in self.columns if x[0] == "rfi"), None) 

214 steals_column = next((x for x in self.columns if x[0] == "steals"), None) 

215 

216 if rfi_column and steals_column: 

217 if rfi_column[colshowsumm] and steals_column[colshowsumm]: 

218 rfi_column[colshowsumm] = False 

219 

220 # If rfi and steal both on for position breakdowns, turn steals off. 

221 if rfi_column and steals_column: 

222 if rfi_column[colshowposn] and steals_column[colshowposn]: 

223 steals_column[colshowposn] = False 

224 

225 def refreshStats(self, checkState): 

226 self.liststore = [] 

227 self.listcols = [] 

228 self.stats_frame.layout().removeWidget(self.stats_vbox) 

229 self.stats_vbox.setParent(None) 

230 self.stats_vbox = QSplitter(Qt.Vertical) 

231 self.stats_frame.layout().addWidget(self.stats_vbox) 

232 self.fillStatsFrame(self.stats_vbox) 

233 

234 if self.liststore: 

235 topsize = self.stats_vbox.widget(0).sizeHint().height() 

236 self.stats_vbox.setSizes([topsize, self.stats_vbox.height() - topsize]) 

237 self.stats_vbox.setStretchFactor(0, 0) 

238 self.stats_vbox.setStretchFactor(1, 1) 

239 

240 def fillStatsFrame(self, vbox): 

241 sites = self.filters.getSites() 

242 heroes = self.filters.getHeroes() 

243 siteids = self.filters.getSiteIds() 

244 limits = self.filters.getLimits() 

245 seats = self.filters.getSeats() 

246 groups = self.filters.getGroups() 

247 dates = self.filters.getDates() 

248 games = self.filters.getGames() 

249 currencies = self.filters.getCurrencies() 

250 sitenos = [] 

251 playerids = [] 

252 

253 # Which sites are selected? 

254 for site in sites: 

255 sitenos.append(siteids[site]) 

256 _hname = heroes.get(site, "") 

257 if not _hname: 

258 raise ValueError(f"Hero name not found for site {site}") 

259 result = self.db.get_player_id(self.conf, site, _hname) 

260 if result is not None: 

261 playerids.append(int(result)) 

262 

263 if not sitenos: 

264 # Should probably pop up here. 

265 print(("No sites selected - defaulting to PokerStars")) 

266 sitenos = [2] 

267 if not playerids: 

268 print(("No player ids found")) 

269 return 

270 if not limits: 

271 print(("No limits found")) 

272 return 

273 

274 self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies) 

275 

276 def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies): 

277 startTime = time() 

278 show_detail = True 

279 

280 # # Display summary table at top of page 

281 # # 3rd parameter passes extra flags, currently includes: 

282 # # holecards - whether to display card breakdown (True/False) 

283 # # numhands - min number hands required when displaying all players 

284 # # gridnum - index for grid data structures 

285 flags = [False, self.filters.getNumHands(), 0] 

286 self.addGrid( 

287 vbox, "playerDetailedStats", flags, playerids, sitenos, limits, seats, groups, dates, games, currencies 

288 ) 

289 

290 if "allplayers" in groups: 

291 # can't currently do this combination so skip detailed table 

292 show_detail = False 

293 

294 if show_detail: 

295 # Separator 

296 frame = QWidget() 

297 vbox2 = QVBoxLayout() 

298 vbox2.setContentsMargins(0, 0, 0, 0) 

299 frame.setLayout(vbox2) 

300 vbox.addWidget(frame) 

301 heading = QLabel(self.filterText["handhead"]) 

302 heading.setAlignment(Qt.AlignHCenter) 

303 vbox2.addWidget(heading) 

304 

305 # Detailed table 

306 flags[0] = True 

307 flags[2] = 1 

308 self.addGrid( 

309 vbox2, "playerDetailedStats", flags, playerids, sitenos, limits, seats, groups, dates, games, currencies 

310 ) 

311 

312 self.db.rollback() 

313 print(("Stats page displayed in %4.2f seconds") % (time() - startTime)) 

314 

315 def addGrid(self, vbox, query, flags, playerids, sitenos, limits, seats, groups, dates, games, currencies): 

316 sqlrow = 0 

317 if not flags: 

318 holecards, grid = False, 0 

319 else: 

320 holecards, grid = flags[0], flags[2] 

321 

322 tmp = self.sql.query[query] 

323 tmp = self.refineQuery(tmp, flags, playerids, sitenos, limits, seats, groups, dates, games, currencies) 

324 self.cursor.execute(tmp) 

325 result = self.cursor.fetchall() 

326 colnames = [desc[0].lower() for desc in self.cursor.description] 

327 

328 # pre-fetch some constant values: 

329 colshow = colshowsumm 

330 if "posn" in groups: 

331 colshow = colshowposn 

332 self.cols_to_show = [x for x in self.columns if x[colshow]] 

333 hgametypeid_idx = colnames.index("hgametypeid") 

334 

335 assert len(self.liststore) == grid, "len(self.liststore)=" + str(len(self.liststore)) + " grid-1=" + str(grid) 

336 view = QTableView() 

337 self.liststore.append(QStandardItemModel(0, len(self.cols_to_show), view)) 

338 self.liststore[grid].setSortRole(Qt.UserRole) 

339 view.setModel(self.liststore[grid]) 

340 view.verticalHeader().hide() 

341 vbox.addWidget(view) 

342 self.listcols.append([]) 

343 

344 # Create header row eg column: ("game", True, "Game", 0.0, "%s") 

345 for col, column in enumerate(self.cols_to_show): 

346 if column[colalias] == "game" and holecards: 

347 s = [x for x in self.columns if x[colalias] == "hand"][0][colheading] 

348 else: 

349 s = column[colheading] 

350 self.listcols[grid].append(s) 

351 self.liststore[grid].setHorizontalHeaderLabels(self.listcols[grid]) 

352 

353 rows = len(result) # +1 for title row 

354 

355 while sqlrow < rows: 

356 treerow = [] 

357 for col, column in enumerate(self.cols_to_show): 

358 if column[colalias] in colnames: 

359 value = result[sqlrow][colnames.index(column[colalias])] 

360 if column[colalias] == "plposition": 

361 if value == "B": 

362 value = "BB" 

363 elif value == "S": 

364 value = "SB" 

365 elif value == "0": 

366 value = "Btn" 

367 else: 

368 if column[colalias] == "game": 

369 if holecards: 

370 value = Card.decodeStartHandValue( 

371 result[sqlrow][colnames.index("category")], result[sqlrow][hgametypeid_idx] 

372 ) 

373 else: 

374 minbb = result[sqlrow][colnames.index("minbigblind")] 

375 maxbb = result[sqlrow][colnames.index("maxbigblind")] 

376 value = ( 

377 result[sqlrow][colnames.index("limittype")] 

378 + " " 

379 + result[sqlrow][colnames.index("category")].title() 

380 + " " 

381 + result[sqlrow][colnames.index("name")] 

382 + " " 

383 + result[sqlrow][colnames.index("currency")] 

384 + " " 

385 ) 

386 if 100 * int(old_div(minbb, 100.0)) != minbb: 

387 value += "%.2f" % (old_div(minbb, 100.0)) 

388 else: 

389 value += "%.0f" % (old_div(minbb, 100.0)) 

390 if minbb != maxbb: 

391 if 100 * int(old_div(maxbb, 100.0)) != maxbb: 

392 value += " - %.2f" % (old_div(maxbb, 100.0)) 

393 else: 

394 value += " - %.0f" % (old_div(maxbb, 100.0)) 

395 ante = result[sqlrow][colnames.index("ante")] 

396 if ante > 0: 

397 value += " ante: %.2f" % (old_div(ante, 100.0)) 

398 if result[sqlrow][colnames.index("fast")] == 1: 

399 value += " " + fast_names[result[sqlrow][colnames.index("name")]] 

400 else: 

401 continue 

402 item = QStandardItem("") 

403 sortValue = -1e9 

404 if value is not None and value != -999: 

405 item = QStandardItem(column[colformat] % value) 

406 if column[colalias] == "game" and holecards: 

407 if result[sqlrow][colnames.index("category")] == "holdem": 

408 sortValue = ( 

409 1000 * ranks[value[0]] 

410 + 10 * ranks[value[1]] 

411 + (1 if len(value) == 3 and value[2] == "s" else 0) 

412 ) 

413 else: 

414 sortValue = -1 

415 elif column[colalias] in ("game", "pname"): 

416 sortValue = value 

417 elif column[colalias] == "plposition": 

418 sortValue = ["BB", "SB", "Btn", "1", "2", "3", "4", "5", "6", "7"].index(value) 

419 else: 

420 sortValue = float(value) 

421 item.setData(sortValue, Qt.UserRole) 

422 item.setEditable(False) 

423 if col != 0: 

424 item.setTextAlignment(Qt.AlignRight | Qt.AlignVCenter) 

425 if column[colalias] != "game": 

426 item.setToolTip( 

427 "<big>%s for %s</big><br/><i>%s</i>" 

428 % (column[colheading], treerow[0].text(), onlinehelp[column[colheading]]) 

429 ) 

430 treerow.append(item) 

431 self.liststore[grid].appendRow(treerow) 

432 sqlrow += 1 

433 

434 view.resizeColumnsToContents() 

435 view.setSortingEnabled( 

436 True 

437 ) # do this after resizing columns, otherwise it leaves room for the sorting triangle in every heading 

438 view.resizeColumnToContents(0) # we want room for the sorting triangle in column 0 where it starts. 

439 view.resizeRowsToContents() 

440 

441 def refineQuery(self, query, flags, playerids, sitenos, limits, seats, groups, dates, games, currencies): 

442 having = "" 

443 if not flags: 

444 holecards = False 

445 numhands = 0 

446 else: 

447 holecards = flags[0] 

448 numhands = flags[1] 

449 colshow = colshowsumm 

450 if "posn" in groups: 

451 colshow = colshowposn 

452 

453 pname_column = next((x for x in self.columns if x[0] == "pname")) 

454 if "allplayers" in groups: 

455 nametest = "(hp.playerId)" 

456 if holecards or "posn" in groups: 

457 pname = "'all players'" 

458 # set flag in self.columns to not show player name column 

459 pname_column[colshow] = False 

460 # can't do this yet (re-write doing more maths in python instead of sql?) 

461 if numhands: 

462 nametest = "(-1)" 

463 else: 

464 pname = "p.name" 

465 # set flag in self.columns to show player name column 

466 pname_column[colshow] = True 

467 if numhands: 

468 having = " and count(1) > %d " % (numhands,) 

469 else: 

470 if playerids: 

471 nametest = str(tuple(playerids)) 

472 nametest = nametest.replace("L", "") 

473 nametest = nametest.replace(",)", ")") 

474 else: 

475 nametest = "1 = 2" 

476 pname = "p.name" 

477 # set flag in self.columns to not show player name column 

478 pname_column[colshow] = False 

479 query = query.replace("<player_test>", nametest) 

480 query = query.replace("<playerName>", pname) 

481 query = query.replace("<havingclause>", having) 

482 

483 gametest = "" 

484 for m in list(self.filters.display.items()): 

485 if m[0] == "Games" and m[1]: 

486 if len(games) > 0: 

487 gametest = str(tuple(games)) 

488 gametest = gametest.replace("L", "") 

489 gametest = gametest.replace(",)", ")") 

490 gametest = gametest.replace("u'", "'") 

491 gametest = "and gt.category in %s" % gametest 

492 else: 

493 gametest = "and gt.category IS NULL" 

494 query = query.replace("<game_test>", gametest) 

495 

496 currencytest = str(tuple(currencies)) 

497 currencytest = currencytest.replace(",)", ")") 

498 currencytest = currencytest.replace("u'", "'") 

499 currencytest = "AND gt.currency in %s" % currencytest 

500 query = query.replace("<currency_test>", currencytest) 

501 

502 sitetest = "" 

503 for m in list(self.filters.display.items()): 

504 if m[0] == "Sites" and m[1]: 

505 if len(sitenos) > 0: 

506 sitetest = str(tuple(sitenos)) 

507 sitetest = sitetest.replace("L", "") 

508 sitetest = sitetest.replace(",)", ")") 

509 sitetest = sitetest.replace("u'", "'") 

510 sitetest = "and gt.siteId in %s" % sitetest 

511 else: 

512 sitetest = "and gt.siteId IS NULL" 

513 query = query.replace("<site_test>", sitetest) 

514 

515 if seats: 

516 query = query.replace("<seats_test>", "between " + str(seats["from"]) + " and " + str(seats["to"])) 

517 if "seats" in groups: 

518 query = query.replace("<groupbyseats>", ",h.seats") 

519 query = query.replace("<orderbyseats>", ",h.seats") 

520 else: 

521 query = query.replace("<groupbyseats>", "") 

522 query = query.replace("<orderbyseats>", "") 

523 else: 

524 query = query.replace("<seats_test>", "between 0 and 100") 

525 query = query.replace("<groupbyseats>", "") 

526 query = query.replace("<orderbyseats>", "") 

527 

528 bbtest = self.filters.get_limits_where_clause(limits) 

529 

530 query = query.replace("<gtbigBlind_test>", bbtest) 

531 

532 if holecards: # re-use level variables for hole card query 

533 query = query.replace("<hgametypeId>", "hp.startcards") 

534 query = query.replace( 

535 "<orderbyhgametypeId>", 

536 ",case when floor((hp.startcards-1)/13) >= mod((hp.startcards-1),13) then hp.startcards + 0.1 " 

537 + " else 13*mod((hp.startcards-1),13) + floor((hp.startcards-1)/13) + 1 " 

538 + " end desc ", 

539 ) 

540 else: 

541 query = query.replace("<orderbyhgametypeId>", "") 

542 groupLevels = "limits" not in groups 

543 if groupLevels: 

544 query = query.replace("<hgametypeId>", "p.name") # need to use p.name for sqlite posn stats to work 

545 else: 

546 query = query.replace("<hgametypeId>", "h.gametypeId") 

547 

548 # process self.detailFilters (a list of tuples) 

549 flagtest = "" 

550 if self.detailFilters: 

551 for f in self.detailFilters: 

552 if len(f) == 3: 

553 # X between Y and Z 

554 flagtest += " and %s between %s and %s " % (f[0], str(f[1]), str(f[2])) 

555 query = query.replace("<flagtest>", flagtest) 

556 if self.cardsFilters: 

557 cardstests = [] 

558 

559 for cardFilter in self.cardsFilters: 

560 cardstests.append(cardFilter) 

561 cardstests = "".join(("and (", " or ".join(cardstests), ")")) 

562 else: 

563 cardstests = "" 

564 query = query.replace("<cardstest>", cardstests) 

565 # allow for differences in sql cast() function: 

566 if self.db.backend == self.MYSQL_INNODB: 

567 query = query.replace("<signed>", "signed ") 

568 else: 

569 query = query.replace("<signed>", "") 

570 

571 # Filter on dates 

572 query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'") 

573 

574 # Group by position? 

575 plposition_column = next((x for x in self.columns if x[0] == "plposition")) 

576 if "posn" in groups: 

577 query = query.replace("<position>", "hp.position") 

578 plposition_column[colshow] = True 

579 else: 

580 query = query.replace("<position>", "gt.base") 

581 plposition_column[colshow] = False 

582 print(query) 

583 return query 

584 

585 def showDetailFilter(self, checkState): 

586 detailDialog = QDialog(self.main_window) 

587 detailDialog.setWindowTitle(("Detailed Filters")) 

588 

589 handbox = QVBoxLayout() 

590 detailDialog.setLayout(handbox) 

591 

592 label = QLabel(("Hand Filters:")) 

593 handbox.addWidget(label) 

594 label.setAlignment(Qt.AlignCenter) 

595 

596 grid = QGridLayout() 

597 handbox.addLayout(grid) 

598 for row, htest in enumerate(self.handtests): 

599 cb = QCheckBox() 

600 lbl_from = QLabel(htest[1]) 

601 lbl_tween = QLabel(("between")) 

602 lbl_to = QLabel(("and")) 

603 sb1 = QSpinBox() 

604 sb1.setRange(0, 10) 

605 sb1.setValue(htest[2]) 

606 sb2 = QSpinBox() 

607 sb2.setRange(2, 10) 

608 sb2.setValue(htest[3]) 

609 

610 for df in self.detailFilters: 

611 if df[0] == htest[0]: 

612 cb.setChecked(True) 

613 break 

614 

615 grid.addWidget(cb, row, 0) 

616 grid.addWidget(lbl_from, row, 1, Qt.AlignLeft) 

617 grid.addWidget(lbl_tween, row, 2) 

618 grid.addWidget(sb1, row, 3) 

619 grid.addWidget(lbl_to, row, 4) 

620 grid.addWidget(sb2, row, 5) 

621 

622 htest[4:7] = [cb, sb1, sb2] 

623 

624 label = QLabel(("Restrict to hand types:")) 

625 handbox.addWidget(label) 

626 for ctest in self.cardstests: 

627 hbox = QHBoxLayout() 

628 handbox.addLayout(hbox) 

629 cb = QCheckBox() 

630 if ctest[0] in self.cardsFilters: 

631 cb.setChecked(True) 

632 label = QLabel(ctest[1]) 

633 hbox.addWidget(cb) 

634 hbox.addWidget(label) 

635 ctest[2:3] = [cb] 

636 btnBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) 

637 handbox.addWidget(btnBox) 

638 btnBox.accepted.connect(detailDialog.accept) 

639 btnBox.rejected.connect(detailDialog.reject) 

640 response = detailDialog.exec_() 

641 

642 if response: 

643 self.detailFilters = [] 

644 for ht in self.handtests: 

645 if ht[4].isChecked(): 

646 self.detailFilters.append((ht[0], ht[5].value(), ht[6].value())) 

647 ht[2], ht[3] = ht[5].value(), ht[6].value() 

648 self.cardsFilters = [] 

649 for ct in self.cardstests: 

650 if ct[2].isChecked(): 

651 self.cardsFilters.append(ct[0]) 

652 self.refreshStats(None) 

653 

654 

655if __name__ == "__main__": 

656 import Configuration 

657 

658 config = Configuration.Config() 

659 

660 settings = {} 

661 

662 settings.update(config.get_db_parameters()) 

663 settings.update(config.get_import_parameters()) 

664 settings.update(config.get_default_paths()) 

665 

666 from PyQt5.QtWidgets import QApplication, QMainWindow 

667 

668 app = QApplication([]) 

669 import SQL 

670 

671 sql = SQL.Sql(db_server=settings["db-server"]) 

672 main_window = QMainWindow() 

673 i = GuiRingPlayerStats(config, sql, main_window) 

674 main_window.setCentralWidget(i) 

675 main_window.show() 

676 main_window.resize(1400, 800) 

677 app.exec_()