Coverage for GuiRingPlayerStats.py: 0%
394 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-09-28 16:41 +0000
« 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 -*-
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 (QCheckBox, QDialog, QDialogButtonBox,
31 QFrame, QGridLayout, QHBoxLayout, QLabel,
32 QScrollArea, QSpinBox, QSplitter,
33 QTableView, QVBoxLayout, QWidget)
35import Card
36import Database
37import Filters
38import Charset
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 }
79class GuiRingPlayerStats(QSplitter):
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
88 self.liststore = [] # gtk.ListStore[] stores the contents of the grids
89 self.listcols = [] # gtk.TreeViewColumn[][] stores the columns in the grids
91 self.MYSQL_INNODB = 2
92 self.PGSQL = 3
93 self.SQLITE = 4
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
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())
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 }
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 }
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)
132 scroll = QScrollArea()
133 scroll.setWidget(self.filters)
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()
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 ]
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 = []
171 self.stats_frame = QFrame()
172 self.stats_frame.setLayout(QVBoxLayout())
174 self.stats_vbox = QSplitter(Qt.Vertical)
175 self.stats_frame.layout().addWidget(self.stats_vbox)
177 self.addWidget(scroll)
178 self.addWidget(self.stats_frame)
179 self.setStretchFactor(0, 0)
180 self.setStretchFactor(1, 1)
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
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)
190 if rfi_column and steals_column:
191 if rfi_column[colshowsumm] and steals_column[colshowsumm]:
192 rfi_column[colshowsumm] = False
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
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)
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)
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 = []
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))
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
248 self.createStatsTable(vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies)
250 def createStatsTable(self, vbox, playerids, sitenos, limits, seats, groups, dates, games, currencies):
251 startTime = time()
252 show_detail = True
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)
263 if 'allplayers' in groups:
264 # can't currently do this combination so skip detailed table
265 show_detail = False
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)
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)
284 self.db.rollback()
285 print (("Stats page displayed in %4.2f seconds") % (time() - startTime))
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]
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]
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')
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( [] )
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])
322 rows = len(result) # +1 for title row
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
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()
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
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)
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)
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)
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)
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>', '')
479 bbtest = self.filters.get_limits_where_clause(limits)
481 query = query.replace("<gtbigBlind_test>", bbtest)
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")
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 = []
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>", '')
520 # Filter on dates
521 query = query.replace("<datestest>", " between '" + dates[0] + "' and '" + dates[1] + "'")
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)
534 def showDetailFilter(self, checkState):
535 detailDialog = QDialog(self.main_window)
536 detailDialog.setWindowTitle(("Detailed Filters"))
538 handbox = QVBoxLayout()
539 detailDialog.setLayout(handbox)
541 label = QLabel(("Hand Filters:"))
542 handbox.addWidget(label)
543 label.setAlignment(Qt.AlignCenter)
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])
559 for df in self.detailFilters:
560 if df[0] == htest[0]:
561 cb.setChecked(True)
562 break
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)
571 htest[4:7] = [cb,sb1,sb2]
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_()
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)
603if __name__ == "__main__":
604 import Configuration
605 config = Configuration.Config()
607 settings = {}
609 settings.update(config.get_db_parameters())
610 settings.update(config.get_import_parameters())
611 settings.update(config.get_default_paths())
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_()