Coverage for GuiRingPlayerStats.py: 0%

394 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-28 16:41 +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 (QCheckBox, QDialog, QDialogButtonBox, 

31 QFrame, QGridLayout, QHBoxLayout, QLabel, 

32 QScrollArea, QSpinBox, QSplitter, 

33 QTableView, QVBoxLayout, QWidget) 

34 

35import Card 

36import Database 

37import Filters 

38import Charset 

39 

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

41ranks = {'x':0, '2':2, '3':3, '4':4, '5':5, '6':6, '7':7, '8':8, '9':9, 'T':10, 'J':11, 'Q':12, 'K':13, 'A':14} 

42fast_names = { 'PokerStars':'Zoom', 'Bovada':'Zone', 'PacificPoker': 'Snap', 'Winamax': 'Go Fast'} 

43onlinehelp = {'Game':('Type of Game'), 

44 'Hand':('Hole Cards'), 

45 'Posn':('Position'), 

46 'Name':('Player Name'), 

47 'Hds':('Number of hands seen'), 

48 'Seats':('Number of Seats'), 

49 'VPIP':('Voluntarily put in preflop/3rd street %'), 

50 'PFR':('Preflop/3rd street raise %'), 

51 'PF3':('% 3 bet preflop/3rd street'), 

52 'PF4':('% 4 bet preflop/3rd street'), 

53 'PFF3':('% fold to 3 bet preflop/3rd street'), 

54 'PFF4':('% fold to 4 bet preflop/3rd street'), 

55 'AggFac':('Aggression factor')+("\n"), 

56 'AggFreq':('Post-flop aggression frequency'), 

57 'ContBet':('% continuation bet'), 

58 'RFI':('% Raise First In / % Raise when first to bet'), 

59 'Steals':('% steal attempted'), 

60 'CARpre':('% called a raise preflop'), 

61 'Saw_F':('Flop/4th street seen %'), 

62 'SawSD':('Saw Showdown / River'), 

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

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

65 'W$SD':('% won some money at showdown'), 

66 'FlAFq':('Aggression frequency flop/4th street'), 

67 'TuAFq':('Aggression frequency turn/5th street'), 

68 'RvAFq':('Aggression frequency river/6th street'), 

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

70 'Net($)':('Total Profit'), 

71 'bb/100':('Big blinds won per 100 hands'), 

72 'Rake($)':('Amount of rake paid'), 

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

74 'Variance':('Measure of uncertainty'), 

75 'Std. Dev.':('Measure of uncertainty') 

76 } 

77 

78 

79class GuiRingPlayerStats(QSplitter): 

80 

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

82 QSplitter.__init__(self, None) 

83 self.debug = debug 

84 self.conf = config 

85 self.main_window = mainwin 

86 self.sql = querylist 

87 

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

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

90 

91 self.MYSQL_INNODB = 2 

92 self.PGSQL = 3 

93 self.SQLITE = 4 

94 

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

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

97 self.cursor = self.db.cursor 

98 

99 settings = {} 

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

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

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

103 

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

105 self.filterText = {'handhead':('Hand Breakdown for all levels listed above') 

106 } 

107 

108 filters_display = { "Heroes" : True, 

109 "Sites" : True, 

110 "Games" : True, 

111 "Currencies": True, 

112 "Limits" : True, 

113 "LimitSep" : True, 

114 "LimitType" : True, 

115 "Type" : True, 

116 "UseType": 'ring', 

117 "Seats" : True, 

118 "SeatSep" : True, 

119 "Dates" : True, 

120 "Groups" : True, 

121 "GroupsAll" : True, 

122 "Button1" : True, 

123 "Button2" : True 

124 } 

125 

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

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

128 self.filters.registerButton1Callback(self.showDetailFilter) 

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

130 self.filters.registerButton2Callback(self.refreshStats) 

131 

132 scroll = QScrollArea() 

133 scroll.setWidget(self.filters) 

134 

135 # ToDo: store in config 

136 # ToDo: create popup to adjust column config 

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

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

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

140 

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

142 # added at the end of these lists during processing 

143 # sql test, screen description, min, max 

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

145 ['gt.maxSeats', 'Size of Table', 2, 10] 

146 ,['h.playersVpi', 'Players who VPI', 0, 10] 

147 ,['h.playersAtStreet1', 'Players at Flop', 0, 10] 

148 ,['h.playersAtStreet2', 'Players at Turn', 0, 10] 

149 ,['h.playersAtStreet3', 'Players at River', 0, 10] 

150 ,['h.playersAtStreet4', 'Players at Street7', 0, 10] 

151 ,['h.playersAtShowdown', 'Players at Showdown', 0, 10] 

152 ,['h.street0Raises', 'Bets to See Flop', 0, 5] 

153 ,['h.street1Raises', 'Bets to See Turn', 0, 5] 

