Coverage for Aux_Base.py: 0%

155 statements  

« prev     ^ index     » next       coverage.py v7.6.1, created at 2024-09-27 18:50 +0000

1#!/usr/bin/env python 

2# -*- coding: utf-8 -*- 

3"""Aux_Base.py 

4 

5Some base classes for Aux_Hud, Mucked, and other aux-handlers. 

6These classes were previously in Mucked, and have been split away 

7for clarity 

8""" 

9 

10import contextlib 

11# Copyright 2008-2012, Ray E. Barker 

12#  

13# This program is free software; you can redistribute it and/or modify 

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

15# the Free Software Foundation; either version 2 of the License, or 

16# (at your option) any later version. 

17#  

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

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

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

21# GNU General Public License for more details. 

22#  

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

24# along with this program; if not, write to the Free Software 

25# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

26 

27######################################################################## 

28 

29# import L10n 

30# _ = L10n.get_translation() 

31# to do 

32 

33# Standard Library modules 

34import logging 

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

36log = logging.getLogger("hud") 

37 

38from PyQt5.QtCore import Qt, QObject 

39from PyQt5.QtWidgets import QWidget 

40 

41# FPDB 

42import Card 

43 

44 

45# This holds all card images in a nice lookup table. One instance is 

46# populated on the first run of Aux_Window.get_card_images() and all 

47# subsequent uses will have the same instance available. 

48deck = None 

49 

50# This allows for a performance gain. Loading and parsing 53 SVG cards 

51# takes some time. If that is done at the first access of 

52# Aux_Window.get_card_images(), it can add a delay of several seconds. 

53# A pre-populated deck on the other hand grants instant access. 

54 

55 

56class Aux_Window(object): 

57 def __init__(self, hud, params, config): 

58 self.hud = hud 

59 self.params = params 

60 self.config = config 

61 

62# Override these methods as needed 

63 def update_data(self, *args): pass 

64 def update_gui(self, *args): pass 

65 def create(self, *args): pass 

66 def save_layout(self, *args): pass 

67 def move_windows(self, *args): pass 

68 

69 def destroy(self): 

70 with contextlib.suppress(Exception): 

71 self.container.destroy() 

72 

73############################################################################ 

74# Some utility routines useful for Aux_Windows 

75# 

76 # Returns the number of places where cards were shown. This can be N 

77 # players + common cards 

78 # XXX XXX: AAAAAGGGGGGHHHHHHHHHHHHHH! 

79 # XXX XXX: 'cards' is a dictionary with EVERY INVOLVED SEAT included; 

80 # XXX XXX: in addition, the unknown/unshown cards are marked with 

81 # zeroes, not None 

82 def count_seats_with_cards(self, cards): 

83 """Returns the number of seats with shown cards in the list.""" 

84 return sum( 

85 seat != 'common' and cards_tuple[0] != 0 

86 for seat, cards_tuple in list(cards.items()) 

87 ) 

88 

89 def get_id_from_seat(self, seat): 

90 """Determine player id from seat number, given stat_dict.""" 

91 

92 # hh_seats is a list of the actual seat numbers used in the hand history. 

93 # Some sites (e.g. iPoker) miss out some seat numbers if max is <10, 

94 # e.g. iPoker 6-max uses seats 1,3,5,6,8,10 NOT 1,2,3,4,5,6 

95 seat = self.hud.layout.hh_seats[seat] 

96 return next( 

97 ( 

98 id 

99 for id, dict in list(self.hud.stat_dict.items()) 

100 if seat == dict['seat'] 

101 ), 

102 None, 

103 ) 

104 

105 

106class Seat_Window(QWidget): 

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

108 super(Seat_Window, self).__init__(None, Qt.Window | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus 

109 | Qt.WindowStaysOnTopHint) # FIXME acceptfocus? splashscreen? 

110 self.lastPos = None 

111 self.aw = aw 

112 self.seat = seat 

113 self.resize(10,10) 

114 self.setAttribute(Qt.WA_AlwaysShowToolTips) 

115 

116 def mousePressEvent(self, event): 

117 if event.button() == Qt.LeftButton: 

118 self.button_press_left(event) 

119 elif event.button() == Qt.MiddleButton: 

120 self.button_press_middle(event) 

121 elif event.button() == Qt.RightButton: 

122 self.button_press_right(event) 

123 

124 def mouseReleaseEvent(self, event): 

125 if event.button() == Qt.LeftButton: 

126 self.button_release_left(event) 

127 elif event.button() == Qt.MiddleButton: 

128 self.button_release_middle(event) 

