"""Some GUI helper functions. Author: Yuhuang Hu Email : duguyue100@gmail.com """ from os.path import join try: from PyQt4 import QtGui, QtCore from PyQt4.QCore import QWidget, QLabel, QGridLayout, QHBoxLayout from PyQt4.QCore import QPushButton, QLCDNumber except ImportError: from PyQt5 import QtGui, QtCore from PyQt5.QtWidgets import QWidget, QLabel, QGridLayout, QHBoxLayout from PyQt5.QtWidgets import QPushButton, QLCDNumber import minesweeper FLAG_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "flag.png") QUESTION_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "question.png") BOOM_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "boom.png") EMPTY_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "blue_circle.png") NUMBER_PATHS = [join(minesweeper.PACKAGE_IMGS_PATH, "zero.png"), join(minesweeper.PACKAGE_IMGS_PATH, "one.png"), join(minesweeper.PACKAGE_IMGS_PATH, "two.png"), join(minesweeper.PACKAGE_IMGS_PATH, "three.png"), join(minesweeper.PACKAGE_IMGS_PATH, "four.png"), join(minesweeper.PACKAGE_IMGS_PATH, "five.png"), join(minesweeper.PACKAGE_IMGS_PATH, "six.png"), join(minesweeper.PACKAGE_IMGS_PATH, "seven.png"), join(minesweeper.PACKAGE_IMGS_PATH, "eight.png")] WIN_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "win.png") LOSE_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "lose.png") CONTINUE_PATH = join(minesweeper.PACKAGE_IMGS_PATH, "continue.png") class ControlWidget(QWidget): """Control widget for showing state of the game.""" def __init__(self): """Init control widget.""" super(ControlWidget, self).__init__() self.init_ui() def init_ui(self): """Setup control widget UI.""" self.control_layout = QHBoxLayout() self.setLayout(self.control_layout) self.reset_button = QPushButton() self.reset_button.setFixedSize(40, 40) self.reset_button.setIcon(QtGui.QIcon(WIN_PATH)) self.game_timer = QLCDNumber() self.game_timer.setStyleSheet("QLCDNumber {color: red;}") self.game_timer.setFixedWidth(100) self.move_counter = QLCDNumber() self.move_counter.setStyleSheet("QLCDNumber {color: red;}") self.move_counter.setFixedWidth(100) self.control_layout.addWidget(self.game_timer) self.control_layout.addWidget(self.reset_button) self.control_layout.addWidget(self.move_counter) class GameWidget(QWidget): """Setup Game Interface.""" def __init__(self, ms_game, ctrl_wg): """Init the game.""" super(GameWidget, self).__init__() self.ms_game = ms_game self.ctrl_wg = ctrl_wg self.init_ui() def init_ui(self): """Init game interface.""" board_width = self.ms_game.board_width board_height = self.ms_game.board_height self.create_grid(board_width, board_height) self.time = 0 self.timer = QtCore.QTimer() self.timer.timeout.connect(self.timing_game) self.timer.start(1000) def create_grid(self, grid_width, grid_height): """Create a grid layout with stacked widgets. Parameters ---------- grid_width : int the width of the grid grid_height : int the height of the grid """ self.grid_layout = QGridLayout() self.setLayout(self.grid_layout) self.grid_layout.setSpacing(1) self.grid_wgs = {} for i in xrange(grid_height): for j in xrange(grid_width): self.grid_wgs[(i, j)] = FieldWidget() self.grid_layout.addWidget(self.grid_wgs[(i, j)], i, j) def timing_game(self): """Timing game.""" self.ctrl_wg.game_timer.display(self.time) self.time += 1 def reset_game(self): """Reset game board.""" self.ms_game.reset_game() self.update_grid() self.time = 0 self.timer.start(1000) def update_grid(self): """Update grid according to info map.""" info_map = self.ms_game.get_info_map() for i in xrange(self.ms_game.board_height): for j in xrange(self.ms_game.board_width): self.grid_wgs[(i, j)].info_label(info_map[i, j]) self.ctrl_wg.move_counter.display(self.ms_game.num_moves) if self.ms_game.game_status == 2: self.ctrl_wg.reset_button.setIcon(QtGui.QIcon(CONTINUE_PATH)) elif self.ms_game.game_status == 1: self.ctrl_wg.reset_button.setIcon(QtGui.QIcon(WIN_PATH)) self.timer.stop() elif self.ms_game.game_status == 0: self.ctrl_wg.reset_button.setIcon(QtGui.QIcon(LOSE_PATH)) self.timer.stop() class FieldWidget(QLabel): """A customized Field Widget.""" def __init__(self, field_width=25, field_height=25): """Init the field.""" super(FieldWidget, self).__init__() self.field_width = field_width self.field_height = field_height self.init_ui() def init_ui(self): """Init the ui.""" self.id = 11 self.setFixedSize(self.field_width, self.field_height) self.setPixmap(QtGui.QPixmap(EMPTY_PATH).scaled( self.field_width*3, self.field_height*3)) self.setStyleSheet("QLabel {background-color: blue;}") def mousePressEvent(self, event): """Define mouse press event.""" if event.button() == QtCore.Qt.LeftButton: # get label position p_wg = self.parent() p_layout = p_wg.layout() idx = p_layout.indexOf(self) loc = p_layout.getItemPosition(idx)[:2] if p_wg.ms_game.game_status == 2: p_wg.ms_game.play_move("click", loc[1], loc[0]) p_wg.update_grid() elif event.button() == QtCore.Qt.RightButton: p_wg = self.parent() p_layout = p_wg.layout() idx = p_layout.indexOf(self) loc = p_layout.getItemPosition(idx)[:2] if p_wg.ms_game.game_status == 2: if self.id == 9: self.info_label(10) p_wg.ms_game.play_move("question", loc[1], loc[0]) p_wg.update_grid() elif self.id == 11: self.info_label(9) p_wg.ms_game.play_move("flag", loc[1], loc[0]) p_wg.update_grid() elif self.id == 10: self.info_label(11) p_wg.ms_game.play_move("unflag", loc[1], loc[0]) p_wg.update_grid() def info_label(self, indicator): """Set info label by given settings. Parameters ---------- indicator : int A number where 0-8 is number of mines in srrounding. 12 is a mine field. """ if indicator in xrange(1, 9): self.id = indicator self.setPixmap(QtGui.QPixmap(NUMBER_PATHS[indicator]).scaled( self.field_width, self.field_height)) elif indicator == 0: self.id == 0 self.setPixmap(QtGui.QPixmap(NUMBER_PATHS[0]).scaled( self.field_width, self.field_height)) elif indicator == 12: self.id = 12 self.setPixmap(QtGui.QPixmap(BOOM_PATH).scaled(self.field_width, self.field_height)) self.setStyleSheet("QLabel {background-color: black;}") elif indicator == 9: self.id = 9 self.setPixmap(QtGui.QPixmap(FLAG_PATH).scaled(self.field_width, self.field_height)) self.setStyleSheet("QLabel {background-color: #A3C1DA;}") elif indicator == 10: self.id = 10 self.setPixmap(QtGui.QPixmap(QUESTION_PATH).scaled( self.field_width, self.field_height)) self.setStyleSheet("QLabel {background-color: yellow;}") elif indicator == 11: self.id = 11 self.setPixmap(QtGui.QPixmap(EMPTY_PATH).scaled( self.field_width*3, self.field_height*3)) self.setStyleSheet('QLabel {background-color: blue;}') class RemoteControlThread(QtCore.QThread): """Thread that covers remote control.""" transfer = QtCore.pyqtSignal("QString") def __init__(self): """Init function of the thread.""" super(RemoteControlThread, self).__init__() self.exiting = False def __del__(self): """Destroy the thread.""" self.exiting = True self.wait() def control_start(self, ms_game): """Start thread control.""" self.ms_game = ms_game self.start() def run(self): """Thread behavior.""" self.ms_game.tcp_accept() while True: data = self.ms_game.tcp_receive() if data == "help\n": self.ms_game.tcp_help() self.ms_game.tcp_send("> ") elif data == "exit\n": self.ms_game.tcp_close() elif data == "print\n": self.ms_game.tcp_send(self.ms_game.get_board()) self.ms_game.tcp_send("> ") elif data == "": self.ms_game.tcp_send("> ") else: self.transfer.emit(data) self.ms_game.tcp_send("> ") if self.ms_game.game_status == 1: self.ms_game.tcp_send("[MESSAGE] YOU WIN!\n") self.ms_game.tcp_close() elif self.ms_game.game_status == 0: self.ms_game.tcp_send("[MESSAGE] YOU LOSE!\n") self.ms_game.tcp_close()