Refactor sudoku graphic components

master
En Yi 2018-07-06 18:25:55 +08:00
parent 371dd810ca
commit 1a4b7a730a
3 changed files with 185 additions and 207 deletions

View File

@ -1,15 +1,11 @@
from PyQt5.QtGui import QPainter, QBrush, QPen, QColor, QFont from PyQt5.QtGui import QPainter, QBrush, QPen, QColor, QFont
from PyQt5.Qt import QApplication, QTimer
from PyQt5.QtWidgets import (QGraphicsScene, QGraphicsWidget, QGraphicsItem, from PyQt5.QtWidgets import (QGraphicsScene, QGraphicsWidget, QGraphicsItem,
QGraphicsLineItem, QGraphicsRectItem, QGraphicsObject, QGraphicsLineItem, QGraphicsRectItem, QGraphicsObject,
QGraphicsItemGroup, QGraphicsPathItem) QGraphicsItemGroup, QGraphicsPathItem)
from PyQt5.QtCore import (QAbstractAnimation, QObject, QPointF, Qt, QRectF, QLineF, from PyQt5.QtCore import (QAbstractAnimation, QObject, QPointF, Qt, QRectF, QLineF,
QPropertyAnimation, pyqtProperty, pyqtSignal, QSizeF) QPropertyAnimation, pyqtProperty, pyqtSignal, QSizeF)
from graphic_components import buttons
from general.extras import bound_value from graphic_components import sudoku_graphics as sdk_grap
from gameplay import sudoku_gameplay as sdk
import numpy as np
import sys, math
class BoxBoard(QGraphicsWidget): class BoxBoard(QGraphicsWidget):
@ -46,7 +42,7 @@ class BoxBoard(QGraphicsWidget):
painter.drawLine(line) painter.drawLine(line)
def sizeHint(self, which, constraint=None): def sizeHint(self, which, constraint=None):
return(QSizeF(self.width+10, self.height+10)) return QSizeF(self.width+10, self.height+10)
class GameBoard(BoxBoard): class GameBoard(BoxBoard):
@ -54,11 +50,11 @@ class GameBoard(BoxBoard):
def __init__(self, width, height, parent=None): def __init__(self, width, height, parent=None):
super().__init__(width, height, parent) super().__init__(width, height, parent)
self.gamegrid = SudokuGrid(self.width, self.height, parent=self) self.gamegrid = sdk_grap.SudokuGrid(self.width, self.height, parent=self)
self.numring = NumberRing(parent=self) self.numring = sdk_grap.NumberRing(parent=self)
self.gamegrid.update() self.gamegrid.buttonClicked.connect(self.show_number_ring)
self.numring.update() self.numring.connect_button_signals(self.select_ring_number)
def show_number_ring(self, x=0, y=0): def show_number_ring(self, x=0, y=0):
if not self.gamegrid.selected: if not self.gamegrid.selected:
@ -74,169 +70,3 @@ class GameBoard(BoxBoard):
val = 0 val = 0
self.gamegrid.replace_cell_number(int(val)) self.gamegrid.replace_cell_number(int(val))
self.show_number_ring() self.show_number_ring()
class SudokuGrid(QGraphicsObject):
# Prepare the signal
buttonClicked = pyqtSignal(float, float)
def __init__(self, width, height, parent=None):
super().__init__(parent)
self.width = width
self.height = height
self.default_pen = QPen()
self.default_pen.setColor(Qt.white)
self.default_pen.setWidth(1)
self.thick_pen = QPen()
self.thick_pen.setColor(Qt.white)
self.thick_unit = 5
self.thick_pen.setWidth(self.thick_unit)
self.horiz_gridlines = []
self.vert_gridlines = []
self.thinlines = []
self.thicklines = []
self.cell_width = self.width / 9
self.cell_height = self.height /9
for i in range(1, 9):
delta_h = self.cell_height * i
delta_w = self.cell_width * i
if i%3 == 0:
self.thicklines.append(QLineF(0, delta_h, self.width, delta_h))
self.thicklines.append(QLineF(delta_w, 0, delta_w, self.height))
else:
self.thinlines.append(QLineF(0, delta_h, self.width, delta_h))
self.thinlines.append(QLineF(delta_w, 0, delta_w, self.height))
self.sudoku_grid = sdk.SudokuSystem()
self.grid_painter = NumberPainter(self, self.sudoku_grid)
self.mouse_w = 0
self.mouse_h = 0
self.selection_unit = 8
self.selection_pen = QPen()
self.selection_pen.setColor(Qt.white)
self.selection_pen.setWidth(self.selection_unit)
self.selection_box = QRectF(0, 0, self.cell_width, self.cell_height)
self.setAcceptHoverEvents(True)
self.setAcceptedMouseButtons(Qt.LeftButton)
self.selected = False
def replace_cell_number(self, val):
self.sudoku_grid.replace_cell_number(self.mouse_h, self.mouse_w, val)
self.grid_painter.update()
def boundingRect(self):
return QRectF(-5, -5, self.width+10, self.height+10)
# Reimplemented paint
def paint(self, painter, style, widget=None):
painter.setPen(self.default_pen)
for line in self.thinlines:
painter.drawLine(line)
painter.setPen(self.thick_pen)
for line in self.thicklines:
painter.drawLine(line)
painter.setPen(self.selection_pen)
painter.drawRect(self.selection_box)
def hoverMoveEvent(self, event):
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)
if not self.selected:
if box_w != self.mouse_w or box_h != self.mouse_h:
self.mouse_w = box_w
self.mouse_h = box_h
self.selection_box.moveTopLeft(QPointF(box_w*self.cell_width, box_h*self.cell_height))
self.update()
def mousePressEvent(self, event):
w = (self.mouse_w + 0.5) * self.cell_width - 5
h = (self.mouse_h + 0.5) * self.cell_height + 5
if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED:
self.buttonClicked.emit(w, h)
class NumberPainter(QGraphicsItem):
def __init__(self, parent, grid):
super().__init__(parent=parent)
self.parent = parent
self.sudoku_grid = grid
self.default_pen = QPen()
self.default_pen.setColor(Qt.white)
self.default_pen.setWidth(1)
self.invalid_pen = QPen()
self.invalid_pen.setColor(Qt.lightGray)
self.invalid_unit = 8
self.invalid_pen.setWidth(self.invalid_unit)
def paint(self, painter, style, widget=None):
for i in range(9):
for j in range(9):
self._draw_number_cell(i, j, painter)
def boundingRect(self):
return QRectF(-5, -5, self.parent.width+10, self.parent.height+10)
def _draw_number_cell(self, w, h, painter):
val = self.sudoku_grid.get_cell_number(h, w)
if val == 0:
val = ''
else:
if self.sudoku_grid.get_cell_status(h, w) == sdk.VALID:
painter.setPen(self.default_pen)
else:
painter.setPen(self.invalid_pen)
painter.drawText((w+0.5)*self.parent.cell_width-5,
(h+0.5)*self.parent.cell_height+5,
str(val))
class NumberRing(QGraphicsItem):
def __init__(self, parent= None):
super().__init__(parent = parent)
self.setVisible(False)
self.radius = 48
self.cell_width = 24
self.cell_height = 24
self.cell_buttons = []
for i in range(10):
cell_x = self.radius * np.sin(np.deg2rad(360/10*i)) - self.cell_width/2
cell_y = - self.radius * np.cos(np.deg2rad(360 / 10 * i)) - self.cell_height/2
if i == 0:
cell_string = 'X'
else:
cell_string = str(i)
btn = buttons.animBox(cell_x, cell_y, self.cell_width,
self.cell_height, cell_string, self)
self.cell_buttons.append(btn)
def boundingRect(self):
return QRectF(-5, -5, self.cell_width+self.radius*2+10,
self.cell_height + self.radius * 2 + 10)
# Reimplemented paint
def paint(self, painter, style, widget=None):
pass
def connect_button_signals(self, func):
for btn in self.cell_buttons:
btn.buttonClicked.connect(func)
#def mousePressEvent(self, event):
# print('Yes')

