python – Tic Tac Toe game with an adjustable grid size in Tkinter

I’ve created a full Tic tac toe game in Tkinter that has single and multiplayer modes. The single player mode then has a hard and easy difficulty. The easy difficulty is just choosing a random open spot and the hard difficulty uses the minimax algorithm. On top of all that, the grid size for the game is adjustable, but only for odd numbers (so that diagonal wins are always possible). This is my first time creating tic tac toe and one of my first Tkinter projects, so I am not sure if my code is efficient or that organized. Any tips or feedback about how I could improve my code would be great. Here’s my code:

Main.py

import gui

if (__name__ == "__main__"):
    controller = gui.GUIController()
 
    gui.window.option_add( "*font", "roboto 22")

    controller.disHome()

Gui.py

from tkinter import *
from tkinter import font
from main import *
import sys
import TicTacToe
import Minimax
import copy

window = Tk()
window.config(padx=15, pady=15)

class GUIController:
    def __init__(self):
        self.backgroundColor = "#FA867A"
        self.titleColor = "#000000"
        self.frameColor = "#FFE6E8"

        self.buttonTextColor = "#0A241C"
        self.buttonColor = "#FA867A"
        self.buttonPressedColor = "#fc4430"
        self.buttonHovorColor = "#fc7365"

        # Single = 0, multiplayer = 1, easy = 2, impossible = 3
        self.buttonList = ((None, self.buttonColor), (None, self.buttonColor), (None, self.buttonColor), (None, self.buttonColor))  
        self.turnLblX = None
        self.turnLblO = None

        self.tictactoe = None
        self.minimax = None

        self.gameMode = None
        self.diff = None

        self.sliderValue = 3
        self.gridSlider = 3

    def resetVariables(self):
        # Single = 0, multiplayer = 1, easy = 2, impossible = 3
        self.buttonList = ((None, self.buttonColor), (None, self.buttonColor), (None, self.buttonColor), (None, self.buttonColor))  
        self.turnLblX = None
        self.turnLblO = None

        self.tictactoe = None

        self.gameMode = None
        self.diff = None

        self.sliderValue = 3
        self.gridSlider = 3

    #Displaying the home window, is the first window called
    def disHome(self):
        # Is called at the beginning of each window function to clear the window before we draw on it again
        self.clearWindow()

        # Reset all values
        self.resetVariables()

        # Creating the frames
        topFrame = Frame(window)
        middleFrames = Frame(window, pady=10, relief=RIDGE, bd=2)
        middleFrameTop = Frame(middleFrames, padx=15, pady=0)
        middleFrameMid = Frame(middleFrames, padx=15, pady=0)
        middleFrameBot = Frame(middleFrames, padx=15, pady=0)
        bottomFrame = Frame(window)

        # Display the title
        Label(topFrame, text="Tic Tac Toe", bg=self.backgroundColor, fg=self.titleColor, font=("Roboto", 35)).grid(column=0, row=0, pady=15)


        # Displaying Single Player Button
        self.buttonList(0)(0) = Button(middleFrameTop, text="Single Player", bg=self.buttonList(0)(1), fg=self.buttonTextColor,
         activebackground=self.buttonPressedColor, padx=10, pady=10, width=10, relief=GROOVE,
         command=lambda x="s": self.setGameMode(x))
        self.buttonList(0)(0).grid(column=0, row=0, padx=2, pady=2)

        # Displaying Multiplayer Button
        self.buttonList(1)(0) = Button(middleFrameTop, text="Multiplayer", bg=self.buttonColor, fg=self.buttonTextColor,
         activebackground=self.buttonPressedColor, padx=10, pady=10, width=10, relief=GROOVE,
         command=lambda x="m": self.setGameMode(x))
        self.buttonList(1)(0).grid(column=0, row=1, padx=2, pady=2)

        # Displaying Easy Button
        self.buttonList(2)(0) = Button(middleFrameMid, text="Easy", bg=self.buttonColor, fg=self.buttonTextColor,
         activebackground=self.buttonPressedColor, padx=10, pady=10, width=10, relief=GROOVE,
         state=DISABLED if self.gameMode==None else NORMAL, command=lambda x=2: self.activateDiffBtns(x))
        self.buttonList(2)(0).grid(column=0, row=0, padx=2, pady=2) 

        # Displaying Impossible Button
        self.buttonList(3)(0) = Button(middleFrameMid, text="Hard", bg=self.buttonColor, fg=self.buttonTextColor,
         activebackground=self.buttonPressedColor, padx=10, pady=10, width=10, relief=GROOVE,
         state=DISABLED if self.gameMode==None else NORMAL, command=lambda x=3: self.activateDiffBtns(x))
        self.buttonList(3)(0).grid(column=1, row=0, padx=2, pady=2)

        # Changing the button colors when the mouse is hovored over
        for button in self.buttonList:
            self.changeOnHover(button(0), self.buttonHovorColor, button(1))

        # Displaying the slider to change the game grid size
        Label(middleFrameBot, text="Enter Board Size:", bg=self.frameColor, fg=self.titleColor,).grid(column=0, row=0)
        
        self.gridSlider = Scale(middleFrameBot, from_=3, to=21, orient=HORIZONTAL, bg=self.frameColor, fg=self.buttonTextColor,
        activebackground=self.backgroundColor, troughcolor=self.buttonColor,
         variable=self.sliderValue, command=self.updateSliderValue)
        self.gridSlider.grid(column=0, row=1, sticky="nsew", padx=20, pady=2)

        self.gridSlider.config()

        # Display the exit button
        Button(bottomFrame, text="Exit", bg="red", fg=self.buttonTextColor, width=10, command=lambda: sys.exit(), relief=GROOVE).grid(column=0, row=0, padx=5, pady=15, sticky="nsew")

        # Displaying the start button
        sB = Button(bottomFrame, text="Start", bg="#4ABF36", activebackground="#62FA47", fg=self.buttonTextColor, 
        width=10, relief=GROOVE, command=lambda: self.disGameWindow((self.gridSlider.get(), True)))
        sB.grid(column=1, row=0, padx=5, pady=15, sticky="nsew")
        self.changeOnHover(sB, "#56F222", "#4ABF36")

        # Displaying the frames
        topFrame.grid(column=0, row=0)
        middleFrames.grid(column=0, row=1)
        middleFrameTop.grid(column=0, row=0)
        middleFrameMid.grid(column=0, row=1)
        middleFrameBot.grid(column=0, row=2)
        bottomFrame.grid(column=0, row=2)
        # Setting the background colors
        topFrame.configure(bg=self.backgroundColor)
        middleFrames.configure(bg=self.frameColor)
        middleFrameTop.configure(bg=self.frameColor)
        middleFrameMid.configure(bg=self.frameColor)
        middleFrameBot.configure(bg=self.frameColor)
        bottomFrame.configure(bg=self.backgroundColor)
        window.configure(bg=self.backgroundColor)

        window.mainloop()
  
    # Displaying the game window with proper grid size
    def disGameWindow(self, p):
        gridSize = p(0)
        initial = p(1)

        if (initial):
            if (self.gameMode == None):
                messagebox.showinfo("Error", "Please select a gamemode.")
                self.disHome()
            elif (self.gameMode == "s" and self.diff == None):
                messagebox.showinfo("Error", "Please select a game difficulty when playing single player.")
                self.disHome()

            if (self.diff == 3 and gridSize > 3):
                messagebox.showinfo("Error", "Impossible difficulty not avaliable for game sizes above 3 :(")
                self.disHome()

            self.tictactoe = TicTacToe.ttt(gridSize)
            self.minimax = Minimax.Minimax(gridSize, self.tictactoe)

        # Clear the current window
        self.clearWindow()

        topFrame = Frame(window)
        gameFrame = Frame(window, padx=15, pady=15)
        gridBackgroundFrame = Frame(gameFrame)
        bottomFrame = Frame(window)

        frames = ()
        self.buttonList = ()
        for i in range(gridSize):
            (i)
            frames.append(())
            self.buttonList.append(())
            for j in range(gridSize):
                px = ((j != 0) * 2,  (j != gridSize-1) * 2)
                py = ((i != 0) * 2,  (i != gridSize-1) * 2)
                frames(i).append(Frame(gridBackgroundFrame, bg=self.frameColor,
                 width=int(400/gridSize), height=int(400/gridSize)))
                frames(i)(j).pack_propagate(False)
                frames(i)(j).grid(row=i, column=j, padx=px, pady=py)

                self.buttonList(i).append(Button(frames(i)(j), text=" ",
                 bg=self.frameColor, relief=FLAT,
                 command=lambda p=(i, j): self.buttonClicked(p)))
                self.buttonList(i)(-1).pack(expand=True, fill=BOTH)
                self.buttonList(i)(j)('font') = self.findFontSize(self.tictactoe.gridSize)

        for i in range(self.tictactoe.gridSize):
            for j in range(self.tictactoe.gridSize):
                self.buttonList(i)(j)('text') = self.displayLetter(i, j)

        # Displaying the score and who's turn it is
        self.turnLblX = Label(topFrame, text="Player" if self.gameMode=="s" else "Player 1", bg=str(self.buttonHovorColor) if self.tictactoe.turn==1 else str(self.frameColor), fg=self.titleColor, padx=5)
        self.turnLblX.grid(column=0, row=0, sticky="nsew")
        Label(topFrame, text="", bg=self.backgroundColor, padx=10).grid(column=1, row=0, sticky="nsew")
        self.turnLblO = Label(topFrame, text="Computer" if self.gameMode=="s" else "Player 2", bg=str(self.buttonHovorColor) if self.tictactoe.turn==-1 else str(self.frameColor), fg=self.titleColor, padx=5)
        self.turnLblO.grid(column=2, row=0, sticky="nsew")

        # Displaying the exit button
        Button(bottomFrame, text="Exit", bg="red", fg=self.buttonTextColor, width=10,
         command=lambda: sys.exit(), relief=GROOVE).grid(column=0, row=0, padx=5, pady=15, sticky="nsew")

        # Displaying the home button
        sB = Button(bottomFrame, text="Home", bg="#4ABF36", activebackground="#62FA47", fg=self.buttonTextColor,
         width=10, relief=GROOVE, command=self.disHome)
        sB.grid(column=1, row=0, padx=5, pady=15, sticky="nsew")
        self.changeOnHover(sB, "#56F222", "#4ABF36")


        topFrame.grid(column=0, row=0, pady=15)
        gameFrame.grid(column=0, row=1)
        gridBackgroundFrame.grid(column=0, row=0)
        bottomFrame.grid(column=0, row=2, pady=5, sticky="n")
        # Drawing Background 
        topFrame.configure(bg=self.backgroundColor)
        gameFrame.configure(bg=self.frameColor)
        gridBackgroundFrame.configure(bg=self.buttonColor)
        bottomFrame.configure(bg=self.backgroundColor)

        window.mainloop()

        # Called when the user clicks a grid in the game board

    # Called when a grid button is clickedSB1206
    def buttonClicked(self, index):
        self.tictactoe.updateGameGrid(index)

        self.buttonList(index(0))(index(1))('text') = ("X" if self.tictactoe.grid(index(0))(index(1))==1 else "O")

        if (self.gameMode == "s"):
            if (self.tictactoe.winner != None):
                self.disWinMessage(self.tictactoe.winner)

            if (self.diff == 2 and self.tictactoe.turn == -1):
                # Easy Difficulty, random choice for computer
                self.buttonClicked(self.tictactoe.randomIndex())
            elif (self.diff == 3 and self.tictactoe.turn == -1):
                winner, nextMove = self.minimax.minimax(copy.deepcopy(self.tictactoe.grid), (-1, -1), 15, self.tictactoe.turn)
                self.tictactoe.winner = None
                self.buttonClicked(nextMove)
        elif (self.gameMode == "m"):
            if (self.tictactoe.winner != None):
                self.disWinMessage(self.tictactoe.winner)

            self.turnLblX('bg') = (self.buttonHovorColor if self.tictactoe.turn==1 else self.frameColor)
            self.turnLblO('bg') = (self.buttonHovorColor if self.tictactoe.turn==-1 else self.frameColor)

    # Called when there is a winner
    def disWinMessage(self, winner):
        if (self.gameMode == "s"):
            if (winner == 1):
                messagebox.showinfo("Winner", "Yayayayay YOU WON!!!")
            elif (self.tictactoe.winner == -1): 
                messagebox.showinfo("Loser", "The computer took the victory this time")
            elif (self.tictactoe.winner == 0):
                messagebox.showinfo("Tie", "This match has resulted in a tie")

            self.disHome()
        else:
            if (winner == 1):
                messagebox.showinfo("Winner", "Player 1 has annihilated player 2!!")
            elif (self.tictactoe.winner == -1): 
                messagebox.showinfo("Loser", "Player 2 has defeated player 1!!")
            elif (self.tictactoe.winner == 0):
                messagebox.showinfo("Tie", "This match has resulted in a tie")

            self.disHome()


    # Function to change properties of button on hover
    def changeOnHover(self, button, colorOnHover, colorOnLeave):
        # Adjusting backgroung of the widget
        # Background on entering widget
        button.bind("<Enter>", func=lambda e: button.config(
            background=colorOnHover))
    
        # Background color on leving widget
        button.bind("<Leave>", func=lambda e: button.config(
            background=colorOnLeave))

    # Clears the entire window
    def clearWindow(self):
        for widget in window.winfo_children():
            widget.destroy()

    def findFontSize(self, size):
        s = 22
        if (size==3): s = 80
        elif (5 <= size <= 9): s = 40
        elif (11 <= size <= 15): s = 25
        else: s = 18

        f = font.Font(family="Roboto", size=s)
        return f

    def updateSliderValue(self, variable):
        temp = int(variable)
        if (temp%2==0):
            self.gridSlider.set(temp+1)

    def activateDiffBtns(self, d):
        d = int(d)

        if (self.buttonList(d)(0)('state') != DISABLED):
            if (self.diff != None and (self.diff != d)):
                i = int(d+1 if d==2 else d-1)
                self.buttonList(i)(1) = self.buttonColor
                self.buttonList(i)(0).configure(bg=self.buttonList(i)(1))
                self.changeOnHover(self.buttonList(i)(0), self.buttonHovorColor, self.buttonList(i)(1))


            self.buttonList(d)(1) = self.buttonPressedColor
        
            self.buttonList(d)(0).configure(bg=self.buttonList(d)(1))
            self.changeOnHover(self.buttonList(d)(0), self.buttonHovorColor, self.buttonList(d)(1))

            self.diff = d

    def setGameMode(self, m):   
        if (m == "s"):
            self.diffButtonState(NORMAL)

        if (m == "s"):
            if (self.gameMode == None):
                self.buttonList(0)(1) = self.buttonPressedColor
                self.gameMode = m
                self.buttonList(1)(0).configure(state=DISABLED)
            elif (self.gameMode == "s"):
                self.buttonList(0)(1) = self.buttonColor
                self.gameMode = None
                self.diffButtonState(DISABLED)
                self.buttonList(1)(0).configure(state=NORMAL)
                
            self.buttonList(0)(0).configure(bg=self.buttonList(0)(1))
            self.changeOnHover(self.buttonList(0)(0), self.buttonHovorColor, self.buttonList(0)(1))
        else:
            if (self.gameMode == None):
                self.buttonList(1)(1) = self.buttonPressedColor
                self.gameMode = m
                self.buttonList(0)(0).configure(state=DISABLED)
            elif (self.gameMode == "m"):
                self.buttonList(1)(1) = self.buttonColor
                self.gameMode = None
                self.buttonList(0)(0).configure(state=NORMAL)


            self.buttonList(1)(0).configure(bg=self.buttonList(1)(1))
            self.changeOnHover(self.buttonList(1)(0), self.buttonHovorColor, self.buttonList(1)(1))
        
    def diffButtonState(self, state):
        self.buttonList(2)(0)('state') = state
        self.buttonList(3)(0)('state') = state

        if (state == DISABLED):
            self.buttonList(3)(1) = self.buttonColor
            self.buttonList(3)(0).configure(bg=self.buttonList(3)(1))
            self.changeOnHover(self.buttonList(3)(0), self.buttonHovorColor, self.buttonList(3)(1))

            self.buttonList(2)(1) = self.buttonColor
            self.buttonList(2)(0).configure(bg=self.buttonList(2)(1))
            self.changeOnHover(self.buttonList(2)(0), self.buttonHovorColor, self.buttonList(2)(1))

    def displayLetter(self, i, j):
        if (self.tictactoe.grid(i)(j) == 1):
            return "X"
        elif (self.tictactoe.grid(i)(j) == -1): 
            return "O"
        else:
            return " "  

