Document board
parent
804f933fa1
commit
7ddc13fdd1
|
@ -1,3 +1,6 @@
|
||||||
|
"""This module contains the two boards shown in the program. A base BoxBoard class provides the drawing and animation
|
||||||
|
of the boards."""
|
||||||
|
|
||||||
from PyQt5.QtGui import QPen
|
from PyQt5.QtGui import QPen
|
||||||
from PyQt5.QtWidgets import QSizePolicy, QGraphicsWidget
|
from PyQt5.QtWidgets import QSizePolicy, QGraphicsWidget
|
||||||
from PyQt5.QtCore import (QAbstractAnimation, Qt, QLineF, QPropertyAnimation, pyqtProperty,
|
from PyQt5.QtCore import (QAbstractAnimation, Qt, QLineF, QPropertyAnimation, pyqtProperty,
|
||||||
|
@ -8,11 +11,21 @@ from . import menu_graphics as menu_grap
|
||||||
|
|
||||||
|
|
||||||
class BoxBoard(QGraphicsWidget):
|
class BoxBoard(QGraphicsWidget):
|
||||||
"""
|
"""A generic board that draws an animated rectangular border
|
||||||
A generic board that draws an animated rectangular border
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, width, height, parent=None):
|
def __init__(self, width, height, parent=None):
|
||||||
|
"""Prepare the lines to be drawn and set up the animation
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
width: float
|
||||||
|
Width of the box
|
||||||
|
height: float
|
||||||
|
Height of the box
|
||||||
|
parent: object
|
||||||
|
Pass into QGraphicsWidget init method
|
||||||
|
"""
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.width = width
|
self.width = width
|
||||||
self.height = height
|
self.height = height
|
||||||
|
@ -39,7 +52,6 @@ class BoxBoard(QGraphicsWidget):
|
||||||
|
|
||||||
self.line_order = [self.up, self.right, self.down, self.left]
|
self.line_order = [self.up, self.right, self.down, self.left]
|
||||||
|
|
||||||
# Length of the box to be drawn
|
|
||||||
self.length = 0
|
self.length = 0
|
||||||
# Set up the length to be animated
|
# Set up the length to be animated
|
||||||
self.anim = QPropertyAnimation(self, b'length')
|
self.anim = QPropertyAnimation(self, b'length')
|
||||||
|
@ -49,25 +61,14 @@ class BoxBoard(QGraphicsWidget):
|
||||||
self.anim.setKeyValueAt(t / 10, self.half_circumference * t/10)
|
self.anim.setKeyValueAt(t / 10, self.half_circumference * t/10)
|
||||||
self.anim.setEndValue(self.half_circumference)
|
self.anim.setEndValue(self.half_circumference)
|
||||||
|
|
||||||
# Toggle the animation to be play forward or backward
|
|
||||||
def toggle_anim(self, toggling):
|
|
||||||
if toggling:
|
|
||||||
self.anim.setDirection(QAbstractAnimation.Forward)
|
|
||||||
else:
|
|
||||||
self.anim.setDirection(QAbstractAnimation.Backward)
|
|
||||||
|
|
||||||
self.anim.start()
|
|
||||||
|
|
||||||
# Reimplemented paint
|
|
||||||
def paint(self, painter, style, widget=None):
|
|
||||||
painter.setPen(self.default_pen)
|
|
||||||
for line in self.line_order:
|
|
||||||
if line.length() > 1:
|
|
||||||
painter.drawLine(line)
|
|
||||||
|
|
||||||
# Defining the length to be drawn as a pyqtProperty
|
# Defining the length to be drawn as a pyqtProperty
|
||||||
@pyqtProperty(float)
|
@pyqtProperty(float)
|
||||||
def length(self):
|
def length(self):
|
||||||
|
"""float: The length of the box to be drawn
|
||||||
|
|
||||||
|
When the value is set, the length of the lines making up the box
|
||||||
|
are calculated and updated.
|
||||||
|
"""
|
||||||
return self._length
|
return self._length
|
||||||
|
|
||||||
# Determine the length of the four lines to be drawn
|
# Determine the length of the four lines to be drawn
|
||||||
|
@ -87,40 +88,94 @@ class BoxBoard(QGraphicsWidget):
|
||||||
self.right.setLine(self.width, self.height, self.width, self.height - remaining_length)
|
self.right.setLine(self.width, self.height, self.width, self.height - remaining_length)
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
|
def toggle_anim(self, toggling):
|
||||||
|
"""Toggle the animation forward and backwards
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
toggling: bool
|
||||||
|
True for forward, False for backwards
|
||||||
|
"""
|
||||||
|
if toggling:
|
||||||
|
self.anim.setDirection(QAbstractAnimation.Forward)
|
||||||
|
else:
|
||||||
|
self.anim.setDirection(QAbstractAnimation.Backward)
|
||||||
|
|
||||||
|
self.anim.start()
|
||||||
|
|
||||||
|
def paint(self, painter, style, widget=None):
|
||||||
|
"""Reimplemented from QGraphicsWdiget paint function. Draws the lines making up the box.
|
||||||
|
"""
|
||||||
|
painter.setPen(self.default_pen)
|
||||||
|
for line in self.line_order:
|
||||||
|
if line.length() > 1:
|
||||||
|
painter.drawLine(line)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class GameBoard(BoxBoard):
|
class GameBoard(BoxBoard):
|
||||||
"""
|
"""The Board in which the main game takes place.
|
||||||
The Board in which the main game takes place.
|
|
||||||
It is intended to swap the interface depending on whether the game is ongoing
|
It is intended to swap the interface depending on whether the game is ongoing
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
----------
|
||||||
|
newGameSelected: pyqtSignal(str)
|
||||||
|
Emitted when the difficulty is selected from here. Emits the difficulty string
|
||||||
|
gridDrawn: pyqtSignal
|
||||||
|
Emitted when the Sudoku grid has been drawn
|
||||||
|
sudokuDone: pyqtSignal
|
||||||
|
Emitted when the Sudoku puzzle is finished
|
||||||
"""
|
"""
|
||||||
boxClicked = pyqtSignal(bool)
|
|
||||||
newGameSelected = pyqtSignal(str)
|
newGameSelected = pyqtSignal(str)
|
||||||
gridDrawn = pyqtSignal()
|
gridDrawn = pyqtSignal()
|
||||||
sudokuDone = pyqtSignal()
|
sudokuDone = pyqtSignal()
|
||||||
|
|
||||||
def __init__(self, width, height, parent=None):
|
def __init__(self, width, height, parent=None):
|
||||||
|
"""Create the game area consisting of a Sudoku Grid and a Number Ring,
|
||||||
|
and a difficulty selector at startup
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
width: float
|
||||||
|
Passed into BoxBoard init method
|
||||||
|
height: float
|
||||||
|
Passed into BoxBoard init method
|
||||||
|
parent: object
|
||||||
|
Passed into BoxBoard init method
|
||||||
|
"""
|
||||||
super().__init__(width, height, parent)
|
super().__init__(width, height, parent)
|
||||||
|
|
||||||
self.gamegrid = sdk_grap.SudokuGrid(self.width, self.height, parent=self)
|
self.gamegrid = sdk_grap.SudokuGrid(self.width, self.height, parent=self)
|
||||||
self.numring = sdk_grap.NumberRing(parent=self)
|
self.numring = sdk_grap.NumberRing(parent=self)
|
||||||
self.playmenu = sdk_grap.PlayMenu(parent=self)
|
self.playmenu = sdk_grap.PlayMenu(parent=self)
|
||||||
|
|
||||||
|
self.gamegrid.setFocus(Qt.MouseFocusReason)
|
||||||
self.show_grid(False)
|
self.show_grid(False)
|
||||||
self.show_playmenu(False)
|
self.show_playmenu(False)
|
||||||
|
|
||||||
self.gamegrid.buttonClicked.connect(self.show_number_ring)
|
self.gamegrid.buttonClicked.connect(self.show_number_ring)
|
||||||
self.numring.keyPressed.connect(self.select_ring_number)
|
|
||||||
|
|
||||||
self.gamegrid.setFocus(Qt.MouseFocusReason)
|
|
||||||
|
|
||||||
self.anim.finished.connect(lambda: self.show_playmenu(True))
|
|
||||||
self.playmenu.buttonClicked.connect(self.new_game)
|
|
||||||
self.gamegrid.finishDrawing.connect(self.gridDrawn.emit)
|
self.gamegrid.finishDrawing.connect(self.gridDrawn.emit)
|
||||||
self.gamegrid.puzzleFinished.connect(self.sudokuDone.emit)
|
self.gamegrid.puzzleFinished.connect(self.sudokuDone.emit)
|
||||||
self.numring.loseFocus.connect(self.game_refocus)
|
self.numring.loseFocus.connect(self.game_refocus)
|
||||||
|
self.numring.keyPressed.connect(self.select_ring_number)
|
||||||
|
self.playmenu.buttonClicked.connect(self.new_game)
|
||||||
|
|
||||||
|
self.anim.finished.connect(lambda: self.show_playmenu(True))
|
||||||
self.toggle_anim(True)
|
self.toggle_anim(True)
|
||||||
|
|
||||||
def show_number_ring(self, x=0, y=0, scribbling=False):
|
def show_number_ring(self, x=0, y=0, scribbling=False):
|
||||||
|
"""Display the Number Ring if it is not visible, while setting the focus to it
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
x: float
|
||||||
|
x coordinate of where to position the Ring
|
||||||
|
y: float
|
||||||
|
y coordinate of where to position the Ring
|
||||||
|
scribbling:
|
||||||
|
True to set Scribble mode, False otherwise
|
||||||
|
"""
|
||||||
if not self.numring.isVisible():
|
if not self.numring.isVisible():
|
||||||
self.numring.setPos(x, y)
|
self.numring.setPos(x, y)
|
||||||
self.numring.setVisible(True)
|
self.numring.setVisible(True)
|
||||||
|
@ -130,6 +185,15 @@ class GameBoard(BoxBoard):
|
||||||
self.numring.set_buttons_transparent(False)
|
self.numring.set_buttons_transparent(False)
|
||||||
|
|
||||||
def select_ring_number(self, val, scribbling):
|
def select_ring_number(self, val, scribbling):
|
||||||
|
"""Get the selected number from the Ring and pass into the grid
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
val: str
|
||||||
|
The number string received
|
||||||
|
scribbling: bool
|
||||||
|
True to indicate Scribble mode, False otherwise
|
||||||
|
"""
|
||||||
if val == 'X':
|
if val == 'X':
|
||||||
val = 0
|
val = 0
|
||||||
if scribbling:
|
if scribbling:
|
||||||
|
@ -138,25 +202,50 @@ class GameBoard(BoxBoard):
|
||||||
self.gamegrid.replace_cell_number(int(val))
|
self.gamegrid.replace_cell_number(int(val))
|
||||||
|
|
||||||
def game_refocus(self):
|
def game_refocus(self):
|
||||||
|
"""Enable the grid and give it grid focus
|
||||||
|
"""
|
||||||
self.gamegrid.set_disabled(False)
|
self.gamegrid.set_disabled(False)
|
||||||
self.gamegrid.setFocus()
|
self.gamegrid.setFocus()
|
||||||
self.gamegrid.scribbling = self.numring.scribbling
|
self.gamegrid.scribbling = self.numring.scribbling # To update the grid scribbling mode
|
||||||
|
|
||||||
def show_grid(self, state):
|
def show_grid(self, state):
|
||||||
|
"""Show the grid, if it is not; Hide the grid, if it is.
|
||||||
|
Note: Animation only plays when showing the grid.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
state: bool
|
||||||
|
True to show the grid, False otherwise
|
||||||
|
"""
|
||||||
if state ^ self.gamegrid.isVisible():
|
if state ^ self.gamegrid.isVisible():
|
||||||
self.gamegrid.setVisible(state)
|
self.gamegrid.setVisible(state)
|
||||||
if state:
|
if state:
|
||||||
self.gamegrid.toggle_anim(True)
|
self.gamegrid.toggle_anim(True)
|
||||||
|
|
||||||
def show_playmenu(self, state):
|
def show_playmenu(self, state):
|
||||||
|
"""Show the startup play menu
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
state: bool
|
||||||
|
True to show the startup play menu, False otherwise
|
||||||
|
"""
|
||||||
self.playmenu.setVisible(state)
|
self.playmenu.setVisible(state)
|
||||||
|
|
||||||
def new_game(self, string):
|
def new_game(self, string):
|
||||||
|
"""Generate a new Sudoku Board, given the difficulty
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
string: str
|
||||||
|
The difficulty e.g. Easy
|
||||||
|
"""
|
||||||
self.gamegrid.generate_new_grid(menu_grap.DIFFICULTIES.index(string))
|
self.gamegrid.generate_new_grid(menu_grap.DIFFICULTIES.index(string))
|
||||||
self.show_grid(True)
|
self.show_grid(True)
|
||||||
self.newGameSelected.emit(string)
|
self.newGameSelected.emit(string)
|
||||||
|
|
||||||
def paint(self, painter, style, widget=None):
|
def paint(self, painter, style, widget=None):
|
||||||
|
"""Reimplemented from BoxBoard paint method. Draw the instruction to toggle scribble mode """
|
||||||
super().paint(painter, style, widget)
|
super().paint(painter, style, widget)
|
||||||
|
|
||||||
painter.drawText(QRectF(0, self.height+15, self.width, 15), Qt.AlignCenter,
|
painter.drawText(QRectF(0, self.height+15, self.width, 15), Qt.AlignCenter,
|
||||||
|
@ -164,8 +253,7 @@ class GameBoard(BoxBoard):
|
||||||
|
|
||||||
|
|
||||||
class MenuBoard(BoxBoard):
|
class MenuBoard(BoxBoard):
|
||||||
"""
|
"""The Board that contains difficulty options, timer, and high scores.
|
||||||
The Board that contains menu options. Also contains the timer.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, width, height, parent=None):
|
def __init__(self, width, height, parent=None):
|
||||||
|
@ -173,8 +261,10 @@ class MenuBoard(BoxBoard):
|
||||||
|
|
||||||
self.margin = 10
|
self.margin = 10
|
||||||
self.spacing = 20
|
self.spacing = 20
|
||||||
w_spacing = (self.width - 2*self.margin) /3
|
w_spacing = (self.width - 2*self.margin) / 3
|
||||||
|
|
||||||
|
# Create the components and manually position them
|
||||||
|
# Not bothered to use the layout item
|
||||||
self.diff_display = menu_grap.DifficultyDisplayer(parent=self)
|
self.diff_display = menu_grap.DifficultyDisplayer(parent=self)
|
||||||
self.diff_display.setX(self.margin)
|
self.diff_display.setX(self.margin)
|
||||||
self.diff_display.setY(self.geometry().height()/2-self.diff_display.height/2)
|
self.diff_display.setY(self.geometry().height()/2-self.diff_display.height/2)
|
||||||
|
@ -191,20 +281,27 @@ class MenuBoard(BoxBoard):
|
||||||
self.show_children(False)
|
self.show_children(False)
|
||||||
self.toggle_anim(True)
|
self.toggle_anim(True)
|
||||||
|
|
||||||
def show_difficulty(self, state):
|
|
||||||
self.diff_display.selected = state
|
|
||||||
self.diff_display.update()
|
|
||||||
|
|
||||||
def show_children(self, state):
|
def show_children(self, state):
|
||||||
|
"""Show the timer and the difficulty button
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
----------
|
||||||
|
state: bool
|
||||||
|
True to show them, False otherwise
|
||||||
|
"""
|
||||||
self.timer_display.setVisible(state)
|
self.timer_display.setVisible(state)
|
||||||
self.diff_display.setVisible(state)
|
self.diff_display.setVisible(state)
|
||||||
self.timer_display.reset_time()
|
self.timer_display.reset_time()
|
||||||
|
|
||||||
def set_difficulty_text(self, string):
|
def set_difficulty_text(self, string):
|
||||||
|
"""Change the difficulty to be display and reset the timer
|
||||||
|
"""
|
||||||
self.diff_display.set_text(string)
|
self.diff_display.set_text(string)
|
||||||
self.timer_display.reset_time()
|
self.timer_display.reset_time()
|
||||||
|
|
||||||
def finish_the_game(self):
|
def finish_the_game(self):
|
||||||
|
"""Stop the timer and prepare the high scores if necessary. Should only happen when the puzzle is finished
|
||||||
|
"""
|
||||||
self.timer_display.timer.stop()
|
self.timer_display.timer.stop()
|
||||||
diff = self.diff_display.text
|
diff = self.diff_display.text
|
||||||
time = self.timer_display.get_time()
|
time = self.timer_display.get_time()
|
||||||
|
@ -214,5 +311,6 @@ class MenuBoard(BoxBoard):
|
||||||
self.score_display.show_board(True)
|
self.score_display.show_board(True)
|
||||||
|
|
||||||
def return_to_normal(self):
|
def return_to_normal(self):
|
||||||
|
"""Reenable the difficulty and high score buttons. Used after setting the high scores"""
|
||||||
self.diff_display.set_disabled(False)
|
self.diff_display.set_disabled(False)
|
||||||
self.score_display.set_disabled(False)
|
self.score_display.set_disabled(False)
|
||||||
|
|
9
main.py
9
main.py
|
@ -1,5 +1,4 @@
|
||||||
"""
|
"""This is the main module to be run. Contains the program itself.
|
||||||
This is the main module to be run. Contains the program itself.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from PyQt5.QtGui import QPainter, QBrush
|
from PyQt5.QtGui import QPainter, QBrush
|
||||||
|
@ -12,8 +11,7 @@ from graphic_components import board
|
||||||
|
|
||||||
|
|
||||||
class SudokuWindow(QGraphicsView):
|
class SudokuWindow(QGraphicsView):
|
||||||
"""
|
"""The main window that shows the Sudoku Board and the Menu Board.
|
||||||
The main window that shows the Sudoku Board and the Menu Board.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
@ -50,8 +48,7 @@ class SudokuWindow(QGraphicsView):
|
||||||
self.menuboard.diff_display.difficultySelected.connect(self.gameboard.new_game)
|
self.menuboard.diff_display.difficultySelected.connect(self.gameboard.new_game)
|
||||||
|
|
||||||
def resizeEvent(self, event):
|
def resizeEvent(self, event):
|
||||||
"""
|
"""Reimplemented from QGraphicsView. Resize and maintain the board aspect ratio.
|
||||||
Reimplemented from QGraphicsView. Resize and maintain the board aspect ratio.
|
|
||||||
"""
|
"""
|
||||||
self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
|
self.fitInView(self.scene.sceneRect(), Qt.KeepAspectRatio)
|
||||||
super().resizeEvent(event)
|
super().resizeEvent(event)
|
||||||
|
|
Loading…
Reference in New Issue