View File

@ -1 +1,177 @@
# Put all Sudoku related graphics here # Put all Sudoku related graphics here
from PyQt5.QtGui import QPainter, QBrush, QPen, QColor, QFont
from PyQt5.QtWidgets import QGraphicsItem, QGraphicsObject
from PyQt5.QtCore import (QAbstractAnimation, QPointF, Qt, QRectF, QLineF,
QPropertyAnimation, pyqtProperty, pyqtSignal)
from gameplay import sudoku_gameplay as sdk
from general.extras import bound_value
from graphic_components import buttons
import numpy as np
class NumberPainter(QGraphicsItem):
def __init__(self, parent, grid):
super().__init__(parent=parent)
self.parent = parent
self.sudoku_grid = grid
self.default_pen = QPen()
self.default_pen.setColor(Qt.white)
self.default_pen.setWidth(1)
self.invalid_pen = QPen()
self.invalid_pen.setColor(Qt.lightGray)
self.invalid_unit = 8
self.invalid_pen.setWidth(self.invalid_unit)
def paint(self, painter, style, widget=None):
for i in range(9):
for j in range(9):
self._draw_number_cell(i, j, painter)
def boundingRect(self):
return QRectF(-5, -5, self.parent.width+10, self.parent.height+10)
def _draw_number_cell(self, w, h, painter):
val = self.sudoku_grid.get_cell_number(h, w)
if val == 0:
val = ''
else:
if self.sudoku_grid.get_cell_status(h, w) == sdk.VALID:
painter.setPen(self.default_pen)
else:
painter.setPen(self.invalid_pen)
painter.drawText((w+0.5)*self.parent.cell_width-5,
(h+0.5)*self.parent.cell_height+5,
str(val))
class SudokuGrid(QGraphicsObject):
# Prepare the signal
buttonClicked = pyqtSignal(float, float)
def __init__(self, width, height, parent=None):
super().__init__(parent)
self.width = width
self.height = height
self.default_pen = QPen()
self.default_pen.setColor(Qt.white)
self.default_pen.setWidth(1)
self.thick_pen = QPen()
self.thick_pen.setColor(Qt.white)
self.thick_unit = 5
self.thick_pen.setWidth(self.thick_unit)
self.horiz_gridlines = []
self.vert_gridlines = []
self.thinlines = []
self.thicklines = []
self.cell_width = self.width / 9
self.cell_height = self.height / 9
for i in range(1, 9):
delta_h = self.cell_height * i
delta_w = self.cell_width * i
if i%3 == 0:
self.thicklines.append(QLineF(0, delta_h, self.width, delta_h))
self.thicklines.append(QLineF(delta_w, 0, delta_w, self.height))
else:
self.thinlines.append(QLineF(0, delta_h, self.width, delta_h))
self.thinlines.append(QLineF(delta_w, 0, delta_w, self.height))
self.sudoku_grid = sdk.SudokuSystem()
self.grid_painter = NumberPainter(self, self.sudoku_grid)
self.mouse_w = 0
self.mouse_h = 0
self.selection_unit = 8
self.selection_pen = QPen()
self.selection_pen.setColor(Qt.white)
self.selection_pen.setWidth(self.selection_unit)
self.selection_box = QRectF(0, 0, self.cell_width, self.cell_height)
self.setAcceptHoverEvents(True)
self.setAcceptedMouseButtons(Qt.LeftButton)
self.selected = False
def replace_cell_number(self, val):
self.sudoku_grid.replace_cell_number(self.mouse_h, self.mouse_w, val)
self.grid_painter.update()
def boundingRect(self):
return QRectF(-5, -5, self.width+10, self.height+10)
# Reimplemented paint
def paint(self, painter, style, widget=None):
painter.setPen(self.default_pen)
for line in self.thinlines:
painter.drawLine(line)
painter.setPen(self.thick_pen)
for line in self.thicklines:
painter.drawLine(line)
painter.setPen(self.selection_pen)
painter.drawRect(self.selection_box)
def hoverMoveEvent(self, event):
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)
if not self.selected:
if box_w != self.mouse_w or box_h != self.mouse_h:
self.mouse_w = box_w
self.mouse_h = box_h
self.selection_box.moveTopLeft(QPointF(box_w*self.cell_width, box_h*self.cell_height))
self.update()
def mousePressEvent(self, event):
w = (self.mouse_w + 0.5) * self.cell_width - 5
h = (self.mouse_h + 0.5) * self.cell_height + 5
if not self.sudoku_grid.get_cell_status(self.mouse_h, self.mouse_w) == sdk.FIXED:
self.buttonClicked.emit(w, h)
class NumberRing(QGraphicsItem):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.setVisible(False)
self.radius = 48
self.cell_width = 24
self.cell_height = 24
self.cell_buttons = []
for i in range(10):
cell_x = self.radius * np.sin(np.deg2rad(360/10*i)) - self.cell_width/2
cell_y = - self.radius * np.cos(np.deg2rad(360 / 10 * i)) - self.cell_height/2
if i == 0:
cell_string = 'X'
else:
cell_string = str(i)
btn = buttons.animBox(cell_x, cell_y, self.cell_width,
self.cell_height, cell_string, self)
self.cell_buttons.append(btn)
def boundingRect(self):
return QRectF(-5, -5, self.cell_width+self.radius*2+10,
self.cell_height + self.radius * 2 + 10)
# Reimplemented paint
def paint(self, painter, style, widget=None):
pass
def connect_button_signals(self, func):
for btn in self.cell_buttons:
btn.buttonClicked.connect(func)
#def mousePressEvent(self, event):
# print('Yes')