154 ,['h.street2Raises', 'Bets to See River', 0, 5] 

155 ,['h.street3Raises', 'Bets to See Street7', 0, 5] 

156 ,['h.street4Raises', 'Bets to See Showdown', 0, 5] 

157 ] 

158 

159 self.cardstests = [ 

160 [Card.DATABASE_FILTERS['pair'], ('Pocket pairs')], 

161 [Card.DATABASE_FILTERS['suited'], ('Suited')], 

162 [Card.DATABASE_FILTERS['suited_connectors'], ('Suited connectors')], 

163 [Card.DATABASE_FILTERS['offsuit'], ('Offsuit')], 

164 [Card.DATABASE_FILTERS['offsuit_connectors'], ('Offsuit connectors')], 

165 ] 

166 self.stats_frame = None 

167 self.stats_vbox = None 

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

169 self.cardsFilters = [] 

170 

171 self.stats_frame = QFrame() 

172 self.stats_frame.setLayout(QVBoxLayout()) 

173 

174 self.stats_vbox = QSplitter(Qt.Vertical) 

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

176 

177 self.addWidget(scroll) 

178 self.addWidget(self.stats_frame) 

179 self.setStretchFactor(0, 0) 

180 self.setStretchFactor(1, 1) 

181 

182 # Make sure Hand column is not displayed. 

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

184 hand_column[colshowsumm] = hand_column[colshowposn] = False 

185 

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

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

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

189 

190 if rfi_column and steals_column: 

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

192 rfi_column[colshowsumm] = False 

193 

194 

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

196 if rfi_column and steals_column: 

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

198 steals_column[colshowposn] = False 

199 

200 

201 def refreshStats(self, checkState): 

202 self.liststore = [] 

203 self.listcols = [] 

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

205 self.stats_vbox.setParent(None) 

206 self.stats_vbox = QSplitter(Qt.Vertical) 

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

208 self.fillStatsFrame(self.stats_vbox) 

209 

210 if self.liststore: 

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

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

213 self.stats_vbox.setStretchFactor(0, 0) 

214 self.stats_vbox.setStretchFactor(1, 1) 

215 

216 def fillStatsFrame(self, vbox): 

217 sites = self.filters.getSites() 

218 heroes = self.filters.getHeroes() 

219 siteids = self.filters.getSiteIds() 

220 limits = self.filters.getLimits() 

221 seats = self.filters.getSeats() 

222 groups = self.filters.getGroups() 

223 dates = self.filters.getDates() 

224 games = self.filters.getGames() 

225 currencies = self.filters.getCurrencies() 

226 sitenos = [] 

227 playerids = [] 

228 

229 # Which sites are selected? 

230 for site in sites: 

231 sitenos.append(siteids[site]) 

232 _hname = Charset.to_utf8(heroes[site]) 

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

234 if result is not None: 

235 playerids.append(int(result)) 

236 

237 if not sitenos: 

238 #Should probably pop up here. 

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

240 sitenos = [2] 

241 if not playerids: 

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

243 return 

244 if not limits: 

245 print(("No limits found")) 

246 return 

247 

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

249 

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

251 startTime = time() 

252 show_detail = True 

253 

254# # Display summary table at top of page 

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

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

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

258# # gridnum - index for grid data structures 

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

260 self.addGrid(vbox, 'playerDetailedStats', flags, playerids 

261 ,sitenos, limits, seats, groups, dates, games, currencies) 

262 

263 if 'allplayers' in groups: 

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

265 show_detail = False 

266 

267 if show_detail: 

268 # Separator 

269 frame = QWidget() 

270 vbox2 = QVBoxLayout() 

271 vbox2.setContentsMargins(0,0,0,0) 

272 frame.setLayout(vbox2) 

273 vbox.addWidget(frame) 

274 heading = QLabel(self.filterText['handhead']) 

275 heading.setAlignment(Qt.AlignHCenter) 

276 vbox2.addWidget(heading) 

277 

278 # Detailed table 

279 flags[0] = True 

280 flags[2] = 1 

281 self.addGrid(vbox2, 'playerDetailedStats', flags, playerids 

282 ,sitenos, limits, seats, groups, dates, games, currencies) 

283 

284 self.db.rollback() 

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

286 

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

288 sqlrow = 0 

289 if not flags: holecards,grid = False,0 

290 else: holecards,grid = flags[0],flags[2] 

291 

292 tmp = self.sql.query[query] 

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

294 self.cursor.execute(tmp) 

295 result = self.cursor.fetchall() 

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

297 

298 # pre-fetch some constant values: 

299 colshow = colshowsumm 

300 if 'posn' in groups: colshow = colshowposn 

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

302 hgametypeid_idx = colnames.index('hgametypeid') 

303 

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

305 view = QTableView() 

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

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

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