Tictactoe.py

from random import randint

class ttt:
    def __init__(self, size):
        self.gridSize = size
        self.grid = self.createGrid()

        # If using minimax algorithm, user is maximizer(1) and computer is minimizer(-1)
        # If single player, then user is 1, computer is -1
        # If multiplayer, user1 is 1, user2 = -1
        self.turn = 1
        self.winner = None

    def createGrid(self):
        grid = ()
        for i in range(self.gridSize):
            grid.append(())
            for j in range(self.gridSize):
                grid(i).append(0)


        # grid = ((-1, 1, 0), (0, -1, 0), (0, 0, 0))

        return grid

    def updateGameGrid(self, index):
        if (self.grid(index(0))(index(1)) != 0):
            return

        self.grid(index(0))(index(1)) = self.turn
        winner = self.findWinner(index, self.grid)  

        self.turn = -self.turn
        
    def randomIndex(self):
        x = randint(0, self.gridSize-1)
        y = randint(0, self.gridSize-1)
        while (self.grid(x)(y) != 0):
            x = randint(0, self.gridSize-1)
            y = randint(0, self.gridSize-1)
        return (x, y)

    def findWinner(self, index, grid):
        # Row
        found = True
        for j in range(self.gridSize-1):
            if (grid(index(0))(j) != grid(index(0))(j+1) or grid(index(0))(j) == 0):
                found = False
                break
        if (found):
            self.winner = self.turn
            return self.turn
        
        # Column
        found = True
        for i in range(self.gridSize-1):
            if (grid(i)(index(1)) != grid(i+1)(index(1)) or grid(i)(index(1)) == 0):
                found = False
                break
        if (found):
            self.winner = self.turn
            return self.turn
        
        # Top Left to Bottom Right Diagonal
        if (index(0) == index(1)):
            found = True
            for i in range(self.gridSize-1):
                if (grid(i)(i) != grid(i+1)(i+1) or grid(i)(i) == 0):
                    found = False
                    break
            if (found):
                self.winner = self.turn
                return self.turn

        # Top Right to Bottom Left Diagonal
        if (index(0) + index(1) == self.gridSize-1):
            found = True
            for i in range(self.gridSize-1):
                if (grid(self.gridSize-i-1)(i) != grid(self.gridSize-i-2)(i+1) or grid(self.gridSize-i-1)(i) == 0):
                    found = False
                    break
            if (found):
                self.winner = self.turn
                return self.turn
            
        
        tie = True
        for i in range(self.gridSize):
            for j in range(self.gridSize):
                if (grid(i)(j) == 0):
                    tie = False
        
        if (tie):
            self.winner = 0
            return 0

        return None

