How to restart a python program using a tkinter button?

Advertisements

I made a python gui using the customtkinter library for a project.
I’m trying to add a button that will restart the program when pressed. I’ve tried multiple ways to do it but I haven’t found a way.

from tkinter import *
from io import BytesIO
from PIL import Image, ImageTk
from CTkMessagebox import CTkMessagebox
import customtkinter as ctk
import mysql.connector, atexit, requests, time

from printer import Printer
from scale import Scale 

BUTTONS_IN_A_ROW = 3
IMG_SIZE = (50, 50)
    
class App(ctk.CTk):
  def __init__(self):
    super().__init__()
    self.lastButtonNumber = 4
    self.fade = 100
    
    self.transparent_color = self._apply_appearance_mode(self._fg_color)
    self.attributes("-transparentcolor", self.transparent_color)
    
    self.config(background=self.transparent_color)

    self.frame_top = ctk.CTkFrame(self, corner_radius=10, width=10, border_width=1)
    self.frame_top.grid(sticky="nswe")

    self.frame_top.bind("<B1-Motion>", self.move_window)
    self.frame_top.bind("<ButtonPress-1>", self.oldxyset)
    self.frame_top.bind("<Map>", self.on_configure)

    self.button_close = ctk.CTkButton(self.frame_top, corner_radius=10, width=10, height=10, hover=False,
                                       text="", fg_color="red", command=self.button_event)
    self.button_close.configure(cursor="arrow")        
    self.button_close.grid(row=0, column=3, sticky="ne", padx=(5, 10), pady=10)

    self.button_minimize = ctk.CTkButton(self.frame_top, corner_radius=10, width=10, height=10, hover=False,
                                    text="", fg_color="orange", command=self.minimize_window)
    self.button_minimize.configure(cursor="arrow")        
    self.button_minimize.grid(row=0, column=2, sticky="ne", padx=5, pady=10)

    try:
        self.idProduct = None
        self.appWidth, self.appHeight = 460, 215
        self.rowX, self.columnY = 2, 0
        self.numberOfProducts = 0

        self.spawn_x = int((self.winfo_screenwidth() - self.appWidth)/2)
        self.spawn_y = int((self.winfo_screenheight() - self.appWidth)/2)
        self.geometry(f'+{self.spawn_x}+{self.spawn_y}')

        # it contains the name of each buttons and its colour
        self.buttonsArr  = [[] for _ in range(2)]
        # it contains the name of the products and their img link 
        self.productsArr = [[] for _ in range(2)]
        
        self.scale = Scale()

        self.mydb = mysql.connector.connect(
        host = "localhost",
        user = "root",
        password = "",
        database = "projet-e-commerce_bdd"
        )
        self.mycursor = self.mydb.cursor()

        # get the id of the first perishable product from the produit table 
        self.mycursor.execute("SELECT MIN(id) AS first_id FROM produit")
        firstID = int(str(self.mycursor.fetchone()).replace("(", "").replace(",)", ""))

        # get the id of the last perishable product from the produit table 
        self.mycursor.execute("SELECT MAX(id) AS first_id FROM produit;")
        lastID = int(str(self.mycursor.fetchone()).replace("(", "").replace(",)", ""))

        for i in range(firstID, lastID + 1):
            # get the name of the products stored in the database and add it in an array
            self.mycursor.execute(f"SELECT nom_produit FROM produit WHERE id = {i} AND TYPE = 'périssable'")
            nameItem = (str(self.mycursor.fetchone())).replace("('", "").replace("',)", "")
            if nameItem != "None":
                self.productsArr[0].append(nameItem)
                self.numberOfProducts += 1

            # get the url of the picture stored in the database and add it in an array
            self.mycursor.execute(f"SELECT URL FROM produit WHERE id = {i} AND TYPE = 'périssable'")
            url = (str(self.mycursor.fetchone())).replace("('", "").replace("',)", "")
            if url != "None":
                self.productsArr[1].append(url)
            
        ctk.set_appearance_mode("Dark")
        ctk.set_default_color_theme("green")   

        self.title("EAN-13 Code Barre")

        # Weight Label
        weightLabel = ctk.CTkLabel(self.frame_top,
                                        text = "Poids")
        weightLabel.grid(row = 0, column = 0,
                            padx = (50, 20), pady = (20, 10),
                            sticky = "ew")

        # Weight Entry Field
        self.weightEntry = ctk.CTkEntry(self.frame_top,
                            state = 'disabled')
        self.weightEntry.grid(row = 0, column = 1,
                            columnspan = 1, padx = (20, 20),
                            pady = (20, 10), sticky = "ew")

        # Display the weight Button
        weightButton = ctk.CTkButton(self.frame_top, text = "Afficher", 
                                        command = self.insertWeight)
        weightButton.grid(row = 1, column = 1,
                                        columnspan = 1,
                                        padx = (20, 20), pady = (0, 20),
                                        sticky = "ew")

        # -------- custom product buttons ------------------------------------------------------
        for i in range(self.numberOfProducts):
            
            # buttons and resize images
            img_url = self.productsArr[1][i]
            img = self.load_image_from_url(img_url, IMG_SIZE)
            
            self.button = ctk.CTkButton(self.frame_top,
                                image = img, text = self.productsArr[0][i],
                                compound = "top", command = lambda productName = self.productsArr[0][i]: self.get_product_id(productName))

            # detect the mouse hovering the buttons
            self.button.bind("<Button-1>"       , lambda e, buttons = self.buttonsArr[0], button = self.button:         self.on_click(button, buttons))
            self.button.bind("<Enter>"          , lambda e, buttons = self.buttonsArr[0], button = self.button: self.buttonEnterHover(button, buttons))
            self.button.bind("<Leave>"          , lambda e, buttons = self.buttonsArr[0], button = self.button: self.buttonLeaveHover(button, buttons))
            self.button.bind("<ButtonRelease-1>", lambda e, buttons = self.buttonsArr[0], button = self.button:    self.buttonClicked(button, buttons))

            if i == 0:
                self.appHeight += 100
            else:
                if i % BUTTONS_IN_A_ROW == 0:
                    self.rowX += 1
                    self.columnY = 0
                    self.appHeight += 100                 
            
            if i == 0:
                self.button.grid(column = self.columnY, row = self.rowX, 
                        columnspan = 1, padx = (25, 0), pady = 10)
            else:
                self.button.grid(column = self.columnY, row = self.rowX, 
                        columnspan = 1, padx = (25 if (i % BUTTONS_IN_A_ROW == 0) else 0, 0), pady = 10)
            self.buttonsArr[0].append(self.button)
            self.buttonsArr[1].append("green")
            self.columnY += 1
    
        printButton = ctk.CTkButton(self.frame_top, 
                                        command = self.print_barcode,
                                        text = "Imprimez Code Barre")
        printButton.grid(row = self.rowX + 1, column = 1,
                                        columnspan = 1,
                                        padx = (20, 20), pady = (20, 5),
                                        sticky = "ew")

        quitButton = ctk.CTkButton(self.frame_top, text = "Quitter", 
                                        command = quit)
        quitButton.grid(row = self.rowX + 2, column = 1,
                                        columnspan = 1,
                                        padx = (20, 20), pady = 5,
                                        sticky = "ew")
        
        reloadButton = ctk.CTkButton(self.frame_top, 
                    image = self.load_image_from_url("https://cdn-icons-png.flaticon.com/512/560/560450.png", (20, 20)),
                    text = None,
                    width = 10, height = 10, 
                    command = self.reload)
        reloadButton.grid(row = self.rowX + 2, column = 2,
                                        padx = (50, 20), pady = 5)
        # ---------------------------------------------------------------------------------------

        self.resizable(False, False)
        atexit.register(self.exit_handler)
        self.frame_top.configure(width = self.appWidth, height = self.appHeight)

        self.iconphoto(False,
            self.load_image_from_url("https://cdn-icons-png.flaticon.com/512/2432/2432797.png", IMG_SIZE))
        self.idProduct = None

    except mysql.connector.Error as e:
        print("Error reading data from MySQL table", e)
        
        ctk.set_appearance_mode("Dark")
        ctk.set_default_color_theme("green")   
        
        self.appWidth, self.appHeight = 280, 130

        self.title("EAN-13 Code Barre")
        self.title("Error")

        weightLabel = ctk.CTkLabel(self,
                                    text="Connexion impossible BDD")
        weightLabel.grid(row = 0, column = 0,
                        padx = 65, pady = 20,
                        sticky = "ew")
        
        quitButton = ctk.CTkButton(self, text = "Ok", 
                                        command = self.destroy)
        quitButton.grid(row = 1, column = 0,
                        columnspan = 2,
                        padx = 70, pady = 5,
                        sticky = "ew")

        self.grab_set()
        self.frame_top.configure(width = self.appWidth, height = self.appHeight)
        self.resizable(False, False)
        self.iconphoto(False,
            self.load_image_from_url("https://cdn-icons-png.flaticon.com/512/2432/2432797.png", IMG_SIZE))

