I am trying to develop a graphical sudoku game/solver as a way to learn more about OOP principals, Python, and GUIs. I have decided to use PyQt5, mostly because I understand the concept more than I do that of Pygame. I do have one issue with PyQt5. I have been loading my GUI application as a library and calling it's functions from the terminal, in order to see the errors when they occur. It helps a great deal with debugging. My issue is that it often closes the Python terminal which was used to run the application immediately after printing the errors. Is there any way to prevent this from happening?
To reproduce, download GSudoku.py and sudoku.py or copy and paste their code in an editor and save them with the correct names. Place them in the same directory and import GSudoku from your python shell. Call GSudoku.Game(), then click on any square twice. Should throw an error and should boot you from the python shell.
Full source is available at the GitHub repo. Here is GSudoku.py. This version throws an attribute error.:
import sudoku
import sys
from PyQt5 import QtWidgets, QtCore
from PyQt5.QtWidgets import QApplication, QMainWindow, QPushButton, QWidget
class sudokuApp(QMainWindow):
def __init__(self):
super(sudokuApp, self).__init__()
self.setWindowTitle('Sudoku')
self.createUI()
self.resizeUI()
self.activateUI()
self.b1 = sudoku.board()
self.activeGrid = 'no active grid'
self.possibleInputs = [1, 2, 3, 4, 5, 6, 7, 8, 9]
def createUI(self): #Create all the buttons, etcetera
#nine columns of buttons, nine buttons to a column
self.grids = [[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)],
[QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self), QPushButton(self)]]
self.setupBtn = QPushButton(self)
self.solveBtn = QPushButton(self)
self.clearBtn = QPushButton(self)
self.setupBtn.setText('Setup')
self.solveBtn.setText('Solve')
self.clearBtn.setText('Clear')
def resizeUI(self, desiredScale = 1): #make them all the right size and scale them properly.
self.scale = desiredScale
self.gridwidth = 40 * self.scale
self.gridheight = 40 * self.scale
self.spacing = 5 * self.scale
self.boardWidth = self.spacing + (9 * (self.spacing + self.gridwidth))
self.boardHeight = self.spacing + (9 * (self.spacing + self.gridheight))
for i in range(9):
for j in range(9):
grid = self.grids[i][j]
grid.setGeometry(self.spacing + (j * (self.spacing + self.gridwidth)), self.spacing + (i * (self.spacing + self.gridheight)), self.gridwidth, self.gridheight)
self.setupBtn.setGeometry(self.spacing, self.boardHeight, (self.scale * 80), (self.scale * 30))
self.solveBtn.setGeometry((self.spacing * 2) + (self.scale * 80), self.boardHeight, (self.scale * 80), (self.scale * 30))
self.clearBtn.setGeometry((self.spacing * 3) + (self.scale * 160), self.boardHeight, (self.scale * 80), (self.scale * 30))
self.setFixedSize(self.boardWidth, self.boardHeight + (self.scale * 30) + self.spacing)
def activateUI(self):
for i in range(9):
for j in range(9):
grid = self.grids[i][j]
grid.clicked.connect(self.selectGrid)
grid.setCheckable(True)
self.setupBtn.clicked.connect(self.setup)
def setup(self):
self.b1.setup()
for i in range(9):
for j in range(9):
val = self.b1.rawread(i, j)
if val != 0:
self.grids[i][j].setText(str(val))
self.grids.setEnabled(False)
elif val == 0:
self.grids[i][j].setText('')
def selectGrid(self):
for i in range(9):
for j in range(9):
grid = self.grids[i][j]
if grid.isChecked():
x = i
y = j
grid.setCheckable(False) #reset the grid's checkable state, thereby unchecking it.
grid.setCheckable(True)
self.activeGrid = (x, y)
def editGrid(self, key):
x, y = self.activeGrid
#for i in self.possibleInputs:
print(key)
def keyPressEvent(self, event): #This is the keypress detector. I use this to determine input to edit grids.
try:
print(event.key())
key = event.key() - 48
print(key)
x, y = self.activeGrid
if len(sudoku.findall(self.possibleInputs, key)) == 1: #if the input is within the legal range
self.grids[x][y].setText(str(key))
sudoku.rawplace(x, y, key)
except:
pass
def Game():
app = QApplication([])
gameInstance = sudokuApp()
gameInstance.show()
app.exec_()
Here is sudoku.py. This is fully functional.
import random as rnd
class row():
def __init__(self):
self.grid = [0, 0, 0, 0, 0, 0, 0, 0, 0]
print('created row')
class col():
def __init__(self):
self.grid = [0, 0, 0, 0, 0, 0, 0, 0, 0]
class sub():
def __init__(self):
self.grid = [0, 0, 0, 0, 0, 0, 0, 0, 0]
class board():
mode = 'easy'
won = False
def __init__(self):
self.rows = [row(), row(), row(), row(), row(), row() ,row(), row(), row()]
self.cols = [col(), col(), col(), col(), col(), col(), col(), col(), col()]
self.subs = [sub(), sub(), sub(), sub(), sub(), sub(), sub(), sub(), sub()]
print('Board created.')
def setup(self):
if self.mode == 'easy':
numtoremove = 40
if self.mode == 'medium':
numtoremove = 50
if self.mode == 'hard':
numtoremove = 60
for i in self.rows:
for j in i.grid:
j = 0
for i in range(9):
for j in range(9):
self.rawplace(i, j, rnd.randint(1, 9))
self.check()
for i in range(rnd.randint(numtoremove, numtoremove + 10)):
self.rawplace(rnd.randint(0, 8), rnd.randint(0, 8), 0)
def clear(self):
for i in self.rows:
for j in i.grid:
j = 0
for i in self.cols:
for j in i.grid:
j = 0
for i in self.subs:
for j in i.grid:
j = 0
def check(self):
self.error = False
for i in self.rows:
#print('checking {}'.format(str(i)))
for j in range(1, 10):
instances = findall(i.grid, j)
#print(instances, end = ' ')
#print(len(instances))
if len(instances) > 1: #check for multiple instances and throw an error
error = True
i.grid[instances[len(instances) - 1]] = 0
if len(instances) == 0: #check to see if all grids are filled and set the won variable accordingly
self.won = True
else:
self.won = False
self.carry('rows', 'cols')
self.carry('rows', 'subs')
for i in self.cols:
#print('checking {}'.format(str(i)))
for j in range(1, 10):
instances = findall(i.grid, j)
#print(instances, end = ' ')
#print(len(instances))
if len(instances) > 1: #check for multiple instances and throw an error
error = True
i.grid[instances[len(instances) - 1]] = 0
if len(instances) == 0: #check to see if all grids are filled and set the won variable accordingly
self.won = True
else:
self.won = False
self.carry('cols', 'rows')
self.carry('rows', 'subs')
for i in self.subs:
#print('checking {}'.format(str(i)))
for j in range(1, 10):
instances = findall(i.grid, j)
#print(instances, end = ' ')
#print(len(instances))
if len(instances) > 1: #check for multiple instances and throw an error
error = True
i.grid[instances[len(instances) - 1]] = 0
if len(instances) == 0: #check to see if all grids are filled and set the won variable accordingly
self.won = True
else:
self.won = False
self.carry('subs', 'rows')
self.carry('rows', 'cols')
if self.error:
self.won = False
self.check()
def printboard(self, mode = 'rows'):
if mode == 'rows':
for i in self.rows:
for j in range(len(i.grid)):
print(i.grid[j], end = ' ')
print()
if mode == 'cols':
for i in range(9):
for j in self.cols:
print(j.grid[i], end = ' ')
print()
if mode == 'pretty':
lnctr = 0
print('-------------------------')
for i in self.rows:
print('| ' + str(i.grid[0]) + ' ' + str(i.grid[1]) + ' ' + str(i.grid[2]) + ' | ' +
str(i.grid[3]) + ' ' + str(i.grid[4]) + ' ' + str(i.grid[5]) + ' | ' +
str(i.grid[6]) + ' ' + str(i.grid[7]) + ' ' + str(i.grid[8]) + ' |')
lnctr += 1
if lnctr in [3, 6]:
print('|-------+-------+-------|')
print('-------------------------')
def place(self, xloc, yloc, val):
try:
#print(str(xloc) + ', ' + str(yloc) + ', ' + str(val))
return self.rawplace(int(xloc) - 1, int(yloc) - 1, int(val))
except:
return False
def rawplace(self, xloc, yloc, val):
#print("function: 'rawplace'")
try:
if xloc > 8 or xloc < 0 or yloc > 8 or yloc < 0:
#print('Out of range')
return False
else:
self.rows[yloc].grid[xloc] = val
self.carry('rows', 'cols')
self.carry('rows', 'subs')
return True
except:
return False
def read(self, xloc, yloc):
try:
return self.rawread(int(xloc - 1), int(yloc - 1))
except:
return False
def rawread(self, xloc, yloc):
try:
if xloc > 8 or xloc < 0 or yloc > 8 or yloc < 0:
#print('Out of range')
return False
else:
return self.rows[yloc].grid[xloc]
except:
return False
def carry(self, set1, set2):
if set1 == 'rows' and set2 == 'cols':
for x in range(9): #for each grid in the board
for y in range(9):
self.cols[x].grid[y] = self.rows[y].grid[x] #set that col.grid equal to that row.grid
if set1 == 'cols' and set2 == 'rows':
for x in range(9): #for each grid in the board
for y in range(9):
self.rows[y].grid[x] = self.cols[x].grid[y] #set that row.grid equal to that col.grid
if set1 == 'rows' and set2 == 'subs': #I couldn't come up with an algorithm here, so I listed the lines and made a script to mirror them for the subs - rows function.
self.subs[0].grid[0] = self.rows[0].grid[0] #first row
self.subs[0].grid[1] = self.rows[0].grid[1]
self.subs[0].grid[2] = self.rows[0].grid[2]
self.subs[0].grid[3] = self.rows[1].grid[0]
self.subs[0].grid[4] = self.rows[1].grid[1]
self.subs[0].grid[5] = self.rows[1].grid[2]
self.subs[0].grid[6] = self.rows[2].grid[0]
self.subs[0].grid[7] = self.rows[2].grid[1]
self.subs[0].grid[8] = self.rows[2].grid[2]
self.subs[1].grid[0] = self.rows[0].grid[3]
self.subs[1].grid[1] = self.rows[0].grid[4]
self.subs[1].grid[2] = self.rows[0].grid[5]
self.subs[1].grid[3] = self.rows[1].grid[3]
self.subs[1].grid[4] = self.rows[1].grid[4]
self.subs[1].grid[5] = self.rows[1].grid[5]
self.subs[1].grid[6] = self.rows[2].grid[3]
self.subs[1].grid[7] = self.rows[2].grid[4]
self.subs[1].grid[8] = self.rows[2].grid[5]
self.subs[2].grid[0] = self.rows[0].grid[6]
self.subs[2].grid[1] = self.rows[0].grid[7]
self.subs[2].grid[2] = self.rows[0].grid[8]
self.subs[2].grid[3] = self.rows[1].grid[6]
self.subs[2].grid[4] = self.rows[1].grid[7]
self.subs[2].grid[5] = self.rows[1].grid[8]
self.subs[2].grid[6] = self.rows[2].grid[6]
self.subs[2].grid[7] = self.rows[2].grid[7]
self.subs[2].grid[8] = self.rows[2].grid[8]
self.subs[3].grid[0] = self.rows[3].grid[0] #second row
self.subs[3].grid[1] = self.rows[3].grid[1]
self.subs[3].grid[2] = self.rows[3].grid[2]
self.subs[3].grid[3] = self.rows[4].grid[0]
self.subs[3].grid[4] = self.rows[4].grid[1]
self.subs[3].grid[5] = self.rows[4].grid[2]
self.subs[3].grid[6] = self.rows[5].grid[0]
self.subs[3].grid[7] = self.rows[5].grid[1]
self.subs[3].grid[8] = self.rows[5].grid[2]
self.subs[4].grid[0] = self.rows[3].grid[3]
self.subs[4].grid[1] = self.rows[3].grid[4]
self.subs[4].grid[2] = self.rows[3].grid[5]
self.subs[4].grid[3] = self.rows[4].grid[3]
self.subs[4].grid[4] = self.rows[4].grid[4]
self.subs[4].grid[5] = self.rows[4].grid[5]
self.subs[4].grid[6] = self.rows[5].grid[3]
self.subs[4].grid[7] = self.rows[5].grid[4]
self.subs[4].grid[8] = self.rows[5].grid[5]
self.subs[5].grid[0] = self.rows[3].grid[6]
self.subs[5].grid[1] = self.rows[3].grid[7]
self.subs[5].grid[2] = self.rows[3].grid[8]
self.subs[5].grid[3] = self.rows[4].grid[6]
self.subs[5].grid[4] = self.rows[4].grid[7]
self.subs[5].grid[5] = self.rows[4].grid[8]
self.subs[5].grid[6] = self.rows[5].grid[6]
self.subs[5].grid[7] = self.rows[5].grid[7]
self.subs[5].grid[8] = self.rows[5].grid[8]
self.subs[6].grid[0] = self.rows[6].grid[0] #third row
self.subs[6].grid[1] = self.rows[6].grid[1]
self.subs[6].grid[2] = self.rows[6].grid[2]
self.subs[6].grid[3] = self.rows[7].grid[0]
self.subs[6].grid[4] = self.rows[7].grid[1]
self.subs[6].grid[5] = self.rows[7].grid[2]
self.subs[6].grid[6] = self.rows[8].grid[0]
self.subs[6].grid[7] = self.rows[8].grid[1]
self.subs[6].grid[8] = self.rows[8].grid[2]
self.subs[7].grid[0] = self.rows[6].grid[3]
self.subs[7].grid[1] = self.rows[6].grid[4]
self.subs[7].grid[2] = self.rows[6].grid[5]
self.subs[7].grid[3] = self.rows[7].grid[3]
self.subs[7].grid[4] = self.rows[7].grid[4]
self.subs[7].grid[5] = self.rows[7].grid[5]
self.subs[7].grid[6] = self.rows[8].grid[3]
self.subs[7].grid[7] = self.rows[8].grid[4]
self.subs[7].grid[8] = self.rows[8].grid[5]
self.subs[8].grid[0] = self.rows[6].grid[6]
self.subs[8].grid[1] = self.rows[6].grid[7]
self.subs[8].grid[2] = self.rows[6].grid[8]
self.subs[8].grid[3] = self.rows[7].grid[6]
self.subs[8].grid[4] = self.rows[7].grid[7]
self.subs[8].grid[5] = self.rows[7].grid[8]
self.subs[8].grid[6] = self.rows[8].grid[6]
self.subs[8].grid[7] = self.rows[8].grid[7]
self.subs[8].grid[8] = self.rows[8].grid[8]
if set1 == 'subs' and set2 == 'rows':
self.rows[0].grid[0] = self.subs[0].grid[0] #first row
self.rows[0].grid[1] = self.subs[0].grid[1]
self.rows[0].grid[2] = self.subs[0].grid[2]
self.rows[1].grid[0] = self.subs[0].grid[3]
self.rows[1].grid[1] = self.subs[0].grid[4]
self.rows[1].grid[2] = self.subs[0].grid[5]
self.rows[2].grid[0] = self.subs[0].grid[6]
self.rows[2].grid[1] = self.subs[0].grid[7]
self.rows[2].grid[2] = self.subs[0].grid[8]
self.rows[0].grid[3] = self.subs[1].grid[0]
self.rows[0].grid[4] = self.subs[1].grid[1]
self.rows[0].grid[5] = self.subs[1].grid[2]
self.rows[1].grid[3] = self.subs[1].grid[3]
self.rows[1].grid[4] = self.subs[1].grid[4]
self.rows[1].grid[5] = self.subs[1].grid[5]
self.rows[2].grid[3] = self.subs[1].grid[6]
self.rows[2].grid[4] = self.subs[1].grid[7]
self.rows[2].grid[5] = self.subs[1].grid[8]
self.rows[0].grid[6] = self.subs[2].grid[0]
self.rows[0].grid[7] = self.subs[2].grid[1]
self.rows[0].grid[8] = self.subs[2].grid[2]
self.rows[1].grid[6] = self.subs[2].grid[3]
self.rows[1].grid[7] = self.subs[2].grid[4]
self.rows[1].grid[8] = self.subs[2].grid[5]
self.rows[2].grid[6] = self.subs[2].grid[6]
self.rows[2].grid[7] = self.subs[2].grid[7]
self.rows[2].grid[8] = self.subs[2].grid[8]
self.rows[3].grid[0] = self.subs[3].grid[0] #second row
self.rows[3].grid[1] = self.subs[3].grid[1]
self.rows[3].grid[2] = self.subs[3].grid[2]
self.rows[4].grid[0] = self.subs[3].grid[3]
self.rows[4].grid[1] = self.subs[3].grid[4]
self.rows[4].grid[2] = self.subs[3].grid[5]
self.rows[5].grid[0] = self.subs[3].grid[6]
self.rows[5].grid[1] = self.subs[3].grid[7]
self.rows[5].grid[2] = self.subs[3].grid[8]
self.rows[3].grid[3] = self.subs[4].grid[0]
self.rows[3].grid[4] = self.subs[4].grid[1]
self.rows[3].grid[5] = self.subs[4].grid[2]
self.rows[4].grid[3] = self.subs[4].grid[3]
self.rows[4].grid[4] = self.subs[4].grid[4]
self.rows[4].grid[5] = self.subs[4].grid[5]
self.rows[5].grid[3] = self.subs[4].grid[6]
self.rows[5].grid[4] = self.subs[4].grid[7]
self.rows[5].grid[5] = self.subs[4].grid[8]
self.rows[3].grid[6] = self.subs[5].grid[0]
self.rows[3].grid[7] = self.subs[5].grid[1]
self.rows[3].grid[8] = self.subs[5].grid[2]
self.rows[4].grid[6] = self.subs[5].grid[3]
self.rows[4].grid[7] = self.subs[5].grid[4]
self.rows[4].grid[8] = self.subs[5].grid[5]
self.rows[5].grid[6] = self.subs[5].grid[6]
self.rows[5].grid[7] = self.subs[5].grid[7]
self.rows[5].grid[8] = self.subs[5].grid[8]
self.rows[6].grid[0] = self.subs[6].grid[0] #third row
self.rows[6].grid[1] = self.subs[6].grid[1]
self.rows[6].grid[2] = self.subs[6].grid[2]
self.rows[7].grid[0] = self.subs[6].grid[3]
self.rows[7].grid[1] = self.subs[6].grid[4]
self.rows[7].grid[2] = self.subs[6].grid[5]
self.rows[8].grid[0] = self.subs[6].grid[6]
self.rows[8].grid[1] = self.subs[6].grid[7]
self.rows[8].grid[2] = self.subs[6].grid[8]
self.rows[6].grid[3] = self.subs[7].grid[0]
self.rows[6].grid[4] = self.subs[7].grid[1]
self.rows[6].grid[5] = self.subs[7].grid[2]
self.rows[7].grid[3] = self.subs[7].grid[3]
self.rows[7].grid[4] = self.subs[7].grid[4]
self.rows[7].grid[5] = self.subs[7].grid[5]
self.rows[8].grid[3] = self.subs[7].grid[6]
self.rows[8].grid[4] = self.subs[7].grid[7]
self.rows[8].grid[5] = self.subs[7].grid[8]
self.rows[6].grid[6] = self.subs[8].grid[0]
self.rows[6].grid[7] = self.subs[8].grid[1]
self.rows[6].grid[8] = self.subs[8].grid[2]
self.rows[7].grid[6] = self.subs[8].grid[3]
self.rows[7].grid[7] = self.subs[8].grid[4]
self.rows[7].grid[8] = self.subs[8].grid[5]
self.rows[8].grid[6] = self.subs[8].grid[6]
self.rows[8].grid[7] = self.subs[8].grid[7]
self.rows[8].grid[8] = self.subs[8].grid[8]
def findall(listin, x):
occurences = []
for i in range(len(listin)):
if listin[i] == x:
occurences.append(i)
return occurences
Python 3.7.4, installed under Visual Studio 2019 Windows 10 Home