Still documenting sudoku_graphics

master
En Yi 2018-09-16 15:21:04 +08:00 committed by En Yi
parent 319c6b6970
commit 54b27ffbac
1 changed files with 108 additions and 6 deletions

View File

@ -143,6 +143,16 @@ class SudokuGrid(BaseSudokuItem):
puzzleFinished = Signal() puzzleFinished = Signal()
def __init__(self, width, height, parent=None): def __init__(self, width, height, parent=None):
"""Initialise the lines and animation to draw the grid, as well as initialising the
graphics components. The parent argument is passed into BaseSudokuItem init method.
Parameters
----------
width: float
Width of the grid
height: float
Height of the grid
"""
super().__init__(parent) super().__init__(parent)
self.width = width self.width = width
self.height = height self.height = height
@ -185,9 +195,8 @@ class SudokuGrid(BaseSudokuItem):
self.setFlag(QGraphicsItem.ItemIsFocusable, True) self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.set_disabled(False) self.set_disabled(False)
# Length of the box to be drawn # Set up the animation
self.length = 0 self.length = 0
# Set up the length to be animated
self.anim = QPropertyAnimation(self, b'length') self.anim = QPropertyAnimation(self, b'length')
self.anim.setDuration(500) # Animation speed self.anim.setDuration(500) # Animation speed
self.anim.setStartValue(0) self.anim.setStartValue(0)
@ -201,6 +210,13 @@ class SudokuGrid(BaseSudokuItem):
self.anim.finished.connect(self.finish_drawing) self.anim.finished.connect(self.finish_drawing)
def set_disabled(self, state): def set_disabled(self, state):
"""Disable or Enable the grid to accept inputs
Parameters
----------
state: bool
If true, enable the grid. Disable otherwise.
"""
if state: if state:
self.setAcceptedMouseButtons(Qt.NoButton) self.setAcceptedMouseButtons(Qt.NoButton)
else: else:
@ -208,12 +224,15 @@ class SudokuGrid(BaseSudokuItem):
self.setAcceptHoverEvents(not state) self.setAcceptHoverEvents(not state)
def finish_drawing(self): def finish_drawing(self):
"""Function to be called once the animaion finishes. Emits the finishDrawing signal
"""
if self.length == self.width: if self.length == self.width:
self.drawn = True self.drawn = True
self.finishDrawing.emit() self.finishDrawing.emit()
# Toggle the animation to be play forward or backward
def toggle_anim(self, toggling): def toggle_anim(self, toggling):
"""Toggle the animation to be play forward or backward
"""
if toggling: if toggling:
self.anim.setDirection(QAbstractAnimation.Forward) self.anim.setDirection(QAbstractAnimation.Forward)
else: else:
@ -222,11 +241,25 @@ class SudokuGrid(BaseSudokuItem):
self.anim.start() self.anim.start()
def generate_new_grid(self, difficulty): def generate_new_grid(self, difficulty):
"""Generate a new puzzle and update the grid
Parameters
----------
difficulty: int
The difficulty level
"""
self.sudoku_grid.generate_random_board(difficulty) self.sudoku_grid.generate_random_board(difficulty)
#self.sudoku_grid.generate_test_board(difficulty) # Uncomment for testing #self.sudoku_grid.generate_test_board(difficulty) # Uncomment for testing
self.update() self.update()
def change_cell_scribbles(self, val): def change_cell_scribbles(self, val):
"""Change the scribble of a digit of a given cell at the mouse position
Parameters
----------
val: int
The scribbled digit to toggle. 0 to clear all
"""
if val == 0: if val == 0:
self.sudoku_grid.clear_scribble(self.mouse_h, self.mouse_w) self.sudoku_grid.clear_scribble(self.mouse_h, self.mouse_w)
else: else:
@ -234,15 +267,26 @@ class SudokuGrid(BaseSudokuItem):
self.grid_painter.update() self.grid_painter.update()
def replace_cell_number(self, val): def replace_cell_number(self, val):
"""Replaces the digit in a given cell at the mouse position
Parameters
----------
val: int
The digit for replacing
"""
self.sudoku_grid.replace_cell_number(self.mouse_h, self.mouse_w, val) self.sudoku_grid.replace_cell_number(self.mouse_h, self.mouse_w, val)
self.grid_painter.update() self.grid_painter.update()
if self.sudoku_grid.completion_check(): if self.sudoku_grid.completion_check():
self.puzzleFinished.emit() self.puzzleFinished.emit()
def boundingRect(self): def boundingRect(self):
"""Reimplemented from QGraphicsObject
"""
return QRectF(-5, -5, self.width+10, self.height+10) return QRectF(-5, -5, self.width+10, self.height+10)
def paint(self, painter, style, widget=None): def paint(self, painter, style, widget=None):
"""Reimplemented from QGraphicsObject. Draws the grid lines and the selection box, which follows the mouse.
"""
painter.setPen(self.default_pen) painter.setPen(self.default_pen)
for line in self.thinlines: for line in self.thinlines:
painter.drawLine(line) painter.drawLine(line)
@ -256,7 +300,10 @@ class SudokuGrid(BaseSudokuItem):
painter.drawRect(self.selection_box) painter.drawRect(self.selection_box)
def hoverMoveEvent(self, event): def hoverMoveEvent(self, event):
if not (self.freeze and self.drawn): """Reimplemented from QGraphicsObject. Updates the mouse grid coordinates as long as the grid is drawn
is not frozen.
"""
if (not self.freeze) and self.drawn:
box_w = bound_value(0, int(event.pos().x()/self.cell_width), 8) box_w = bound_value(0, int(event.pos().x()/self.cell_width), 8)
box_h = bound_value(0, int(event.pos().y() / self.cell_height), 8) box_h = bound_value(0, int(event.pos().y() / self.cell_height), 8)
if box_w != self.mouse_w or box_h != self.mouse_h: if box_w != self.mouse_w or box_h != self.mouse_h:
@ -266,9 +313,13 @@ class SudokuGrid(BaseSudokuItem):
self.update() self.update()
def mousePressEvent(self, event): def mousePressEvent(self, event):
"""Reimplemented from QGraphicsObject. May be useless
"""
event.accept() event.accept()
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
"""Reimplemented from QGraphicsObject. Emits buttonCLicked signal once the player releases the mouse button.
"""
if self.drawn: if self.drawn:
w = (self.mouse_w + 0.5) * self.cell_width w = (self.mouse_w + 0.5) * self.cell_width
h = (self.mouse_h + 0.5) * self.cell_height h = (self.mouse_h + 0.5) * self.cell_height
@ -279,17 +330,25 @@ class SudokuGrid(BaseSudokuItem):
self.buttonClicked.emit(0, 0, self.scribbling) self.buttonClicked.emit(0, 0, self.scribbling)
def focusInEvent(self, event): def focusInEvent(self, event):
"""Reimplemented from QGraphicsObject. Unfreeze the grid on focus
"""
self.set_disabled(False) self.set_disabled(False)
def focusOutEvent(self, event): def focusOutEvent(self, event):
"""Reimplemented from QGraphicsObject. Freeze the grid when out of focus
"""
self.set_disabled(True) self.set_disabled(True)
def keyPressEvent(self, event): def keyPressEvent(self, event):
"""Reimplemented from QGraphicsObject. Check if scribble key is held, toggling on scribbling mode.
"""
if not event.isAutoRepeat(): if not event.isAutoRepeat():
if (event.key() == SCRIBBLE_KEY) and not self.scribbling: if (event.key() == SCRIBBLE_KEY) and not self.scribbling:
self.scribbling = True self.scribbling = True
def keyReleaseEvent(self, event): def keyReleaseEvent(self, event):
"""Reimplemented from QGraphicsObject. Check if scribble key is released, toggling off scribbling mode.
"""
if not event.isAutoRepeat(): if not event.isAutoRepeat():
if event.key() == SCRIBBLE_KEY and self.scribbling: if event.key() == SCRIBBLE_KEY and self.scribbling:
self.scribbling = False self.scribbling = False
@ -297,6 +356,9 @@ class SudokuGrid(BaseSudokuItem):
# Defining the length to be drawn as a Property # Defining the length to be drawn as a Property
@Property(float) @Property(float)
def length(self): def length(self):
"""float: The length of the grid lines to be drawn.
When set, the grid lines points are set.
"""
return self._length return self._length
@length.setter @length.setter
@ -319,10 +381,21 @@ class SudokuGrid(BaseSudokuItem):
class NumberRing(BaseSudokuItem): class NumberRing(BaseSudokuItem):
"""The number ring which consists of the ringButtons for player input.
Attributes
----------
loseFocus : Signal
An explicit signal for when the ring loses focus.
keyPressed : Signal(str, bool)
Emit when a digit is pressed, and whether scribbling mode is on
"""
loseFocus = Signal() loseFocus = Signal()
keyPressed = Signal(str, bool) keyPressed = Signal(str, bool)
def __init__(self, parent=None): def __init__(self, parent=None):
"""Create the ring buttons and layout. The parent argument is passed into BaseSudokuItem init method.
"""
super().__init__(parent=parent) super().__init__(parent=parent)
self.setVisible(False) self.setVisible(False)
@ -357,6 +430,8 @@ class NumberRing(BaseSudokuItem):
self.scribbling = False self.scribbling = False
def finish_animation(self): def finish_animation(self):
"""When the animation is finished, hide away and freeze the buttons and loses the focus if it closes, or
unfreeze it and set the transparency depending on mouse position if it opens"""
if self.radius == 0: if self.radius == 0:
self.setVisible(False) self.setVisible(False)
self.freeze_buttons(True) self.freeze_buttons(True)
@ -368,8 +443,9 @@ class NumberRing(BaseSudokuItem):
else: else:
self.set_buttons_transparent(True) self.set_buttons_transparent(True)
# Toggle the animation to be play forward or backward
def toggle_anim(self, toggling): def toggle_anim(self, toggling):
"""Toggle the animation to be play forward or backward
"""
self.freeze_buttons(True) self.freeze_buttons(True)
if toggling: if toggling:
self.anim.setDirection(QAbstractAnimation.Forward) self.anim.setDirection(QAbstractAnimation.Forward)
@ -379,38 +455,64 @@ class NumberRing(BaseSudokuItem):
self.anim.start() self.anim.start()
def boundingRect(self): def boundingRect(self):
"""Reimplemented from QGraphicsObject
"""
return QRectF(-5-self.radius-self.cell_width/2, -5-self.radius-self.cell_height/2, return QRectF(-5-self.radius-self.cell_width/2, -5-self.radius-self.cell_height/2,
self.cell_width+self.radius*2+10, self.cell_height + self.radius * 2 + 10) self.cell_width+self.radius*2+10, self.cell_height + self.radius * 2 + 10)
# Reimplemented paint
def paint(self, painter, style, widget=None): def paint(self, painter, style, widget=None):
"""Reimplemented from QGraphicsObject. Does nothing but is needed. May be used for fancy effects?
"""
pass pass
def send_button_press(self, val): def send_button_press(self, val):
"""Emits the keyPressed signal if any of the buttons is pressed, and attempts to close the ring
Parameters
----------
val : str
The digit to be emitted
"""
self.keyPressed.emit(val, self.scribbling) self.keyPressed.emit(val, self.scribbling)
self.close_menu() self.close_menu()
def freeze_buttons(self, freeze): def freeze_buttons(self, freeze):
"""Freezes the button
Parameters
----------
freeze: bool
If true, freezes the button. Unfreezes otherwise.
"""
for btn in self.cell_buttons: for btn in self.cell_buttons:
btn.set_freeze(freeze) btn.set_freeze(freeze)
def focusOutEvent(self, event): def focusOutEvent(self, event):
"""Reimplemented from QGraphicsObject. Checks whether the mouse if over any of the buttons and refocus of so.
This is here because clicking the button can cause the ring to focus out for some reason.
"""
if not any(btn.isUnderMouse() for btn in self.cell_buttons): if not any(btn.isUnderMouse() for btn in self.cell_buttons):
self.toggle_anim(False) self.toggle_anim(False)
else: else:
self.setFocus() self.setFocus()
def mousePressEvent(self, event): def mousePressEvent(self, event):
"""Reimplemented from QGraphicsObject. Similar reason to reimplementing focusOutEvent.
"""
if not any(btn.isUnderMouse() for btn in self.cell_buttons): if not any(btn.isUnderMouse() for btn in self.cell_buttons):
self.toggle_anim(False) self.toggle_anim(False)
else: else:
self.setFocus() self.setFocus()
def close_menu(self): def close_menu(self):
"""Closes the ring if scribbling mode is off.
"""
if not self.scribbling: if not self.scribbling:
self.toggle_anim(False) self.toggle_anim(False)
def keyPressEvent(self, event): def keyPressEvent(self, event):
"""Get the digit pressed and emits the keyPressed signal. Check also if scribbling mode is on
"""
if not event.isAutoRepeat(): if not event.isAutoRepeat():
if (event.key() == SCRIBBLE_KEY) and not self.scribbling: if (event.key() == SCRIBBLE_KEY) and not self.scribbling:
self.scribbling = True self.scribbling = True