Compare commits

...

5 Commits

Author SHA1 Message Date
En Yi 44cb7a697b Update README 2022-07-09 14:14:20 +08:00
En Yi d815281abc Extra fixes after rebasing 2022-07-09 14:10:16 +08:00
En Yi 9ce1ae0ff7 Fix focusing issue of game board 2022-07-09 13:42:24 +08:00
En Yi de5af7cb44 Allow right-clicking for scribbling 2022-07-09 13:37:25 +08:00
En Yi b7eba38f0f Comment on dig sequence 2022-07-09 13:36:26 +08:00
7 changed files with 49 additions and 28 deletions

View File

@ -1,9 +1,9 @@
# About # About
This is just a Sudoku program written in python using the PyQt5 library. It's supposed to be a learning experience This is just a Sudoku program written in python using the PySide2 library. It's supposed to be a learning experience
about the graphics system of PyQt. The goal(?) is to make it as animated as possible. about the graphics system of PyQt.
## Requirements ## Requirements
Requires `Python 3`, `PyQt5`, and `Numpy`. Requires `Python 3`, `PySide2`, and `Numpy`.
Use the requirements.txt to install the dependencies. Use the requirements.txt to install the dependencies.

View File

@ -43,10 +43,12 @@ def generate_completed_grid(n):
def generate_dig_sequence(difficulty): def generate_dig_sequence(difficulty):
if difficulty <= 1: if difficulty <= 1:
# Random Digging
random_number = list(range(81)) random_number = list(range(81))
while len(random_number) > 0: while len(random_number) > 0:
yield random_number.pop(random.randint(0, len(random_number)-1)) yield random_number.pop(random.randint(0, len(random_number)-1))
elif difficulty == 2: elif difficulty == 2:
# Skip one cell
current = 0 current = 0
while current < 162: while current < 162:
actual = current % 81 actual = current % 81
@ -57,6 +59,7 @@ def generate_dig_sequence(difficulty):
yield (row+1) * 9 - 1 - (actual % 9) % 81 yield (row+1) * 9 - 1 - (actual % 9) % 81
current += 2 current += 2
elif difficulty == 3: elif difficulty == 3:
# S wandering
current = 0 current = 0
while current < 81: while current < 81:
row = int(current / 9) row = int(current / 9)
@ -66,6 +69,7 @@ def generate_dig_sequence(difficulty):
yield (row+1) * 9 - 1 - (current % 9) yield (row+1) * 9 - 1 - (current % 9)
current += 1 current += 1
elif difficulty == 4: elif difficulty == 4:
# Left-to-right, top-to-bottom
current = 0 current = 0
while current < 81: while current < 81:
yield current yield current

View File

@ -3,7 +3,7 @@ of the boards."""
from PySide2.QtGui import QPen from PySide2.QtGui import QPen
from PySide2.QtWidgets import QSizePolicy, QGraphicsWidget from PySide2.QtWidgets import QSizePolicy, QGraphicsWidget
from PySide2.QtCore import (QAbstractAnimation, Qt, QLineF, QPropertyAnimation, Property, Signal, QSizeF, QRectF) from PySide2.QtCore import (QAbstractAnimation, Qt, QLineF, QPropertyAnimation, Property, Signal, QSizeF, QRectF, QTimer)
from . import sudoku_graphics as sdk_grap from . import sudoku_graphics as sdk_grap
from . import menu_graphics as menu_grap from . import menu_graphics as menu_grap
@ -147,7 +147,7 @@ class GameBoard(BoxBoard):
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.gamegrid.setFocus(Qt.MouseFocusReason)
self.show_grid(False) self.show_grid(False)
self.show_playmenu(False) self.show_playmenu(False)
@ -161,6 +161,10 @@ class GameBoard(BoxBoard):
self.anim.finished.connect(lambda: self.show_playmenu(True)) self.anim.finished.connect(lambda: self.show_playmenu(True))
self.toggle_anim(True) self.toggle_anim(True)
self.refocus_timer = QTimer()
self.refocus_timer.timeout.connect(self.game_refocus)
self.refocus_timer.setSingleShot(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 """Display the Number Ring if it is not visible, while setting the focus to it
@ -174,6 +178,7 @@ class GameBoard(BoxBoard):
True to set Scribble mode, False otherwise True to set Scribble mode, False otherwise
""" """
if not self.numring.isVisible(): if not self.numring.isVisible():
self.game_unfocus()
self.numring.setPos(x, y) self.numring.setPos(x, y)
self.numring.setVisible(True) self.numring.setVisible(True)
self.numring.setFocus() self.numring.setFocus()
@ -201,9 +206,17 @@ class GameBoard(BoxBoard):
"""Enable the grid and give it grid focus """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 # To update the grid scribbling mode self.gamegrid.scribbling = self.numring.scribbling # To update the grid scribbling mode
def game_unfocus(self):
"""Enable the grid and give it grid focus
"""
self.gamegrid.set_disabled(True)
#self.gamegrid.setFocus()
#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. """Show the grid, if it is not; Hide the grid, if it is.
Note: Animation only plays when showing the grid. Note: Animation only plays when showing the grid.