309 view.verticalHeader().hide() 

310 vbox.addWidget(view) 

311 self.listcols.append( [] ) 

312 

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

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

315 if column[colalias] == 'game' and holecards: 

316 s = [x for x in self.columns if x[colalias] == 'hand'][0][colheading] 

317 else: 

318 s = column[colheading] 

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

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

321 

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

323 

324 while sqlrow < rows: 

325 treerow = [] 

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

327 if column[colalias] in colnames: 

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

329 if column[colalias] == 'plposition': 

330 if value == 'B': 

331 value = 'BB' 

332 elif value == 'S': 

333 value = 'SB' 

334 elif value == '0': 

335 value = 'Btn' 

336 else: 

337 if column[colalias] == 'game': 

338 if holecards: 

339 value = Card.decodeStartHandValue(result[sqlrow][colnames.index('category')], result[sqlrow][hgametypeid_idx] ) 

340 else: 

341 minbb = result[sqlrow][colnames.index('minbigblind')] 

342 maxbb = result[sqlrow][colnames.index('maxbigblind')] 

343 value = result[sqlrow][colnames.index('limittype')] + ' ' \ 

344 + result[sqlrow][colnames.index('category')].title() + ' ' \ 

345 + result[sqlrow][colnames.index('name')] + ' ' \ 

346 + result[sqlrow][colnames.index('currency')] + ' ' 

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

348 value += '%.2f' % (old_div(minbb,100.0)) 

349 else: 

350 value += '%.0f' % (old_div(minbb,100.0)) 

351 if minbb != maxbb: 

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

353 value += ' - %.2f' % (old_div(maxbb,100.0)) 

354 else: 

355 value += ' - %.0f' % (old_div(maxbb,100.0)) 

356 ante = result[sqlrow][colnames.index('ante')] 

357 if ante > 0: 

358 value += ' ante: %.2f' % (old_div(ante,100.0)) 

359 if result[sqlrow][colnames.index('fast')] == 1: 

360 value += ' ' + fast_names[result[sqlrow][colnames.index('name')]] 

361 else: 

362 continue 

363 item = QStandardItem('') 

364 sortValue = -1e9 

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

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

367 if column[colalias] == 'game' and holecards: 

368 if result[sqlrow][colnames.index('category')] == 'holdem': 

369 sortValue = 1000 * ranks[value[0]] + 10 * ranks[value[1]] + (1 if len(value) == 3 and value[2] == 's' else 0) 

370 else: 

371 sortValue = -1 

372 elif column[colalias] in ('game', 'pname'): 

373 sortValue = value 

374 elif column[colalias] == 'plposition': 

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

376 else: 

377 sortValue = float(value) 

378 item.setData(sortValue, Qt.UserRole) 

379 item.setEditable(False) 

380 if col != 0: 

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

382 if column[colalias] != 'game': 

383 item.setToolTip('<big>%s for %s</big><br/><i>%s</i>' % (column[colheading],treerow[0].text(),onlinehelp[column[colheading]])) 

384 treerow.append(item) 

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

386 sqlrow += 1 

387 

388 view.resizeColumnsToContents() 

389 view.setSortingEnabled(True) # do this after resizing columns, otherwise it leaves room for the sorting triangle in every heading 

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

391 view.resizeRowsToContents() 

392 

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

394 having = '' 

395 if not flags: 

396 holecards = False 

397 numhands = 0 

398 else: 

399 holecards = flags[0] 

400 numhands = flags[1] 

401 colshow = colshowsumm 

402 if 'posn' in groups: colshow = colshowposn 

403 

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

405 if 'allplayers' in groups: 

406 nametest = "(hp.playerId)" 

407 if holecards or 'posn' in groups: 

408 pname = "'all players'" 

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

410 pname_column[colshow] = False 

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

412 if numhands: 

413 nametest = "(-1)" 

414 else: 

415 pname = "p.name" 

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

417 pname_column[colshow] = True 

418 if numhands: 

419 having = ' and count(1) > %d ' % (numhands,) 

420 else: 

421 if playerids: 

422 nametest = str(tuple(playerids)) 

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

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

425 else: 

426 nametest = "1 = 2" 

427 pname = "p.name" 

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

429 pname_column[colshow] = False 

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

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

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

433 

434 gametest = "" 

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

436 if m[0] == 'Games' and m[1]: 

437 if len(games) > 0: 

438 gametest = str(tuple(games)) 

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

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

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

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

443 else: 

444 gametest = "and gt.category IS NULL" 

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

446 

447 currencytest = str(tuple(currencies)) 

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

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

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

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

452 

453 sitetest = "" 

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

455 if m[0] == 'Sites' and m[1]: 

456 if len(sitenos) > 0: 

457 sitetest = str(tuple(sitenos)) 

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

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

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

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

