Overlapping Frames with PySimpleGUI

Viewed 202

I want to create a GUI using pySimpleGUI that resembles the Killer Sudoku game. For those unfamiliar with Killer Sudoku, it is similar to regular sudoku however there is an extra level of complexity where not only do you have the 9 3x3 boxes where the number 1-9 has to be used exactly one time per frame, column and row but there are also cages. These cages have a number that specifies what all the numbers in the cage must sum up to. No number can be repeated in the cage either. Here is an example of what one of these boards looks like.

Killer Sudoku Board

As you can see, not only would I need a frame to outline each 3x3, but also a frame for each cage on the board. The issue comes when a cage is in both a column and a row (an L shape). Does pySimpleGUI have the ability to frame abnormal shapes like what would be required for this sudoku game?

Thanks for any assistance

1 Answers

Show how to draw the board on sg.Graph in following example script, of course, not everything drawn here

There're still lot of works to do to implement your Killer Sudoku game.

import PySimpleGUI as sg

class Sudoku():

    def __init__(self):
        # Use monospaced font here
        self.columns = 9
        self.blocks = 3
        self.bg = 'green'
        self.line_width = (1, 3, 6)
        self.line_color = ("black", "black", 'grey')
        self.number_color = ("white", "yellow")
        self.font0 = ("Courier New", 20, 'bold')
        self.font1 = ("Courier New", 12)
        self.w0, self.h0 = self.char_size(self.font0)
        self.w1, self.h1 = self.char_size(self.font1)
        self.pad = 8
        w = self.w0 + 2 * (self.w1 + 2 * self.pad)
        h = self.h0 + 2 * self.pad
        self.cell_width = max(w, h)
        self.width = (self.cell_width * self.columns + 2 * self.line_width[2])
        self.size = (self.width, self.width)
        self.numbers0, self.numbers1 = [], []

    def char_size(self, font):
        root = sg.tk.Tk()
        size = (sg.tk.font.Font(font=font).measure("W"),
            sg.tk.font.Font(font=font).metrics('linespace'))
        root.destroy()
        return size

    def draw_board1(self, element):
        delta = self.columns * self.cell_width
        for i in range(self.columns+1):
            x = self.line_width[2] + i * self.cell_width
            x0, x1 = self.line_width[2], self.line_width[2] + delta
            y = self.line_width[2] + i * self.cell_width
            y0, y1 = self.line_width[2], self.line_width[2] + delta
            element.draw_line((x0, y), (x1, y), color=self.line_color[0],
                width=self.line_width[0])
            element.draw_line((x, y0), (x, y1), color=self.line_color[0],
                width=self.line_width[0])

    def draw_board2(self, element):
        delta = self.columns * self.cell_width
        for i in range(self.blocks+1):
            x = self.line_width[2] + 3 * i * self.cell_width
            x0, x1 = self.line_width[2], self.line_width[2] + delta
            y = self.line_width[2] + 3 * i * self.cell_width
            y0, y1 = self.line_width[2], self.line_width[2] + delta
            element.draw_line((x0, y), (x1, y), color=self.line_color[1],
                width=self.line_width[1])
            element.draw_line((x, y0), (x, y1), color=self.line_color[1],
                width=self.line_width[1])

    def draw_number0(self, element, data):
        if self.numbers0:
            element.Widget.delete(*self.numbers)
        self.numbers0 = []
        offset_x = self.line_width[2] + self.cell_width//2
        offset_y = self.cell_width*self.columns + self.line_width[2] - self.cell_width//2
        for row in range(self.columns):
            for col in range(self.columns):
                item = element.draw_text(str(data[row][col]),
                    (offset_x + self.cell_width * col, offset_y - self.cell_width * row),
                    font=self.font0, color=self.number_color[0])
                self.numbers0.append(item)

    def draw_number1(self, element, data):
        if self.numbers1:
            element.Widget.delete(*self.numbers)
        self.numbers1 = []
        offset_x = self.line_width[2] + self.pad + self.w1
        offset_y = self.cell_width*self.columns + self.line_width[2] - self.pad - self.h1//2
        for row, col, number in data:
            item = element.draw_text(str(number),
                (offset_x + self.cell_width * col, offset_y - self.cell_width * row), font=self.font1,
                color=self.number_color[1])
            self.numbers1.append(item)

s = Sudoku()
data0 = list(map(list, [
    "739541286", "168923457", "245687139", "973862541", "512374968",
    "684195372", "421738695", "397256814", "856419723"]))
data1 = [
    (0, 0, 11), (0, 2, 17), (0, 3, 9), (0, 5, 3), (0, 7, 19),
    (1, 1, 15), (1, 3, 11), (1, 5, 10), (1,6, 10), (1,8, 16),
    (2, 0, 16), (2, 3, 14), (2, 4, 16), (2, 7, 8),
    (3, 1, 8), (3, 2, 5),
    (4, 3, 14), (4, 6, 12), (4, 7, 13), (4, 8, 15),
    (5, 0, 16), (5, 2, 12), (5, 3, 13), (5, 5, 13),
    (6, 0, 7), (6, 3, 9), (6, 6, 16),
    (7, 1, 22), (7, 4, 11), (7, 6, 15), (7, 8, 9),
    (8, 2, 10), (8, 4, 10),
]

layout = [
    [sg.Graph(s.size, (0, 0), s.size, background_color=s.bg, key='GRAPH',
        enable_events=True)],
]

window = sg.Window("Killer Sudoku", layout, margins=(0, 0), finalize=True)
graph = window['GRAPH']
s.draw_board1(graph)
s.draw_board2(graph)
s.draw_number0(graph, data0)
s.draw_number1(graph, data1)

while True:
    event, values = window.read()
    if event in (sg.WINDOW_CLOSED, 'Exit'):
        break
    print(event, values)
window.close()

enter image description here

Related