Coverage for Aux_Base.py: 0%
155 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 -*-
3"""Aux_Base.py
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"""
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
27########################################################################
29# import L10n
30# _ = L10n.get_translation()
31# to do
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")
38from PyQt5.QtCore import Qt, QObject
39from PyQt5.QtWidgets import QWidget
41# FPDB
42import Card
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
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.
56class Aux_Window(object):
57 def __init__(self, hud, params, config):
58 self.hud = hud
59 self.params = params
60 self.config = config
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
69 def destroy(self):
70 with contextlib.suppress(Exception):
71 self.container.destroy()
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 )
89 def get_id_from_seat(self, seat):
90 """Determine player id from seat number, given stat_dict."""
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 )
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)
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)
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)
132 def button_press_left(self, event):
133 self.lastPos = event.globalPos()
135 def button_press_middle(self, event): pass # subclass will define this
136 def button_press_right(self, event): pass # subclass will define this
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()
143 def button_release_left(self, event):
144 self.lastPos = None
145 self.aw.configure_event_cb(self, self.seat)
147 def button_release_middle(self, event): pass # subclass will define this
148 def button_release_right(self, event): pass # subclass will define this
150 def create_contents(self, *args): pass
151 def update_contents(self, *args): pass
154class Aux_Seats(Aux_Window):
155 """A super class to display an aux_window or a stat block at each seat."""
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
165 self.aw_class_window = Seat_Window # classname to be used by the aw_class_window
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
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()
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)
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']))
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)
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()
218 self.hud.layout.height = self.hud.table.height
219 self.hud.layout.width = self.hud.table.width
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)
227 # any subsequent resizing of this table will be handled through
228 # hud_main.idle_resize
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)
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()
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])
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
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 """
267 log.error("Aux_Seats.save_layout called - this shouldn't happen")
268 log.error("save_layout method should be handled in the aux")
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
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
291 adj = list(range(self.hud.max + 1)) # default seat adjustments = no adjustment
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
297 # find the hero's actual seat
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)
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
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
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
328 return adj