View File

@ -64,6 +64,7 @@ class AnimBox(QGraphicsObject):
self.line_order = [self.up, self.right, self.down, self.left] self.line_order = [self.up, self.right, self.down, self.left]
self.accepted_buttons = Qt.LeftButton
self.set_freeze(False) self.set_freeze(False)
self.length = 0 self.length = 0
@ -85,7 +86,7 @@ class AnimBox(QGraphicsObject):
self.setAcceptedMouseButtons(Qt.NoButton) self.setAcceptedMouseButtons(Qt.NoButton)
self.setAcceptHoverEvents(False) self.setAcceptHoverEvents(False)
else: else:
self.setAcceptedMouseButtons(Qt.LeftButton) self.setAcceptedMouseButtons(self.accepted_buttons)
self.setAcceptHoverEvents(True) self.setAcceptHoverEvents(True)
def toggle_anim(self, toggling): def toggle_anim(self, toggling):
@ -201,7 +202,7 @@ class RingButton(AnimBox):
buttonClicked: Signal(str) buttonClicked: Signal(str)
Emitted when it is clicked. Sends the text of the button Emitted when it is clicked. Sends the text of the button
""" """
buttonClicked = Signal(str) buttonClicked = Signal(str, int)
# Initialisation # Initialisation
def __init__(self, x, y, width, height, text, parent=None): def __init__(self, x, y, width, height, text, parent=None):
@ -216,6 +217,7 @@ class RingButton(AnimBox):
super().__init__(x, y, width, height, parent=parent) super().__init__(x, y, width, height, parent=parent)
self.text = text self.text = text
self.transparent = False self.transparent = False
self.accepted_buttons = self.accepted_buttons | Qt.RightButton
def set_transparent(self, state): def set_transparent(self, state):
"""Make the button transparent """Make the button transparent
@ -252,7 +254,7 @@ class RingButton(AnimBox):
""" """
event.accept() event.accept()
self.toggle_anim(False) self.toggle_anim(False)
self.buttonClicked.emit(self.text) self.buttonClicked.emit(self.text, event.button())
class MenuButton(AnimBox): class MenuButton(AnimBox):

View File