462 else: 

463 sitetest = "and gt.siteId IS NULL" 

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

465 

466 if seats: 

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

468 if 'seats' in groups: 

469 query = query.replace('<groupbyseats>', ',h.seats') 

470 query = query.replace('<orderbyseats>', ',h.seats') 

471 else: 

472 query = query.replace('<groupbyseats>', '') 

473 query = query.replace('<orderbyseats>', '') 

474 else: 

475 query = query.replace('<seats_test>', 'between 0 and 100') 

476 query = query.replace('<groupbyseats>', '') 

477 query = query.replace('<orderbyseats>', '') 

478 

479 bbtest = self.filters.get_limits_where_clause(limits) 

480 

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

482 

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

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

485 query = query.replace("<orderbyhgametypeId>" 

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

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

488 + " end desc ") 

489 else: 

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

491 groupLevels = 'limits' not in groups 

492 if groupLevels: 

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

494 else: 

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

496 

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

498 flagtest = '' 

499 if self.detailFilters: 

500 for f in self.detailFilters: 

501 if len(f) == 3: 

502 # X between Y and Z 

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

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

505 if self.cardsFilters: 

506 cardstests = [] 

507 

508 for cardFilter in self.cardsFilters: 

509 cardstests.append(cardFilter) 

510 cardstests = ''.join(('and (', ' or '.join(cardstests), ')')) 

511 else: 

512 cardstests = '' 

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

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

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

516 query = query.replace("<signed>", 'signed ') 

517 else: 

518 query = query.replace("<signed>", '') 

519 

520 # Filter on dates 

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

522 

523 # Group by position? 

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

525 if 'posn' in groups: 

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

527 plposition_column[colshow] = True 

528 else: 

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

530 plposition_column[colshow] = False 

531 print(query) 

532 return(query) 

533 

534 def showDetailFilter(self, checkState): 

535 detailDialog = QDialog(self.main_window) 

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

537 

538 handbox = QVBoxLayout() 

539 detailDialog.setLayout(handbox) 

540 

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

542 handbox.addWidget(label) 

543 label.setAlignment(Qt.AlignCenter) 

544 

545 grid = QGridLayout() 

546 handbox.addLayout(grid) 

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

548 cb = QCheckBox() 

549 lbl_from = QLabel(htest[1]) 

550 lbl_tween = QLabel(('between')) 

551 lbl_to = QLabel(('and')) 

552 sb1 = QSpinBox() 

553 sb1.setRange(0, 10) 

554 sb1.setValue(htest[2]) 

555 sb2 = QSpinBox() 

556 sb2.setRange(2, 10) 

557 sb2.setValue(htest[3]) 

558 

559 for df in self.detailFilters: 

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

561 cb.setChecked(True) 

562 break 

563 

564 grid.addWidget(cb, row, 0) 

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

566 grid.addWidget(lbl_tween, row, 2) 

567 grid.addWidget(sb1, row, 3) 

568 grid.addWidget(lbl_to, row, 4) 

569 grid.addWidget(sb2, row, 5) 

570 

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

572 

573 label = QLabel(('Restrict to hand types:')) 

574 handbox.addWidget(label) 

575 for ctest in self.cardstests: 

576 hbox = QHBoxLayout() 

577 handbox.addLayout(hbox) 

578 cb = QCheckBox() 

579 if ctest[0] in self.cardsFilters: 

580 cb.setChecked(True) 

581 label = QLabel(ctest[1]) 

582 hbox.addWidget(cb) 

583 hbox.addWidget(label) 

584 ctest[2:3] = [cb] 

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

586 handbox.addWidget(btnBox) 

587 btnBox.accepted.connect(detailDialog.accept) 

588 btnBox.rejected.connect(detailDialog.reject) 

589 response = detailDialog.exec_() 

590 

591 if response: 

592 self.detailFilters = [] 

593 for ht in self.handtests: 

594 if ht[4].isChecked(): 

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

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

597 self.cardsFilters = [] 

598 for ct in self.cardstests: 

599 if ct[2].isChecked(): 

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

601 self.refreshStats(None) 

602 

603if __name__ == "__main__": 

604 import Configuration 

605 config = Configuration.Config() 

606 

607 settings = {} 

608 

609 settings.update(config.get_db_parameters()) 

610 settings.update(config.get_import_parameters()) 

611 settings.update(config.get_default_paths()) 

612 

613 from PyQt5.QtWidgets import QApplication, QMainWindow 

614 app = QApplication([]) 

615 import SQL 

616 sql = SQL.Sql(db_server=settings['db-server']) 

617 main_window = QMainWindow() 

618 i = GuiRingPlayerStats(config, sql, main_window) 

619 main_window.setCentralWidget(i) 

620 main_window.show() 

621 main_window.resize(1400, 800) 

622 app.exec_()