Minimax.py

import copy

class Minimax:
    def __init__(self, gs, t):
        self.gridSize = gs
        self.ttt = t

    def minimax(self, state, currIndex, depth, turn):
        if (currIndex(0) != -1 and currIndex(1) != -1):
            winner = self.ttt.findWinner(currIndex, state)

            if (winner == -1):
                return winner - depth, currIndex
            elif (winner == -1):
                return winner + depth, currIndex
            elif (winner == 0):
                return 0, currIndex

            if (depth==0 and winner==None):
                return 0, currIndex
        
        evalLimit = -turn * 1000
        bestIndex = None
        for i in range(self.gridSize):
            for j in range(self.gridSize):
                if (state(i)(j) == 0):
                    state(i)(j) = turn

                    eval, newIndex = self.minimax(state, (i, j), depth-1, -turn)
                    state(i)(j) = 0
                    if (turn > 0 and eval > evalLimit):
                        bestIndex = newIndex
                        evalLimit = eval
                    elif (turn < 0 and eval < evalLimit):
                        bestIndex = newIndex
                        evalLimit = eval
        
        return evalLimit, bestIndex

python – Make a Simultaneous linear equation solver using Tkinter GUI

It’s a simple program, the user is prompted to enter number of variables.

enter image description here

Then the user is prompted to enter coefficients and constants. That many equations will be there as many variables.