@ -84,23 +84,19 @@ class TimerDisplayer(QGraphicsWidget):
class DifficultyDisplayer(QGraphicsWidget): class DifficultyDisplayer(QGraphicsWidget):
<<<<<<< HEAD
notFocus = Signal()
difficultySelected = Signal(str)
=======
"""Display the current difficulty. Clicking on it displays the difficulty menu. """Display the current difficulty. Clicking on it displays the difficulty menu.
Attributes Attributes
---------- ----------
notFocus: pyqtSignal notFocus: Signal
Emitted when it loses focus Emitted when it loses focus
difficultySelected = pyqtSignal(str) difficultySelected = Signal(str)
Emitted when a difficulty is selected. Emits the selected difficulty Emitted when a difficulty is selected. Emits the selected difficulty
""" """
notFocus = pyqtSignal() notFocus = Signal()
difficultySelected = pyqtSignal(str) difficultySelected = Signal(str)
>>>>>>> Document menu_graphics menuClicked = Signal(str)
def __init__(self, parent=None): def __init__(self, parent=None):
"""Create the box and the text. """Create the box and the text.
@ -119,6 +115,7 @@ class DifficultyDisplayer(QGraphicsWidget):
self.diff_menu = DifficultyMenu(self.width, self.height, self) self.diff_menu = DifficultyMenu(self.width, self.height, self)
self.diff_menu.setY(-self.diff_menu.height) self.diff_menu.setY(-self.diff_menu.height)
self.diff_menu.setVisible(False) self.diff_menu.setVisible(False)
self.diff_menu.menuClicked.connect(self.menuClicked.emit)
self.box_pen = QPen() self.box_pen = QPen()
self.box_pen.setColor(Qt.white) self.box_pen.setColor(Qt.white)
@ -167,6 +164,7 @@ class DifficultyDisplayer(QGraphicsWidget):
if not self.diff_menu.isVisible(): if not self.diff_menu.isVisible():
self.diff_menu.setFocus() self.diff_menu.setFocus()
self.diff_menu.setVisible(True) self.diff_menu.setVisible(True)
#self.clicked.emit()
else: else:
self.diff_menu.setVisible(False) self.diff_menu.setVisible(False)
self.notFocus.emit() self.notFocus.emit()
@ -247,7 +245,7 @@ class DifficultyMenu(QGraphicsWidget):
return QRectF(0, 0, self.width, self.height) return QRectF(0, 0, self.width, self.height)
def clicked_on(self, string): def clicked_on(self, string):
"""Emits the menuCLicked signal with the selected difficulty, when one of the buttons is pressed. """Emits the menuClicked signal with the selected difficulty, when one of the buttons is pressed.
Parameters Parameters
---------- ----------

View File

@ -192,7 +192,7 @@ class SudokuGrid(BaseSudokuItem):
self.setAcceptHoverEvents(True) self.setAcceptHoverEvents(True)
self.setAcceptedMouseButtons(Qt.LeftButton) self.setAcceptedMouseButtons(Qt.LeftButton)
self.setFlag(QGraphicsItem.ItemIsFocusable, True) #self.setFlag(QGraphicsItem.ItemIsFocusable, True)
self.set_disabled(False) self.set_disabled(False)
# Set up the animation # Set up the animation
@ -326,18 +326,19 @@ class SudokuGrid(BaseSudokuItem):
if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED: if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED:
self.buttonClicked.emit(w, h, self.scribbling) self.buttonClicked.emit(w, h, self.scribbling)
self.set_disabled(True)
else: else:
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 """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 """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. """Reimplemented from QGraphicsObject. Check if scribble key is held, toggling on scribbling mode.
@ -465,7 +466,7 @@ class NumberRing(BaseSudokuItem):
""" """
pass pass
def send_button_press(self, val): def send_button_press(self, val, btn):
"""Emits the keyPressed signal if any of the buttons is pressed, and attempts to close the ring """Emits the keyPressed signal if any of the buttons is pressed, and attempts to close the ring
Parameters Parameters
@ -473,8 +474,10 @@ class NumberRing(BaseSudokuItem):
val : str val : str
The digit to be emitted The digit to be emitted
""" """
self.keyPressed.emit(val, self.scribbling) scribble = btn == 2
self.close_menu() self.keyPressed.emit(val, scribble)
if not scribble:
self.close_menu()
def freeze_buttons(self, freeze): def freeze_buttons(self, freeze):
"""Freezes the button """Freezes the button

View File

@ -43,7 +43,8 @@ class SudokuWindow(QGraphicsView):
self.gameboard.gridDrawn.connect(lambda: self.menuboard.show_children(True)) self.gameboard.gridDrawn.connect(lambda: self.menuboard.show_children(True))
self.gameboard.newGameSelected.connect(self.menuboard.set_difficulty_text) self.gameboard.newGameSelected.connect(self.menuboard.set_difficulty_text)
self.gameboard.sudokuDone.connect(self.menuboard.finish_the_game) self.gameboard.sudokuDone.connect(self.menuboard.finish_the_game)
self.menuboard.diff_display.notFocus.connect(self.gameboard.game_refocus) self.menuboard.diff_display.menuClicked.connect(self.gameboard.game_unfocus)
self.menuboard.diff_display.notFocus.connect(lambda: self.gameboard.refocus_timer.start(10))
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):