def oldxyset(self, event):
        self.oldx = event.x
        self.oldy = event.y

def on_configure(self, event):
    if self.wm_state() != "zoomed":
        self.overrideredirect(1)

def minimize_window(self):
    self.overrideredirect(0)
    self.iconify()
    
def move_window(self, event):
    self.overrideredirect(1)
    self.y = event.y_root - self.oldy
    self.x = event.x_root - self.oldx
    self.geometry(f'+{self.x}+{self.y}')

def button_event(self, event=None):
    try:
        self.button_1.configure(state="disabled")
        self.button_2.configure(state="disabled")
        self.button_3.configure(state="disabled")
    except AttributeError:
        pass

    if self.fade:
        self.fade_out()
    self.grab_release()
    self.destroy()
    self.event = event

def fade_out(self):
    for i in range(100,0,-10):
        if not self.winfo_exists():
            break
        self.attributes("-alpha", i/100)
        self.update()
        time.sleep(1/self.fade)

# ------------- Custom Button ------------------------

def buttonLeaveHover(self, button, buttons):  
    for b in buttons:
        checkButtonName = str(b)

        if b == button:
            if self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] == "blue":
                b.configure(fg_color = "#1f6aa5") # blue
                
def buttonClicked(self, button, buttons):
    for b in buttons:
        checkButtonName = str(b)
        
        if b == button:
            if self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] == "blue":
                b.configure(fg_color = "#144870") # dark blue
        else:
            if self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] == "blue":
                b.configure(fg_color = "#1f6aa5") # blue

  def buttonEnterHover(self, button, buttons):
    for b in buttons:
        checkButtonName = str(b)
        
        if b == button:
            if self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] == "blue":
                b.configure(fg_color = "#144870") # dark blue
        else:
            if self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] == "blue":
                b.configure(fg_color = "#1f6aa5") # blue

  def on_click(self, button, buttons):
    for b in buttons:
        checkButtonName = str(b)
        
        if b == button:
            b.configure(fg_color = "#1f6aa5") # blue
            self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] = "blue"
            id(self.productsArr[0][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber])

        else:
            b.configure(fg_color="#2fa572") # green
            self.buttonsArr[1][0 if (checkButtonName == ".!ctkframe.!ctkbutton") else int(checkButtonName.replace(".!ctkframe.!ctkbutton", "")) - self.lastButtonNumber] = "green"