129 elif event.button() == Qt.RightButton: 

130 self.button_release_right(event) 

131 

132 def button_press_left(self, event): 

133 self.lastPos = event.globalPos() 

134 

135 def button_press_middle(self, event): pass # subclass will define this 

136 def button_press_right(self, event): pass # subclass will define this 

137 

138 def mouseMoveEvent(self, event): 

139 if self.lastPos is not None: 

140 self.move(self.pos() + event.globalPos() - self.lastPos) 

141 self.lastPos = event.globalPos() 

142 

143 def button_release_left(self, event): 

144 self.lastPos = None 

145 self.aw.configure_event_cb(self, self.seat) 

146 

147 def button_release_middle(self, event): pass # subclass will define this 

148 def button_release_right(self, event): pass # subclass will define this 

149 

150 def create_contents(self, *args): pass 

151 def update_contents(self, *args): pass 

152 

153 

154class Aux_Seats(Aux_Window): 

155 """A super class to display an aux_window or a stat block at each seat.""" 

156 

157 def __init__(self, hud, config, params): 

158 super(Aux_Seats, self).__init__(hud, params, config) 

159 self.positions = {} # dict of window positions. normalised for favourite seat and offset 

160 # but _not_ offset to the absolute screen position 

161 self.displayed = False # the seat windows are displayed 

162 self.uses_timer = False # the Aux_seats object uses a timer to control hiding 

163 self.timer_on = False # bool = Ture if the timeout for removing the cards is on 

164 

165 self.aw_class_window = Seat_Window # classname to be used by the aw_class_window 

166 

167# placeholders that should be overridden--so we don't throw errors 

168 def create_contents(self): pass 

169 def create_common(self, x, y): pass 

170 def update_contents(self): pass 

171 

172 def resize_windows(self): 

173 # Resize calculation has already happened in HUD_main&hud.py 

174 # refresh our internal map to reflect these changes 

175 for i in (list(range(1, self.hud.max + 1))): 

176 self.positions[i] = self.hud.layout.location[self.adj[i]] 

177 self.positions["common"] = self.hud.layout.common 

178 # and then move everything to the new places 

179 self.move_windows() 

180 

181 def move_windows(self): 

182 for i in (list(range(1, self.hud.max + 1))): 

183 self.m_windows[i].move(self.positions[i][0] + self.hud.table.x,self.positions[i][1] + self.hud.table.y) 

184 self.m_windows["common"].move(self.hud.layout.common[0] + self.hud.table.x,self.hud.layout.common[1] 

185 + self.hud.table.y) 

186 

187 def create(self): 

188 self.adj = self.adj_seats() 

189 self.m_windows = {} # windows to put the card/hud items in 

190 for i in (list(range(1, self.hud.max + 1)) + ['common']): 

191 if i == 'common': 

192 # The common window is different from the others. Note that it needs to  

193 # get realized, shown, topified, etc. in create_common 

194 # self.hud.layout.xxxxx is updated here after scaling, to ensure 

195 # layout and positions are in sync 

196 (x, y) = self.hud.layout.common 

197 self.m_windows[i] = self.create_common(x, y) 

198 self.hud.layout.common = self.create_scale_position(x, y) 

199 else: 

200 (x, y) = self.hud.layout.location[self.adj[i]] 

201 self.m_windows[i] = self.aw_class_window(self, i) 

202 self.positions[i] = self.create_scale_position(x, y) 

203 self.m_windows[i].move(self.positions[i][0] + self.hud.table.x,self.positions[i][1] + self.hud.table.y) 

204 self.hud.layout.location[self.adj[i]] = self.positions[i] 

205 if 'opacity' in self.params: 

206 self.m_windows[i].setWindowOpacity(float(self.params['opacity'])) 

207 

208 # main action below - fill the created window with content 

209 # the create_contents method is supplied by the subclass 

210 # for hud's this is probably Aux_Hud.stat_window 

211 self.create_contents(self.m_windows[i], i) 

212 

213 self.m_windows[i].create() # ensure there is a native window handle for topify 

214 self.hud.table.topify(self.m_windows[i]) 

215 if not self.uses_timer: 

216 self.m_windows[i].show() 

217 

218 self.hud.layout.height = self.hud.table.height 

219 self.hud.layout.width = self.hud.table.width 

220 

221 def create_scale_position(self, x, y): 

222 # for a given x/y, scale according to current height/wid vs. reference 

223 # height/width 

224 # This method is needed for create (because the table may not be  

225 # the same size as the layout in config) 

226 

