Me95
Me95

Reputation: 45

How do I run multiple tkinter windows simultaneously in Python?

so I am making an application that takes notes(similar to Windows Sticky Notes). Since I need to display multiple notes simultaneously, I have used a class which inherits from Thread and also creates a tkinter window. The problem is that my windows do not open simultaneously. The second opens up after the first is closed. Here is the code. What am I doing wrong? Is there another method that I can use? [For now I am just displaying notes I have hard-coded.]

from tkinter import *
from threading import Thread

class Note(Thread):
nid = 0
title = ""
message = ""

    def __init__(self, nid, title, message):
      Thread.__init__(self)
      self.nid = nid
      self.title = title
      self.message = message


    def display_note_gui(self): 
      '''Tkinter to create a note gui window with parameters '''    
      window = Tk()
      window.title(self.title)
      window.geometry("200x200")
      window.configure(background="#BAD0EF")

      title = Entry(relief=FLAT, bg="#BAD0EF", bd=0)
      title.pack(side=TOP)
      scrollBar = Scrollbar(window, takefocus=0, width=20)
      textArea = Text(window, height=4, width=1000, bg="#BAD0EF", font=("Times", "14"))
      scrollBar.pack(side=RIGHT, fill=Y)
      textArea.pack(side=LEFT, fill=Y)
      scrollBar.config(command=textArea.yview)
      textArea.config(yscrollcommand=scrollBar.set)
      textArea.insert(END, self.message)
      window.mainloop()

    def run(self):
      self.display_note_gui()

new_note1 = Note(0, "Hello", "Hi, how are you?")
new_note1.start()
new_note1.join()


new_note2 = Note(1, "2", "How's everyone else?")
new_note2.start()
new_note2.join()

Upvotes: 2

Views: 7601

Answers (3)

Problem statement and the Solutions are wonderful for learning. These kinds of examples are needed for Teaching Multithreaded GUI with python's tkinter module.

I faced a similar issue when I created an App with one thread per GUI idea (common idea, I think). BTW, the error is “RuntimeError: main thread is not in main loop”.

There is another solution I came across using PySimpleGUI package. But that requires a big shift in architecture. I recommend to look at the approach using PySimpleGUI package, It gives some ideas for abstraction on the UI elements.

Upvotes: 0

Tadhg McDonald-Jensen
Tadhg McDonald-Jensen

Reputation: 21453

Instead of subclassing Thread just subclass Toplevel, a top level in tkinter is a separate window in the same application which sounds like exactly what you are trying to accomplish:

from tkinter import *
#from threading import Thread #no longer needed

class Note(Toplevel):
    nid = 0
    #title = "" #this would block the method to override the current title
    message = ""

    def __init__(self, master, nid, title, message):
      Toplevel.__init__(self,master)
      self.nid = nid 
      self.title(title) #since toplevel widgets define a method called title you can't store it as an attribute
      self.message = message
      self.display_note_gui() #maybe just leave that code part of the __init__?


    def display_note_gui(self): 
      '''Tkinter to create a note gui window with parameters '''    
      #no window, just self
      self.geometry("200x200")
      self.configure(background="#BAD0EF")
      #pass self as the parent to all the child widgets instead of window
      title = Entry(self,relief=FLAT, bg="#BAD0EF", bd=0)
      title.pack(side=TOP)
      scrollBar = Scrollbar(self, takefocus=0, width=20)
      textArea = Text(self, height=4, width=1000, bg="#BAD0EF", font=("Times", "14"))
      scrollBar.pack(side=RIGHT, fill=Y)
      textArea.pack(side=LEFT, fill=Y)
      scrollBar.config(command=textArea.yview)
      textArea.config(yscrollcommand=scrollBar.set)
      textArea.insert(END, self.message)
      #self.mainloop() #leave this to the root window

    def run(self):
      self.display_note_gui()

root = Tk()
root.withdraw() #hide the root so that only the notes will be visible

new_note1 = Note(root, 0, "Hello", "Hi, how are you?")
#new_note1.start()
#new_note1.join()


new_note2 = Note(root, 1, "2", "How's everyone else?")
#new_note2.start()
#new_note2.join()

root.mainloop() #still call mainloop on the root

note that instead of storing the title as an attribute you can call self.title() to get the current title of the window and self.title("new title") to change it.

Upvotes: 2

Bryan Oakley
Bryan Oakley

Reputation: 385940

If all you need is multiple note windows then you definitely don't need threads. Tkinter is quite capable of managing dozens or hundreds of open windows.

Just create instances of Toplevel for every window except the root window. Here's a somewhat over-engineered example:

import Tkinter as tk

class Notepad(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)
        self.text = tk.Text(self, wrap="word")
        self.vsb = tk.Scrollbar(self, orient="vertical", comman=self.text.yview)
        self.text.configure(yscrollcommand=self.vsb.set)
        self.vsb.pack(side="right", fill="y")
        self.text.pack(side="left", fill="both", expand=True)

def main():
    root = tk.Tk()
    Notepad(root).pack(fill="both", expand=True)
    for i in range(5):
        top = tk.Toplevel(root)
        Notepad(top).pack(fill="both", expand=True)

    root.mainloop()

if __name__ == "__main__":
    main()

Upvotes: 2

Related Questions