enter image description here

Once filled up, submit button is clicked and the solutions are displayed.

enter image description here

I’ve accomplished this much successfully. However I’m new to Tkinter. I would appreciate if someone can help me improve the user interface and the code in general. The code is here. No need to make any change to the solving algorithm. I’m not allowed to use numpy for this assignment šŸ™ I want to improve the interface and the design language, that’s it.

def minor(matrix, i, j):
    return ((matrix(r)(c) for c in range(len(matrix(r))) if c != j)
            for r in range(len(matrix)) if r != i)


def det(matrix):
    if len(matrix) == len(matrix(0)) == 1:
        return matrix(0)(0)
    return sum(matrix(0)(i) * cofac(matrix, 0, i) for i in range(len(matrix(0))))


cofac = lambda matrix, i, j: (-1) ** ((i + j) % 2) * det(minor(matrix, i, j))

transpose = lambda matrix: ((matrix(r)(c) for r in range(len(matrix))) for c in range(len(matrix(0))))


def adj(matrix):
    return transpose(((cofac(matrix, r, c) for c in range(len(matrix(r)))) for r in range(len(matrix))))


def div_and_store(a, d):
    toPrint = ()
    for i in a:
        toPrint.append(())
        for j in i:
            if j % d == 0:
                toPrint(-1).append(f'{j//d}')
            else:
                h = hcf(j, d)
                denominator = d//h
                numerator = j//h
                if denominator > 0:
                    toPrint(-1).append(f'{numerator}/{denominator}')
                else:
                    toPrint(-1).append(f'{-numerator}/{-denominator}')
    return toPrint


hcf = lambda x, y: y if x == 0 else hcf(y % x, x)


