class UiVScreenOverview: def __init__(self, view, vscreen): """ :type view: PySide2.QtWidgets.QGraphicsView.QGraphicsView :param vscreen: hwmonitor.vscreen.vscreen.VScreen """ self.view = view self.vscreen = vscreen self.view_margin = 10 self.background_color = QColor(100, 100, 100) view.setInteractive(False) view.setStyleSheet('border: 0px;') view.setRenderHint(QPainter.Antialiasing) view.setCacheMode(QGraphicsView.CacheBackground) view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.scene = QGraphicsScene(view) self.scene.setBackgroundBrush(self.background_color) for monitor in self.vscreen.monitors: item = MonitorRepresentation(index_from_device_name(monitor.device_name), monitor) item.update_position() self.scene.addItem(item) view.setScene(self.scene) def update_position(self): for item in self.scene.items(): item.update_position() def resize_scene(self): """Resize the scene to fit inside the available space of the viewport with an given margin. Update the scene rect, since it probably changes when changing the layout. """ rect = self.scene.itemsBoundingRect() self.view.setSceneRect(rect) view_rect = self.view.viewport().rect().adjusted(self.view_margin, self.view_margin, -self.view_margin, -self.view_margin) scene_rect = self.view.matrix().mapRect(rect) x_factor = view_rect.width() / scene_rect.width() y_factor = view_rect.height() / scene_rect.height() factor = min(x_factor, y_factor) self.view.scale(factor, factor) self.view.centerOn(rect.center())
def __init__(self, start_state: GridGameState): super().__init__(start_state) self.start_state: GridGameState = start_state self.spaces = [] # self.spaces[i][j] holds row i, column j self.column_dividers = [] self.row_dividers = [] self.column_labels = [] self.row_labels = [] self.text_x = self.text_y = 0 ui = self.ui = Ui_GridControls() ui.setupUi(self) scene = QGraphicsScene() ui.game_display.setScene(scene) scene.setBackgroundBrush(self.background_colour) self.player1_icon = self.create_icon(self.player1_colour) self.player2_icon = self.create_icon(self.player2_colour) ui.black_count_pixmap.setText('') ui.white_count_pixmap.setText('') ui.black_count.setText('') ui.white_count.setText('') for _ in range(start_state.board_height - 1): self.row_dividers.append(scene.addLine(0, 0, 1, 1)) for _ in range(start_state.board_width - 1): self.column_dividers.append(scene.addLine(0, 0, 1, 1)) for i in range(start_state.board_height): self.row_labels.append(scene.addSimpleText(f'{i + 1}')) for j in range(start_state.board_width): self.column_labels.append(scene.addSimpleText(chr(65 + j))) self.to_move = scene.addEllipse(0, 0, 1, 1, brush=self.get_player_brush( self.start_state.X_PLAYER)) self.to_move.setVisible(False) self.move_text = ui.move_text for i in range(self.start_state.board_height): row: typing.List[QGraphicsItem] = [] self.spaces.append(row) for j in range(self.start_state.board_width): piece = GraphicsPieceItem(i, j, self) scene.addItem(piece) piece.setBrush(self.background_colour) piece.setPen(self.background_colour) row.append(piece) self.debug_message = ''
class TimeLineLabelView(QGraphicsView): def __init__(self, parent=None): super(TimeLineLabelView, self).__init__(parent) self.x = 0 self.y = 0 self.m_originX = 0 self.m_originY = 0 self.label_height = 10 self.frame_width = 10 self.time_line_length = 10 self.label_width = 80 self.zoom_factor = 0.0001 self._min_scale_factor = 0.01 self.scale_factor = 1.0 self.time_lines = dict() self.setInteractive(True) self.setDragMode(drag_mode) self.setSceneRect(0, 0, self.time_line_length, self.label_height) self.labels = [] self.setTransformationAnchor( transform_anchor ) # needs to be set to 0 to allow view transform changes https://bugreports.qt.io/browse/QTBUG- self.frame_indicator = None self.edit_start_frame_indicator = None self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) def create_frame_indicator(self): size = QSizeF() height = 100 size.setHeight(height) size.setWidth(self.label_height) self.frame_indicator = FrameIndicator(size, self.frame_width, self.label_width, 0) self._label_scene.addItem(self.frame_indicator) self.edit_start_frame_indicator = FrameIndicator( size, self.frame_width, self.label_width, 0, blue) self._label_scene.addItem(self.edit_start_frame_indicator) def initScene(self): brush = QBrush() color = QColor() color.setGreen(255) color.setAlpha(255) brush.setColor(color) self._label_scene = QGraphicsScene() self._label_scene.setBackgroundBrush(grey) #self._label_scene.setForegroundBrush(color) self.setScene(self._label_scene) def clearScene(self): self.labels = [] self.time_lines = dict() self._label_scene.clear() def setTimeLineParameters(self, time_line_length, label_height): self.label_height = label_height self.time_line_length = self.frame_width * time_line_length def addLabel(self, l, indices, color): y = len(self.labels) * self.label_height pos = QPointF(0, y) label = l timeline = TimeLine(label, indices, pos, self.time_line_length, self.label_height, self.label_width, self.frame_width, color) self._label_scene.addItem(timeline) self.time_lines[label] = timeline self.labels.append(l) self.setSceneRect(0, 0, self.time_line_length, self.label_height * len(self.labels)) def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.m_originX = event.x() self.m_originY = event.y() def mouseMoveEvent(self, event): if event.buttons() and Qt.LeftButton: deltaX = event.x() - self.m_originX deltaY = event.y() - self.m_originY #self.translate(deltaX, deltaY) self.x += deltaX self.y += deltaY self.updateTransform() self.m_originX = event.x() self.m_originY = event.y() def wheelEvent(self, event): self.scale_factor += event.angleDelta().y() * self.zoom_factor self.scale_factor = max(self._min_scale_factor, self.scale_factor) self.updateTransform() def setFrame(self, idx): self.x = self.scale_factor * -idx * self.frame_width + self.label_width #transform = Qt.QTransform() #transform.translate(-deltaX, 0) #self.setTransform(transform) self.updateTransform() if self.frame_indicator is not None: self.frame_indicator.setFrame(idx) def set_edit_start_frame(self, idx): if self.edit_start_frame_indicator is not None: self.edit_start_frame_indicator.setFrame(idx) def updateTransform(self): if self.x > 0: self.x = 0 self.resetTransform() m = QTransform() m.translate(self.x, self.y) m.scale(self.scale_factor, 1.0) self.setTransform(m)
class DynamicView(QGraphicsView): viewChanged = Signal(QRect, float, int, int) def __init__(self, image, parent=None): super(DynamicView, self).__init__(parent) self.scene = QGraphicsScene() self.scene.setBackgroundBrush(Qt.darkGray) self.setScene(self.scene) self.set_image(image) self.setRenderHint(QPainter.SmoothPixmapTransform) self.setDragMode(QGraphicsView.ScrollHandDrag) self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.ZOOM_STEP = 0.2 self.mouse_pressed = False self.next_fit = False self.fit_scale = 0 self.zoom_fit() def set_image(self, image): if type(image) is QPixmap: pixmap = image elif type(image) is QImage: pixmap = QPixmap.fromImage(image) elif type(image) is np.ndarray: pixmap = QPixmap.fromImage(mat2img(image)) else: raise TypeError( self.tr('DynamicView.set_image: Unsupported type: {}'.format( type(image)))) if not self.scene.items(): self.scene.addPixmap(pixmap) else: self.scene.items()[0].setPixmap(pixmap) self.scene.setSceneRect(QRectF(pixmap.rect())) def zoom_full(self): self.set_scaling(1) self.next_fit = True self.notify_change() def zoom_fit(self): self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio) self.fit_scale = self.matrix().m11() if self.fit_scale > 1: self.fit_scale = 1 self.zoom_full() else: self.next_fit = False self.notify_change() def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = True QGraphicsView.mousePressEvent(self, event) def mouseMoveEvent(self, event): QGraphicsView.mouseMoveEvent(self, event) if self.mouse_pressed: self.notify_change() def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: self.mouse_pressed = False QGraphicsView.mouseReleaseEvent(self, event) def mouseDoubleClickEvent(self, event): if event.button() == Qt.LeftButton: if self.next_fit: self.zoom_fit() else: self.zoom_full() QGraphicsView.mouseDoubleClickEvent(self, event) def wheelEvent(self, event): if event.delta() > 0: self.change_zoom(+1) else: self.change_zoom(-1) def resizeEvent(self, event): # FIXME: Se la finestra viene massimizzata, il valore di fit_scale non si aggiorna if self.matrix().m11() <= self.fit_scale: self.zoom_fit() else: self.notify_change() QGraphicsView.resizeEvent(self, event) def change_zoom(self, direction): level = math.log2(self.matrix().m11()) if direction > 0: level += self.ZOOM_STEP else: level -= self.ZOOM_STEP scaling = 2**level if scaling < self.fit_scale: scaling = self.fit_scale self.next_fit = False elif scaling > 1: # scaling = 1 if scaling > 4: scaling = 4 self.next_fit = True self.set_scaling(scaling) self.notify_change() def set_scaling(self, scaling): matrix = QMatrix() matrix.scale(scaling, scaling) self.setMatrix(matrix) def change_view(self, _, new_scaling, new_horiz, new_vert): old_factor = self.matrix().m11() old_horiz = self.horizontalScrollBar().value() old_vert = self.verticalScrollBar().value() if new_scaling != old_factor or new_horiz != old_horiz or new_vert != old_vert: self.set_scaling(new_scaling) self.horizontalScrollBar().setValue(new_horiz) self.verticalScrollBar().setValue(new_vert) self.notify_change() def notify_change(self): scene_rect = self.get_rect() horiz_scroll = self.horizontalScrollBar().value() vert_scroll = self.verticalScrollBar().value() zoom_factor = self.matrix().m11() self.viewChanged.emit(scene_rect, zoom_factor, horiz_scroll, vert_scroll) def get_rect(self): top_left = self.mapToScene(0, 0).toPoint() if top_left.x() < 0: top_left.setX(0) if top_left.y() < 0: top_left.setY(0) view_size = self.viewport().size() bottom_right = self.mapToScene(view_size.width(), view_size.height()).toPoint() image_size = self.sceneRect().toRect() if bottom_right.x() >= image_size.width(): bottom_right.setX(image_size.width() - 1) if bottom_right.y() >= image_size.height(): bottom_right.setY(image_size.height() - 1) return QRect(top_left, bottom_right)
class window(QWidget): def __init__(self): # main window settings super().__init__() self.setWindowTitle("Snake") self.setGeometry(150, 50, 820, 320) self.setFixedSize(820, 320) self.scene = QGraphicsScene() self.game_loop = False self.scoreboard = None self.hot_seat = 0 self.config = 0 self.against_ai = 0 self.game = snake_map(width, height) self.multi_info = [] self.hex = [] self.grahpicsitem = [] self.end_connection = False self.guicomponents() # key listener for a gameplay that is not provided by tcp def keyPressEvent(self, event: PySide2.QtGui.QKeyEvent): if self.game_loop: if self.against_ai is 0: if event.text() in p1_controls: p1_keys.append(event.text()) if event.text() in p2_controls: p2_keys.append(event.text()) else: if event.text() in p1_controls: p1_keys.append(event.text()) # main window graphic layout def guicomponents(self): button_exit = QPushButton("Exit", self) button_start = QPushButton("1 player", self) button_hot_seat = QPushButton("2 players", self) button_multi = QPushButton("Multiplayer", self) button_replay = QPushButton("Replay", self) button_ai = QPushButton("Against bot", self) self.connection_info = QLabel(self) self.controls_info = QLabel(self) self.scoreboard = QLabel(self) self.config_info = QLabel(self) self.config_info.setText("") self.scoreboard.setText("") self.config_info.setFont(PySide2.QtGui.QFont("Courier", 9)) self.scoreboard.setFont(PySide2.QtGui.QFont("Courier", 10)) self.scoreboard.setGeometry(QRect(10, 225, 500, 30)) self.config_info.setGeometry(QRect(10, 225, 790, 30)) button_start.setGeometry(QRect(10, 250, 100, 30)) button_exit.setGeometry(QRect(510, 250, 100, 30)) button_hot_seat.setGeometry(QRect(110, 250, 100, 30)) button_multi.setGeometry(QRect(210, 250, 100, 30)) button_replay.setGeometry(QRect(410, 250, 100, 30)) button_ai.setGeometry(QRect(310, 250, 100, 30)) self.connection_info.setGeometry(10, 280, 200, 30) self.controls_info.setGeometry(220, 280, 200, 30) button_hot_seat.clicked.connect(self.prepare_hot_seat) button_exit.clicked.connect(exit_app) button_start.clicked.connect(self.prepare_single) button_multi.clicked.connect(self.prepare_multi) button_replay.clicked.connect(self.prepare_replay) button_ai.clicked.connect(self.prepare_ai) self.view = QGraphicsView(self.scene, self) self.view.setGeometry(10, 10, 10 * (width + 2), 10 * (height + 2)) self.scene.setBackgroundBrush(QBrush(QColor(0, 120, 0, 255))) self.grass = QPixmap(":/map/grass.png").scaled(20, 20) self.snake = QPixmap(":/map/orange.png").scaled(20, 20) self.black = QPixmap(":/map/black.png").scaled(20, 20) self.apple = QPixmap(":/map/apple.png").scaled(20, 20) self.white = QPixmap(":/map/white.png").scaled(20, 20) self.clear_map() # load configuration from h5 file and map history from xml file def prepare_replay(self): try: self.scoreboard.setText("") with open("config.json", "r") as f: self.config = json.load(f) output = [] for k in self.config: output.append([k, self.config[k]]) output = [item for sublist in output for item in sublist] self.config_info.setText("".join(output)) tree = ET.parse("replay.xml") root = tree.getroot() game_history = [] rounds = [] for turn in root: for cells in turn: for row in cells: if cells.attrib["indexes"] == "19" and row.attrib[ "indexes"] == "77": game_history.append(row.text) copy_history = deepcopy(game_history) rounds.append(copy_history) game_history.clear() else: game_history.append(row.text) for round in rounds: round = np.reshape(round, (20, 78)) self.game.map = round Snake.get_hex(self.hex, self.game) self.initialize_map() self.update_map() myApp.processEvents() time.sleep(0.5) except: self.config_info.setText("Failed to load a replay!") # key listener for multiplayer def send_key(self, number, socket): self.game_loop = True while True: time.sleep(0.3) if number == "0" and len(p1_keys) > 0: try: socket.send(bytes(p1_keys[-1], "utf-8")) except: break elif number == "1" and len(p2_keys) > 0: try: socket.send(bytes(p2_keys[-1], "utf-8")) except: break if self.end_connection: break # receive map configuration from the server def prepare_multi(self): self.end_connection = False root = ET.Element("game") s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect(("127.0.0.1", 1235)) self.connection_info.setText("Connected to server!") msg = s.recv(1024).decode("utf-8") init_msg = msg.split() score_1, score_2 = 0, 0 if init_msg[0] == "Start": if init_msg[1] == "0": self.controls_info.setText("Controls Q, A, Z, E, D, C") elif init_msg[1] == "1": self.controls_info.setText("Controls T, G, B, U, J, M") Thread(target=self.send_key, args=(init_msg[-1], s)).start() end = False frames = 0 while end is False: myApp.processEvents() msg = s.recv(4096) msg = pickle.loads(msg) if msg[0] == 'end': end = True self.config = { "mode-": "Multiplayer", " Client 1 -": str(msg[1]), " Client 2 -": str(msg[-1]), " P1 points-": str(score_1), " P2 points-": str(score_2) } with open("config.json", "w") as f: json.dump(self.config, f) continue self.game.map = msg[0] doc = ET.SubElement(root, "turn", name=str(frames)) for i in range(height): row = ET.SubElement(doc, "cell", indexes=str(i)) for j in range(width): ET.SubElement( row, "row", indexes=str(j)).text = self.game.map[i][j] if len(msg) == 3: score_1, score_2 = msg[1], msg[2] if msg[0] != "end": if init_msg[1] == "0": self.scoreboard.setText("Your score - " + str(msg[1]) + " | " + "Enemy - " + str(msg[2])) if init_msg[1] == "1": self.scoreboard.setText("Your score - " + str(msg[2]) + " | " + "Enemy - " + str(msg[1])) if frames == 0: Snake.get_hex(self.hex, self.game) self.initialize_map() self.update_map() myApp.processEvents() frames = frames + 1 time.sleep(0.25) if init_msg[1] == "0": if score_1 > score_2: self.scoreboard.setText("You won!") elif score_2 > score_1: self.scoreboard.setText("Enemy won!") else: self.scoreboard.setText("Draw!") if init_msg[1] == "1": if score_1 < score_2: self.scoreboard.setText("You won!") elif score_2 < score_1: self.scoreboard.setText("Enemy won!") else: self.scoreboard.setText("Draw!") self.connection_info.setText("Disconnected!") tree = ET.ElementTree(root) tree.write("replay.xml") self.end_connection = True def prepare_hot_seat(self): self.hot_seat = 1 self.against_ai = 0 self.clear_map() self.start_game() def prepare_single(self): self.hot_seat = 0 self.against_ai = 0 self.clear_map() self.start_game() def prepare_ai(self): self.against_ai = 1 self.hot_seat = 0 self.clear_map() self.start_game() def clear_map(self): self.game.init_map() Snake.get_hex(self.hex, self.game) self.initialize_map() def initialize_map(self): self.grahpicsitem = [] self.scene.clear() for i in self.hex: if self.game.map[i[0]][i[1]] == " ": cell = QGraphicsPixmapItem(self.grass) cell.setPos(QPointF(i[1] * 10, i[0] * 10)) self.grahpicsitem.append(cell) self.scene.addItem(cell) elif self.game.map[i[0]][i[1]] == "#": cell = QGraphicsPixmapItem(self.black) cell.setPos(QPointF(i[1] * 10, i[0] * 10)) self.grahpicsitem.append(cell) self.scene.addItem(cell) elif self.game.map[i[0]][i[1]] == "+": cell = QGraphicsPixmapItem(self.white) cell.setPos(QPointF(i[1] * 10, i[0] * 10)) self.grahpicsitem.append(cell) self.scene.addItem(cell) if self.game.map[i[0]][i[1]] == "@": cell = QGraphicsPixmapItem(self.apple) cell.setPos(QPointF(i[1] * 10, i[0] * 10)) self.grahpicsitem.append(cell) self.scene.addItem(cell) def update_map(self): for i in range(len(self.hex)): if self.game.map[self.hex[i][0]][self.hex[i][1]] == " ": self.grahpicsitem[i].setPixmap(self.grass) elif self.game.map[self.hex[i][0]][self.hex[i][1]] == "#": self.grahpicsitem[i].setPixmap(self.black) elif self.game.map[self.hex[i][0]][self.hex[i][1]] == "@": self.grahpicsitem[i].setPixmap(self.apple) elif self.game.map[self.hex[i][0]][self.hex[i][1]] == "+": self.grahpicsitem[i].setPixmap(self.white) def start_game(self): self.config_info.setText("") self.game_loop, spawn_fruit = False, False if self.game_loop is not True: p1_keys.clear(), p2_keys.clear() root = ET.Element("game") spawn_animal1, spawn_animal2, self.game_loop = False, False, True p1_can_play, p2_can_play = True, True animal1, animal2 = [], [] points1, points2 = 0, 0 # find free space on the map and spawn snake (or snakes) if (self.hot_seat and self.against_ai is 0) or (self.hot_seat is 0 and self.against_ai): self.scoreboard.setText("1.P1 points - " + str(points1) + " | 2.P2 points - " + str(points2)) while (spawn_animal1 is False) and (spawn_animal2 is False): if not spawn_animal1: x = np.random.randint(0, len(self.hex)) spawn_animal1, _ = Snake.check_space( self.game.map, self.hex[x], 1, [0, 0, "#", "+"], 0) if not spawn_animal2: y = np.random.randint(0, len(self.hex)) spawn_animal2, _ = Snake.check_space( self.game.map, self.hex[y], 1, [0, 0, "#", "+"], 0) animal1.insert(0, snake(self.hex[x], self.game.map, "#")) animal2.insert(0, snake(self.hex[y], self.game.map, "+")) elif self.hot_seat is 0 and self.against_ai is 0: self.scoreboard.setText("Points - " + str(points1)) while not spawn_animal1: x = np.random.randint(0, len(self.hex)) spawn_animal1, _ = Snake.check_space(self.game.map, self.hex[x], 1, [0, 0, "#"], 0) animal1.insert(0, snake(self.hex[x], self.game.map, "#")) #main game loop index = 0 while self.game_loop: # save actual map to xml file doc = ET.SubElement(root, "turn", name=str(index)) for i in range(height): row = ET.SubElement(doc, "cell", indexes=str(i)) for j in range(width): ET.SubElement(row, "row", indexes=str(j)).text = self.game.map[i][j] # spawn fruit on the map if not spawn_fruit: while not spawn_fruit: x = np.random.randint(0, len(self.hex)) spawn_fruit, _ = Snake.check_space(self.game.map, self.hex[x], 1, [0, 0, "@"], 0) fruit_on_map = fruit(self.hex[x], self.game.map, "@") spawn_fruit, points1, points2, p1_can_play, p2_can_play, self.game_loop = \ Snake.move_anim(p1_can_play, p2_can_play, spawn_fruit, animal1, animal2, points1, points2, fruit_on_map, p1_keys, p2_keys, self.hot_seat, self.against_ai, self.game, self.hex, self.scoreboard, self.game_loop) self.update_map() index = index + 1 myApp.processEvents() time.sleep(0.5) # prepare configuration and map history if self.hot_seat is 0 and self.against_ai is 0: self.config = {"mode-": "Singleplayer", " points-": str(points1)} elif self.hot_seat and self.against_ai is 0: self.config = { "mode-": "Hotseat", " P1 points-": str(points1), " P2 points-": str(points2) } elif self.hot_seat is 0 and self.against_ai: self.config = { "mode-": "Against Bot", " P1 points-": str(points1), " P2 points-": str(points2) } with open("config.json", "w") as f: json.dump(self.config, f) tree = ET.ElementTree(root) tree.write("replay.xml") if (self.hot_seat and self.against_ai is 0) or (self.hot_seat is 0 and self.against_ai): if points1 == points2: self.scoreboard.setText("It's draw! 1.P1 score - " + str(points1) + " | 2.P2 score - " + str(points2)) elif points1 > points2: self.scoreboard.setText("P1 won! 1.P1 score - " + str(points1) + " | 2.P2 score - " + str(points2)) else: self.scoreboard.setText("P2 won! 1.P1 score - " + str(points1) + " | 2.P2 score - " + str(points2)) else: self.scoreboard.setText("You lost! Your score - " + str(points1))
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): super(MainWindow, self).__init__(*args, **kwargs) view = QGraphicsView() self.scene = QGraphicsScene() self.scene.setSceneRect(QRectF(0, 0, *WINDOW_SIZE)) felt = QBrush(QPixmap(os.path.join('images', 'felt.png'))) self.scene.setBackgroundBrush(felt) name = QGraphicsPixmapItem() name.setPixmap(QPixmap(os.path.join('images', 'ronery.png'))) name.setPos(QPointF(170, 375)) self.scene.addItem(name) view.setScene(self.scene) # Timer for the win animation only. self.timer = QTimer() self.timer.setInterval(5) self.timer.timeout.connect(self.win_animation) self.animation_event_cover = AnimationCover() self.scene.addItem(self.animation_event_cover) menu = self.menuBar().addMenu("&Game") deal_action = QAction( QIcon(os.path.join('images', 'playing-card.png')), "Deal...", self) deal_action.triggered.connect(self.restart_game) menu.addAction(deal_action) menu.addSeparator() deal1_action = QAction("1 card", self) deal1_action.setCheckable(True) deal1_action.triggered.connect(lambda: self.set_deal_n(1)) menu.addAction(deal1_action) deal3_action = QAction("3 card", self) deal3_action.setCheckable(True) deal3_action.setChecked(True) deal3_action.triggered.connect(lambda: self.set_deal_n(3)) menu.addAction(deal3_action) dealgroup = QActionGroup(self) dealgroup.addAction(deal1_action) dealgroup.addAction(deal3_action) dealgroup.setExclusive(True) menu.addSeparator() rounds3_action = QAction("3 rounds", self) rounds3_action.setCheckable(True) rounds3_action.setChecked(True) rounds3_action.triggered.connect(lambda: self.set_rounds_n(3)) menu.addAction(rounds3_action) rounds5_action = QAction("5 rounds", self) rounds5_action.setCheckable(True) rounds5_action.triggered.connect(lambda: self.set_rounds_n(5)) menu.addAction(rounds5_action) roundsu_action = QAction("Unlimited rounds", self) roundsu_action.setCheckable(True) roundsu_action.triggered.connect(lambda: self.set_rounds_n(None)) menu.addAction(roundsu_action) roundgroup = QActionGroup(self) roundgroup.addAction(rounds3_action) roundgroup.addAction(rounds5_action) roundgroup.addAction(roundsu_action) roundgroup.setExclusive(True) menu.addSeparator() quit_action = QAction("Quit", self) quit_action.triggered.connect(self.quit) menu.addAction(quit_action) self.deck = [] self.deal_n = 3 # Number of cards to deal each time self.rounds_n = 3 # Number of rounds (restacks) before end. for suit in SUITS: for value in range(1, 14): card = Card(value, suit) self.deck.append(card) self.scene.addItem(card) card.signals.doubleclicked.connect( lambda card=card: self.auto_drop_card(card)) self.setCentralWidget(view) self.setFixedSize(*WINDOW_SIZE) self.deckstack = DeckStack() self.deckstack.setPos(OFFSET_X, OFFSET_Y) self.scene.addItem(self.deckstack) # Set up the working locations. self.works = [] for n in range(7): stack = WorkStack() stack.setPos(OFFSET_X + CARD_SPACING_X * n, WORK_STACK_Y) self.scene.addItem(stack) self.works.append(stack) self.drops = [] # Set up the drop locations. for n in range(4): stack = DropStack() stack.setPos(OFFSET_X + CARD_SPACING_X * (3 + n), OFFSET_Y) stack.signals.complete.connect(self.check_win_condition) self.scene.addItem(stack) self.drops.append(stack) # Add the deal location. self.dealstack = DealStack() self.dealstack.setPos(OFFSET_X + CARD_SPACING_X, OFFSET_Y) self.scene.addItem(self.dealstack) # Add the deal click-trigger. dealtrigger = DealTrigger() dealtrigger.signals.clicked.connect(self.deal) self.scene.addItem(dealtrigger) self.shuffle_and_stack() self.setWindowTitle("Ronery") self.show() def restart_game(self): reply = QMessageBox.question( self, "Deal again", "Are you sure you want to start a new game?", QMessageBox.Yes | QMessageBox.No) if reply == QMessageBox.Yes: self.shuffle_and_stack() def quit(self): self.close() def set_deal_n(self, n): self.deal_n = n def set_rounds_n(self, n): self.rounds_n = n self.deckstack.update_stack_status(self.rounds_n) def shuffle_and_stack(self): # Stop any ongoing animation. self.timer.stop() self.animation_event_cover.hide() # Remove cards from all stacks. for stack in [self.deckstack, self.dealstack ] + self.drops + self.works: stack.reset() random.shuffle(self.deck) # Deal out from the top of the deck, turning over the # final card on each line. cards = self.deck[:] for n, workstack in enumerate(self.works, 1): for a in range(n): card = cards.pop() workstack.add_card(card) card.turn_back_up() if a == n - 1: card.turn_face_up() # Ensure removed from all other stacks here. self.deckstack.stack_cards(cards) def deal(self): if self.deckstack.cards: self.dealstack.spread_from = len(self.dealstack.cards) for n in range(self.deal_n): card = self.deckstack.take_top_card() if card: self.dealstack.add_card(card) card.turn_face_up() elif self.deckstack.can_restack(self.rounds_n): self.deckstack.restack(self.dealstack) self.deckstack.update_stack_status(self.rounds_n) def auto_drop_card(self, card): for stack in self.drops: if stack.is_valid_drop(card): card.stack.remove_card(card) stack.add_card(card) break def check_win_condition(self): complete = all(s.is_complete for s in self.drops) if complete: # Add click-proof cover to play area. self.animation_event_cover.show() # Get the stacks of cards from the drop,stacks. self.timer.start() def win_animation(self): # Start off a new card for drop in self.drops: if drop.cards: card = drop.cards.pop() if card.vector is None: card.vector = QPoint(-random.randint(3, 10), -random.randint(0, 10)) break for card in self.deck: if card.vector is not None: card.setPos(card.pos() + card.vector) card.vector += QPoint(0, 1) # Gravity if card.pos().y() > WINDOW_SIZE[1] - CARD_DIMENSIONS.height(): # Bounce the card, losing some energy. card.vector = QPoint( card.vector.x(), -max(1, int(card.vector.y() * BOUNCE_ENERGY))) # Bump back up to base of screen. card.setPos(card.pos().x(), WINDOW_SIZE[1] - CARD_DIMENSIONS.height()) if card.pos().x() < -CARD_DIMENSIONS.width(): card.vector = None # Put the card back where it started. card.stack.add_card(card)