Coverage for Aux_Base.py: 0%

169 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# Copyright 2008-2012, Ray E. Barker 

4# 

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

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

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

8# (at your option) any later version. 

9# 

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

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

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

13# GNU General Public License for more details. 

14# 

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

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

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

18 

19######################################################################## 

20 

21# import L10n 

22# _ = L10n.get_translation() 

23# to do 

24 

25# Standard Library modules 

26import logging 

27import contextlib 

28from PyQt5.QtCore import Qt 

29from PyQt5.QtWidgets import QWidget 

30 

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

32log = logging.getLogger("hud") 

33 

34 

35### Aux_Base.py 

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

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

38# for clarity 

39### 

40 

41# FPDB 

42 

43 

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

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

46# subsequent uses will have the same instance available. 

47deck = None 

48 

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

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

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

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

53 

54 

55class Aux_Window(object): 

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

57 self.hud = hud 

58 self.params = params 

59 self.config = config 

60 

61 # Override these methods as needed 

62 def update_data(self, *args): 

63 pass 

64 

65 def update_gui(self, *args): 

66 pass 

67 

68 def create(self, *args): 

69 pass 

70 

71 def save_layout(self, *args): 

72 pass 

73 

74 def move_windows(self, *args): 

75 pass 

76 

77 def destroy(self): 

78 with contextlib.suppress(Exception): 

79 self.container.destroy() 

80 

81 ############################################################################ 

82 # Some utility routines useful for Aux_Windows 

83 # 

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

85 # players + common cards 

86 # XXX XXX: AAAAAGGGGGGHHHHHHHHHHHHHH! 

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

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

89 # zeroes, not None 

90 def count_seats_with_cards(self, cards): 

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

92 return sum(seat != "common" and cards_tuple[0] != 0 for seat, cards_tuple in list(cards.items())) 

93 

94 def get_id_from_seat(self, seat): 

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

96 

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

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

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

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

101 return next( 

102 (id for id, dict in list(self.hud.stat_dict.items()) if seat == dict["seat"]), 

103 None, 

104 ) 

105 

106 

107class Seat_Window(QWidget): 

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

109 super(Seat_Window, self).__init__( 

110 None, Qt.Window | Qt.FramelessWindowHint | Qt.WindowDoesNotAcceptFocus | Qt.WindowStaysOnTopHint 

111 ) # FIXME acceptfocus? splashscreen? 

112 self.lastPos = None 

113 self.aw = aw 

114 self.seat = seat 

115 self.resize(10, 10) 

116 self.setAttribute(Qt.WA_AlwaysShowToolTips) 

117 

118 def mousePressEvent(self, event): 

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

120 self.button_press_left(event) 

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

122 self.button_press_middle(event) 

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

124 self.button_press_right(event) 

125 

126 def mouseReleaseEvent(self, event): 

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

128 self.button_release_left(event) 

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

130 self.button_release_middle(event) 

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

132 self.button_release_right(event) 

133 

134 def button_press_left(self, event): 

135 self.lastPos = event.globalPos() 

136 

137 def button_press_middle(self, event): 

138 pass # subclass will define this 

139 

140 def button_press_right(self, event): 

141 pass # subclass will define this 

142 

143 def mouseMoveEvent(self, event): 

144 if self.lastPos is not None: 

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

146 self.lastPos = event.globalPos() 

147 

148 def button_release_left(self, event): 

149 self.lastPos = None 

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

151 

152 def button_release_middle(self, event): 

153 pass # subclass will define this 

154 

155 def button_release_right(self, event): 

156 pass # subclass will define this 

157 

158 def create_contents(self, *args): 

159 pass 

160 

161 def update_contents(self, *args): 

162 pass 

163 

164 

165class Aux_Seats(Aux_Window): 

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

167 

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

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

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

171 # but _not_ offset to the absolute screen position 

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

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

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

175 

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

177 

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

179 def create_contents(self): 

180 pass 

181 

182 def create_common(self, x, y): 

183 pass 

184 

185 def update_contents(self): 

186 pass 

187 

188 def resize_windows(self): 

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

190 # refresh our internal map to reflect these changes 

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

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

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

194 # and then move everything to the new places 

195 self.move_windows() 

196 

197 def move_windows(self): 

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

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

200 self.m_windows["common"].move( 

201 self.hud.layout.common[0] + self.hud.table.x, self.hud.layout.common[1] + self.hud.table.y 

202 ) 

203 

204 def create(self): 

205 self.adj = self.adj_seats() 

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

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

208 if i == "common": 

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

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

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

212 # layout and positions are in sync 

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

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

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

216 else: 

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

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

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

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

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

222 if "opacity" in self.params: 

223 self.m_windows[i].setWindowOpacity(float(self.params["opacity"])) 

224 

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

226 # the create_contents method is supplied by the subclass 

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

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

229 

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

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

232 if not self.uses_timer: 

233 self.m_windows[i].show() 

234 

235 self.hud.layout.height = self.hud.table.height 

236 self.hud.layout.width = self.hud.table.width 

237 

238 def create_scale_position(self, x, y): 

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

240 # height/width 

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

242 # the same size as the layout in config) 

243 

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

245 # hud_main.idle_resize 

246 

247 x_scale = 1.0 * self.hud.table.width / self.hud.layout.width 

248 y_scale = 1.0 * self.hud.table.height / self.hud.layout.height 

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

250 

251 def update_gui(self, new_hand_id): 

252 """Update the gui, LDO.""" 

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

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

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

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

257 # the hud and mucked handlers for this table 

258 self.resize_windows() 

259 

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

261 def destroy(self): 

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

263 with contextlib.suppress(AttributeError): 

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

265 self.m_windows[i].destroy() 

266 del self.m_windows[i] 

267 

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

269 def hide(self): 

270 """Hide the seat windows.""" 

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

272 if w is not None: 

273 w.hide() 

274 self.displayed = False 

275 

276 def save_layout(self, *args): 

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

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

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

280 the mucked card aux's control the common location 

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

282 over all seat and common locations 

283 """ 

284 

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

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

287 

288 def configure_event_cb(self, widget, i): 

289 """ 

290 This method updates the current location for each statblock. 

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

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

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

294 """ 

295 if i: 

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

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

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

299 if i != "common": 

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

301 # so other aux can be told 

302 else: 

303 self.hud.layout.common = new_position 

304 

305 def adj_seats(self): 

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

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

308 

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

310 

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

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

313 return adj 

314 

315 # find the hero's actual seat 

316 

317 actual_seat = None 

318 for key in self.hud.stat_dict: 

319 if self.hud.stat_dict[key]["screen_name"] == self.config.supported_sites[self.hud.site].screen_name: 

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

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

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

323 # here as the self.layout.hh_seats list 

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

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

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

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

328 

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

330 if self.hud.layout.hh_seats[i] == self.hud.stat_dict[key]["seat"]: 

331 actual_seat = i 

332 break 

333 

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

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

336 return adj 

337 

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

339 j = actual_seat + i 

340 if j > self.hud.max: 

341 j = j - self.hud.max 

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

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

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

345 

346 return adj