def product(A, B):
    if len(A(0)) != len(B):
        return
    Bt = transpose(B)
    return ((sum(a * b for a, b in zip(i, j)) for j in Bt) for i in A)


from tkinter import *
root = Tk()
root.resizable(width=False, height=False)  # not resizable in both directions
root.title('Simultaneous liner equation Solver')
my_label = Label(root, text='How many variables?')
my_label.grid(row=0, column=0)
e = Entry(root, width=10, borderwidth=5)


def done():
    global row, n
    try:
        A = ()
        S = ()
        for record in entries:
            A.append(())
            for i in range(0, len(record)-1):
                entry = record(i).get()
                if entry:
                    A(-1).append(int(entry))
                else:
                    A(-1).append(0)
            entry = record(-1).get()
            S.append((int(entry)))
    except ValueError:
        new_label = Label(root, text='Invalid. Try again!')
        new_label.grid(row=row, columnspan=n * 3, sticky='W')
        new_label.after(1000, lambda: new_label.destroy())
        return
    for record in entries:
        for entry in record:
            entry('state') = DISABLED
    new_button('state') = DISABLED
    determinant = det(A)
    if determinant == 0:
        new_label = Label(root, text='No unique solution set!')
        new_label.grid(row=row, columnspan=n * 3, sticky='W')
        return
    adjoin = adj(A)
    solution = div_and_store(product(adjoin, S), determinant)
    for i in range(n):
        new_label = Label(root, text=chr(97+i) + ' = '+solution(i)(0))
        new_label.grid(row=row, columnspan=2, sticky='W')
        row += 1


def submit():
    try:
        global n
        n = int(e.get())
    except ValueError:
        label = Label(root, text="You're supposed to enter a number, Try again")
        label.grid(row=1, column=0, columnspan=3)
        label.after(1000, lambda: label.destroy())
        return
    if n < 2:
        label = Label(root, text="At least two variables are required!")
        label.grid(row=1, column=0, columnspan=3)
        label.after(1000, lambda: label.destroy())
        return
    e('state') = DISABLED
    my_label.grid_forget()
    my_button.grid_forget()
    e.grid_forget()
    global row, entries
    row = 0
    entries = ()
    for i in range(n):
        Label(root, text='').grid(row=row, columnspan=3*n+1)
        row += 1
        entries.append(())
        col = 0
        for j in map(chr, range(97, 97+n)):
            entry = Entry(root, width=5, borderwidth=2, justify='right')
            entries(i).append(entry)
            entry.grid(row=row, column=col)
            col += 1
            Label(root, text=j).grid(row=row, column=col, padx=5, sticky='W')
            col += 1
            if col == 3*n-1:
                Label(root, text='=').grid(row=row, column=col)
                col += 1
                entry = Entry(root, width=5, borderwidth=2, justify='right')
                entries(i).append(entry)
                entry.grid(row=row, column=col)
            else:
                Label(root, text='+').grid(row=row, column=col)
            col += 1
        row += 1
    Label(root, text='').grid(row=row)
    row += 1
    txt = 'May leave the blank empty if the coefficient is 0!'
    Label(root, text=txt).grid(row=row, columnspan=3*n-1, sticky='W')
    row += 1
    global new_button
    new_button = Button(root, text='Submit', command=done)
    new_button.grid(row=row, column=3*n)
    row += 1


my_button = Button(root, text='Submit', command=submit)
e.grid(row=0, column=2)
my_button.grid(row=0, column=3)
root.mainloop()

python – Trying to add bmp image to ScrolledText widget and it says it does not exist (Tkinter)

I’m trying to add a program icon I got as a bmp image to a ScrolledText widget. I’m getting this error:

_tkinter.TclError: image “<PIL.BmpImagePlugin.BmpImageFile image mode=RGB size=32×32 at 0x19B2FA18EB0>” doesn’t exist

Here’s the relevant code:

def get_icon(exe):
    ico_x = win32api.GetSystemMetrics(win32con.SM_CXICON)
    ico_y = win32api.GetSystemMetrics(win32con.SM_CYICON)

    large, small = win32gui.ExtractIconEx(exe, 0)
    win32gui.DestroyIcon(small(0))

    hdc = win32ui.CreateDCFromHandle(win32gui.GetDC(0))
    hbmp = win32ui.CreateBitmap()
    hbmp.CreateCompatibleBitmap(hdc, ico_x, ico_y)
    hdc = hdc.CreateCompatibleDC()

    hdc.SelectObject(hbmp)
    hdc.DrawIcon((0, 0), large(0))

    hbmp.SaveBitmapFile(hdc, 'icon.bmp')

    img = Image.open("icon.bmp")

    return img

text_area = ScrolledText(root,
                            width = 40,
                            height = 40,
                            font = ("Times New Roman",
                                    25))

img = get_icon("C:Program FilesBraveSoftwareBrave-BrowserApplicationBrave.exe")
text_area.image_create("current", padx=5, pady=5, image=ImageTk.PhotoImage(img))
text_area.place(relx=0.2, rely=0.1, relwidth=0.5, relheight=0.5, anchor='s')

Any ideas?

python – Tkinter, Linux: How to view window in windows task bar which has no title bar?

One way is using libX11 directly and telling it not to draw the title bar on a normal tk.Tk() window like this:

# Mostly taken from: https://www.tonyobryan.com//index.php?article=9
# Inspired by: https://github.com/EDCD/EDMarketConnector/blob/main/theme.py
import tkinter as tk
import ctypes

# Defining types
CHAR = ctypes.c_char
UCHAR = ctypes.c_ubyte
BOOL = ctypes.c_bool
INT = ctypes.c_int
UINT = ctypes.c_uint
LONG = ctypes.c_long
PTR = ctypes.c_void_p

