Coverage for Aux_Base.py: 0%
169 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 -*-
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
19########################################################################
21# import L10n
22# _ = L10n.get_translation()
23# to do
25# Standard Library modules
26import logging
27import contextlib
28from PyQt5.QtCore import Qt
29from PyQt5.QtWidgets import QWidget
31# logging has been set up in fpdb.py or HUD_main.py, use their settings:
32log = logging.getLogger("hud")
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###
41# FPDB
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
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.
55class Aux_Window(object):
56 def __init__(self, hud, params, config):
57 self.hud = hud
58 self.params = params
59 self.config = config
61 # Override these methods as needed
62 def update_data(self, *args):
63 pass
65 def update_gui(self, *args):
66 pass
68 def create(self, *args):
69 pass
71 def save_layout(self, *args):
72 pass
74 def move_windows(self, *args):
75 pass
77 def destroy(self):
78 with contextlib.suppress(Exception):
79 self.container.destroy()
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()))
94 def get_id_from_seat(self, seat):
95 """Determine player id from seat number, given stat_dict."""
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 )
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)
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)
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)
134 def button_press_left(self, event):
135 self.lastPos = event.globalPos()
137 def button_press_middle(self, event):
138 pass # subclass will define this
140 def button_press_right(self, event):
141 pass # subclass will define this
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()
148 def button_release_left(self, event):
149 self.lastPos = None
150 self.aw.configure_event_cb(self, self.seat)
152 def button_release_middle(self, event):
153 pass # subclass will define this
155 def button_release_right(self, event):
156 pass # subclass will define this
158 def create_contents(self, *args):
159 pass
161 def update_contents(self, *args):
162 pass
165class Aux_Seats(Aux_Window):
166 """A super class to display an aux_window or a stat block at each seat."""
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
176 self.aw_class_window = Seat_Window # classname to be used by the aw_class_window
178 # placeholders that should be overridden--so we don't throw errors
179 def create_contents(self):
180 pass
182 def create_common(self, x, y):
183 pass
185 def update_contents(self):
186 pass
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()
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 )
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"]))
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)
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()
235 self.hud.layout.height = self.hud.table.height
236 self.hud.layout.width = self.hud.table.width
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)
244 # any subsequent resizing of this table will be handled through
245 # hud_main.idle_resize
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)
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()
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]
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
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 """
285 log.error("Aux_Seats.save_layout called - this shouldn't happen")
286 log.error("save_layout method should be handled in the aux")
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
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
309 adj = list(range(self.hud.max + 1)) # default seat adjustments = no adjustment
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
315 # find the hero's actual seat
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)
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
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
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
346 return adj