So there are several red flags right away… (multiple mainloop
calls, classes in classes, calling root
inside listbox
)
It seems like you are missing one of the main concepts of many gui frameworks, and that is that 1 window is not 1 application. In general the “root” of the application is the controller in the background handling events from all windows the program has spawned. The processing of those events happens in the mainloop
function. When you define your Notepad
class, at the very end you create a class attribute by calling root2 = listbox()
, assign it some properties, and then run its mainloop
. This happens at the time of definition because it’s not inside a method, and blocks you from ever making it past this line.
I also notice that you are trying to use root
as if it’s a global inside listbox.selection
. This causes a NameError
as you have not used the global
keyword. It is far more common to pass “self” to child objects at the time of creation rather than to try to access the parent via inheritance. I think this may also be related to why you defined listbox
inside Notebook
. This is technically valid, but very uncommon. Putting a class inside another class does not automatically make the inner class inherit anything from the parent class, and only makes it part of the namespace of the parent class. In the example of GUI’s it is more common to separate different parts of a gui by creating separate modules which are imported where needed. This way files are smaller and easier to read for large applications. In this instance, I wouldn’t bother, but I would lake listbox
its own class.
Here’s what I would do to fix your situations (please read through comments I made as well):
from tkinter import *
from tkinter import messagebox as msg
from tkinter.filedialog import askopenfilename, asksaveasfilename
from functools import partial
import os
class Notepad(Tk):
"""Class for notepad """
# Command for menus-
def newFile(self):
self.crfile = None
self.txtarea.delete(1.0, END)
def saveFile(self):
if self.crfile == None:
self.crfile = asksaveasfilename(initialfile="Untitled.txt",
defaultextension=".txt", filetypes=[("All Files", "*.*"), ("Text Documents", "*.txt")])
if self.crfile == "":
self.crfile = None
else:
# Save as new file
with open(self.crfile, "w") as f:
f.write(self.txtarea.get(1.0, END))
self.title(os.path.basename(
self.crfile + "- Tanish's Notepad"))
else:
# Save the file
with open(self.crfile, "w") as f:
f.write(self.txtarea.get(1.0, END))
self.title(os.path.basename(
self.crfile + "- Tanish's Notepad"))
def openFile(self):
self.crfile = askopenfilename(defaultextension=".txt", filetypes=[
("All Files", "*.*"), ("Text Documents", "*.txt")])
if self.crfile == "":
self.crfile = None
else:
self.title(os.path.basename(self.crfile) +
"-" + "Tanish's Notepad")
self.txtarea.delete(1.0, END)
with open(self.crfile, "r") as f:
self.txtarea.insert(1.0, f.read())
# commands for edit menu-
def copy(self):
self.txtarea.event_generate(("<<Copy>>"))
def paste(self):
self.txtarea.event_generate(("<<Paste>>"))
def cut(self):
self.txtarea.event_generate(("<<Cut>>"))
# commands for help menu-
def About(self):
msg.showinfo("Tanish's Notepad -Help",
"This is a simple notepad made by Tanish Sarmah")
def menus(self):
# TODO Menus
mainmenu = Menu(self, tearoff="0")
# file menu
file = Menu(mainmenu, tearoff=0)
# Creating new file
file.add_command(label="New", command=self.newFile)
# Saving the current file
file.add_command(label="Save", command=self.saveFile)
# Opening a Pre-Existing file
file.add_command(label="Open", command=self.openFile)
file.add_separator()
file.add_command(label="Exit", command=self.destroy) # Exit command
mainmenu.add_cascade(menu=file, label="File")
self.config(menu=mainmenu)
# Edit menu---
edit = Menu(mainmenu, tearoff=0)
# To cut any part of the Textarea
edit.add_command(label="Cut", command=self.cut)
# To copy any part of the Textarea
edit.add_command(label="Copy", command=self.copy)
# To paste any cut or copied Text.
edit.add_command(label="Paste", command=self.paste)
mainmenu.add_cascade(menu=edit, label="Edit")
self.config(menu=mainmenu)
# Help menu---
helpm = Menu(mainmenu, tearoff=0)
# Displays about the notepad
helpm.add_command(label="About", command=self.About)
mainmenu.add_cascade(menu=helpm, label="Help")
self.config(menu=mainmenu)
# Window menu
win = Menu(mainmenu, tearoff=0)
win.add_command(label="Colour", command=partial(listbox, self)) #we'll use a partial functino to bind self as the parent here. listbx is now called in __init__
mainmenu.add_cascade(menu=win, label="Window")
self.config(menu=mainmenu)
def textwid(self):
# TODO Text widget---
self.txtarea = Text(self)
self.crfile = None
self.txtarea.pack(expand=True, fill=BOTH)
# TODO Scrollbar---
self.scbar = Scrollbar(self.txtarea)
self.txtarea.config(yscrollcommand=self.scbar.set)
self.scbar.config(command=self.txtarea.yview)
self.scbar.pack(side=RIGHT, fill=BOTH)
class listbox(Toplevel):
#listbox moved to its own class to prevent confusion of inheritance
#child windows should not inherit from application root, rather from "Toplevel"
def __init__(self, parent):
#moved window setup to __init__ method as we are creating a new window each time we want this menu
#calling mainloop() is not necessary, as the "mainloop" from if __name__ == "__main__": is now running
super().__init__()
self.parent = parent #we need access to the parent so we chan change its color
self.geometry("300x200")
self.title("Window-Colour")
self.listbx()
def listbx(self):
self.lbx = Listbox(self)
colours = ["blue", "red", "yellow",
"grey", "green", "light blue", "black"]
for c in colours:
self.lbx.insert(END, c)
self.lbx.pack()
self.lbx.bind("<<ListboxSelect>>", self.selection)
def selection(self, event):
"""Selection of colour from the lisbox above to apply changes"""
colour = self.lbx.get(ANCHOR)
msgselect = msg.askokcancel(
"Window-Colour", "Confirm the colour to apply to the window")
if msgselect == True:
self.parent.configure(bg=colour) #call the parent without a global because we inherited it earlier
self.destroy()
else:
self.destroy()
if __name__ == "__main__":
# Basic tkinter startup
root = Notepad()
root.geometry("800x700")
root.title("Tanish's Notepad-Untitled-1")
root.menus()
root.textwid()
# root.wm_iconbitmap("noteicon.ico") #I didn't have this file, so I commented it out
root.mainloop()
2
solved Why the secondary GUI opens before the primary GUI?