227 # any subsequent resizing of this table will be handled through 

228 # hud_main.idle_resize 

229 

230 x_scale = (1.0 * self.hud.table.width / self.hud.layout.width) 

231 y_scale = (1.0 * self.hud.table.height / self.hud.layout.height) 

232 return int(x * x_scale), int(y * y_scale) 

233 

234 def update_gui(self, new_hand_id): 

235 """Update the gui, LDO.""" 

236 for i in list(self.m_windows.keys()): 

237 self.update_contents(self.m_windows[i], i) 

238 # reload latest block positions, in case another aux has changed them 

239 # these lines allow the propagation of block-moves across 

240 # the hud and mucked handlers for this table 

241 self.resize_windows() 

242 

243# Methods likely to be of use for any Seat_Window implementation 

244 def destroy(self): 

245 """Destroy all of the seat windows.""" 

246 with contextlib.suppress(AttributeError): 

247 for i in list(self.m_windows.keys()): 

248 self.m_windows[i].destroy() 

249 del(self.m_windows[i]) 

250 

251# Methods likely to be useful for mucked card windows (or similar) only 

252 def hide(self): 

253 """Hide the seat windows.""" 

254 for (i, w) in list(self.m_windows.items()): 

255 if w is not None: w.hide() 

256 self.displayed = False 

257 

258 def save_layout(self, *args): 

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

260 """ this method is overridden in the specific aux because 

261 the HUD's controlling stat boxes set the seat positions and 

262 the mucked card aux's control the common location 

263 This class method would only be valid for an aux which has full control 

264 over all seat and common locations 

265 """ 

266 

267 log.error("Aux_Seats.save_layout called - this shouldn't happen") 

268 log.error("save_layout method should be handled in the aux") 

269 

270 def configure_event_cb(self, widget, i): 

271 """ 

272 This method updates the current location for each statblock. 

273 This method is needed to record moves for an individual block. 

274 Move/resize also end up in here due to it being a configure. 

275 This is not optimal, but isn't easy to work around. fixme. 

276 """ 

277 if (i): 

278 new_abs_position = widget.pos() # absolute value of the new position 

279 new_position = (new_abs_position.x()-self.hud.table.x, new_abs_position.y()-self.hud.table.y) 

280 self.positions[i] = new_position # write this back to our map 

281 if i != "common": 

282 self.hud.layout.location[self.adj[i]] = new_position # update the hud-level dict, 

283 # so other aux can be told 

284 else: 

285 self.hud.layout.common = new_position 

286 

287 def adj_seats(self): 

288 # determine how to adjust seating arrangements, if a "preferred seat" is set in the hud layout configuration 

289 # Need range here, not xrange -> need the actual list 

290 

291 adj = list(range(self.hud.max + 1)) # default seat adjustments = no adjustment 

292 

293 # does the user have a fav_seat? if so, just get out now 

294 if self.hud.site_parameters["fav_seat"][self.hud.max] == 0: 

295 return adj 

296 

297 # find the hero's actual seat 

298 

299 actual_seat = None 

300 for key in self.hud.stat_dict: 

301 if self.hud.stat_dict[key]['screen_name'] == self.config.supported_sites[self.hud.site].screen_name: 

302 # Seat from stat_dict is the seat num recorded in the hand history and database 

303 # For tables <10-max, some sites omit some seat nums (e.g. iPoker 6-max uses 1,3,5,6,8,10) 

304 # The seat nums in the hh from the site are recorded in config file for each layout, and available 

305 # here as the self.layout.hh_seats list 

306 # (e.g. for iPoker - [None,1,3,5,6,8,10]; 

307 # for most sites- [None, 1,2,3,4,5,6] 

308 # we need to match 'seat' from hand history with the postion in the list, as the hud 

309 # always numbers its stat_windows using consecutive numbers (e.g. 1-6) 

310 

311 for i in range(1, self.hud.max + 1): 

312 if self.hud.layout.hh_seats[i] == self.hud.stat_dict[key]['seat']: 

313 actual_seat = i 

314 break 

315 

316 if not actual_seat: # this shouldn't happen because we don't create huds if the hero isn't seated. 

317 log.error("Error finding hero seat.") 

318 return adj 

319 

320 for i in range(self.hud.max + 1): 

321 j = actual_seat + i 

322 if j > self.hud.max: 

323 j = j - self.hud.max 

324 adj[j] = self.hud.site_parameters["fav_seat"][self.hud.max] + i 

325 if adj[j] > self.hud.max: 

326 adj[j] = adj[j] - self.hud.max 

327 

328 return adj