30
main.py
View File

@ -18,54 +18,26 @@ class SudokuWindow(QGraphicsView):
# Set up the Scene to manage the GraphicItems # Set up the Scene to manage the GraphicItems
self.scene = QGraphicsScene(0, 0, 400, 500, self) self.scene = QGraphicsScene(0, 0, 400, 500, self)
self.setScene(self.scene) self.setScene(self.scene)
self.setSceneRect(self.scene.sceneRect()) self.setSceneRect(self.scene.sceneRect())
self.gameboard = board.GameBoard(400, 400) self.gameboard = board.GameBoard(400, 400)
self.menuboard = board.BoxBoard(400, 50) self.menuboard = board.BoxBoard(400, 50)
self.gamegrid = board.SudokuGrid(450, 450)
self.numring = board.NumberRing()
self.layout = QGraphicsLinearLayout(Qt.Vertical) self.layout = QGraphicsLinearLayout(Qt.Vertical)
self.layout.addItem(self.gameboard) self.layout.addItem(self.gameboard)
self.layout.addItem(self.menuboard) self.layout.addItem(self.menuboard)
self.form = QGraphicsWidget() self.form = QGraphicsWidget()
self.form.setLayout(self.layout) self.form.setLayout(self.layout)
#self.layout.addItem(self.gamegrid)
#self.button1 = buttons.animBox(0, 0, 20, 20, 'a')
#self.scene.addItem(self.button1)
#self.scene.addItem(self.gameboard)
self.scene.addItem(self.form) self.scene.addItem(self.form)
#self.scene.addItem(self.gamegrid)
#self.scene.addItem(self.numring)
self.setBackgroundBrush(QBrush(Qt.black)) self.setBackgroundBrush(QBrush(Qt.black))
self.setRenderHint(QPainter.Antialiasing) self.setRenderHint(QPainter.Antialiasing)
self.setGeometry(0, 0, 600, 600) self.setGeometry(0, 0, 600, 600)
self.gamegrid.buttonClicked.connect(self.show_number_ring) self.ensureVisible(self.scene.sceneRect(), 10, 10)
self.numring.connect_button_signals(self.select_ring_number)
self.ensureVisible(self.scene.sceneRect(), 50, 50)
self.fitInView(self.gameboard.boundingRect(), Qt.KeepAspectRatio) self.fitInView(self.gameboard.boundingRect(), Qt.KeepAspectRatio)
self.show() self.show()
def show_number_ring(self, x=0, y=0):
if not self.gamegrid.selected:
self.numring.setPos(x, y)
self.numring.setVisible(True)
self.gamegrid.selected = True
else:
self.numring.setVisible(False)
self.gamegrid.selected = False
def select_ring_number(self, val):
if val == 'X':
val = 0
self.gamegrid.replace_cell_number(int(val))
self.show_number_ring()
if __name__ == "__main__": if __name__ == "__main__":
app = 0 app = 0