def __init__(self): """ Initializes the main game's window and sets its attributes to their default values. """ super(RagingSeasWindow, self).__init__() self.ui = Ui_RagingSeasWindow() self.ui.setupUi(self) self.ui.menuFile.menuAction().setStatusTip(self.tr("File")) self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help")) self.ui.playerOneLabel.hide() self.ui.playerTwoLabel.hide() self.leftGridMapper = QSignalMapper(self) self.rightGridMapper = QSignalMapper(self) self.statusLabel = QLabel(self) self.statusLabel.setObjectName("StatusLabel") self.ui.statusBar.setSizeGripEnabled(False) self.ui.statusBar.addPermanentWidget(self.statusLabel) self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE) self.setFixedSize(self.size()) self.setWindowFlags( self.windowFlags() ^ (QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) ) self._session = None self._grid_size = None self._previous_grid_size = None self._is_new_game = False self._has_game_been_started = False self._current_ship_index = 0 self._current_orientation = ShipOrientation.horizontal
class RagingSeasWindow(QMainWindow): """A window used to play the game (contains both players' grids) """ def __init__(self): """ Initializes the main game's window and sets its attributes to their default values. """ super(RagingSeasWindow, self).__init__() self.ui = Ui_RagingSeasWindow() self.ui.setupUi(self) self.ui.menuFile.menuAction().setStatusTip(self.tr("File")) self.ui.menuHelp.menuAction().setStatusTip(self.tr("Help")) self.ui.playerOneLabel.hide() self.ui.playerTwoLabel.hide() self.leftGridMapper = QSignalMapper(self) self.rightGridMapper = QSignalMapper(self) self.statusLabel = QLabel(self) self.statusLabel.setObjectName("StatusLabel") self.ui.statusBar.setSizeGripEnabled(False) self.ui.statusBar.addPermanentWidget(self.statusLabel) self.setStyleSheet(style.INITIAL_MAIN_WINDOW_STYLE) self.setFixedSize(self.size()) self.setWindowFlags( self.windowFlags() ^ (QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowMaximizeButtonHint) ) self._session = None self._grid_size = None self._previous_grid_size = None self._is_new_game = False self._has_game_been_started = False self._current_ship_index = 0 self._current_orientation = ShipOrientation.horizontal def get_session(self): return self._session def set_session(self, value): self._session = value session = property(get_session, set_session) def get_grid_size(self): return self._grid_size def set_grid_size(self, value): self._grid_size = value grid_size = property(get_grid_size, set_grid_size) def get_is_new_game(self): return self._is_new_game def set_is_new_game(self, value): self._is_new_game = value is_new_game = property(get_is_new_game, set_is_new_game) def get_has_game_been_started(self): return self._has_game_been_started def set_has_game_been_started(self, value): self._has_game_been_started = value has_game_been_started = property(get_has_game_been_started, set_has_game_been_started) def get_current_ship_index(self): return self._current_ship_index def set_current_ship_index(self, value): self._current_ship_index = value current_ship_index = property(get_current_ship_index, set_current_ship_index) def get_current_orientation(self): return self._current_orientation def set_current_orientation(self, value): self._current_orientation = value current_orientation = property(get_current_orientation, set_current_orientation) def initialize_grids(self): """Creates the players' grids using the chose grid size. """ dimensions = self._grid_size.value # disconnect the signal mappers and clear the layouts if a game has # previously been started if self._has_game_been_started: self._disconnect_mappers() self._clear_layout(self.ui.gridLayoutLeft) self._clear_layout(self.ui.gridLayoutRight) else: self._has_game_been_started = True for i in xrange(0, dimensions): for j in xrange(0, dimensions): # creating QCustomPushButtons self._populate_grid_at_position(GridPosition.left, self.leftGridMapper, self.ui.gridLayoutLeft, (i, j)) self._populate_grid_at_position(GridPosition.right, self.rightGridMapper, self.ui.gridLayoutRight, (i, j)) # mapping QSignalMappers to the slots that they will be using self.leftGridMapper.mapped[str].connect(self.leftGridButtonClick) self.rightGridMapper.mapped[str].connect(self.rightGridButtonClick) self._adjust_main_window() self._update_status_bar() def resolve_grid_button(self, grid_layout, coordinates): """Fetches a button at the given coordinates. :param grid_layout: an instance of QGridLayout, :param coordinates: an instance of type Coordinates, location of the item :return: an instance of type QPushButton, located at the specified coordinates """ x, y = coordinates.x, coordinates.y layout_item = grid_layout.itemAtPosition(x, y) return layout_item.widget() def _adjust_main_window(self): # resizes the main window to fit the chosen size of the grid dimensions = self._grid_size.value width = dimensions * (style.FIELD_ICON_SIZE + 10) * 2 + \ style.SPACER_WIDTH height = dimensions * (style.FIELD_ICON_SIZE + 10) + \ style.PLAYER_LABEL_HEIGHT status_bar_geometry = self.ui.statusBar.geometry() self.setFixedSize(width, height + (status_bar_geometry.height() * 2)) self.setStyleSheet(style.NEW_GAME_MAIN_WINDOW_STYLE) self.ui.horizontalLayout.setGeometry(QRect(0, 0, width, height)) self.ui.playerOneLabel.show() self.ui.playerTwoLabel.show() def _populate_grid_at_position(self, grid_position, mapper, layout, coordinate_pair): # creates a button and positions it on the grid x, y = coordinate_pair[0], coordinate_pair[1] textual_coordinates = "{0}-{1}".format(x, y) button = QCustomPushButton( self, grid_position, Coordinates.parse_coordinates(textual_coordinates, self._grid_size) ) Helpers.paint_grid_button(button, style.FIELD_BLUE) button.setObjectName("GridButton") button.setFixedSize(style.FIELD_ICON_SIZE + 10, style.FIELD_ICON_SIZE + 10) button.setIconSize(QSize(style.FIELD_ICON_SIZE, style.FIELD_ICON_SIZE)) # set the QSignalMapper's mapping to work with strings mapper.setMapping(button, textual_coordinates) # connecting the button's clicked signal to the QSignalMappers # mapped slot button.clicked.connect(mapper.map) # finally, add the button to the QGridLayout layout.addWidget(button, x, y) def _clear_layout(self, layout): # recursively deletes a QLayout's items making it ready to accept new # ones if layout is not None: while layout.count(): child = layout.takeAt(0) if child.widget() is not None: child.widget().deleteLater() elif child.layout() is not None: self._clear_layout(child.layout()) def _disconnect_mappers(self): # resets the QSignalMappers so that they can be used again dimensions = self._previous_grid_size.value for i in xrange(0, dimensions): for j in xrange(0, dimensions): button_left = self.resolve_grid_button( self.ui.gridLayoutLeft, Coordinates(i, j) ) button_right = self.resolve_grid_button( self.ui.gridLayoutRight, Coordinates(i, j) ) self.leftGridMapper.removeMappings(button_left) self.rightGridMapper.removeMappings(button_right) self.leftGridMapper.disconnect() self.rightGridMapper.disconnect() def _update_status_bar(self): session_phase = self.session.session_phase message = EnumConverters.session_phase_to_string_converter( session_phase ) if session_phase == SessionPhase.fleet_layout: fleet = self.session.player_one.fleet current_ship = fleet.ships[self._current_ship_index] message += self.tr(", Current ship: {0} (size - {1})".format( EnumConverters.ship_type_to_string_converter( current_ship.ship_type ), current_ship.ship_type.value) ) self.statusLabel.setText(self.tr("Phase: " + message)) def _paint_player_grid(self, grid_layout, player, reveal=False): # marks the grid's squares according to their state grid = player.grid dimensions = grid.grid_size.value if player.player_type == PlayerType.human: reveal = True for i in xrange(0, dimensions): for j in xrange(0, dimensions): current_square = grid.squares.item((i, j)) owner = current_square.owner square_state = current_square.square_state button = self.resolve_grid_button(grid_layout, Coordinates(i, j)) if ( square_state == SquareState.vacant or square_state == SquareState.unoccupiable ): Helpers.paint_grid_button(button, style.FIELD_BLUE) elif square_state == SquareState.populated: if reveal: Helpers.paint_grid_button(button, style.FIELD_GRAY) else: Helpers.paint_grid_button(button, style.FIELD_BLUE) elif square_state == SquareState.hit: if owner is not None: if owner.ship_state == ShipState.damaged: Helpers.paint_grid_button(button, style.FIELD_RED) elif owner.ship_state == ShipState.sunk: Helpers.paint_grid_button(button, style.FIELD_BLACK) else: Helpers.paint_grid_button(button, style.FIELD_LIGHT_BLUE) def _paint_both_player_grids(self, player_one, player_two, reveal=False): self._paint_player_grid(self.ui.gridLayoutLeft, player_one, reveal) self._paint_player_grid(self.ui.gridLayoutRight, player_two, reveal) def _declare_winner(self, winner): self.session.session_phase = SessionPhase.game_over self._is_new_game = False self._update_status_bar() player_type_textual = EnumConverters.\ player_type_to_string_converter(winner.player_type) Helpers.raise_info( self, self.tr("Game over - {0} wins!".format( player_type_textual)) ) @pyqtSlot(str, name="leftGridButtonClick") def leftGridButtonClick(self, textual_coordinates): """Slot used when the left grid's QCustomPushButton is clicked. :param textual_coordinates: position of the button represented as a string """ numerical_coordinates = Coordinates.parse_coordinates( textual_coordinates, self._grid_size ) session_phase = self._session.session_phase if session_phase == SessionPhase.fleet_layout: grid = self._session.player_one.grid fleet = self._session.player_one.fleet ship = fleet.ships[self._current_ship_index] if fleet.position_ship(grid, ship, self._current_orientation, numerical_coordinates): self._current_ship_index += 1 if self._current_ship_index == len(fleet.ships): self._session.session_phase = SessionPhase.battle self._session.player_one.initialize_fleet() self._session.player_two.initialize_fleet() self._update_status_bar() self._paint_player_grid(self.ui.gridLayoutLeft, self._session.player_one) @pyqtSlot(str, name="rightGridButtonClick") def rightGridButtonClick(self, textual_coordinates): """Slot used when the right grid's QCustomPushButton is clicked. :param textual_coordinates: position of the button represented as a string """ numerical_coordinates = Coordinates.parse_coordinates( textual_coordinates, self._grid_size ) session_phase = self._session.session_phase player_one = self.session.player_one player_two = self.session.player_two if session_phase == SessionPhase.battle: if not player_one.shoot(player_two.grid, numerical_coordinates): return else: if player_two.fleet.is_sunk(): self._paint_both_player_grids(player_one, player_two, True) self._declare_winner(player_one) return if not player_two.shoot(player_one.grid): return else: if player_one.fleet.is_sunk(): self._paint_both_player_grids(player_one, player_two, True) self._declare_winner(player_two) return self._paint_both_player_grids(player_one, player_two) @pyqtSlot(name="menuNewGameClick") def menuNewGameClick(self): """Slot used when the "New Game" menu item is clicked. """ raise_dialog = True new_game_dialog = NewGameDialog(self) if self._is_new_game: if not Helpers.raise_question( self, self.tr("Are you sure you want to start a new game?") ): raise_dialog = False if raise_dialog: self._previous_grid_size = self._grid_size new_game_dialog.exec_() @pyqtSlot(name="menuAboutClick") def menuAboutClick(self): """Slot used when the "About" menu item is clicked. """ about_dialog = AboutDialog(self) about_dialog.exec_() @pyqtSlot(name="menuExitClick") def menuExitClick(self): """Slot used when the "Exit" menu item is clicked. """ self.close() def closeEvent(self, e): """Overridden close event. :param e: an instance of type QCloseEvent """ if self._is_new_game: if Helpers.raise_question( self, self.tr("Are you sure you want to quit?") ): e.accept() else: e.ignore()