Как собрать проект на Python в один исполняемый .exe файл с Tkinter?

Хочу собрать проект в один файл, чтобы можно было скачать и на любом пк запустить ?

from tkinter import *
from tkinter import messagebox
import random
from collections import deque


class Minesweeper:

def __init__(self, master):

    # import images
    self.tile_plain = PhotoImage(file="images/tile_plain.png")
    self.tile_clicked = PhotoImage(file="images/tile_clicked.png")
    self.tile_mine = PhotoImage(file="images/tile_mine.png")
    self.tile_flag = PhotoImage(file="images/tile_flag.png")
    self.tile_wrong = PhotoImage(file="images/tile_wrong.png")
    self.tile_no = []
    for x in range(1, 9):
        self.tile_no.append(PhotoImage(file="images/tile_"+str(x)+".png"))

    # set up frame
    frame = Frame(master)
    frame.pack()

    # show "Minesweeper" at the top
    self.label1 = Label(frame, text="САПЕЕЕР!!!")
    self.label1.grid(row=0, column=0, columnspan=10)

    # create flag and clicked tile variables
    self.flags = 0
    self.correct_flags = 0
    self.clicked = 0

    # create buttons
    self.buttons = dict({})
    self.mines = 0
    x_coord = 1
    y_coord = 0
    for x in range(0, 100):
        mine = 0
        # tile image changeable for debug reasons:
        gfx = self.tile_plain
        # currently random amount of mines
        if random.uniform(0.0, 0.5) < 0.1:
            mine = 1
            self.mines += 1
        # 0 = Button widget
        # 1 = if a mine y/n (1/0)
        # 2 = state (0 = unclicked, 1 = clicked, 2 = flagged)
        # 3 = button id
        # 4 = [x, y] coordinates in the grid
        # 5 = nearby mines, 0 by default, calculated after placement in grid
        self.buttons[x] = [Button(frame, image=gfx),
                           mine,
                           0,
                           x,
                           [x_coord, y_coord],
                           0]
        self.buttons[x][0].bind('<Button-1>', self.lclicked_wrapper(x))
        self.buttons[x][0].bind('<Button-3>', self.rclicked_wrapper(x))

        # calculate coords:
        y_coord += 1
        if y_coord == 10:
            y_coord = 0
            x_coord += 1

    # lay buttons in grid
    for key in self.buttons:
        self.buttons[key][0].grid(
            row=self.buttons[key][4][0], column=self.buttons[key][4][1])

    # find nearby mines and display number on tile
    for key in self.buttons:
        nearby_mines = 0
        if self.check_for_mines(key-9):
            nearby_mines += 1
        if self.check_for_mines(key-10):
            nearby_mines += 1
        if self.check_for_mines(key-11):
            nearby_mines += 1
        if self.check_for_mines(key-1):
            nearby_mines += 1
        if self.check_for_mines(key+1):
            nearby_mines += 1
        if self.check_for_mines(key+9):
            nearby_mines += 1
        if self.check_for_mines(key+10):
            nearby_mines += 1
        if self.check_for_mines(key+11):
            nearby_mines += 1
        # store mine count in button data list
        self.buttons[key][5] = nearby_mines
        #if self.buttons[key][1] != 1:
        #    if nearby_mines != 0:
        #        self.buttons[key][0].config(image = self.tile_no[nearby_mines-1])

    #add mine and count at the end
    self.label2 = Label(frame, text="Мины: "+str(self.mines))
    self.label2.grid(row=11, column=0, columnspan=5)

    self.label3 = Label(frame, text="Флажки: "+str(self.flags))
    self.label3.grid(row=11, column=4, columnspan=5)

## End of __init__

def check_for_mines(self, key):
    try:
        if self.buttons[key][1] == 1:
            return True
    except KeyError:
        pass

def lclicked_wrapper(self, x):
    return lambda Button: self.lclicked(self.buttons[x])

def rclicked_wrapper(self, x):
    return lambda Button: self.rclicked(self.buttons[x])

def lclicked(self, button_data):
    if button_data[1] == 1:  # if a mine
        # show all mines and check for flags
        for key in self.buttons:
            if self.buttons[key][1] != 1 and self.buttons[key][2] == 2:
                self.buttons[key][0].config(image=self.tile_wrong)
            if self.buttons[key][1] == 1 and self.buttons[key][2] != 2:
                self.buttons[key][0].config(image=self.tile_mine)
        # end game
        self.gameover()
    else:
        #change image
        if button_data[5] == 0:
            button_data[0].config(image=self.tile_clicked)
            self.clear_empty_tiles(button_data[3])
        else:
            button_data[0].config(image=self.tile_no[button_data[5]-1])
        # if not already set as clicked, change state and count
        if button_data[2] != 1:
            button_data[2] = 1
            self.clicked += 1
        if self.clicked == 100 - self.mines:
            self.victory()

def rclicked(self, button_data):
    # if not clicked
    if button_data[2] == 0:
        button_data[0].config(image=self.tile_flag)
        button_data[2] = 2
        button_data[0].unbind('<Button-1>')
        # if a mine
        if button_data[1] == 1:
            self.correct_flags += 1
        self.flags += 1
        self.update_flags()
    # if flagged, unflag
    elif button_data[2] == 2:
        button_data[0].config(image=self.tile_plain)
        button_data[2] = 0
        button_data[0].bind(
            '<Button-1>', self.lclicked_wrapper(button_data[3]))
        # if a mine
        if button_data[1] == 1:
            self.correct_flags -= 1
        self.flags -= 1
        self.update_flags()

def check_tile(self, key, queue):
    try:
        if self.buttons[key][2] == 0:
            if self.buttons[key][5] == 0:
                self.buttons[key][0].config(image=self.tile_clicked)
                queue.append(key)
            else:
                self.buttons[key][0].config(
                    image=self.tile_no[self.buttons[key][5]-1])
            self.buttons[key][2] = 1
            self.clicked += 1
    except KeyError:
        pass

def clear_empty_tiles(self, main_key):
    queue = deque([main_key])

    while len(queue) != 0:
        key = queue.popleft()
        self.check_tile(key-9, queue)  
        self.check_tile(key-10, queue)  
        self.check_tile(key-11, queue)  
        self.check_tile(key-1, queue) 
        self.check_tile(key+1, queue)  
        self.check_tile(key+9, queue) 
        self.check_tile(key+10, queue)  
        self.check_tile(key+11, queue)  


def gameover(self):
    messagebox.showinfo("ЛОХ)", "НУ ТЫ ПЫТАВСЯ!")
    global root
    root.destroy()

def victory(self):
    messagebox.showinfo("кАнец игры", "Ты Пабедил!")
    global root
    root.destroy()

def update_flags(self):
    self.label3.config(text="Flags: "+str(self.flags))




def main():
    global root
    root = Tk()
    # ну типа загаловак
    root.title("Сапер")
    # акно праграммы
    minesweeper = Minesweeper(root)
    # а теперь залупим
    root.mainloop()


if __name__ == "__main__":
  main()

Расположение файлов в папке


Ответы (0 шт):