# ----------------------------------------------------

  def insertWeight(self):
    ...

  def load_image_from_url(self, url, new_size):
    ...

  def generate_barcode(self, product_id, product_weight):
    ...

  def get_product_id(self, product_name):
    ...

  def print_barcode(self):
    printer = Printer()
    printer.send_command(self.generate_barcode(self.idProduct, self.scale.getWeight()))

  def quit(self):
    ...

  # execute this function when the window is closed using the red close button
  def exit_handler(self):
    self.mydb.close()

  def reload(self): # THIS IS THE FUNCTION THAT ISN'T WORKING
    self.destroy()
    self.__init__()

if __name__ == "__main__":
    app = App()
    app.mainloop()

The function to restart the program is at the very bottom

I’d like some assistance with my problem.

Thanks,

>Solution :

You can use the subprocess module to run your program again.

Here’s an example code snippet that shows how to do it:

import subprocess

import sys
import customtkinter as tk

def restart_program():
    # restart the current program
    python = sys.executable
    subprocess.call([python, __file__])
    sys.exit()

root = tk.Tk()

# create a button to restart the program
restart_button = tk.Button(root, text="Restart", command=restart_program)
restart_button.pack()

root.mainloop()

In the above code, the restart_program() function is called when the restart button is clicked. This function uses the subprocess.call() method to run the current Python program again, and then exits the current process using sys.exit().

Leave a ReplyCancel reply