Coverage for GuiRingPlayerStats.py: 0%
398 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 -*-
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.
18from __future__ import print_function
19from __future__ import division
22from past.utils import old_div
23# import L10n
24# _ = L10n.get_translation()
26from time import time
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)
46import Card
47import Database
48import Filters
49# import Charset
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}
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
114 self.liststore = [] # gtk.ListStore[] stores the contents of the grids
115 self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids
117 self.MYSQL_INNODB = 2
118 self.PGSQL = 3
119 self.SQLITE = 4
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
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())
130 # text used on screen stored here so that it can be configured
131 self.filterText = {"handhead": ("Hand Breakdown for all levels listed above")}
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 }
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)
158 scroll = QScrollArea()
159 scroll.setWidget(self.filters)
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()
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 ]
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 = []
197 self.stats_frame = QFrame()
198 self.stats_frame.setLayout(QVBoxLayout())
200 self.stats_vbox = QSplitter(Qt.Vertical)
201 self.stats_frame.layout().addWidget(self.stats_vbox)
203 self.addWidget(scroll)
204 self.addWidget(self.stats_frame)
205 self.setStretchFactor(0, 0)
206 self.setStretchFactor(1, 1)
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
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)
216 if rfi_column and steals_column:
217 if rfi_column[colshowsumm] and steals_column[colshowsumm]:
218 rfi_column[colshowsumm] = False
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
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)
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)
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 = []
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))
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
274 self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies)
276 def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies):
277 startTime = time()
278 show_detail = True
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 )
290 if "allplayers" in groups:
291 # can't currently do this combination so skip detailed table
292 show_detail = False
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)
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 )
312 self.db.rollback()
313 print(("Stats page displayed in %4.2f seconds") % (time() - startTime))
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]
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]
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")
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([])
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])
353 rows = len(result) # +1 for title row
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
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()
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
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)
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)
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)
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)
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>", "")
528 bbtest = self.filters.get_limits_where_clause(limits)
530 query = query.replace("<gtbigBlind_test>", bbtest)
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")
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 = []
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>", "")
571 # Filter on dates
572 query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
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
585 def showDetailFilter(self, checkState):
586 detailDialog = QDialog(self.main_window)
587 detailDialog.setWindowTitle(("Detailed Filters"))
589 handbox = QVBoxLayout()
590 detailDialog.setLayout(handbox)
592 label = QLabel(("Hand Filters:"))
593 handbox.addWidget(label)
594 label.setAlignment(Qt.AlignCenter)
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])
610 for df in self.detailFilters:
611 if df[0] == htest[0]:
612 cb.setChecked(True)
613 break
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)
622 htest[4:7] = [cb, sb1, sb2]
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_()
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)
655if __name__ == "__main__":
656 import Configuration
658 config = Configuration.Config()
660 settings = {}
662 settings.update(config.get_db_parameters())
663 settings.update(config.get_import_parameters())
664 settings.update(config.get_default_paths())
666 from PyQt5.QtWidgets import QApplication, QMainWindow
668 app = QApplication([])
669 import SQL
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_()