CHAR_PTR = ctypes.POINTER(CHAR)
UINT_PTR = ctypes.POINTER(UINT)
ULONG = ctypes.c_ulong

class HINTS(ctypes.Structure):
    _fields_ = (("flags", ULONG),
                ("functions", ULONG),
                ("decorations", ULONG),
                ("inputMode", LONG),
                ("status", ULONG))

DISPLAY = PTR
ATOM = LONG
WINDOW = LONG
WINDOW_PTR = ctypes.POINTER(WINDOW)
HINTS_PTR = ctypes.POINTER(HINTS)

def _errcheck_not_zero(value, func, args):
    if value == 0:
        args_str = ", ".join(map(str, args))
        raise OSError(f"{func.__name__}({args_str}) => {value}")
    return args

def string_to_c(data:str) -> CHAR_PTR:
    return ctypes.create_string_buffer(data.encode())

libx11 = ctypes.cdll.LoadLibrary("libX11.so.6")

# Constants
PropModeReplace = 0
XA_ATOM = 4

# Defining functions
XInternAtom = libx11.XInternAtom
XInternAtom.argtypes = (PTR, CHAR_PTR, BOOL)
XInternAtom.restype = ATOM
XInternAtom.errcheck = _errcheck_not_zero

XOpenDisplay = libx11.XOpenDisplay
XOpenDisplay.argtypes = (CHAR_PTR, )
XOpenDisplay.restype = DISPLAY
XOpenDisplay.errcheck = _errcheck_not_zero

XChangeProperty = libx11.XChangeProperty
XChangeProperty.argtypes = (DISPLAY, WINDOW, ATOM, ATOM, INT, INT, HINTS_PTR, INT)
XChangeProperty.restype = INT
XChangeProperty.errcheck = _errcheck_not_zero

XQueryTree = libx11.XQueryTree
XQueryTree.argtypes = (DISPLAY, WINDOW, WINDOW_PTR, WINDOW_PTR, WINDOW_PTR, UINT_PTR)
XQueryTree.restype = INT
XQueryTree.errcheck = _errcheck_not_zero

XFlush = libx11.XFlush
XFlush.argtypes = (DISPLAY, )
XFlush.restype = INT
XFlush.errcheck = _errcheck_not_zero


if __name__ == "__main__":
    root = tk.Tk()

    # This is needed:
    root.update_idletasks()
    # Get the handle of the window
    handle:int = root.winfo_id()

    # Get the default display
    display = XOpenDisplay(None)

    # Get the parent of the window
    parent = WINDOW()
    XQueryTree(display, handle, ctypes.byref(WINDOW()),
               ctypes.byref(parent), ctypes.byref(WINDOW()),
               ctypes.byref(UINT()))

    # Change the motif hints of the window
    motif_hints = XInternAtom(display, string_to_c("_MOTIF_WM_HINTS"), False)
    hints = HINTS(2, 0, 0, 0, 0)
    XChangeProperty(display, parent, motif_hints, XA_ATOM, 32,
                    PropModeReplace, ctypes.byref(hints), 5)
    # Flush the changes?
    XFlush(display)

    # Normal `tkinter` code can follow
    root.mainloop()

It uses root.update_idletasks() and XQueryTree(...) to get the handle of the window. Then it modifies the "_MOTIF_WM_HINTS" so that x11 would remove all of the window decorations including the title bar.

How can you run loops alongside tkinter? – Python QR code reader GUI

I am trying to write a Tkinter application that will also process QR codes. For that to work, I need to have a loop checking if the QR code is valid and I need to make a post request. I’m fully aware that the way I have coded this is highly inefficient. Is there a better way to do this? My Pi’s CPU is at 200%. Here is what I have so far:

import cv2
import tkinter as tk
from PIL import Image, ImageTk
import sys
import os
import pyzbar.pyzbar as zbar
import threading
import requests
import queue

result = ()
decodedCode = ""

logo     = "logo.png"
settings = "settings.png"

if os.environ.get('DISPLAY','') == '':
    print('no display found. Using :0.0')
    os.environ.__setitem__('DISPLAY', ':0.0')


#create main window
master = tk.Tk()
master.title("tester")
master.geometry("480x800")
master.configure(bg='white')
ttelogo = tk.PhotoImage(file = logo)
settingslogo = tk.PhotoImage(file = settings)

#settings button
settings_frame = tk.Frame(master,width=50,height=50,bg="white")
settings_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
settingsBtn = tk.Button(settings_frame, image=settingslogo).pack()
settings_frame.place(x=430,y=0)

#logo
img = tk.Label(master, image=ttelogo, bg='white')
img.image = ttelogo
img.place(x=176.5,y=10)

#Name Label
label_frame = tk.Frame(master,width=400,height=100,bg="white")
label_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(label_frame,bg="white",fg="black",text="John Smith Smithington III",font=("Calibri",22)).pack()
label_frame.place(x=40,y=140)

#Instructions Label
instructions_frame = tk.Frame(master,width=440,height=100,bg="white")
instructions_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(instructions_frame,bg="white",fg="black",text="Place your pass under the scanner below.",font=("Calibri",10)).pack()
instructions_frame.place(x=20,y=210)

#Camera Window
cameraFrame = tk.Frame(master, width=440, height=480)
cameraFrame.place(x=20, y=260)

#Camera Feed
lmain = tk.Label(cameraFrame)
lmain.place(x=0, y=0)
cap = cv2.VideoCapture(0)

def startScanning():
    global cap
    _, frame = cap.read()
    frame = cv2.flip(frame, 1)
    cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
    img = Image.fromarray(cv2image)
    imgtk = ImageTk.PhotoImage(image=img)
    lmain.imgtk = imgtk
    lmain.configure(image=imgtk)
    lmain.after(10, startScanning)

