title: Disable Exit (or [ X ]) in tkinter Window
body: <p>I am posting this because I myself have struggled with finding a clear answer on this problem . . .</p>
  
  <p>In search of trying to create a progress bar for my program, I find that it is difficult to do using tkinter. To accomplish creating a progress bar without running into the dreaded "mainloop", <a href="https://stackoverflow.com/questions/459083/how-do-you-run-your-own-code-alongside-tkinters-event-loop">I opted to make a class out of the progress bar using threads</a>. Through lots of trial an error, I found that there is not much that can be customized due to the use of multithreading (tkinter likes being in the main thread). Here are two options I have tried, followed by a third that best fits my needs:</p>
  
  <h1>Option 1: Using a callback function</h1>
  
  <p>Given the following code:</p>
  
  <pre><code>import tkinter as tk
  import tkinter.ttk as ttk
  import threading
  
  
  class ProgressbarApp(threading.Thread):
  
      def __init__(self, max_value: int):
          self.max_value = max_value
  
          self.root = None
          self.pb = None
  
          threading.Thread.__init__(self)
          self.lock = threading.Lock()    # (1)
          self.lock.acquire()             # (2)
          self.start()
  
          # (1) Makes sure progressbar is fully loaded before executing anything
          with self.lock:
              return
  
      def close(self):
          self.root.quit()
  
      def run(self):
  
          self.root = tk.Tk()
          self.root.protocol("WM_DELETE_WINDOW", self.__callback)
  
          self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate')
          self.pb['value'] = 0
          self.pb['maximum'] = self.max_value
          self.pb.pack()
  
          self.lock.release()             # (2) Will release lock when finished
          self.root.mainloop()
  
      def update(self, value: int):
          self.pb['value'] = value
  
      @staticmethod
      def __callback():
          return
  
  if __name__ == '__main__':
      interval = 100000
      my_pb = ProgressbarApp(interval)
  
      for i in range(interval):
          my_pb.update(i)
  
      my_pb.close()
  
      # Other stuff goes on . . .
  </code></pre>
  
  <p>Where</p>
  
  <pre><code>self.root.protocol("WM_DELETE_WINDOW", self.__callback)
  </code></pre>
  
  <p>Prevents the window from being closed. However, if the Exit, or [ X ], button were to be held down, the progress bar would freeze until the user releases the button. (The __callback function is constantly being called, preventing other tasks from being completed).</p>
  
  <h1>Option 2: Using root.overriderdirect(True)</h1>
  
  <p>Given the following code:</p>
  
  <pre><code>import tkinter as tk
  import tkinter.ttk as ttk
  import threading
  
  
  class ProgressbarApp(threading.Thread):
  
      def __init__(self, max_value: int):
          self.max_value = max_value
  
          self.root = None
          self.pb = None
  
          threading.Thread.__init__(self)
          self.lock = threading.Lock()    # (1)
          self.lock.acquire()             # (2)
          self.start()
  
          # (1) Makes sure progressbar is fully loaded before executing anything
          with self.lock:
              return
  
      def close(self):
          self.root.quit()
  
      def run(self):
  
          self.root = tk.Tk()
          self.root.overrideredirect(True)
  
          self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate')
          self.pb['value'] = 0
          self.pb['maximum'] = self.max_value
          self.pb.pack()
  
          self.lock.release()             # (2) Will release lock when finished
          self.root.mainloop()
  
      def update(self, value: int):
          self.pb['value'] = value
  
  if __name__ == '__main__':
      interval = 100000
      my_pb = ProgressbarApp(interval)
  
      for i in range(interval):
          my_pb.update(i)
  
      my_pb.close()
  
      # Other stuff goes on . . .
  </code></pre>
  
  <p>Where</p>
  
  <pre><code>self.root.overrideredirect(True)
  </code></pre>
  
  <p>Clears all of tkinters window options. However, the progress bar is not only in an odd location, but it also obscures the users window. The progress bar should user friendly.</p>
  
  <h1>Option 3: Using root.attributes('-disabled', True)</h1>
  
  <p>Given the following code:</p>
  
  <pre><code>import tkinter as tk
  import tkinter.ttk as ttk
  import threading
  
  
  class ProgressbarApp(threading.Thread):
  
      def __init__(self, max_value: int):
          self.max_value = max_value
  
          self.root = None
          self.pb = None
  
          threading.Thread.__init__(self)
          self.lock = threading.Lock()    # (1)
          self.lock.acquire()             # (2)
          self.start()
  
          # (1) Makes sure progressbar is fully loaded before executing anything
          with self.lock:
              return
  
      def close(self):
          self.root.quit()
  
      def run(self):
  
          self.root = tk.Tk()
          self.root.attributes('-disabled', True)
  
          self.pb = ttk.Progressbar(self.root, orient='horizontal', length=400, mode='determinate')
          self.pb['value'] = 0
          self.pb['maximum'] = self.max_value
          self.pb.pack()
  
          self.lock.release()             # (2) Will release lock when finished
          self.root.mainloop()
  
      def update(self, value: int):
          self.pb['value'] = value
  
  if __name__ == '__main__':
      interval = 100000
      my_pb = ProgressbarApp(interval)
  
      for i in range(interval):
          my_pb.update(i)
  
      my_pb.close()
  
      # Other stuff goes on . . .
  </code></pre>
  
  <p>Where</p>
  
  <pre><code>self.root.attributes('-disabled', True)
  </code></pre>
  
  <p>Prevents any user interaction with the window. This has best suited my needs for this program as it prevents the window from closing and still has a nice appearance to it. (My only minor issue with it is that the user can no longer minimize the progress bar or move it around).</p>
  
  <p>If there are any better solutions, I would love to see them. Hopefully, this has helped someone.</p>