def processScan():
    global decodedCode
    stopped = False
    delay = 1
    
    while(True):
        ret = cv2.waitKey(delay) & 0xFF
        if ret == ord('c'): # continue
            stopped = False
            delay = 1
        if ret == ord('q'):
            break
        if stopped or (ret == ord('s')): # stop
            stopped = True
            delay = 30
            continue

        # Capture frame-by-frame
        ret, frame = cap.read()

        decodedObjects = zbar.decode(frame)
        if len(decodedObjects) > 0:
            stopped = True
            for code in decodedObjects:
                #print("Data", obj.data)
                #API Calls
                decodedCode = code.data.decode('utf-8')

    # When everything done, release the capture
    cap.release()
    cv2.destroyAllWindows()

def checkCode():
    global decodedCode
    while True:
        if decodedCode != "":
            print (decodedCode)
            result = requests.post("https://example.com/login/index.php", data={'action': 'validate_scan', 'uuid': decodedCode}).text
            print(result)
            decodedCode = ""

startScanning()  #Display 2
threading.Thread(name='background', target=processScan).start()
threading.Thread(name='background2', target=checkCode).start()
master.mainloop()  #Starts GUI

python – Tkinter Sticky Notes

Currently I am creating an app in Tkinter that creates, displays, and saves sticky notes. I am new to python so I keep trying to rewrite my classes so I have my code more organized but every time I try to create a separate class that inherits from my root class, it quits working properly and won’t let me get variables from the inherited class. This is the working code, let me know if this looks terrible or if I just need to better understand how to structure this. It seems far too complicated. I’ve watched videos on OOP and classes and have been reading Programming Python for some help but I can’t seem to impliment classes correctly in Tkinter although I have done general examples that work. The code if functioning fine right now but I am having a hard time adding functionality (allowing user to change font and default colors) with the way it is written.

import tkinter as tk
from tkinter import *
from tkinter.filedialog import asksaveasfile

tiny_font = ('Lucida Console', 8)
small_font = ('CalibriLight', 10)
medium_font = ('CalibriLight', 12)
large_font = ('Lucida Bright', 20)

class MyApp(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.title(string="Stickies")
        self.attributes('-alpha', 0.95)
        self.configure(bg="#ffffff")

        def makecolor(self):
            orange = self.orange.get()
            blue = self.blue.get()
            purple = self.purple.get()
            green = self.green.get()
            yellow = self.yellow.get()
            pink = self.pink.get()
            if orange == '1':
                return '#f7d9c4'
            if blue == "1":
                return '#c6def1'
            if purple == "1":
                return '#dbcdf0'
            if green == "1":
                return '#d0f4de'
            if yellow == "1":
                return '#fcf6bd'
            if pink == "1":
                return '#f2c6de'
            else:
                return '#FFFFFF'

        def openSettings(self, *args):
            settingsWindow = tk.Toplevel(self)
            settingsWindow.wm_title('Settings')
            Settingsframe = LabelFrame(
                settingsWindow,
                bg= '#ffffff',
                text='Settings',
                font=medium_font,
            )

            Filetypelabel = Label(
                Settingsframe,
                bg = '#ffffff',
                text= 'Set default file save type:'
            )

            defaulttype = StringVar
            Filetypeentry = Entry(
                Settingsframe,
                bg = '#ffffff',
                textvariable = defaulttype,
            )
            Filetypelabel.pack(padx=10, pady=10)
            Filetypeentry.pack(padx=10, pady=10)
            saveSettings = Button(Settingsframe,text='Save Settings')
            saveSettings.pack(padx=10, pady=10)
            Settingsframe.pack(fill=BOTH, padx=10, pady=10)

        def getscreensize(self):
            screen_width = self.winfo_screenwidth()
            screen_height = self.winfo_screenheight()
            screen_size = (screen_width, screen_height)
            return screen_size

        # function invoked when you click the create button ont the main frame
        def create_sticky(self, *args):
            newWindow = tk.Toplevel(self)
            nameentry = self.nameentry.get()
            if nameentry == "":
                nameentry = 'Untitled'
            newWindow.wm_title(nameentry)
            Topnoteframe = LabelFrame(
                newWindow,
                bg= '#ffffff',
                font=small_font,
            )

            saveButton = Button(Topnoteframe, text='Save', command=lambda: save_file())
            saveButton.pack(pady=5, expand=True, side=LEFT)

            hideButton = Button(Topnoteframe, text='Hide', command=lambda: hide_sticky())
            hideButton.pack(pady=5, expand=True, side=LEFT)

            showButton = Button(Topnoteframe, text='Show', command=lambda: show_sticky())
            showButton.pack(pady=5, expand=True, side=LEFT)

            Topnoteframe.pack(fill=BOTH, side=BOTTOM)

            newwindowscreenwidth = (getscreensize(newWindow))(1) * 0.3
            newwindowscreenheight = (getscreensize(newWindow))(1) * 0.3

            stickydims = str(int(newwindowscreenwidth)) + "x" + str(int(newwindowscreenheight))

            newWindow.geometry(stickydims)

            T = Text(newWindow, height=20, width=40, bg=makecolor(self))
            T.pack()
            T.configure(font='Calibri')

            def save_file():
                txtcontent = T.get("1.0", 'end-1c')
                f = asksaveasfile(initialfile=(nameentry), defaultextension=".txt",
                                              filetypes=(("All Files", "*.*"), ("Text Documents", "*.txt")))
                f.write(txtcontent)
                newWindow.destroy()
            def show_sticky():
                myscreenwidth = (getscreensize(newWindow))(1) * 0.3
                myscreenheight = (getscreensize(newWindow))(1) * 0.3
                stickydims = str(int(myscreenwidth)) + "x" + str(int(myscreenheight))
                newWindow.geometry(stickydims)
            def hide_sticky():
                stickydims = str(int(0)) + "x" + str(int(50))
                newWindow.geometry(stickydims)


        myscreenwidth = (getscreensize(self))(1)
        myscreenheight = (getscreensize(self))(1)
        myscreendims = str(int(myscreenwidth * 0.3)) + "x" + str(int(myscreenheight* 0.5))
        myscreenposition = str("+" + str((myscreenwidth+70)) + "+" + str(10))
        self.geometry(myscreendims + myscreenposition)

        MyMainFrame = frame = LabelFrame(
            self,
            text='Stickies',
            bg='#ffffff',
            font=medium_font
        )
        MyNameFrame = frame_name = LabelFrame(
            frame,
            text='Name your note',
            bg='#ffffff',
            font=medium_font
        )
        MyColorFrame = frame_colors = LabelFrame(
            frame,
            text='Colors',
            bg='#ffffff',
            font=medium_font
        )

        self.nameentry = StringVar()
        MyNameEntry = Entry(
            frame_name,
            textvariable = self.nameentry,
            font=small_font,
            bg='#f1f1f1'
        )


        self.purple = StringVar()
        self.blue = StringVar()
        self.orange = StringVar()
        self.green = StringVar()
        self.yellow = StringVar()
        self.pink = StringVar()

        color1 = Checkbutton(
            frame_colors,
            text = 'Purple',
            bg = "#dbcdf0",
            variable = self.purple,
            font=small_font,
        )
        color1.deselect()
        color2 = Checkbutton(
            frame_colors,
            text = 'Green',
            bg = "#d0f4de",
            variable = self.green,
            font=small_font,

        )
        color2.deselect()
        color3 = Checkbutton(
            frame_colors,
            text = 'Blue',
            bg = "#c6def1",
            variable = self.blue,
            font=small_font,
        )
        color3.deselect()
        color4 = Checkbutton(
            frame_colors,
            text = 'Pink',
            bg = "#f2c6de",
            variable = self.pink,
            font = small_font,
        )
        color4.deselect()
        color5 = Checkbutton(
            frame_colors,
            text = 'Orange',
            bg = "#f7d9c4",
            variable = self.orange,
            font=small_font,
        )
        color5.deselect()
        color6 = Checkbutton(
            frame_colors,
            text = 'Yellow',
            bg= "#fcf6bd",
            variable = self.yellow,
            font=small_font,
        )
        color6.deselect()
        # --PACK ELEMENTs----------------------------------------------------------------
        color1.pack(padx=5, pady=5)
        color2.pack(padx=5, pady=5)
        color3.pack(padx=5, pady=5)
        color4.pack(padx=5, pady=5)
        color5.pack(padx=5, pady=5)
        color6.pack(padx=5, pady=5)
        MyNameFrame.pack(padx=10, pady=10, fill=BOTH)
        MyNameEntry.pack(padx=5, pady=5)
        MyMainFrame.pack(expand=False, fill=BOTH, padx=10, pady=10)
        MyColorFrame.pack(expand=False, fill=BOTH, padx=10, pady=10)

        # Tells the program to return to the create sticky function on click
        myCreateButton = Button(
            frame,
            text='Create',
            command=lambda: create_sticky(self),
            font = small_font,
        )
        myCreateButton.pack(padx=5, pady=5)

        settingsbutton = Button(
            frame,
            text= 'Settings',
            font = small_font,
            command= lambda: openSettings(self),
        )
        settingsbutton.pack(padx=5, pady=10, side=BOTTOM)

help(MyApp)

if __name__ == '__main__':

    w = MyApp()
    w.mainloop()
```

Tkinter Python – Json + Entry

Turma, Boa Tarde!

estou tentando criar uma janela com 3 Entry, e 2 botoes, um para abrir um Json e carregar alguns dados nestes entrys e o outro botao para salvar as alteraƧƵes no json.

o botao de salvar e as posiƧƵes dos Entry ja estao OK.

O problema Ć© para abrir e carregar os dados do Json, pois ele Ć© um array um pouco longo.
abaixo minha funĆ§Ć£o.

def abrirArquivo(self):

    data = {
            "first_name": self.txtemail.get()
            }
    with open('name.json', 'w') as f:
        json.dump(data, f, indent=4)

    filename =  filedialog.askopenfile(title = "Select file",filetypes = (("Json Files","*.json"),("all files","*.*")))
    
    self.txtemail.insert(0, filename)

python 3.7 – Como puedo saber cuando el usuario escribe determinada letra en el widget de texto de tkinter

Estoy haciendo un editor de textos con diferentes funcionalidades, una de ellas es que el usuario al presionar (“) en el widget de textos de tkinter, y tener activada una funciĆ³n el sistema escriba otras (“).

Para mas claro la funciĆ³n conciste en que cuando yo escriba unas comillas dobles (“) el sistema escriba otras (“), justamente como en programaciĆ³n al escribir un tipo de comillas, llave o parĆ©ntesis.
Esta funcion la activo y desactivo con un boton y un if.

from tkinter import *
root = Tk()


Hoja = Text(root, width=75, height=36)
Hoja.grid(row=0, column=0)
Hoja.config(bg='gray95', state='normal')

def Comellas():
#if Activacion == 'True': Activacion = 'False'
#elif Activacion == 'False':
#if Hoja == '"':
Hoja.insert(INSERT, """)

BotonComilla = Button(root, text=""-"", command=Comellas)
BotonComilla.grid(row='1', column='0')
BotonComilla.config(width='50', height='2')

mainloop()